feat: route-send-collectible

This commit is contained in:
Anthony Laibe 2023-08-24 10:45:14 +02:00
parent 195214765b
commit 01babe3632
10 changed files with 328 additions and 108 deletions

View File

@ -445,7 +445,7 @@ func (api *API) GetSuggestedRoutes(
sendType SendType, sendType SendType,
account common.Address, account common.Address,
amountIn *hexutil.Big, amountIn *hexutil.Big,
tokenSymbol string, tokenID string,
disabledFromChainIDs, disabledFromChainIDs,
disabledToChaindIDs, disabledToChaindIDs,
preferedChainIDs []uint64, preferedChainIDs []uint64,
@ -453,7 +453,7 @@ func (api *API) GetSuggestedRoutes(
fromLockedAmount map[uint64]*hexutil.Big, fromLockedAmount map[uint64]*hexutil.Big,
) (*SuggestedRoutes, error) { ) (*SuggestedRoutes, error) {
log.Debug("call to GetSuggestedRoutes") log.Debug("call to GetSuggestedRoutes")
return api.router.suggestedRoutes(ctx, sendType, account, amountIn.ToInt(), tokenSymbol, disabledFromChainIDs, disabledToChaindIDs, preferedChainIDs, gasFeeMode, fromLockedAmount) return api.router.suggestedRoutes(ctx, sendType, account, amountIn.ToInt(), tokenID, disabledFromChainIDs, disabledToChaindIDs, preferedChainIDs, gasFeeMode, fromLockedAmount)
} }
// Generates addresses for the provided paths, response doesn't include `HasActivity` value (if you need it check `GetAddressDetails` function) // Generates addresses for the provided paths, response doesn't include `HasActivity` value (if you need it check `GetAddressDetails` function)

View File

