status-go/services/wallet/router/pathprocessor/processor_transfer.go
saledjenic c39baa80d2
chore_: cherry-pick #5658: improvements on resolving nonce (#5690)
* chore_: unused `BuildTx` function removed from the processor interface and types that are implement it

Since the `BuildTx` function is not used anywhere, it's removed from the code.

* fix_: resolving nonce improvements

When the app sends more than a single tx from the same account on the same chain, some
chains do not return appropriate nonce (they do not consider pending txs), because of
that we place more tx with the same nonce, where all but the first one fail.

Changes in this PR keep track of nonces being used in the same sending/bridging flow, which means
for the first tx from the multi txs the app asks the chain for the nonce, and every next nonce is resolved
by incrementing the last used nonce by 1.
2024-08-13 11:17:08 +01:00

131 lines
3.8 KiB
Go

package pathprocessor
import (
"context"
"math/big"
"strings"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
ethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/contracts/ierc20"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/rpc"
"github.com/status-im/status-go/transactions"
)
type TransferProcessor struct {
rpcClient *rpc.Client
transactor transactions.TransactorIface
}
func NewTransferProcessor(rpcClient *rpc.Client, transactor transactions.TransactorIface) *TransferProcessor {
return &TransferProcessor{rpcClient: rpcClient, transactor: transactor}
}
func createTransferErrorResponse(err error) error {
return createErrorResponse(ProcessorTransferName, err)
}
func (s *TransferProcessor) Name() string {
return ProcessorTransferName
}
func (s *TransferProcessor) AvailableFor(params ProcessorInputParams) (bool, error) {
if params.FromChain == nil || params.ToChain == nil {
return false, ErrNoChainSet
}
if params.FromToken == nil {
return false, ErrNoTokenSet
}
if params.ToToken != nil {
return false, ErrToTokenShouldNotBeSet
}
return params.FromChain.ChainID == params.ToChain.ChainID, nil
}
func (s *TransferProcessor) CalculateFees(params ProcessorInputParams) (*big.Int, *big.Int, error) {
return ZeroBigIntValue, ZeroBigIntValue, nil
}
func (s *TransferProcessor) PackTxInputData(params ProcessorInputParams) ([]byte, error) {
if params.FromToken.IsNative() {
return []byte("eth_sendRawTransaction"), nil
} else {
abi, err := abi.JSON(strings.NewReader(ierc20.IERC20ABI))
if err != nil {
return []byte{}, createTransferErrorResponse(err)
}
return abi.Pack("transfer",
params.ToAddr,
params.AmountIn,
)
}
}
func (s *TransferProcessor) EstimateGas(params ProcessorInputParams) (uint64, error) {
if params.TestsMode {
if params.TestEstimationMap != nil {
if val, ok := params.TestEstimationMap[s.Name()]; ok {
return val, nil
}
}
return 0, ErrNoEstimationFound
}
estimation := uint64(0)
var err error
input, err := s.PackTxInputData(params)
if err != nil {
return 0, createTransferErrorResponse(err)
}
if params.FromToken.IsNative() {
estimation, err = s.transactor.EstimateGas(params.FromChain, params.FromAddr, params.ToAddr, params.AmountIn, input)
if err != nil {
return 0, createTransferErrorResponse(err)
}
} else {
ethClient, err := s.rpcClient.EthClient(params.FromChain.ChainID)
if err != nil {
return 0, createTransferErrorResponse(err)
}
ctx := context.Background()
msg := ethereum.CallMsg{
From: params.FromAddr,
To: &params.FromToken.Address,
Data: input,
}
estimation, err = ethClient.EstimateGas(ctx, msg)
if err != nil {
return 0, createTransferErrorResponse(err)
}
}
increasedEstimation := float64(estimation) * IncreaseEstimatedGasFactor
return uint64(increasedEstimation), nil
}
func (s *TransferProcessor) Send(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64, verifiedAccount *account.SelectedExtKey) (types.Hash, uint64, error) {
return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, *sendArgs.TransferTx, lastUsedNonce, verifiedAccount)
}
func (s *TransferProcessor) BuildTransaction(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error) {
return s.transactor.ValidateAndBuildTransaction(sendArgs.ChainID, *sendArgs.TransferTx, lastUsedNonce)
}
func (s *TransferProcessor) CalculateAmountOut(params ProcessorInputParams) (*big.Int, error) {
return params.AmountIn, nil
}
func (s *TransferProcessor) GetContractAddress(params ProcessorInputParams) (common.Address, error) {
return common.Address{}, nil
}