fix_: calculating route for erc721 checks one more call if the function doesn't exist on the contract (#5691)

Old ERC721 contracts do not have  `safeTransferFrom` function, because of that we couldn't estimate the gas
and we couldn't resolve the right route. Now we firstly check for `safeTransferFrom` function which is supported
by ERC721A contracts and if the contract doesn't have it then we do `transferFrom` function call.

Example of ERC721A contract:
- https://etherscan.io/address/0x0024bc1035d30ae229a4712189b32131758cb000#code

Example of ERC721 contract:
- https://etherscan.io/address/0x06012c8cf97bead5deae237070f9587f8e7a266d#code
This commit is contained in:
saledjenic 2024-08-13 17:26:20 +02:00 committed by GitHub
parent c39baa80d2
commit f7821cfc3d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 5501 additions and 7 deletions

File diff suppressed because one or more lines are too long

5
contracts/erc721/doc.go Normal file
View File

@ -0,0 +1,5 @@
package erc721
// contract address https://etherscan.io/address/0xc0c7eb8e88e7244f7c3569e483ad891f5a508a60#code
//go:generate abigen -abi ./ERC721.abi -pkg erc721 -out erc721.go

2069
contracts/erc721/erc721.go Normal file

File diff suppressed because one or more lines are too long

3360
contracts/erc721/erc721.sol Normal file

File diff suppressed because it is too large Load Diff

View File

@ -14,11 +14,18 @@ import (
ethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/contracts/community-tokens/collectibles"
"github.com/status-im/status-go/contracts/erc721"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/rpc"
"github.com/status-im/status-go/services/wallet/token"
"github.com/status-im/status-go/transactions"
)
const (
functionNameSafeTransferFrom = "safeTransferFrom"
functionNameTransferFrom = "transferFrom"
)
type ERC721TxArgs struct {
transactions.SendTxArgs
TokenID *hexutil.Big `json:"tokenId"`
@ -50,8 +57,8 @@ func (s *ERC721Processor) CalculateFees(params ProcessorInputParams) (*big.Int,
return ZeroBigIntValue, ZeroBigIntValue, nil
}
func (s *ERC721Processor) PackTxInputData(params ProcessorInputParams) ([]byte, error) {
abi, err := abi.JSON(strings.NewReader(collectibles.CollectiblesMetaData.ABI))
func (s *ERC721Processor) packTxInputDataInternally(params ProcessorInputParams, functionName string) ([]byte, error) {
abi, err := abi.JSON(strings.NewReader(erc721.Erc721MetaData.ABI))
if err != nil {
return []byte{}, createERC721ErrorResponse(err)
}
@ -61,13 +68,45 @@ func (s *ERC721Processor) PackTxInputData(params ProcessorInputParams) ([]byte,
return []byte{}, createERC721ErrorResponse(fmt.Errorf("failed to convert %s to big.Int", params.FromToken.Symbol))
}
return abi.Pack("safeTransferFrom",
return abi.Pack(functionName,
params.FromAddr,
params.ToAddr,
id,
)
}
func (s *ERC721Processor) checkIfFunctionExists(params ProcessorInputParams, functionName string) error {
data, err := s.packTxInputDataInternally(params, functionName)
if err != nil {
return createERC721ErrorResponse(err)
}
ethClient, err := s.rpcClient.EthClient(params.FromChain.ChainID)
if err != nil {
return createERC721ErrorResponse(err)
}
value := new(big.Int)
msg := ethereum.CallMsg{
From: params.FromAddr,
To: &params.FromToken.Address,
Value: value,
Data: data,
}
_, err = ethClient.CallContract(context.Background(), msg, nil)
return err
}
func (s *ERC721Processor) PackTxInputData(params ProcessorInputParams) ([]byte, error) {
err := s.checkIfFunctionExists(params, functionNameSafeTransferFrom)
if err == nil {
return s.packTxInputDataInternally(params, functionNameSafeTransferFrom)
}
return s.packTxInputDataInternally(params, functionNameTransferFrom)
}
func (s *ERC721Processor) EstimateGas(params ProcessorInputParams) (uint64, error) {
if params.TestsMode {
if params.TestEstimationMap != nil {
@ -107,6 +146,21 @@ func (s *ERC721Processor) EstimateGas(params ProcessorInputParams) (uint64, erro
}
func (s *ERC721Processor) sendOrBuild(sendArgs *MultipathProcessorTxArgs, signerFn bind.SignerFn, lastUsedNonce int64) (tx *ethTypes.Transaction, err error) {
from := common.Address(sendArgs.ERC721TransferTx.From)
useSafeTransferFrom := true
inputParams := ProcessorInputParams{
FromAddr: from,
ToAddr: sendArgs.ERC721TransferTx.Recipient,
FromToken: &token.Token{
Symbol: sendArgs.ERC721TransferTx.TokenID.String(),
},
}
err = s.checkIfFunctionExists(inputParams, functionNameSafeTransferFrom)
if err != nil {
useSafeTransferFrom = false
}
ethClient, err := s.rpcClient.EthClient(sendArgs.ChainID)
if err != nil {
return tx, createERC721ErrorResponse(err)
@ -130,10 +184,15 @@ func (s *ERC721Processor) sendOrBuild(sendArgs *MultipathProcessorTxArgs, signer
argNonce := hexutil.Uint64(nonce)
sendArgs.ERC721TransferTx.Nonce = &argNonce
txOpts := sendArgs.ERC721TransferTx.ToTransactOpts(signerFn)
from := common.Address(sendArgs.ERC721TransferTx.From)
tx, err = contract.SafeTransferFrom(txOpts, from,
sendArgs.ERC721TransferTx.Recipient,
sendArgs.ERC721TransferTx.TokenID.ToInt())
if useSafeTransferFrom {
tx, err = contract.SafeTransferFrom(txOpts, from,
sendArgs.ERC721TransferTx.Recipient,
sendArgs.ERC721TransferTx.TokenID.ToInt())
} else {
tx, err = contract.TransferFrom(txOpts, from,
sendArgs.ERC721TransferTx.Recipient,
sendArgs.ERC721TransferTx.TokenID.ToInt())
}
if err != nil {
return tx, createERC721ErrorResponse(err)
}