@ -24,54 +24,63 @@ func getSigner(chainID uint64, from types.Address, verifiedAccount *account.Sele
type TransactionBridge struct { type TransactionBridge struct {
BridgeName string BridgeName string
ChainID uint64 ChainID uint64
SimpleTx *transactions.SendTxArgs TransferTx *transactions.SendTxArgs
HopTx *HopTxArgs HopTx *HopTxArgs
CbridgeTx *CBridgeTxArgs CbridgeTx *CBridgeTxArgs
ERC721TransferTx *ERC721TransferTxArgs
} }
func (t *TransactionBridge) Value() *big.Int { func (t *TransactionBridge) Value() *big.Int {
if t.SimpleTx != nil && t.SimpleTx.To != nil { if t.TransferTx != nil && t.TransferTx.To != nil {
return t.SimpleTx.Value.ToInt() return t.TransferTx.Value.ToInt()
} else if t.HopTx != nil { } else if t.HopTx != nil {
return t.HopTx.Amount.ToInt() return t.HopTx.Amount.ToInt()
} else if t.CbridgeTx != nil { } else if t.CbridgeTx != nil {
return t.CbridgeTx.Amount.ToInt() return t.CbridgeTx.Amount.ToInt()
} else if t.ERC721TransferTx != nil {
return big.NewInt(1)
} }
return big.NewInt(0) return big.NewInt(0)
} }
func (t *TransactionBridge) From() types.Address { func (t *TransactionBridge) From() types.Address {
if t.SimpleTx != nil && t.SimpleTx.To != nil { if t.TransferTx != nil && t.TransferTx.To != nil {
return t.SimpleTx.From return t.TransferTx.From
} else if t.HopTx != nil { } else if t.HopTx != nil {
return t.HopTx.From return t.HopTx.From
} else if t.CbridgeTx != nil { } else if t.CbridgeTx != nil {
return t.CbridgeTx.From return t.CbridgeTx.From
} else if t.ERC721TransferTx != nil {
return t.ERC721TransferTx.From
} }
return types.HexToAddress("0x0") return types.HexToAddress("0x0")
} }
func (t *TransactionBridge) To() types.Address { func (t *TransactionBridge) To() types.Address {
if t.SimpleTx != nil && t.SimpleTx.To != nil { if t.TransferTx != nil && t.TransferTx.To != nil {
return *t.SimpleTx.To return *t.TransferTx.To
} else if t.HopTx != nil { } else if t.HopTx != nil {
return types.Address(t.HopTx.Recipient) return types.Address(t.HopTx.Recipient)
} else if t.CbridgeTx != nil { } else if t.CbridgeTx != nil {
return types.Address(t.HopTx.Recipient) return types.Address(t.HopTx.Recipient)
} else if t.ERC721TransferTx != nil {
return types.Address(t.ERC721TransferTx.Recipient)
} }
return types.HexToAddress("0x0") return types.HexToAddress("0x0")
} }
func (t *TransactionBridge) Data() types.HexBytes { func (t *TransactionBridge) Data() types.HexBytes {
if t.SimpleTx != nil && t.SimpleTx.To != nil { if t.TransferTx != nil && t.TransferTx.To != nil {
return t.SimpleTx.Data return t.TransferTx.Data
} else if t.HopTx != nil { } else if t.HopTx != nil {
return types.HexBytes("") return types.HexBytes("")
} else if t.CbridgeTx != nil { } else if t.CbridgeTx != nil {
return types.HexBytes("") return types.HexBytes("")
} else if t.ERC721TransferTx != nil {
return types.HexBytes("")
} }
return types.HexBytes("") return types.HexBytes("")
@ -81,7 +90,7 @@ type Bridge interface {
Name() string Name() string
Can(from *params.Network, to *params.Network, token *token.Token, balance *big.Int) (bool, error) Can(from *params.Network, to *params.Network, token *token.Token, balance *big.Int) (bool, error)
CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int, nativeTokenPrice, tokenPrice float64, gasPrice *big.Float) (*big.Int, *big.Int, error) CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int, nativeTokenPrice, tokenPrice float64, gasPrice *big.Float) (*big.Int, *big.Int, error)
EstimateGas(from *params.Network, to *params.Network, token *token.Token, amountIn *big.Int) (uint64, error) EstimateGas(from *params.Network, to *params.Network, account common.Address, token *token.Token, amountIn *big.Int) (uint64, error)
CalculateAmountOut(from, to *params.Network, amountIn *big.Int, symbol string) (*big.Int, error) CalculateAmountOut(from, to *params.Network, amountIn *big.Int, symbol string) (*big.Int, error)
Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (types.Hash, error) Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (types.Hash, error)
GetContractAddress(network *params.Network, token *token.Token) *common.Address GetContractAddress(network *params.Network, token *token.Token) *common.Address

View File

@ -216,7 +216,7 @@ func (s *CBridge) CalculateFees(from, to *params.Network, token *token.Token, am
return big.NewInt(0), new(big.Int).Add(baseFee, percFee), nil return big.NewInt(0), new(big.Int).Add(baseFee, percFee), nil
} }
func (s *CBridge) EstimateGas(from, to *params.Network, token *token.Token, amountIn *big.Int) (uint64, error) { func (s *CBridge) EstimateGas(from, to *params.Network, account common.Address, token *token.Token, amountIn *big.Int) (uint64, error) {
// TODO: replace by estimate function // TODO: replace by estimate function
if token.IsNative() { if token.IsNative() {
return 22000, nil // default gas limit for eth transaction return 22000, nil // default gas limit for eth transaction

View File

@ -0,0 +1,109 @@
package bridge
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/contracts/community-tokens/collectibles"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/rpc"
"github.com/status-im/status-go/services/wallet/token"
"github.com/status-im/status-go/transactions"
)
type ERC721TransferTxArgs struct {
transactions.SendTxArgs
TokenID *hexutil.Big `json:"tokenId"`
Recipient common.Address `json:"recipient"`
}
type ERC721TransferBridge struct {
rpcClient *rpc.Client
transactor *transactions.Transactor
}
func NewERC721TransferBridge(rpcClient *rpc.Client, transactor *transactions.Transactor) *ERC721TransferBridge {
return &ERC721TransferBridge{rpcClient: rpcClient, transactor: transactor}
}
func (s *ERC721TransferBridge) Name() string {
return "ERC721Transfer"
}
func (s *ERC721TransferBridge) Can(from, to *params.Network, token *token.Token, balance *big.Int) (bool, error) {
return from.ChainID == to.ChainID, nil
}
func (s *ERC721TransferBridge) CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int, nativeTokenPrice, tokenPrice float64, gasPrice *big.Float) (*big.Int, *big.Int, error) {
return big.NewInt(0), big.NewInt(0), nil
}
func (s *ERC721TransferBridge) EstimateGas(from, to *params.Network, account common.Address, token *token.Token, amountIn *big.Int) (uint64, error) {
// ethClient, err := s.rpcClient.EthClient(from.ChainID)
// if err != nil {
// return 0, err
// }
// collectiblesABI, err := abi.JSON(strings.NewReader(collectibles.CollectiblesABI))
// if err != nil {
// return 0, err
// }
// toAddress := common.HexToAddress("0x0")
// tokenID, success := new(big.Int).SetString(token.Symbol, 10)
// if !success {
// return 0, err
// }
// data, err := collectiblesABI.Pack("safeTransferFrom", account, toAddress, tokenID)
// if err != nil {
// return 0, err
// }
// estimate, err := ethClient.EstimateGas(context.Background(), ethereum.CallMsg{
// From: account,
// To: &toAddress,
// Value: big.NewInt(0),
// Data: data,
// })
// if err != nil {
// return 0, err
// }
// return estimate + 1000, nil
return 80000, nil
}
func (s *ERC721TransferBridge) Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error) {
ethClient, err := s.rpcClient.EthClient(sendArgs.ChainID)
if err != nil {
return hash, err
}
contract, err := collectibles.NewCollectibles(common.Address(*sendArgs.ERC721TransferTx.To), ethClient)
if err != nil {
return hash, err
}
nonce, unlock, err := s.transactor.NextNonce(s.rpcClient, sendArgs.ChainID, sendArgs.ERC721TransferTx.From)
if err != nil {
return hash, err
}
defer func() {
unlock(err == nil, nonce)
}()
argNonce := hexutil.Uint64(nonce)
sendArgs.ERC721TransferTx.Nonce = &argNonce
txOpts := sendArgs.ERC721TransferTx.ToTransactOpts(getSigner(sendArgs.ChainID, sendArgs.ERC721TransferTx.From, verifiedAccount))
tx, err := contract.SafeTransferFrom(txOpts, common.Address(sendArgs.ERC721TransferTx.From), sendArgs.ERC721TransferTx.Recipient, sendArgs.ERC721TransferTx.TokenID.ToInt())
if err != nil {
return hash, err
}
return types.Hash(tx.Hash()), nil
}
func (s *ERC721TransferBridge) CalculateAmountOut(from, to *params.Network, amountIn *big.Int, symbol string) (*big.Int, error) {
return amountIn, nil
}
func (s *ERC721TransferBridge) GetContractAddress(network *params.Network, token *token.Token) *common.Address {
return &token.Address
}

View File

@ -125,7 +125,7 @@ func (h *HopBridge) Can(from, to *params.Network, token *token.Token, balance *b
return true, nil return true, nil
} }
func (h *HopBridge) EstimateGas(from, to *params.Network, token *token.Token, amountIn *big.Int) (uint64, error) { func (h *HopBridge) EstimateGas(from, to *params.Network, account common.Address, token *token.Token, amountIn *big.Int) (uint64, error) {
// TODO: find why this doesn't work // TODO: find why this doesn't work
// ethClient, err := s.contractMaker.RPCClient.EthClient(from.ChainID) // ethClient, err := s.contractMaker.RPCClient.EthClient(from.ChainID)
// if err != nil { // if err != nil {

View File

@ -1,53 +0,0 @@
package bridge
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/services/wallet/token"
"github.com/status-im/status-go/transactions"
)
type SimpleBridge struct {
transactor *transactions.Transactor
}
func NewSimpleBridge(transactor *transactions.Transactor) *SimpleBridge {
return &SimpleBridge{transactor: transactor}
}
func (s *SimpleBridge) Name() string {
return "Simple"
}
func (s *SimpleBridge) Can(from, to *params.Network, token *token.Token, balance *big.Int) (bool, error) {
return from.ChainID == to.ChainID, nil
}
func (s *SimpleBridge) CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int, nativeTokenPrice, tokenPrice float64, gasPrice *big.Float) (*big.Int, *big.Int, error) {
return big.NewInt(0), big.NewInt(0), nil
}
func (s *SimpleBridge) EstimateGas(from, to *params.Network, token *token.Token, amountIn *big.Int) (uint64, error) {
// TODO: replace by estimate function
if token.IsNative() {
return 22000, nil // default gas limit for eth transaction
}
return 200000, nil //default gas limit for erc20 transaction
}
func (s *SimpleBridge) Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (types.Hash, error) {
return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, *sendArgs.SimpleTx, verifiedAccount)
}
func (s *SimpleBridge) CalculateAmountOut(from, to *params.Network, amountIn *big.Int, symbol string) (*big.Int, error) {
return amountIn, nil
}
func (s *SimpleBridge) GetContractAddress(network *params.Network, token *token.Token) *common.Address {
return nil
}

