github.com/status-im/status-go@v1.1.0/services/wallet/router/pathprocessor/processor_erc1155.go (about) 1 package pathprocessor 2 3 import ( 4 "context" 5 "fmt" 6 "math/big" 7 "strings" 8 9 "github.com/ethereum/go-ethereum" 10 "github.com/ethereum/go-ethereum/accounts/abi" 11 "github.com/ethereum/go-ethereum/accounts/abi/bind" 12 "github.com/ethereum/go-ethereum/common" 13 "github.com/ethereum/go-ethereum/common/hexutil" 14 ethTypes "github.com/ethereum/go-ethereum/core/types" 15 "github.com/status-im/status-go/account" 16 "github.com/status-im/status-go/contracts/ierc1155" 17 "github.com/status-im/status-go/eth-node/types" 18 "github.com/status-im/status-go/rpc" 19 "github.com/status-im/status-go/transactions" 20 ) 21 22 type ERC1155TxArgs struct { 23 transactions.SendTxArgs 24 TokenID *hexutil.Big `json:"tokenId"` 25 Recipient common.Address `json:"recipient"` 26 Amount *hexutil.Big `json:"amount"` 27 } 28 29 type ERC1155Processor struct { 30 rpcClient *rpc.Client 31 transactor transactions.TransactorIface 32 } 33 34 func NewERC1155Processor(rpcClient *rpc.Client, transactor transactions.TransactorIface) *ERC1155Processor { 35 return &ERC1155Processor{rpcClient: rpcClient, transactor: transactor} 36 } 37 38 func createERC1155ErrorResponse(err error) error { 39 return createErrorResponse(ProcessorERC1155Name, err) 40 } 41 42 func (s *ERC1155Processor) Name() string { 43 return ProcessorERC1155Name 44 } 45 46 func (s *ERC1155Processor) AvailableFor(params ProcessorInputParams) (bool, error) { 47 return params.FromChain.ChainID == params.ToChain.ChainID && params.ToToken == nil, nil 48 } 49 50 func (s *ERC1155Processor) CalculateFees(params ProcessorInputParams) (*big.Int, *big.Int, error) { 51 return ZeroBigIntValue, ZeroBigIntValue, nil 52 } 53 54 func (s *ERC1155Processor) PackTxInputData(params ProcessorInputParams) ([]byte, error) { 55 abi, err := abi.JSON(strings.NewReader(ierc1155.Ierc1155ABI)) 56 if err != nil { 57 return []byte{}, createERC1155ErrorResponse(err) 58 } 59 60 id, success := big.NewInt(0).SetString(params.FromToken.Symbol, 0) 61 if !success { 62 return []byte{}, createERC1155ErrorResponse(fmt.Errorf("failed to convert %s to big.Int", params.FromToken.Symbol)) 63 } 64 65 return abi.Pack("safeTransferFrom", 66 params.FromAddr, 67 params.ToAddr, 68 id, 69 params.AmountIn, 70 []byte{}, 71 ) 72 } 73 74 func (s *ERC1155Processor) EstimateGas(params ProcessorInputParams) (uint64, error) { 75 if params.TestsMode { 76 if params.TestEstimationMap != nil { 77 if val, ok := params.TestEstimationMap[s.Name()]; ok { 78 return val.Value, val.Err 79 } 80 } 81 return 0, ErrNoEstimationFound 82 } 83 84 ethClient, err := s.rpcClient.EthClient(params.FromChain.ChainID) 85 if err != nil { 86 return 0, createERC1155ErrorResponse(err) 87 } 88 89 value := new(big.Int) 90 91 input, err := s.PackTxInputData(params) 92 if err != nil { 93 return 0, createERC1155ErrorResponse(err) 94 } 95 96 msg := ethereum.CallMsg{ 97 From: params.FromAddr, 98 To: ¶ms.FromToken.Address, 99 Value: value, 100 Data: input, 101 } 102 103 estimation, err := ethClient.EstimateGas(context.Background(), msg) 104 if err != nil { 105 return 0, createERC1155ErrorResponse(err) 106 } 107 increasedEstimation := float64(estimation) * IncreaseEstimatedGasFactor 108 return uint64(increasedEstimation), nil 109 } 110 111 func (s *ERC1155Processor) sendOrBuild(sendArgs *MultipathProcessorTxArgs, signerFn bind.SignerFn, lastUsedNonce int64) (tx *ethTypes.Transaction, err error) { 112 ethClient, err := s.rpcClient.EthClient(sendArgs.ChainID) 113 if err != nil { 114 return tx, createERC1155ErrorResponse(err) 115 } 116 117 contract, err := ierc1155.NewIerc1155(common.Address(*sendArgs.ERC1155TransferTx.To), ethClient) 118 if err != nil { 119 return tx, createERC1155ErrorResponse(err) 120 } 121 122 var nonce uint64 123 if lastUsedNonce < 0 { 124 nonce, err = s.transactor.NextNonce(s.rpcClient, sendArgs.ChainID, sendArgs.ERC1155TransferTx.From) 125 if err != nil { 126 return tx, createERC1155ErrorResponse(err) 127 } 128 } else { 129 nonce = uint64(lastUsedNonce) + 1 130 } 131 132 argNonce := hexutil.Uint64(nonce) 133 sendArgs.ERC1155TransferTx.Nonce = &argNonce 134 txOpts := sendArgs.ERC1155TransferTx.ToTransactOpts(signerFn) 135 from := common.Address(sendArgs.ERC1155TransferTx.From) 136 tx, err = contract.SafeTransferFrom( 137 txOpts, from, 138 sendArgs.ERC1155TransferTx.Recipient, 139 sendArgs.ERC1155TransferTx.TokenID.ToInt(), 140 sendArgs.ERC1155TransferTx.Amount.ToInt(), 141 []byte{}, 142 ) 143 if err != nil { 144 return tx, createERC1155ErrorResponse(err) 145 } 146 err = s.transactor.StoreAndTrackPendingTx(from, sendArgs.ERC1155TransferTx.Symbol, sendArgs.ChainID, sendArgs.ERC1155TransferTx.MultiTransactionID, tx) 147 if err != nil { 148 return tx, createERC1155ErrorResponse(err) 149 } 150 return tx, nil 151 } 152 153 func (s *ERC1155Processor) Send(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64, verifiedAccount *account.SelectedExtKey) (hash types.Hash, usedNonce uint64, err error) { 154 tx, err := s.sendOrBuild(sendArgs, getSigner(sendArgs.ChainID, sendArgs.ERC1155TransferTx.From, verifiedAccount), lastUsedNonce) 155 if err != nil { 156 return hash, 0, createERC1155ErrorResponse(err) 157 } 158 return types.Hash(tx.Hash()), tx.Nonce(), nil 159 } 160 161 func (s *ERC1155Processor) BuildTransaction(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error) { 162 tx, err := s.sendOrBuild(sendArgs, nil, lastUsedNonce) 163 return tx, tx.Nonce(), err 164 } 165 166 func (s *ERC1155Processor) CalculateAmountOut(params ProcessorInputParams) (*big.Int, error) { 167 return params.AmountIn, nil 168 } 169 170 func (s *ERC1155Processor) GetContractAddress(params ProcessorInputParams) (common.Address, error) { 171 return params.FromToken.Address, nil 172 }