Skip to content

Commit

Permalink
rebased
Browse files Browse the repository at this point in the history
add tokenCache to mintbuy example
linter fixes
wip fixes for token cache
token cache wip
Adding token cache
storing addr as value
go mod
Merge branch 'dev' into nkoljanin/erc20_support
Package import
fixes
renaming
ERC20 Token Payment Support
Changed host for camino also to messenger.chain4travel.com in example config (#55)

Co-authored-by: Noctunus <[email protected]>
Fixing ordering of processing in auto-generation of services to make sure that on every system we get the same result. (#54)

Co-authored-by: Noctunus <[email protected]>
Add mint v1 support (#53)
wip - testing
ERC20 token support
  • Loading branch information
nkoljanin authored and evlekht committed Oct 18, 2024
1 parent bae3ff2 commit 18b6e24
Show file tree
Hide file tree
Showing 11 changed files with 332 additions and 161 deletions.
141 changes: 51 additions & 90 deletions examples/booking/mintnbuy.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,18 @@ import (
typesv2 "buf.build/gen/go/chain4travel/camino-messenger-protocol/protocolbuffers/go/cmp/types/v2"
"google.golang.org/protobuf/types/known/emptypb"

"github.com/chain4travel/camino-messenger-contracts/go/contracts/erc20"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"go.uber.org/zap"

"github.com/chain4travel/camino-messenger-bot/pkg/booking"
"github.com/chain4travel/camino-messenger-bot/pkg/cache"
"github.com/chain4travel/camino-messenger-contracts/go/contracts/bookingtoken"
)

var zeroAddress = common.HexToAddress("0x0000000000000000000000000000000000000000")

// https://columbus.caminoscan.com/token/0x5b1c852dad36854B0dFFF61d2C13F108D8E01975
// https://caminoscan.com/token/0x026816DF82F78882DaC9370a35c497C254Ebd88E
var eurshToken = common.HexToAddress("0x5b1c852dad36854B0dFFF61d2C13F108D8E01975")

var polygonToken = common.HexToAddress("0x0000000000000000000000000000000000001010")

// https://polygonscan.com/token/0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6
var wBtcToken = common.HexToAddress("0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6") // not on Camino

// Simple usage example for the BookingService
func main() {
logger, err := zap.NewDevelopment()
Expand All @@ -42,6 +33,11 @@ func main() {

sugar.Info("Starting Mint & Buy Example...")

tokenCache, err := cache.NewTokenCache(20)
if err != nil {
sugar.Errorf("Failed to create token cache: %v", err)
}

cmAccountAddrString := flag.String("cmaccount", "", "CMAccount Address. Ex: 0x....")
// Take private key from command line
pkString := flag.String("pk", "", "Private Key without the 0x notation")
Expand Down Expand Up @@ -72,7 +68,7 @@ func main() {
}

sugar.Info("Creating Booking Service...")
bs, err := booking.NewService(cmAccountAddr, pk, client, sugar)
bs, err := booking.NewService(&cmAccountAddr, pk, client, sugar)
if err != nil {
sugar.Fatalf("Failed to create Booking Service: %v", err)
}
Expand All @@ -88,29 +84,16 @@ func main() {
// expiration timestamp
expiration := big.NewInt(time.Now().Add(time.Hour).Unix())

var zeroAddress = common.HexToAddress("0x0000000000000000000000000000000000000000")
// https://columbus.caminoscan.com/token/0x5b1c852dad36854B0dFFF61d2C13F108D8E01975
// var eurshToken = common.HexToAddress("0x5b1c852dad36854B0dFFF61d2C13F108D8E01975")
var testToken = common.HexToAddress("0x53A0b6A344C8068B211d47f177F0245F5A99eb2d")

var paymentToken common.Address = zeroAddress
var bigIntPrice *big.Int
var priceBigInt *big.Int
var price *typesv2.Price
// https://polygonscan.com/unitconverter
// ### Simple Price type message Price
//
// Value of the price, this should be an integer converted to string.
//
// This field is a string intentionally. Because the currency can be a crypto
// currency, we need a reliable way to represent big integers as most of the crypto
// currencies have 18 decimals precision.
//
// Definition of the price message: The combination of "value" and "decimals" fields
// express always the value of the currency, not of the fraction of the currency [
// ETH not wei, CAM and not aCAM, BTC and not Satoshi, EUR not EUR-Cents ] Be aware
// that partners should not do rounding with crypto currencies.
//
// price
// Example implementations: off-chain payment of 100 € or 100 $:
// value=10000
// decimals=2
// iso_currency=EUR or USD

// Example prices for ISO Currency
priceEUR := &typesv2.Price{
Value: "10000",
Decimals: 2,
Expand All @@ -121,57 +104,20 @@ func main() {
},
}

// On-chain payment of 100.65 EURSH
// value=10065
// decimals=2
// contract_address=0x...
// this currency has 5 decimals on Columbus and conclusively to create the
// transaction value, 10065 must be divided by 10^2 = 100.65 EURSH and created in
// its smallest fraction by multiplying 100.65 EURSH * 10^5 => 10065000 (example
// conversion to bigint without losing accuracy: bigint(10065) * 10^(5-2))

// Example prices for Token Currency
priceEURSH := &typesv2.Price{
Value: "10065",
Decimals: 2,
Currency: &typesv2.Currency{
Currency: &typesv2.Currency_TokenCurrency{
TokenCurrency: &typesv2.TokenCurrency{
ContractAddress: eurshToken.Hex(),
ContractAddress: testToken.Hex(),
},
},
},
}

// TODO: call decimals on eurshToken (should get 5)

// On-chain payment of 0.0065 BTC
// value=65
// decimals=4
// contract_address=0x... Using
//
// the contract address, we get the decimals decimals and the currency name or
// abbreviation: 8 decimals & WBTC Because we see 4 decimals specified in the
// message we divide 65 by 10^4 == 0.0065 WBTC (for showing in the front-end UIs)
//
// This currency has 8 decimals on-chain and conclusively to use the value of
// 0.0065 for on-chain operations must be converted to big integer as bigint(65) *
// 10^(8-4) == 650000

priceBTC := &typesv2.Price{
Value: "65",
Decimals: 4,
Currency: &typesv2.Currency{
Currency: &typesv2.Currency_TokenCurrency{
TokenCurrency: &typesv2.TokenCurrency{},
},
},
}
// On-chain payment of 1 nCAM value=1 decimals=9 this currency has denominator 18 on
//
// Columbus and conclusively to mint the value of 1 nCam must be divided by 10^9 =
// 0.000000001 CAM and minted in its smallest fraction by multiplying 0.000000001 *
// 10^18 => 1000000000 aCAM

// Example prices for Native Token
priceCAM := &typesv2.Price{
Value: "1",
Decimals: 9,
Expand All @@ -182,36 +128,51 @@ func main() {
},
}

sugar.Infof("%v %v %v %v", priceEUR, priceEURSH, priceBTC, priceCAM)
sugar.Infof("%v %v %v %v", priceEUR, priceEURSH, priceCAM)
sugar.Infof("%v", price)

// bigIntPrice, _ = bs.ConvertPriceToBigInt(*priceEURSH, int32(5))
// bigIntPrice, _ = bs.ConvertPriceToBigInt(*priceCAM, int32(18))

paymentToken = zeroAddress
bigIntPrice = big.NewInt(0)
priceBigInt = big.NewInt(0)
// price = priceEURSH
// price = priceBTC // case of unsupported token?
price = priceCAM
price = priceEURSH

switch price.Currency.Currency.(type) {
switch currency := price.Currency.Currency.(type) {
case *typesv2.Currency_NativeToken:
bigIntPrice, err = bs.ConvertPriceToBigInt(price.Value, price.Decimals, int32(18)) // CAM uses 18 decimals
priceBigInt, err = bs.ConvertPriceToBigInt(price.Value, price.Decimals, int32(18)) // CAM uses 18 decimals
if err != nil {
sugar.Errorf("Failed to convert price to big.Int: %v", err)
return
}
sugar.Infof("Converted the price big.Int: %v", bigIntPrice)
sugar.Infof("Converted the price big.Int: %v", priceBigInt)
paymentToken = zeroAddress
case *typesv2.Currency_TokenCurrency:
// Add logic to handle TokenCurrency
// if contract address is zeroAddress, then it is native token
sugar.Infof("TokenCurrency not supported yet")
return
if !common.IsHexAddress(currency.TokenCurrency.ContractAddress) {
sugar.Errorf("invalid contract address: %v", currency.TokenCurrency.ContractAddress)
}
contractAddress := common.HexToAddress(currency.TokenCurrency.ContractAddress)
tokenDecimals, found := tokenCache.Get(contractAddress)
if !found {
// Fetch decimals from the ERC20 contract
token, err := erc20.NewErc20(contractAddress, client)
if err != nil {
sugar.Errorf("failed to instantiate ERC20 contract: %v", err)
}
decimals, err := token.Decimals(&bind.CallOpts{})
if err != nil {
sugar.Errorf("failed to fetch token decimals: %w", err)
}
// Cache decimals
tokenCache.Add(contractAddress, int32(decimals))
tokenDecimals = int32(decimals)
}
priceBigInt, err = bs.ConvertPriceToBigInt(price.Value, price.Decimals, tokenDecimals)
if err != nil {
sugar.Errorf("Failed to convert price to big.Int: %v", err)
}
paymentToken = contractAddress
case *typesv2.Currency_IsoCurrency:
// Add logic to handle IsoCurrency
sugar.Infof("IsoCurrency not supported yet")
return
priceBigInt = big.NewInt(0)
paymentToken = zeroAddress
}

// Mint a new booking token
Expand All @@ -228,7 +189,7 @@ func main() {
cmAccountAddr, // reservedFor address
tokenURI,
expiration,
bigIntPrice,
priceBigInt,
paymentToken,
)
if err != nil {
Expand Down
48 changes: 39 additions & 9 deletions examples/rpc/partner-plugin/handlers/mint_v1.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,44 @@ var _ bookv1grpc.MintServiceServer = (*MintServiceV1Server)(nil)

type MintServiceV1Server struct{}

type PaymentConfigMintV1 struct {
NativeToken *typesv1.Price
Token *typesv1.Price
Offchain *typesv1.Price
}

var priceConfigMintV1 = PaymentConfigMintV1{
NativeToken: &typesv1.Price{
Value: "1",
Decimals: 9,
Currency: &typesv1.Currency{
Currency: &typesv1.Currency_NativeToken{
NativeToken: &emptypb.Empty{},
},
},
},
Offchain: &typesv1.Price{
Value: "1",
Decimals: 9,
Currency: &typesv1.Currency{
Currency: &typesv1.Currency_IsoCurrency{
IsoCurrency: typesv1.IsoCurrency_ISO_CURRENCY_EUR, // EUR
},
},
},
Token: &typesv1.Price{
Value: "100",
Decimals: 2,
Currency: &typesv1.Currency{
Currency: &typesv1.Currency_TokenCurrency{
TokenCurrency: &typesv1.TokenCurrency{
ContractAddress: "0x87a131801978d1ffBa53a6D4180cBef3F8C9e760",
},
},
},
},
}

func (*MintServiceV1Server) Mint(ctx context.Context, _ *bookv1.MintRequest) (*bookv1.MintResponse, error) {
md := metadata.Metadata{}

Expand All @@ -35,15 +73,7 @@ func (*MintServiceV1Server) Mint(ctx context.Context, _ *bookv1.MintRequest) (*b
BuyableUntil: &timestamppb.Timestamp{
Seconds: time.Now().Add(5 * time.Minute).Unix(),
},
Price: &typesv1.Price{
Value: "1",
Decimals: 9,
Currency: &typesv1.Currency{
Currency: &typesv1.Currency_NativeToken{
NativeToken: &emptypb.Empty{},
},
},
},
Price: priceConfigMintV1.Token, // change to Token or Offchain to test different scenarios
}

log.Printf("CMAccount %s received request from CMAccount %s", md.Recipient, md.Sender)
Expand Down
64 changes: 39 additions & 25 deletions examples/rpc/partner-plugin/handlers/mint_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,44 @@ var _ bookv2grpc.MintServiceServer = (*MintServiceV2Server)(nil)

type MintServiceV2Server struct{}

type PaymentConfigMintV2 struct {
NativeToken *typesv2.Price
Token *typesv2.Price
Offchain *typesv2.Price
}

var priceConfigMintV2 = PaymentConfigMintV2{
NativeToken: &typesv2.Price{
Value: "1",
Decimals: 9,
Currency: &typesv2.Currency{
Currency: &typesv2.Currency_NativeToken{
NativeToken: &emptypb.Empty{},
},
},
},
Offchain: &typesv2.Price{
Value: "1",
Decimals: 9,
Currency: &typesv2.Currency{
Currency: &typesv2.Currency_IsoCurrency{
IsoCurrency: typesv2.IsoCurrency_ISO_CURRENCY_EUR, // EUR
},
},
},
Token: &typesv2.Price{
Value: "100",
Decimals: 2,
Currency: &typesv2.Currency{
Currency: &typesv2.Currency_TokenCurrency{
TokenCurrency: &typesv2.TokenCurrency{
ContractAddress: "0x87a131801978d1ffBa53a6D4180cBef3F8C9e760",
},
},
},
},
}

func (*MintServiceV2Server) Mint(ctx context.Context, _ *bookv2.MintRequest) (*bookv2.MintResponse, error) {
md := metadata.Metadata{}

Expand All @@ -31,36 +69,12 @@ func (*MintServiceV2Server) Mint(ctx context.Context, _ *bookv2.MintRequest) (*b

log.Printf("Responding to request: %s (MintV2)", md.RequestID)

// On-chain payment of 1 nCAM value=1 decimals=9 this currency has denominator 18 on
//
// Columbus and conclusively to mint the value of 1 nCam must be divided by 10^9 =
// 0.000000001 CAM and minted in its smallest fraction by multiplying 0.000000001 *
// 10^18 => 1000000000 aCAM
response := bookv2.MintResponse{
MintId: &typesv1.UUID{Value: md.RequestID},
BuyableUntil: &timestamppb.Timestamp{
Seconds: time.Now().Add(5 * time.Minute).Unix(),
},
// NATIVE TOKEN EXAMPLE:
Price: &typesv2.Price{
Value: "12345",
Decimals: 9,
Currency: &typesv2.Currency{
Currency: &typesv2.Currency_NativeToken{
NativeToken: &emptypb.Empty{},
},
},
},
// ISO CURRENCY EXAMPLE:
// Price: &typesv2.Price{
// Value: "10000",
// Decimals: 2,
// Currency: &typesv2.Currency{
// Currency: &typesv2.Currency_IsoCurrency{
// IsoCurrency: typesv2.IsoCurrency_ISO_CURRENCY_EUR,
// },
// },
// },
Price: priceConfigMintV2.NativeToken, // change to Token or Offchain to test different scenarios
BookingTokenId: uint64(123456),
ValidationId: &typesv1.UUID{Value: "123456"},
BookingTokenUri: "https://example.com/booking-token",
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ go 1.23.1
require (
buf.build/gen/go/chain4travel/camino-messenger-protocol/grpc/go v1.5.1-20240924170438-a97744087df6.1
buf.build/gen/go/chain4travel/camino-messenger-protocol/protocolbuffers/go v1.34.2-20240924170438-a97744087df6.2
github.com/chain4travel/camino-messenger-contracts/go/contracts v0.0.0-20240918114804-fbc75cbe60fc
github.com/chain4travel/camino-messenger-contracts/go/contracts v0.0.0-20241011074135-9f5c573d3e25
github.com/ethereum/go-ethereum v1.14.9
github.com/go-viper/mapstructure/v2 v2.2.1
github.com/golang-migrate/migrate/v4 v4.18.1
github.com/google/uuid v1.6.0
github.com/hashicorp/golang-lru v1.0.2
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/jmoiron/sqlx v1.4.0
github.com/jonboulle/clockwork v0.4.0
Expand Down
Loading

0 comments on commit 18b6e24

Please sign in to comment.