View File

@ -0,0 +1,53 @@
package bridge
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/services/wallet/token"
"github.com/status-im/status-go/transactions"
)
type TransferBridge struct {
transactor *transactions.Transactor
}
func NewTransferBridge(transactor *transactions.Transactor) *TransferBridge {
return &TransferBridge{transactor: transactor}
}
func (s *TransferBridge) Name() string {
return "Transfer"
}
func (s *TransferBridge) Can(from, to *params.Network, token *token.Token, balance *big.Int) (bool, error) {
return from.ChainID == to.ChainID, nil
}
func (s *TransferBridge) CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int, nativeTokenPrice, tokenPrice float64, gasPrice *big.Float) (*big.Int, *big.Int, error) {
return big.NewInt(0), big.NewInt(0), nil
}
func (s *TransferBridge) EstimateGas(from, to *params.Network, account common.Address, token *token.Token, amountIn *big.Int) (uint64, error) {
// TODO: replace by estimate function
if token.IsNative() {
return 22000, nil // default gas limit for eth transaction
}
return 200000, nil //default gas limit for erc20 transaction
}
func (s *TransferBridge) Send(sendArgs *TransactionBridge, verifiedAccount *account.SelectedExtKey) (types.Hash, error) {
return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, *sendArgs.TransferTx, verifiedAccount)
}
func (s *TransferBridge) CalculateAmountOut(from, to *params.Network, amountIn *big.Int, symbol string) (*big.Int, error) {
return amountIn, nil
}
func (s *TransferBridge) GetContractAddress(network *params.Network, token *token.Token) *common.Address {
return nil
}

View File

@ -134,3 +134,32 @@ func (o *OwnershipDB) GetOwnedCollectibles(chainIDs []w_common.ChainID, ownerAdd
return rowsToCollectibles(rows) return rowsToCollectibles(rows)
} }
func (o *OwnershipDB) GetOwnedCollectible(chainID w_common.ChainID, ownerAddresses common.Address, contractAddress common.Address, tokenID *big.Int) (*thirdparty.CollectibleUniqueID, error) {
query := fmt.Sprintf(`SELECT %s
FROM collectibles_ownership_cache
WHERE chain_id = ? AND owner_address = ? AND contract_address = ? AND token_id = ?`, selectOwnershipColumns)
stmt, err := o.db.Prepare(query)
if err != nil {
return nil, err
}
defer stmt.Close()
rows, err := stmt.Query(chainID, ownerAddresses, contractAddress, (*bigint.SQLBigIntBytes)(tokenID))
if err != nil {
return nil, err
}
defer rows.Close()
ids, err := rowsToCollectibles(rows)
if err != nil {
return nil, err
}
if len(ids) == 0 {
return nil, nil
}
return &ids[0], nil
}

View File

@ -5,6 +5,7 @@ import (
"database/sql" "database/sql"
"encoding/json" "encoding/json"
"errors" "errors"
"math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
@ -265,3 +266,7 @@ func (s *Service) GetOwnedCollectibles(chainIDs []walletCommon.ChainID, owners [
return ids, hasMore, nil return ids, hasMore, nil
} }
func (s *Service) GetOwnedCollectible(chainID walletCommon.ChainID, owner common.Address, contractAddress common.Address, tokenID *big.Int) (*thirdparty.CollectibleUniqueID, error) {
return s.ownershipDB.GetOwnedCollectible(chainID, owner, contractAddress, tokenID)
}

View File

@ -23,10 +23,15 @@ import (
"github.com/status-im/status-go/services/wallet/async" "github.com/status-im/status-go/services/wallet/async"
"github.com/status-im/status-go/services/wallet/bigint" "github.com/status-im/status-go/services/wallet/bigint"
"github.com/status-im/status-go/services/wallet/bridge" "github.com/status-im/status-go/services/wallet/bridge"
walletCommon "github.com/status-im/status-go/services/wallet/common"
"github.com/status-im/status-go/services/wallet/token" "github.com/status-im/status-go/services/wallet/token"
"github.com/status-im/status-go/transactions" "github.com/status-im/status-go/transactions"
) )
const EstimateUsername = "RandomUsername"
const EstimatePubKey = "0x04bb2024ce5d72e45d4a4f8589ae657ef9745855006996115a23a1af88d536cf02c0524a585fce7bfa79d6a9669af735eda6205d6c7e5b3cdc2b8ff7b2fa1f0b56"
const ERC721TransferString = "ERC721Transfer"
type SendType int type SendType int
const ( const (
@ -36,20 +41,79 @@ const (
ENSSetPubKey ENSSetPubKey
StickersBuy StickersBuy
Bridge Bridge
ERC721Transfer
) )
const EstimateUsername = "RandomUsername"
const EstimatePubKey = "0x04bb2024ce5d72e45d4a4f8589ae657ef9745855006996115a23a1af88d536cf02c0524a585fce7bfa79d6a9669af735eda6205d6c7e5b3cdc2b8ff7b2fa1f0b56" func (s SendType) FetchPrices(service *Service, tokenID string) (map[string]float64, error) {
symbols := []string{tokenID, "ETH"}
if s == ERC721Transfer {
symbols = []string{"ETH"}
}
pricesMap, err := service.marketManager.FetchPrices(symbols, []string{"USD"})
if err != nil {
return nil, err
}
prices := make(map[string]float64, 0)
for symbol, pricePerCurrency := range pricesMap {
prices[symbol] = pricePerCurrency["USD"]
}
if s == ERC721Transfer {
prices[tokenID] = 0
}
return prices, nil
}
func (s SendType) FindToken(service *Service, account common.Address, network *params.Network, tokenID string) *token.Token {
if s != ERC721Transfer {
return service.tokenManager.FindToken(network, tokenID)
}
parts := strings.Split(tokenID, ":")
contractAddress := common.HexToAddress(parts[0])
collectibleTokenID, success := new(big.Int).SetString(parts[1], 10)
if !success {
return nil
}
uniqueID, err := service.collectibles.GetOwnedCollectible(walletCommon.ChainID(network.ChainID), account, contractAddress, collectibleTokenID)
if err != nil || uniqueID == nil {
return nil
}
return &token.Token{
Address: contractAddress,
Symbol: collectibleTokenID.String(),
Decimals: 0,
ChainID: network.ChainID,
}
}
func (s SendType) isTransfer() bool { func (s SendType) isTransfer() bool {
return s == Transfer return s == Transfer || s == ERC721Transfer
} }
func (s SendType) isAvailableBetween(from, to *params.Network) bool { func (s SendType) isAvailableBetween(from, to *params.Network) bool {
if s != Bridge { if s == ERC721Transfer {
return from.ChainID == to.ChainID
}
if s == Bridge {
return from.ChainID != to.ChainID
}
return true return true
} }
return from.ChainID != to.ChainID func (s SendType) canUseBridge(b bridge.Bridge) bool {
if s == ERC721Transfer && b.Name() != ERC721TransferString {
return false
}
if s != ERC721Transfer && b.Name() == ERC721TransferString {
return false
}
return true
} }
func (s SendType) isAvailableFor(network *params.Network) bool { func (s SendType) isAvailableFor(network *params.Network) bool {
@ -64,10 +128,9 @@ func (s SendType) isAvailableFor(network *params.Network) bool {
return false return false
} }
func (s SendType) EstimateGas(service *Service, network *params.Network) uint64 { func (s SendType) EstimateGas(service *Service, network *params.Network, from common.Address, tokenID string) uint64 {
from := types.Address(common.HexToAddress("0x5ffa75ce51c3a7ebe23bde37b5e3a0143dfbcee0"))
tx := transactions.SendTxArgs{ tx := transactions.SendTxArgs{
From: from, From: (types.Address)(from),
Value: (*hexutil.Big)(zero), Value: (*hexutil.Big)(zero),
} }
if s == ENSRegister { if s == ENSRegister {
@ -96,7 +159,7 @@ func (s SendType) EstimateGas(service *Service, network *params.Network) uint64
if s == StickersBuy { if s == StickersBuy {
packID := &bigint.BigInt{Int: big.NewInt(2)} packID := &bigint.BigInt{Int: big.NewInt(2)}
estimate, err := service.stickers.API().BuyEstimate(context.Background(), network.ChainID, from, packID) estimate, err := service.stickers.API().BuyEstimate(context.Background(), network.ChainID, (types.Address)(from), packID)
if err != nil { if err != nil {
return 400000 return 400000
} }
@ -343,10 +406,12 @@ func newSuggestedRoutes(
func NewRouter(s *Service) *Router { func NewRouter(s *Service) *Router {
bridges := make(map[string]bridge.Bridge) bridges := make(map[string]bridge.Bridge)
simple := bridge.NewSimpleBridge(s.transactor) transfer := bridge.NewTransferBridge(s.transactor)
erc721Transfer := bridge.NewERC721TransferBridge(s.rpcClient, s.transactor)
cbridge := bridge.NewCbridge(s.rpcClient, s.transactor, s.tokenManager) cbridge := bridge.NewCbridge(s.rpcClient, s.transactor, s.tokenManager)
hop := bridge.NewHopBridge(s.rpcClient, s.transactor, s.tokenManager) hop := bridge.NewHopBridge(s.rpcClient, s.transactor, s.tokenManager)
bridges[simple.Name()] = simple bridges[transfer.Name()] = transfer
bridges[erc721Transfer.Name()] = erc721Transfer
bridges[hop.Name()] = hop bridges[hop.Name()] = hop
bridges[cbridge.Name()] = cbridge bridges[cbridge.Name()] = cbridge
@ -369,7 +434,11 @@ type Router struct {
rpcClient *rpc.Client rpcClient *rpc.Client
} }
func (r *Router) requireApproval(ctx context.Context, bridge bridge.Bridge, account common.Address, network *params.Network, token *token.Token, amountIn *big.Int) (bool, *big.Int, uint64, *common.Address, error) { func (r *Router) requireApproval(ctx context.Context, sendType SendType, bridge bridge.Bridge, account common.Address, network *params.Network, token *token.Token, amountIn *big.Int) (bool, *big.Int, uint64, *common.Address, error) {
if sendType == ERC721Transfer {
return false, nil, 0, nil, nil
}
if token.IsNative() { if token.IsNative() {
return false, nil, 0, nil, nil return false, nil, 0, nil, nil
} }
@ -443,7 +512,7 @@ func (r *Router) suggestedRoutes(
sendType SendType, sendType SendType,
account common.Address, account common.Address,
amountIn *big.Int, amountIn *big.Int,
tokenSymbol string, tokenID string,
disabledFromChainIDs, disabledFromChainIDs,
disabledToChaindIDs, disabledToChaindIDs,
preferedChainIDs []uint64, preferedChainIDs []uint64,
@ -460,14 +529,10 @@ func (r *Router) suggestedRoutes(
return nil, err return nil, err
} }
pricesMap, err := r.s.marketManager.FetchPrices([]string{"ETH", tokenSymbol}, []string{"USD"}) prices, err := sendType.FetchPrices(r.s, tokenID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
prices := make(map[string]float64, 0)
for symbol, pricePerCurrency := range pricesMap {
prices[symbol] = pricePerCurrency["USD"]
}
var ( var (
group = async.NewAtomicGroup(ctx) group = async.NewAtomicGroup(ctx)
@ -486,7 +551,8 @@ func (r *Router) suggestedRoutes(
if !sendType.isAvailableFor(network) { if !sendType.isAvailableFor(network) {
continue continue
} }
token := r.s.tokenManager.FindToken(network, tokenSymbol)
token := sendType.FindToken(r.s, account, network, tokenID)
if token == nil { if token == nil {
continue continue
} }
@ -500,10 +566,14 @@ func (r *Router) suggestedRoutes(
return err return err
} }
balance, err := r.getBalance(ctx, network, token, account) // Default value is 1 as in case of erc721 as we built the token we are sure the account owns it
balance := big.NewInt(1)
if sendType != ERC721Transfer {
balance, err = r.getBalance(ctx, network, token, account)
if err != nil { if err != nil {
return err return err
} }
}
maxAmountIn := (*hexutil.Big)(balance) maxAmountIn := (*hexutil.Big)(balance)
if amount, ok := fromLockedAmount[network.ChainID]; ok { if amount, ok := fromLockedAmount[network.ChainID]; ok {
@ -522,6 +592,10 @@ func (r *Router) suggestedRoutes(
estimatedTime := r.s.feesManager.transactionEstimatedTime(ctx, network.ChainID, maxFees) estimatedTime := r.s.feesManager.transactionEstimatedTime(ctx, network.ChainID, maxFees)
for _, bridge := range r.bridges { for _, bridge := range r.bridges {
if !sendType.canUseBridge(bridge) {
continue
}
for _, dest := range networks { for _, dest := range networks {
if dest.IsTest != areTestNetworksEnabled { if dest.IsTest != areTestNetworksEnabled {
continue continue
@ -547,12 +621,10 @@ func (r *Router) suggestedRoutes(
if err != nil || !can { if err != nil || !can {
continue continue
} }
bonderFees, tokenFees, err := bridge.CalculateFees(network, dest, token, amountIn, prices["ETH"], prices[tokenID], gasFees.GasPrice)
bonderFees, tokenFees, err := bridge.CalculateFees(network, dest, token, amountIn, prices["ETH"], prices[tokenSymbol], gasFees.GasPrice)
if err != nil { if err != nil {
continue continue
} }
if maxAmountIn.ToInt().Cmp(amountIn) >= 0 { if maxAmountIn.ToInt().Cmp(amountIn) >= 0 {
if bonderFees.Cmp(amountIn) >= 0 { if bonderFees.Cmp(amountIn) >= 0 {
continue continue
@ -562,28 +634,24 @@ func (r *Router) suggestedRoutes(
continue continue
} }
} }
gasLimit := uint64(0) gasLimit := uint64(0)
if sendType.isTransfer() { if sendType.isTransfer() {
gasLimit, err = bridge.EstimateGas(network, dest, token, amountIn) gasLimit, err = bridge.EstimateGas(network, dest, account, token, amountIn)
if err != nil { if err != nil {
continue continue
} }
} else { } else {
gasLimit = sendType.EstimateGas(r.s, network) gasLimit = sendType.EstimateGas(r.s, network, account, tokenID)
} }
requiredNativeBalance := new(big.Int).Mul(gweiToWei(maxFees), big.NewInt(int64(gasLimit))) requiredNativeBalance := new(big.Int).Mul(gweiToWei(maxFees), big.NewInt(int64(gasLimit)))
// Removed the required fees from maxAMount in case of native token tx // Removed the required fees from maxAMount in case of native token tx
if token.IsNative() { if token.IsNative() {
maxAmountIn = (*hexutil.Big)(new(big.Int).Sub(maxAmountIn.ToInt(), requiredNativeBalance)) maxAmountIn = (*hexutil.Big)(new(big.Int).Sub(maxAmountIn.ToInt(), requiredNativeBalance))
} }
if nativeBalance.Cmp(requiredNativeBalance) <= 0 { if nativeBalance.Cmp(requiredNativeBalance) <= 0 {
continue continue
} }
approvalRequired, approvalAmountRequired, approvalGasLimit, approvalContractAddress, err := r.requireApproval(ctx, sendType, bridge, account, network, token, amountIn)
approvalRequired, approvalAmountRequired, approvalGasLimit, approvalContractAddress, err := r.requireApproval(ctx, bridge, account, network, token, amountIn)
if err != nil { if err != nil {
continue continue
} }
@ -606,12 +674,11 @@ func (r *Router) suggestedRoutes(
big.NewFloat(math.Pow(10, float64(token.Decimals))), big.NewFloat(math.Pow(10, float64(token.Decimals))),
) )
tokenCost := new(big.Float) tokenCost := new(big.Float)
tokenCost.Mul(tokenFeesAsFloat, big.NewFloat(prices[tokenSymbol])) tokenCost.Mul(tokenFeesAsFloat, big.NewFloat(prices[tokenID]))
cost := new(big.Float) cost := new(big.Float)
cost.Add(tokenCost, gasCost) cost.Add(tokenCost, gasCost)
cost.Add(cost, approvalGasCost) cost.Add(cost, approvalGasCost)
mu.Lock() mu.Lock()
candidates = append(candidates, &Path{ candidates = append(candidates, &Path{
BridgeName: bridge.Name(), BridgeName: bridge.Name(),
@ -641,14 +708,15 @@ func (r *Router) suggestedRoutes(
group.Wait() group.Wait()
suggestedRoutes := newSuggestedRoutes(amountIn, candidates, fromLockedAmount) suggestedRoutes := newSuggestedRoutes(amountIn, candidates, fromLockedAmount)
suggestedRoutes.TokenPrice = prices[tokenSymbol] suggestedRoutes.TokenPrice = prices[tokenID]
suggestedRoutes.NativeChainTokenPrice = prices["ETH"] suggestedRoutes.NativeChainTokenPrice = prices["ETH"]
for _, path := range suggestedRoutes.Best { for _, path := range suggestedRoutes.Best {
amountOut, err := r.bridges[path.BridgeName].CalculateAmountOut(path.From, path.To, (*big.Int)(path.AmountIn), tokenSymbol) amountOut, err := r.bridges[path.BridgeName].CalculateAmountOut(path.From, path.To, (*big.Int)(path.AmountIn), tokenID)
if err != nil { if err != nil {
continue continue
} }
path.AmountOut = (*hexutil.Big)(amountOut) path.AmountOut = (*hexutil.Big)(amountOut)
} }
return suggestedRoutes, nil return suggestedRoutes, nil
} }