Improvements on resolving nonce (#5658)

* 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.
This commit is contained in:
saledjenic 2024-08-12 14:07:32 +02:00 committed by GitHub
parent 74fa567cda
commit 79b1c547d1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 231 additions and 413 deletions

View File

@ -2228,7 +2228,8 @@ func (b *GethStatusBackend) SendTransaction(sendArgs transactions.SendTxArgs, pa
return hash, err
}
return b.transactor.SendTransaction(sendArgs, verifiedAccount)
hash, _, err = b.transactor.SendTransaction(sendArgs, verifiedAccount, -1)
return hash, err
}
func (b *GethStatusBackend) SendTransactionWithChainID(chainID uint64, sendArgs transactions.SendTxArgs, password string) (hash types.Hash, err error) {
@ -2237,7 +2238,8 @@ func (b *GethStatusBackend) SendTransactionWithChainID(chainID uint64, sendArgs
return hash, err
}
return b.transactor.SendTransactionWithChainID(chainID, sendArgs, verifiedAccount)
hash, _, err = b.transactor.SendTransactionWithChainID(chainID, sendArgs, -1, verifiedAccount)
return hash, err
}
func (b *GethStatusBackend) SendTransactionWithSignature(sendArgs transactions.SendTxArgs, sig []byte) (hash types.Hash, err error) {

View File

@ -388,7 +388,7 @@ func (s *Service) suggestedFeesToSendTxArgs(from common.Address, to *common.Addr
}
func (s *Service) estimateL1Fee(ctx context.Context, chainID uint64, sendArgs transactions.SendTxArgs) (uint64, error) {
transaction, err := s.transactor.ValidateAndBuildTransaction(chainID, sendArgs)
transaction, _, err := s.transactor.ValidateAndBuildTransaction(chainID, sendArgs, -1)
if err != nil {
return 0, err
}

View File

@ -8,10 +8,9 @@ import (
big "math/big"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
common "github.com/ethereum/go-ethereum/common"
types "github.com/ethereum/go-ethereum/core/types"
gomock "github.com/golang/mock/gomock"
account "github.com/status-im/status-go/account"
types0 "github.com/status-im/status-go/eth-node/types"
pathprocessor "github.com/status-im/status-go/services/wallet/router/pathprocessor"
@ -56,33 +55,19 @@ func (mr *MockPathProcessorMockRecorder) AvailableFor(params interface{}) *gomoc
}
// BuildTransaction mocks base method.
func (m *MockPathProcessor) BuildTransaction(sendArgs *pathprocessor.MultipathProcessorTxArgs) (*types.Transaction, error) {
func (m *MockPathProcessor) BuildTransaction(sendArgs *pathprocessor.MultipathProcessorTxArgs, lastUsedNonce int64) (*types.Transaction, uint64, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "BuildTransaction", sendArgs)
ret := m.ctrl.Call(m, "BuildTransaction", sendArgs, lastUsedNonce)
ret0, _ := ret[0].(*types.Transaction)
ret1, _ := ret[1].(error)
return ret0, ret1
ret1, _ := ret[1].(uint64)
ret2, _ := ret[2].(error)
return ret0, ret1, ret2
}
// BuildTransaction indicates an expected call of BuildTransaction.
func (mr *MockPathProcessorMockRecorder) BuildTransaction(sendArgs interface{}) *gomock.Call {
func (mr *MockPathProcessorMockRecorder) BuildTransaction(sendArgs, lastUsedNonce interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BuildTransaction", reflect.TypeOf((*MockPathProcessor)(nil).BuildTransaction), sendArgs)
}
// BuildTx mocks base method.
func (m *MockPathProcessor) BuildTx(params pathprocessor.ProcessorInputParams) (*types.Transaction, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "BuildTx", params)
ret0, _ := ret[0].(*types.Transaction)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// BuildTx indicates an expected call of BuildTx.
func (mr *MockPathProcessorMockRecorder) BuildTx(params interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BuildTx", reflect.TypeOf((*MockPathProcessor)(nil).BuildTx), params)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BuildTransaction", reflect.TypeOf((*MockPathProcessor)(nil).BuildTransaction), sendArgs, lastUsedNonce)
}
// CalculateAmountOut mocks base method.
@ -116,18 +101,6 @@ func (mr *MockPathProcessorMockRecorder) CalculateFees(params interface{}) *gomo
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CalculateFees", reflect.TypeOf((*MockPathProcessor)(nil).CalculateFees), params)
}
// Clear mocks base method.
func (m *MockPathProcessor) Clear() {
m.ctrl.T.Helper()
m.ctrl.Call(m, "Clear")
}
// Clear indicates an expected call of Clear.
func (mr *MockPathProcessorMockRecorder) Clear() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Clear", reflect.TypeOf((*MockPathProcessor)(nil).Clear))
}
// EstimateGas mocks base method.
func (m *MockPathProcessor) EstimateGas(params pathprocessor.ProcessorInputParams) (uint64, error) {
m.ctrl.T.Helper()
@ -188,16 +161,52 @@ func (mr *MockPathProcessorMockRecorder) PackTxInputData(params interface{}) *go
}
// Send mocks base method.
func (m *MockPathProcessor) Send(sendArgs *pathprocessor.MultipathProcessorTxArgs, verifiedAccount *account.SelectedExtKey) (types0.Hash, error) {
func (m *MockPathProcessor) Send(sendArgs *pathprocessor.MultipathProcessorTxArgs, lastUsedNonce int64, verifiedAccount *account.SelectedExtKey) (types0.Hash, uint64, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Send", sendArgs, verifiedAccount)
ret := m.ctrl.Call(m, "Send", sendArgs, lastUsedNonce, verifiedAccount)
ret0, _ := ret[0].(types0.Hash)
ret1, _ := ret[1].(error)
return ret0, ret1
ret1, _ := ret[1].(uint64)
ret2, _ := ret[2].(error)
return ret0, ret1, ret2
}
// Send indicates an expected call of Send.
func (mr *MockPathProcessorMockRecorder) Send(sendArgs, verifiedAccount interface{}) *gomock.Call {
func (mr *MockPathProcessorMockRecorder) Send(sendArgs, lastUsedNonce, verifiedAccount interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Send", reflect.TypeOf((*MockPathProcessor)(nil).Send), sendArgs, verifiedAccount)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Send", reflect.TypeOf((*MockPathProcessor)(nil).Send), sendArgs, lastUsedNonce, verifiedAccount)
}
// MockPathProcessorClearable is a mock of PathProcessorClearable interface.
type MockPathProcessorClearable struct {
ctrl *gomock.Controller
recorder *MockPathProcessorClearableMockRecorder
}
// MockPathProcessorClearableMockRecorder is the mock recorder for MockPathProcessorClearable.
type MockPathProcessorClearableMockRecorder struct {
mock *MockPathProcessorClearable
}
// NewMockPathProcessorClearable creates a new mock instance.
func NewMockPathProcessorClearable(ctrl *gomock.Controller) *MockPathProcessorClearable {
mock := &MockPathProcessorClearable{ctrl: ctrl}
mock.recorder = &MockPathProcessorClearableMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockPathProcessorClearable) EXPECT() *MockPathProcessorClearableMockRecorder {
return m.recorder
}
// Clear mocks base method.
func (m *MockPathProcessorClearable) Clear() {
m.ctrl.T.Helper()
m.ctrl.Call(m, "Clear")
}
// Clear indicates an expected call of Clear.
func (mr *MockPathProcessorClearableMockRecorder) Clear() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Clear", reflect.TypeOf((*MockPathProcessorClearable)(nil).Clear))
}

View File

@ -25,14 +25,12 @@ type PathProcessor interface {
EstimateGas(params ProcessorInputParams) (uint64, error)
// CalculateAmountOut calculates the amount out
CalculateAmountOut(params ProcessorInputParams) (*big.Int, error)
// Send sends the tx
Send(sendArgs *MultipathProcessorTxArgs, verifiedAccount *account.SelectedExtKey) (types.Hash, error)
// Send sends the tx, returns the hash and the used nonce (lastUsedNonce is -1 if it's the first tx)
Send(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64, verifiedAccount *account.SelectedExtKey) (types.Hash, uint64, error)
// GetContractAddress returns the contract address
GetContractAddress(params ProcessorInputParams) (common.Address, error)
// BuildTransaction builds the transaction based on MultipathProcessorTxArgs
BuildTransaction(sendArgs *MultipathProcessorTxArgs) (*ethTypes.Transaction, error)
// BuildTx builds the transaction based on ProcessorInputParams
BuildTx(params ProcessorInputParams) (*ethTypes.Transaction, error)
// BuildTransaction builds the transaction based on MultipathProcessorTxArgs, returns the transaction and the used nonce (lastUsedNonce is -1 if it's the first tx)
BuildTransaction(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error)
}
type PathProcessorClearable interface {

View File

@ -283,27 +283,6 @@ func (s *CelerBridgeProcessor) EstimateGas(params ProcessorInputParams) (uint64,
return uint64(increasedEstimation), nil
}
func (s *CelerBridgeProcessor) BuildTx(params ProcessorInputParams) (*ethTypes.Transaction, error) {
toAddr := types.Address(params.ToAddr)
sendArgs := &MultipathProcessorTxArgs{
CbridgeTx: &CelerBridgeTxArgs{
SendTxArgs: transactions.SendTxArgs{
From: types.Address(params.FromAddr),
To: &toAddr,
Value: (*hexutil.Big)(params.AmountIn),
Data: types.HexBytes("0x0"),
},
ChainID: params.ToChain.ChainID,
Symbol: params.FromToken.Symbol,
Recipient: params.ToAddr,
Amount: (*hexutil.Big)(params.AmountIn),
},
ChainID: params.FromChain.ChainID,
}
return s.BuildTransaction(sendArgs)
}
func (s *CelerBridgeProcessor) GetContractAddress(params ProcessorInputParams) (common.Address, error) {
transferConfig, err := s.getTransferConfig(params.FromChain.IsTest)
if err != nil {
@ -322,7 +301,7 @@ func (s *CelerBridgeProcessor) GetContractAddress(params ProcessorInputParams) (
return common.Address{}, ErrContractNotFound
}
func (s *CelerBridgeProcessor) sendOrBuild(sendArgs *MultipathProcessorTxArgs, signerFn bind.SignerFn) (*ethTypes.Transaction, error) {
func (s *CelerBridgeProcessor) sendOrBuild(sendArgs *MultipathProcessorTxArgs, signerFn bind.SignerFn, lastUsedNonce int64) (*ethTypes.Transaction, error) {
fromChain := s.rpcClient.NetworkManager.Find(sendArgs.ChainID)
if fromChain == nil {
return nil, ErrNetworkNotFound
@ -347,6 +326,11 @@ func (s *CelerBridgeProcessor) sendOrBuild(sendArgs *MultipathProcessorTxArgs, s
return nil, createBridgeCellerErrorResponse(err)
}
if lastUsedNonce >= 0 {
lastUsedNonceHexUtil := hexutil.Uint64(uint64(lastUsedNonce) + 1)
sendArgs.CbridgeTx.Nonce = &lastUsedNonceHexUtil
}
var tx *ethTypes.Transaction
txOpts := sendArgs.CbridgeTx.ToTransactOpts(signerFn)
if token.IsNative() {
@ -379,17 +363,18 @@ func (s *CelerBridgeProcessor) sendOrBuild(sendArgs *MultipathProcessorTxArgs, s
return tx, nil
}
func (s *CelerBridgeProcessor) Send(sendArgs *MultipathProcessorTxArgs, verifiedAccount *account.SelectedExtKey) (types.Hash, error) {
tx, err := s.sendOrBuild(sendArgs, getSigner(sendArgs.ChainID, sendArgs.CbridgeTx.From, verifiedAccount))
func (s *CelerBridgeProcessor) Send(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64, verifiedAccount *account.SelectedExtKey) (types.Hash, uint64, error) {
tx, err := s.sendOrBuild(sendArgs, getSigner(sendArgs.ChainID, sendArgs.CbridgeTx.From, verifiedAccount), lastUsedNonce)
if err != nil {
return types.HexToHash(""), createBridgeCellerErrorResponse(err)
return types.HexToHash(""), 0, createBridgeCellerErrorResponse(err)
}
return types.Hash(tx.Hash()), nil
return types.Hash(tx.Hash()), tx.Nonce(), nil
}
func (s *CelerBridgeProcessor) BuildTransaction(sendArgs *MultipathProcessorTxArgs) (*ethTypes.Transaction, error) {
return s.sendOrBuild(sendArgs, nil)
func (s *CelerBridgeProcessor) BuildTransaction(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error) {
tx, err := s.sendOrBuild(sendArgs, nil, lastUsedNonce)
return tx, tx.Nonce(), err
}
func (s *CelerBridgeProcessor) CalculateAmountOut(params ProcessorInputParams) (*big.Int, error) {

View File

@ -277,34 +277,12 @@ func (h *HopBridgeProcessor) EstimateGas(params ProcessorInputParams) (uint64, e
return uint64(increasedEstimation), nil
}
func (h *HopBridgeProcessor) BuildTx(params ProcessorInputParams) (*ethTypes.Transaction, error) {
toAddr := types.Address(params.ToAddr)
sendArgs := &MultipathProcessorTxArgs{
HopTx: &HopBridgeTxArgs{
SendTxArgs: transactions.SendTxArgs{
From: types.Address(params.FromAddr),
To: &toAddr,
Value: (*hexutil.Big)(params.AmountIn),
Data: types.HexBytes("0x0"),
},
Symbol: params.FromToken.Symbol,
Recipient: params.ToAddr,
Amount: (*hexutil.Big)(params.AmountIn),
BonderFee: (*hexutil.Big)(params.BonderFee),
ChainID: params.ToChain.ChainID,
},
ChainID: params.FromChain.ChainID,
}
return h.BuildTransaction(sendArgs)
}
func (h *HopBridgeProcessor) GetContractAddress(params ProcessorInputParams) (common.Address, error) {
address, _, err := hop.GetContractAddress(params.FromChain.ChainID, params.FromToken.Symbol)
return address, createBridgeHopErrorResponse(err)
}
func (h *HopBridgeProcessor) sendOrBuild(sendArgs *MultipathProcessorTxArgs, signerFn bind.SignerFn) (tx *ethTypes.Transaction, err error) {
func (h *HopBridgeProcessor) sendOrBuild(sendArgs *MultipathProcessorTxArgs, signerFn bind.SignerFn, lastUsedNonce int64) (tx *ethTypes.Transaction, err error) {
fromChain := h.networkManager.Find(sendArgs.HopTx.ChainID)
if fromChain == nil {
return tx, fmt.Errorf("ChainID not supported %d", sendArgs.HopTx.ChainID)
@ -312,9 +290,14 @@ func (h *HopBridgeProcessor) sendOrBuild(sendArgs *MultipathProcessorTxArgs, sig
token := h.tokenManager.FindToken(fromChain, sendArgs.HopTx.Symbol)
nonce, err := h.transactor.NextNonce(h.contractMaker.RPCClient, fromChain.ChainID, sendArgs.HopTx.From)
if err != nil {
return tx, createBridgeHopErrorResponse(err)
var nonce uint64
if lastUsedNonce < 0 {
nonce, err = h.transactor.NextNonce(h.contractMaker.RPCClient, fromChain.ChainID, sendArgs.HopTx.From)
if err != nil {
return tx, createBridgeHopErrorResponse(err)
}
} else {
nonce = uint64(lastUsedNonce) + 1
}
argNonce := hexutil.Uint64(nonce)
@ -366,16 +349,17 @@ func (h *HopBridgeProcessor) sendOrBuild(sendArgs *MultipathProcessorTxArgs, sig
return tx, nil
}
func (h *HopBridgeProcessor) Send(sendArgs *MultipathProcessorTxArgs, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error) {
tx, err := h.sendOrBuild(sendArgs, getSigner(sendArgs.HopTx.ChainID, sendArgs.HopTx.From, verifiedAccount))
func (h *HopBridgeProcessor) Send(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64, verifiedAccount *account.SelectedExtKey) (hash types.Hash, nonce uint64, err error) {
tx, err := h.sendOrBuild(sendArgs, getSigner(sendArgs.HopTx.ChainID, sendArgs.HopTx.From, verifiedAccount), lastUsedNonce)
if err != nil {
return types.Hash{}, createBridgeHopErrorResponse(err)
return types.Hash{}, 0, createBridgeHopErrorResponse(err)
}
return types.Hash(tx.Hash()), nil
return types.Hash(tx.Hash()), tx.Nonce(), nil
}
func (h *HopBridgeProcessor) BuildTransaction(sendArgs *MultipathProcessorTxArgs) (*ethTypes.Transaction, error) {
return h.sendOrBuild(sendArgs, nil)
func (h *HopBridgeProcessor) BuildTransaction(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error) {
tx, err := h.sendOrBuild(sendArgs, nil, lastUsedNonce)
return tx, tx.Nonce(), createBridgeHopErrorResponse(err)
}
func (h *HopBridgeProcessor) CalculateFees(params ProcessorInputParams) (*big.Int, *big.Int, error) {

View File

@ -8,7 +8,6 @@ import (
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
ethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/contracts"
@ -104,32 +103,12 @@ func (s *ENSPublicKeyProcessor) EstimateGas(params ProcessorInputParams) (uint64
return uint64(increasedEstimation), nil
}
func (s *ENSPublicKeyProcessor) BuildTx(params ProcessorInputParams) (*ethTypes.Transaction, error) {
toAddr := types.Address(params.ToAddr)
inputData, err := s.PackTxInputData(params)
if err != nil {
return nil, createENSPublicKeyErrorResponse(err)
}
sendArgs := &MultipathProcessorTxArgs{
TransferTx: &transactions.SendTxArgs{
From: types.Address(params.FromAddr),
To: &toAddr,
Value: (*hexutil.Big)(ZeroBigIntValue),
Data: inputData,
},
ChainID: params.FromChain.ChainID,
}
return s.BuildTransaction(sendArgs)
func (s *ENSPublicKeyProcessor) Send(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64, verifiedAccount *account.SelectedExtKey) (hash types.Hash, usedNonce uint64, err error) {
return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, *sendArgs.TransferTx, lastUsedNonce, verifiedAccount)
}
func (s *ENSPublicKeyProcessor) Send(sendArgs *MultipathProcessorTxArgs, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error) {
return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, *sendArgs.TransferTx, verifiedAccount)
}
func (s *ENSPublicKeyProcessor) BuildTransaction(sendArgs *MultipathProcessorTxArgs) (*ethTypes.Transaction, error) {
return s.transactor.ValidateAndBuildTransaction(sendArgs.ChainID, *sendArgs.TransferTx)
func (s *ENSPublicKeyProcessor) BuildTransaction(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error) {
return s.transactor.ValidateAndBuildTransaction(sendArgs.ChainID, *sendArgs.TransferTx, lastUsedNonce)
}
func (s *ENSPublicKeyProcessor) CalculateAmountOut(params ProcessorInputParams) (*big.Int, error) {

View File

@ -9,7 +9,6 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
ethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/contracts"
@ -140,32 +139,12 @@ func (s *ENSRegisterProcessor) EstimateGas(params ProcessorInputParams) (uint64,
return uint64(increasedEstimation), nil
}
func (s *ENSRegisterProcessor) BuildTx(params ProcessorInputParams) (*ethTypes.Transaction, error) {
toAddr := types.Address(params.ToAddr)
inputData, err := s.PackTxInputData(params)
if err != nil {
return nil, createENSRegisterProcessorErrorResponse(err)
}
sendArgs := &MultipathProcessorTxArgs{
TransferTx: &transactions.SendTxArgs{
From: types.Address(params.FromAddr),
To: &toAddr,
Value: (*hexutil.Big)(ZeroBigIntValue),
Data: inputData,
},
ChainID: params.FromChain.ChainID,
}
return s.BuildTransaction(sendArgs)
func (s *ENSRegisterProcessor) Send(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64, verifiedAccount *account.SelectedExtKey) (hash types.Hash, usedNonce uint64, err error) {
return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, *sendArgs.TransferTx, lastUsedNonce, verifiedAccount)
}
func (s *ENSRegisterProcessor) Send(sendArgs *MultipathProcessorTxArgs, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error) {
return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, *sendArgs.TransferTx, verifiedAccount)
}
func (s *ENSRegisterProcessor) BuildTransaction(sendArgs *MultipathProcessorTxArgs) (*ethTypes.Transaction, error) {
return s.transactor.ValidateAndBuildTransaction(sendArgs.ChainID, *sendArgs.TransferTx)
func (s *ENSRegisterProcessor) BuildTransaction(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error) {
return s.transactor.ValidateAndBuildTransaction(sendArgs.ChainID, *sendArgs.TransferTx, lastUsedNonce)
}
func (s *ENSRegisterProcessor) CalculateAmountOut(params ProcessorInputParams) (*big.Int, error) {

View File

@ -8,7 +8,6 @@ import (
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
ethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/contracts"
@ -103,32 +102,12 @@ func (s *ENSReleaseProcessor) EstimateGas(params ProcessorInputParams) (uint64,
return uint64(increasedEstimation), nil
}
func (s *ENSReleaseProcessor) BuildTx(params ProcessorInputParams) (*ethTypes.Transaction, error) {
toAddr := types.Address(params.ToAddr)
inputData, err := s.PackTxInputData(params)
if err != nil {
return nil, createENSReleaseErrorResponse(err)
}
sendArgs := &MultipathProcessorTxArgs{
TransferTx: &transactions.SendTxArgs{
From: types.Address(params.FromAddr),
To: &toAddr,
Value: (*hexutil.Big)(ZeroBigIntValue),
Data: inputData,
},
ChainID: params.FromChain.ChainID,
}
return s.BuildTransaction(sendArgs)
func (s *ENSReleaseProcessor) Send(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64, verifiedAccount *account.SelectedExtKey) (hash types.Hash, usedNonce uint64, err error) {
return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, *sendArgs.TransferTx, lastUsedNonce, verifiedAccount)
}
func (s *ENSReleaseProcessor) Send(sendArgs *MultipathProcessorTxArgs, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error) {
return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, *sendArgs.TransferTx, verifiedAccount)
}
func (s *ENSReleaseProcessor) BuildTransaction(sendArgs *MultipathProcessorTxArgs) (*ethTypes.Transaction, error) {
return s.transactor.ValidateAndBuildTransaction(sendArgs.ChainID, *sendArgs.TransferTx)
func (s *ENSReleaseProcessor) BuildTransaction(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error) {
return s.transactor.ValidateAndBuildTransaction(sendArgs.ChainID, *sendArgs.TransferTx, lastUsedNonce)
}
func (s *ENSReleaseProcessor) CalculateAmountOut(params ProcessorInputParams) (*big.Int, error) {

View File

@ -108,34 +108,7 @@ func (s *ERC1155Processor) EstimateGas(params ProcessorInputParams) (uint64, err
return uint64(increasedEstimation), nil
}
func (s *ERC1155Processor) BuildTx(params ProcessorInputParams) (*ethTypes.Transaction, error) {
contractAddress := types.Address(params.FromToken.Address)
// We store ERC1155 Token ID using big.Int.String() in token.Symbol
tokenID, success := new(big.Int).SetString(params.FromToken.Symbol, 10)
if !success {
return nil, createERC1155ErrorResponse(fmt.Errorf("failed to convert ERC1155's Symbol %s to big.Int", params.FromToken.Symbol))
}
sendArgs := &MultipathProcessorTxArgs{
ERC1155TransferTx: &ERC1155TxArgs{
SendTxArgs: transactions.SendTxArgs{
From: types.Address(params.FromAddr),
To: &contractAddress,
Value: (*hexutil.Big)(params.AmountIn),
Data: types.HexBytes("0x0"),
},
TokenID: (*hexutil.Big)(tokenID),
Recipient: params.ToAddr,
Amount: (*hexutil.Big)(params.AmountIn),
},
ChainID: params.FromChain.ChainID,
}
return s.BuildTransaction(sendArgs)
}
func (s *ERC1155Processor) sendOrBuild(sendArgs *MultipathProcessorTxArgs, signerFn bind.SignerFn) (tx *ethTypes.Transaction, err error) {
func (s *ERC1155Processor) sendOrBuild(sendArgs *MultipathProcessorTxArgs, signerFn bind.SignerFn, lastUsedNonce int64) (tx *ethTypes.Transaction, err error) {
ethClient, err := s.rpcClient.EthClient(sendArgs.ChainID)
if err != nil {
return tx, createERC1155ErrorResponse(err)
@ -146,9 +119,14 @@ func (s *ERC1155Processor) sendOrBuild(sendArgs *MultipathProcessorTxArgs, signe
return tx, createERC1155ErrorResponse(err)
}
nonce, err := s.transactor.NextNonce(s.rpcClient, sendArgs.ChainID, sendArgs.ERC1155TransferTx.From)
if err != nil {
return tx, createERC1155ErrorResponse(err)
var nonce uint64
if lastUsedNonce < 0 {
nonce, err = s.transactor.NextNonce(s.rpcClient, sendArgs.ChainID, sendArgs.ERC1155TransferTx.From)
if err != nil {
return tx, createERC1155ErrorResponse(err)
}
} else {
nonce = uint64(lastUsedNonce) + 1
}
argNonce := hexutil.Uint64(nonce)
@ -172,16 +150,17 @@ func (s *ERC1155Processor) sendOrBuild(sendArgs *MultipathProcessorTxArgs, signe
return tx, nil
}
func (s *ERC1155Processor) Send(sendArgs *MultipathProcessorTxArgs, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error) {
tx, err := s.sendOrBuild(sendArgs, getSigner(sendArgs.ChainID, sendArgs.ERC1155TransferTx.From, verifiedAccount))
func (s *ERC1155Processor) Send(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64, verifiedAccount *account.SelectedExtKey) (hash types.Hash, usedNonce uint64, err error) {
tx, err := s.sendOrBuild(sendArgs, getSigner(sendArgs.ChainID, sendArgs.ERC1155TransferTx.From, verifiedAccount), lastUsedNonce)
if err != nil {
return hash, createERC1155ErrorResponse(err)
return hash, 0, createERC1155ErrorResponse(err)
}
return types.Hash(tx.Hash()), nil
return types.Hash(tx.Hash()), tx.Nonce(), nil
}
func (s *ERC1155Processor) BuildTransaction(sendArgs *MultipathProcessorTxArgs) (*ethTypes.Transaction, error) {
return s.sendOrBuild(sendArgs, nil)
func (s *ERC1155Processor) BuildTransaction(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error) {
tx, err := s.sendOrBuild(sendArgs, nil, lastUsedNonce)
return tx, tx.Nonce(), err
}
func (s *ERC1155Processor) CalculateAmountOut(params ProcessorInputParams) (*big.Int, error) {

View File

@ -106,33 +106,7 @@ func (s *ERC721Processor) EstimateGas(params ProcessorInputParams) (uint64, erro
return uint64(increasedEstimation), nil
}
func (s *ERC721Processor) BuildTx(params ProcessorInputParams) (*ethTypes.Transaction, error) {
contractAddress := types.Address(params.FromToken.Address)
// We store ERC721 Token ID using big.Int.String() in token.Symbol
tokenID, success := new(big.Int).SetString(params.FromToken.Symbol, 10)
if !success {
return nil, createERC721ErrorResponse(fmt.Errorf("failed to convert ERC721's Symbol %s to big.Int", params.FromToken.Symbol))
}
sendArgs := &MultipathProcessorTxArgs{
ERC721TransferTx: &ERC721TxArgs{
SendTxArgs: transactions.SendTxArgs{
From: types.Address(params.FromAddr),
To: &contractAddress,
Value: (*hexutil.Big)(params.AmountIn),
Data: types.HexBytes("0x0"),
},
TokenID: (*hexutil.Big)(tokenID),
Recipient: params.ToAddr,
},
ChainID: params.FromChain.ChainID,
}
return s.BuildTransaction(sendArgs)
}
func (s *ERC721Processor) sendOrBuild(sendArgs *MultipathProcessorTxArgs, signerFn bind.SignerFn) (tx *ethTypes.Transaction, err error) {
func (s *ERC721Processor) sendOrBuild(sendArgs *MultipathProcessorTxArgs, signerFn bind.SignerFn, lastUsedNonce int64) (tx *ethTypes.Transaction, err error) {
ethClient, err := s.rpcClient.EthClient(sendArgs.ChainID)
if err != nil {
return tx, createERC721ErrorResponse(err)
@ -143,9 +117,14 @@ func (s *ERC721Processor) sendOrBuild(sendArgs *MultipathProcessorTxArgs, signer
return tx, createERC721ErrorResponse(err)
}
nonce, err := s.transactor.NextNonce(s.rpcClient, sendArgs.ChainID, sendArgs.ERC721TransferTx.From)
if err != nil {
return tx, createERC721ErrorResponse(err)
var nonce uint64
if lastUsedNonce < 0 {
nonce, err = s.transactor.NextNonce(s.rpcClient, sendArgs.ChainID, sendArgs.ERC721TransferTx.From)
if err != nil {
return tx, createERC721ErrorResponse(err)
}
} else {
nonce = uint64(lastUsedNonce) + 1
}
argNonce := hexutil.Uint64(nonce)
@ -165,16 +144,17 @@ func (s *ERC721Processor) sendOrBuild(sendArgs *MultipathProcessorTxArgs, signer
return tx, nil
}
func (s *ERC721Processor) Send(sendArgs *MultipathProcessorTxArgs, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error) {
tx, err := s.sendOrBuild(sendArgs, getSigner(sendArgs.ChainID, sendArgs.ERC721TransferTx.From, verifiedAccount))
func (s *ERC721Processor) Send(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64, verifiedAccount *account.SelectedExtKey) (hash types.Hash, usedNonce uint64, err error) {
tx, err := s.sendOrBuild(sendArgs, getSigner(sendArgs.ChainID, sendArgs.ERC721TransferTx.From, verifiedAccount), lastUsedNonce)
if err != nil {
return hash, createERC721ErrorResponse(err)
return hash, 0, createERC721ErrorResponse(err)
}
return types.Hash(tx.Hash()), nil
return types.Hash(tx.Hash()), tx.Nonce(), nil
}
func (s *ERC721Processor) BuildTransaction(sendArgs *MultipathProcessorTxArgs) (*ethTypes.Transaction, error) {
return s.sendOrBuild(sendArgs, nil)
func (s *ERC721Processor) BuildTransaction(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error) {
tx, err := s.sendOrBuild(sendArgs, nil, lastUsedNonce)
return tx, tx.Nonce(), err
}
func (s *ERC721Processor) CalculateAmountOut(params ProcessorInputParams) (*big.Int, error) {

View File

@ -9,7 +9,6 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
ethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/contracts"
@ -132,32 +131,12 @@ func (s *StickersBuyProcessor) EstimateGas(params ProcessorInputParams) (uint64,
return uint64(increasedEstimation), nil
}
func (s *StickersBuyProcessor) BuildTx(params ProcessorInputParams) (*ethTypes.Transaction, error) {
toAddr := types.Address(params.ToAddr)
inputData, err := s.PackTxInputData(params)
if err != nil {
return nil, createStickersBuyErrorResponse(err)
}
sendArgs := &MultipathProcessorTxArgs{
TransferTx: &transactions.SendTxArgs{
From: types.Address(params.FromAddr),
To: &toAddr,
Value: (*hexutil.Big)(ZeroBigIntValue),
Data: inputData,
},
ChainID: params.FromChain.ChainID,
}
return s.BuildTransaction(sendArgs)
func (s *StickersBuyProcessor) Send(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64, verifiedAccount *account.SelectedExtKey) (hash types.Hash, usedNonce uint64, err error) {
return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, *sendArgs.TransferTx, lastUsedNonce, verifiedAccount)
}
func (s *StickersBuyProcessor) Send(sendArgs *MultipathProcessorTxArgs, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error) {
return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, *sendArgs.TransferTx, verifiedAccount)
}
func (s *StickersBuyProcessor) BuildTransaction(sendArgs *MultipathProcessorTxArgs) (*ethTypes.Transaction, error) {
return s.transactor.ValidateAndBuildTransaction(sendArgs.ChainID, *sendArgs.TransferTx)
func (s *StickersBuyProcessor) BuildTransaction(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error) {
return s.transactor.ValidateAndBuildTransaction(sendArgs.ChainID, *sendArgs.TransferTx, lastUsedNonce)
}
func (s *StickersBuyProcessor) CalculateAmountOut(params ProcessorInputParams) (*big.Int, error) {

View File

@ -209,27 +209,6 @@ func (s *SwapParaswapProcessor) GetContractAddress(params ProcessorInputParams)
return
}
func (s *SwapParaswapProcessor) BuildTx(params ProcessorInputParams) (*ethTypes.Transaction, error) {
toAddr := types.Address(params.ToAddr)
sendArgs := &MultipathProcessorTxArgs{
SwapTx: &SwapParaswapTxArgs{
SendTxArgs: transactions.SendTxArgs{
From: types.Address(params.FromAddr),
To: &toAddr,
Value: (*hexutil.Big)(params.AmountIn),
Data: types.HexBytes("0x0"),
Symbol: params.FromToken.Symbol,
},
ChainID: params.FromChain.ChainID,
ChainIDTo: params.ToChain.ChainID,
TokenIDFrom: params.FromToken.Symbol,
TokenIDTo: params.ToToken.Symbol,
},
}
return s.BuildTransaction(sendArgs)
}
func (s *SwapParaswapProcessor) prepareTransaction(sendArgs *MultipathProcessorTxArgs) error {
slippageBP := uint(sendArgs.SwapTx.SlippagePercentage * 100) // convert to basis points
@ -276,21 +255,21 @@ func (s *SwapParaswapProcessor) prepareTransaction(sendArgs *MultipathProcessorT
return nil
}
func (s *SwapParaswapProcessor) BuildTransaction(sendArgs *MultipathProcessorTxArgs) (*ethTypes.Transaction, error) {
func (s *SwapParaswapProcessor) BuildTransaction(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64) (*ethTypes.Transaction, uint64, error) {
err := s.prepareTransaction(sendArgs)
if err != nil {
return nil, createSwapParaswapErrorResponse(err)
return nil, 0, createSwapParaswapErrorResponse(err)
}
return s.transactor.ValidateAndBuildTransaction(sendArgs.ChainID, sendArgs.SwapTx.SendTxArgs)
return s.transactor.ValidateAndBuildTransaction(sendArgs.ChainID, sendArgs.SwapTx.SendTxArgs, lastUsedNonce)
}
func (s *SwapParaswapProcessor) Send(sendArgs *MultipathProcessorTxArgs, verifiedAccount *account.SelectedExtKey) (types.Hash, error) {
func (s *SwapParaswapProcessor) Send(sendArgs *MultipathProcessorTxArgs, lastUsedNonce int64, verifiedAccount *account.SelectedExtKey) (types.Hash, uint64, error) {
err := s.prepareTransaction(sendArgs)
if err != nil {
return types.Hash{}, createSwapParaswapErrorResponse(err)
return types.Hash{}, 0, createSwapParaswapErrorResponse(err)
}
return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, sendArgs.SwapTx.SendTxArgs, verifiedAccount)
return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, sendArgs.SwapTx.SendTxArgs, lastUsedNonce, verifiedAccount)
}
func (s *SwapParaswapProcessor) CalculateAmountOut(params ProcessorInputParams) (*big.Int, error) {

View File

@ -8,7 +8,6 @@ import (
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
ethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/contracts/ierc20"
@ -114,51 +113,12 @@ func (s *TransferProcessor) EstimateGas(params ProcessorInputParams) (uint64, er
return uint64(increasedEstimation), nil
}
func (s *TransferProcessor) BuildTx(params ProcessorInputParams) (*ethTypes.Transaction, error) {
toAddr := types.Address(params.ToAddr)
if params.FromToken.IsNative() {
sendArgs := &MultipathProcessorTxArgs{
TransferTx: &transactions.SendTxArgs{
From: types.Address(params.FromAddr),
To: &toAddr,
Value: (*hexutil.Big)(params.AmountIn),
Data: types.HexBytes("0x0"),
},
ChainID: params.FromChain.ChainID,
}
return s.BuildTransaction(sendArgs)
}
abi, err := abi.JSON(strings.NewReader(ierc20.IERC20ABI))
if err != nil {
return nil, createTransferErrorResponse(err)
}
input, err := abi.Pack("transfer",
params.ToAddr,
params.AmountIn,
)
if err != nil {
return nil, createTransferErrorResponse(err)
}
sendArgs := &MultipathProcessorTxArgs{
TransferTx: &transactions.SendTxArgs{
From: types.Address(params.FromAddr),
To: &toAddr,
Value: (*hexutil.Big)(ZeroBigIntValue),
Data: input,
},
ChainID: params.FromChain.ChainID,
}
return s.BuildTransaction(sendArgs)
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) Send(sendArgs *MultipathProcessorTxArgs, verifiedAccount *account.SelectedExtKey) (types.Hash, error) {
return s.transactor.SendTransactionWithChainID(sendArgs.ChainID, *sendArgs.TransferTx, verifiedAccount)
}
func (s *TransferProcessor) BuildTransaction(sendArgs *MultipathProcessorTxArgs) (*ethTypes.Transaction, error) {
return s.transactor.ValidateAndBuildTransaction(sendArgs.ChainID, *sendArgs.TransferTx)
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) {

View File

@ -158,12 +158,21 @@ func sendTransactions(data []*pathprocessor.MultipathProcessorTxArgs, pathProces
map[uint64][]types.Hash, error) {
hashes := make(map[uint64][]types.Hash)
usedNonces := make(map[uint64]int64)
for _, tx := range data {
hash, err := pathProcessors[tx.Name].Send(tx, account)
lastUsedNonce := int64(-1)
if nonce, ok := usedNonces[tx.ChainID]; ok {
lastUsedNonce = nonce
}
hash, usedNonce, err := pathProcessors[tx.Name].Send(tx, lastUsedNonce, account)
if err != nil {
return nil, err // TODO: One of transfers within transaction could have been sent. Need to notify user about it
}
hashes[tx.ChainID] = append(hashes[tx.ChainID], hash)
usedNonces[tx.ChainID] = int64(usedNonce)
}
return hashes, nil
}

View File

@ -182,7 +182,7 @@ func (tm *TransactionManager) BuildTransaction(chainID uint64, sendArgs transact
return nil, err
}
txBeingSigned, err := tm.transactor.ValidateAndBuildTransaction(chainID, sendArgs)
txBeingSigned, _, err := tm.transactor.ValidateAndBuildTransaction(chainID, sendArgs, -1)
if err != nil {
return nil, err
}

View File

@ -11,8 +11,15 @@ import (
func (tm *TransactionManager) buildTransactions(pathProcessors map[string]pathprocessor.PathProcessor) ([]string, error) {
tm.transactionsForKeycardSigning = make(map[common.Hash]*TransactionDescription)
var hashes []string
usedNonces := make(map[uint64]int64)
for _, bridgeTx := range tm.multipathTransactionsData {
builtTx, err := pathProcessors[bridgeTx.Name].BuildTransaction(bridgeTx)
lastUsedNonce := int64(-1)
if nonce, ok := usedNonces[bridgeTx.ChainID]; ok {
lastUsedNonce = nonce
}
builtTx, usedNonce, err := pathProcessors[bridgeTx.Name].BuildTransaction(bridgeTx, lastUsedNonce)
if err != nil {
return hashes, err
}
@ -26,6 +33,8 @@ func (tm *TransactionManager) buildTransactions(pathProcessors map[string]pathpr
builtTx: builtTx,
}
usedNonces[bridgeTx.ChainID] = int64(usedNonce)
hashes = append(hashes, txHash.String())
}

View File

@ -166,7 +166,7 @@ func TestSendTransactionsETHSuccess(t *testing.T) {
// Verify that the SendTransactionWithChainID method is called for each transaction with proper arguments
// Return values are not checked, because they must be checked in Transactor tests
for _, tx := range expectedData {
transactor.EXPECT().SendTransactionWithChainID(tx.ChainID, *(tx.TransferTx), account).Return(types.Hash{}, nil)
transactor.EXPECT().SendTransactionWithChainID(tx.ChainID, *(tx.TransferTx), int64(-1), account).Return(types.Hash{}, uint64(0), nil)
}
// Call the SendTransactions method
@ -182,7 +182,7 @@ func TestSendTransactionsApproveSuccess(t *testing.T) {
// Verify that the SendTransactionWithChainID method is called for each transaction with proper arguments
// Return values are not checked, because they must be checked in Transactor tests
for _, tx := range expectedData {
transactor.EXPECT().SendTransactionWithChainID(tx.ChainID, *(tx.TransferTx), account).Return(types.Hash{}, nil)
transactor.EXPECT().SendTransactionWithChainID(tx.ChainID, *(tx.TransferTx), int64(-1), account).Return(types.Hash{}, uint64(0), nil)
}
// Call the SendTransactions method
@ -205,7 +205,7 @@ func TestSendTransactionsETHFailOnBridge(t *testing.T) {
expectedErr := transactions.ErrInvalidTxSender // Any error to verify
// In case of bridge error, verify that the error is returned
transferBridge.EXPECT().Send(gomock.Any(), gomock.Any()).Return(types.Hash{}, transactions.ErrInvalidTxSender)
transferBridge.EXPECT().Send(gomock.Any(), int64(-1), gomock.Any()).Return(types.Hash{}, uint64(0), transactions.ErrInvalidTxSender)
// Call the SendTransactions method
_, err := tm.SendTransactions(context.Background(), multiTransaction, data, bridges, account)
@ -220,8 +220,8 @@ func TestSendTransactionsETHFailOnTransactor(t *testing.T) {
// Verify that the SendTransactionWithChainID method is called for each transaction with proper arguments
// Return values are not checked, because they must be checked in Transactor tests. Only error propagation matters here
expectedErr := transactions.ErrInvalidTxSender // Any error to verify
transactor.EXPECT().SendTransactionWithChainID(expectedData[0].ChainID, *(expectedData[0].TransferTx), account).Return(types.Hash{}, nil)
transactor.EXPECT().SendTransactionWithChainID(expectedData[1].ChainID, *(expectedData[1].TransferTx), account).Return(types.Hash{}, expectedErr)
transactor.EXPECT().SendTransactionWithChainID(expectedData[0].ChainID, *(expectedData[0].TransferTx), int64(-1), account).Return(types.Hash{}, uint64(0), nil)
transactor.EXPECT().SendTransactionWithChainID(expectedData[1].ChainID, *(expectedData[1].TransferTx), int64(-1), account).Return(types.Hash{}, uint64(0), expectedErr)
// Call the SendTransactions method
_, err := tm.SendTransactions(context.Background(), multiTransaction, data, bridges, account)

View File

@ -280,7 +280,7 @@ func TestBuildTransaction(t *testing.T) {
}
expectedTx := gethtypes.NewTransaction(nonce, common.Address(*sendArgs.To), sendArgs.Value.ToInt(), gas, sendArgs.GasPrice.ToInt(), nil)
transactor.EXPECT().ValidateAndBuildTransaction(chainID, sendArgs).Return(expectedTx, nil)
transactor.EXPECT().ValidateAndBuildTransaction(chainID, sendArgs, int64(-1)).Return(expectedTx, uint64(0), nil)
response, err := manager.BuildTransaction(chainID, sendArgs)
require.NoError(t, err)
@ -334,7 +334,7 @@ func TestBuildTransaction_InvalidSendTxArgs(t *testing.T) {
}
expectedErr := fmt.Errorf("invalid SendTxArgs")
transactor.EXPECT().ValidateAndBuildTransaction(chainID, sendArgs).Return(nil, expectedErr)
transactor.EXPECT().ValidateAndBuildTransaction(chainID, sendArgs, int64(-1)).Return(nil, uint64(0), expectedErr)
tx, err := manager.BuildTransaction(chainID, sendArgs)
require.Equal(t, expectedErr, err)
require.Nil(t, tx)

View File

@ -79,7 +79,7 @@ func (api *API) sendTransaction(chainID uint64, sendArgs transactions.SendTxArgs
return hash, err
}
hash, err = api.s.transactor.SendTransactionWithChainID(chainID, sendArgs, verifiedAccount)
hash, _, err = api.s.transactor.SendTransactionWithChainID(chainID, sendArgs, -1, verifiedAccount)
if err != nil {
return
}

View File

@ -8,10 +8,9 @@ import (
big "math/big"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
common "github.com/ethereum/go-ethereum/common"
types "github.com/ethereum/go-ethereum/core/types"
gomock "github.com/golang/mock/gomock"
account "github.com/status-im/status-go/account"
types0 "github.com/status-im/status-go/eth-node/types"
params "github.com/status-im/status-go/params"
@ -118,33 +117,35 @@ func (mr *MockTransactorIfaceMockRecorder) SendRawTransaction(chainID, rawTx int
}
// SendTransaction mocks base method.
func (m *MockTransactorIface) SendTransaction(sendArgs transactions.SendTxArgs, verifiedAccount *account.SelectedExtKey) (types0.Hash, error) {
func (m *MockTransactorIface) SendTransaction(sendArgs transactions.SendTxArgs, verifiedAccount *account.SelectedExtKey, lastUsedNonce int64) (types0.Hash, uint64, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SendTransaction", sendArgs, verifiedAccount)
ret := m.ctrl.Call(m, "SendTransaction", sendArgs, verifiedAccount, lastUsedNonce)
ret0, _ := ret[0].(types0.Hash)
ret1, _ := ret[1].(error)
return ret0, ret1
ret1, _ := ret[1].(uint64)
ret2, _ := ret[2].(error)
return ret0, ret1, ret2
}
// SendTransaction indicates an expected call of SendTransaction.
func (mr *MockTransactorIfaceMockRecorder) SendTransaction(sendArgs, verifiedAccount interface{}) *gomock.Call {
func (mr *MockTransactorIfaceMockRecorder) SendTransaction(sendArgs, verifiedAccount, lastUsedNonce interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendTransaction", reflect.TypeOf((*MockTransactorIface)(nil).SendTransaction), sendArgs, verifiedAccount)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendTransaction", reflect.TypeOf((*MockTransactorIface)(nil).SendTransaction), sendArgs, verifiedAccount, lastUsedNonce)
}
// SendTransactionWithChainID mocks base method.
func (m *MockTransactorIface) SendTransactionWithChainID(chainID uint64, sendArgs transactions.SendTxArgs, verifiedAccount *account.SelectedExtKey) (types0.Hash, error) {
func (m *MockTransactorIface) SendTransactionWithChainID(chainID uint64, sendArgs transactions.SendTxArgs, lastUsedNonce int64, verifiedAccount *account.SelectedExtKey) (types0.Hash, uint64, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SendTransactionWithChainID", chainID, sendArgs, verifiedAccount)
ret := m.ctrl.Call(m, "SendTransactionWithChainID", chainID, sendArgs, lastUsedNonce, verifiedAccount)
ret0, _ := ret[0].(types0.Hash)
ret1, _ := ret[1].(error)
return ret0, ret1
ret1, _ := ret[1].(uint64)
ret2, _ := ret[2].(error)
return ret0, ret1, ret2
}
// SendTransactionWithChainID indicates an expected call of SendTransactionWithChainID.
func (mr *MockTransactorIfaceMockRecorder) SendTransactionWithChainID(chainID, sendArgs, verifiedAccount interface{}) *gomock.Call {
func (mr *MockTransactorIfaceMockRecorder) SendTransactionWithChainID(chainID, sendArgs, lastUsedNonce, verifiedAccount interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendTransactionWithChainID", reflect.TypeOf((*MockTransactorIface)(nil).SendTransactionWithChainID), chainID, sendArgs, verifiedAccount)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendTransactionWithChainID", reflect.TypeOf((*MockTransactorIface)(nil).SendTransactionWithChainID), chainID, sendArgs, lastUsedNonce, verifiedAccount)
}
// SendTransactionWithSignature mocks base method.
@ -177,16 +178,17 @@ func (mr *MockTransactorIfaceMockRecorder) StoreAndTrackPendingTx(from, symbol,
}
// ValidateAndBuildTransaction mocks base method.
func (m *MockTransactorIface) ValidateAndBuildTransaction(chainID uint64, sendArgs transactions.SendTxArgs) (*types.Transaction, error) {
func (m *MockTransactorIface) ValidateAndBuildTransaction(chainID uint64, sendArgs transactions.SendTxArgs, lastUsedNonce int64) (*types.Transaction, uint64, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ValidateAndBuildTransaction", chainID, sendArgs)
ret := m.ctrl.Call(m, "ValidateAndBuildTransaction", chainID, sendArgs, lastUsedNonce)
ret0, _ := ret[0].(*types.Transaction)
ret1, _ := ret[1].(error)
return ret0, ret1
ret1, _ := ret[1].(uint64)
ret2, _ := ret[2].(error)
return ret0, ret1, ret2
}
// ValidateAndBuildTransaction indicates an expected call of ValidateAndBuildTransaction.
func (mr *MockTransactorIfaceMockRecorder) ValidateAndBuildTransaction(chainID, sendArgs interface{}) *gomock.Call {
func (mr *MockTransactorIfaceMockRecorder) ValidateAndBuildTransaction(chainID, sendArgs, lastUsedNonce interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateAndBuildTransaction", reflect.TypeOf((*MockTransactorIface)(nil).ValidateAndBuildTransaction), chainID, sendArgs)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateAndBuildTransaction", reflect.TypeOf((*MockTransactorIface)(nil).ValidateAndBuildTransaction), chainID, sendArgs, lastUsedNonce)
}

View File

@ -48,9 +48,9 @@ func (e *ErrBadNonce) Error() string {
type TransactorIface interface {
NextNonce(rpcClient rpc.ClientInterface, chainID uint64, from types.Address) (uint64, error)
EstimateGas(network *params.Network, from common.Address, to common.Address, value *big.Int, input []byte) (uint64, error)
SendTransaction(sendArgs SendTxArgs, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error)
SendTransactionWithChainID(chainID uint64, sendArgs SendTxArgs, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error)
ValidateAndBuildTransaction(chainID uint64, sendArgs SendTxArgs) (tx *gethtypes.Transaction, err error)
SendTransaction(sendArgs SendTxArgs, verifiedAccount *account.SelectedExtKey, lastUsedNonce int64) (hash types.Hash, nonce uint64, err error)
SendTransactionWithChainID(chainID uint64, sendArgs SendTxArgs, lastUsedNonce int64, verifiedAccount *account.SelectedExtKey) (hash types.Hash, nonce uint64, err error)
ValidateAndBuildTransaction(chainID uint64, sendArgs SendTxArgs, lastUsedNonce int64) (tx *gethtypes.Transaction, nonce uint64, err error)
AddSignatureToTransaction(chainID uint64, tx *gethtypes.Transaction, sig []byte) (*gethtypes.Transaction, error)
SendRawTransaction(chainID uint64, rawTx string) error
BuildTransactionWithSignature(chainID uint64, args SendTxArgs, sig []byte) (*gethtypes.Transaction, error)
@ -139,21 +139,21 @@ func (t *Transactor) EstimateGas(network *params.Network, from common.Address, t
}
// SendTransaction is an implementation of eth_sendTransaction. It queues the tx to the sign queue.
func (t *Transactor) SendTransaction(sendArgs SendTxArgs, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error) {
hash, err = t.validateAndPropagate(t.rpcWrapper, verifiedAccount, sendArgs)
func (t *Transactor) SendTransaction(sendArgs SendTxArgs, verifiedAccount *account.SelectedExtKey, lastUsedNonce int64) (hash types.Hash, nonce uint64, err error) {
hash, nonce, err = t.validateAndPropagate(t.rpcWrapper, verifiedAccount, sendArgs, lastUsedNonce)
return
}
func (t *Transactor) SendTransactionWithChainID(chainID uint64, sendArgs SendTxArgs, verifiedAccount *account.SelectedExtKey) (hash types.Hash, err error) {
func (t *Transactor) SendTransactionWithChainID(chainID uint64, sendArgs SendTxArgs, lastUsedNonce int64, verifiedAccount *account.SelectedExtKey) (hash types.Hash, nonce uint64, err error) {
wrapper := newRPCWrapper(t.rpcWrapper.RPCClient, chainID)
hash, err = t.validateAndPropagate(wrapper, verifiedAccount, sendArgs)
hash, nonce, err = t.validateAndPropagate(wrapper, verifiedAccount, sendArgs, lastUsedNonce)
return
}
func (t *Transactor) ValidateAndBuildTransaction(chainID uint64, sendArgs SendTxArgs) (tx *gethtypes.Transaction, err error) {
func (t *Transactor) ValidateAndBuildTransaction(chainID uint64, sendArgs SendTxArgs, lastUsedNonce int64) (tx *gethtypes.Transaction, nonce uint64, err error) {
wrapper := newRPCWrapper(t.rpcWrapper.RPCClient, chainID)
tx, err = t.validateAndBuildTransaction(wrapper, sendArgs)
return
tx, err = t.validateAndBuildTransaction(wrapper, sendArgs, lastUsedNonce)
return tx, tx.Nonce(), err
}
func (t *Transactor) AddSignatureToTransaction(chainID uint64, tx *gethtypes.Transaction, sig []byte) (*gethtypes.Transaction, error) {
@ -361,7 +361,7 @@ func (t *Transactor) validateAccount(args SendTxArgs, selectedAccount *account.S
return nil
}
func (t *Transactor) validateAndBuildTransaction(rpcWrapper *rpcWrapper, args SendTxArgs) (tx *gethtypes.Transaction, err error) {
func (t *Transactor) validateAndBuildTransaction(rpcWrapper *rpcWrapper, args SendTxArgs, lastUsedNonce int64) (tx *gethtypes.Transaction, err error) {
if !args.Valid() {
return tx, ErrInvalidSendTxArgs
}
@ -370,9 +370,14 @@ func (t *Transactor) validateAndBuildTransaction(rpcWrapper *rpcWrapper, args Se
if args.Nonce != nil {
nonce = uint64(*args.Nonce)
} else {
nonce, err = t.NextNonce(rpcWrapper.RPCClient, rpcWrapper.chainID, args.From)
if err != nil {
return tx, err
// some chains, like arbitrum doesn't count pending txs in the nonce, so we need to calculate it manually
if lastUsedNonce < 0 {
nonce, err = t.NextNonce(rpcWrapper.RPCClient, rpcWrapper.chainID, args.From)
if err != nil {
return tx, err
}
} else {
nonce = uint64(lastUsedNonce) + 1
}
}
@ -433,23 +438,24 @@ func (t *Transactor) validateAndBuildTransaction(rpcWrapper *rpcWrapper, args Se
return tx, nil
}
func (t *Transactor) validateAndPropagate(rpcWrapper *rpcWrapper, selectedAccount *account.SelectedExtKey, args SendTxArgs) (hash types.Hash, err error) {
func (t *Transactor) validateAndPropagate(rpcWrapper *rpcWrapper, selectedAccount *account.SelectedExtKey, args SendTxArgs, lastUsedNonce int64) (hash types.Hash, nonce uint64, err error) {
if err = t.validateAccount(args, selectedAccount); err != nil {
return hash, err
return hash, nonce, err
}
tx, err := t.validateAndBuildTransaction(rpcWrapper, args)
tx, err := t.validateAndBuildTransaction(rpcWrapper, args, lastUsedNonce)
if err != nil {
return hash, err
return hash, nonce, err
}
chainID := big.NewInt(int64(rpcWrapper.chainID))
signedTx, err := gethtypes.SignTx(tx, gethtypes.NewLondonSigner(chainID), selectedAccount.AccountKey.PrivateKey)
if err != nil {
return hash, err
return hash, nonce, err
}
return t.sendTransaction(rpcWrapper, common.Address(args.From), args.Symbol, args.MultiTransactionID, signedTx)
hash, err = t.sendTransaction(rpcWrapper, common.Address(args.From), args.Symbol, args.MultiTransactionID, signedTx)
return hash, tx.Nonce(), err
}
func (t *Transactor) buildTransaction(args SendTxArgs) *gethtypes.Transaction {

View File

@ -207,7 +207,7 @@ func (s *TransactorSuite) TestGasValues() {
}
s.setupTransactionPoolAPI(args, testNonce, testNonce, selectedAccount, nil)
hash, err := s.manager.SendTransaction(args, selectedAccount)
hash, _, err := s.manager.SendTransaction(args, selectedAccount, -1)
s.NoError(err)
s.False(reflect.DeepEqual(hash, common.Hash{}))
})
@ -256,7 +256,7 @@ func (s *TransactorSuite) TestBuildAndValidateTransaction() {
}
s.setupBuildTransactionMocks(args, selectedAccount)
tx, err := s.manager.ValidateAndBuildTransaction(chainID, args)
tx, _, err := s.manager.ValidateAndBuildTransaction(chainID, args, -1)
s.NoError(err)
s.Equal(tx.Gas(), uint64(gas), "The gas shouldn't be estimated, but should use the gas from the Tx")
s.Equal(tx.GasFeeCap(), expectedGasPrice, "The maxFeePerGas should be the same as in the original Tx")
@ -275,7 +275,7 @@ func (s *TransactorSuite) TestBuildAndValidateTransaction() {
}
s.setupBuildTransactionMocks(args, selectedAccount)
tx, err := s.manager.ValidateAndBuildTransaction(chainID, args)
tx, _, err := s.manager.ValidateAndBuildTransaction(chainID, args, -1)
s.NoError(err)
s.Equal(tx.Gas(), expectedGas, "The gas should be estimated if not present in the original Tx")
s.Equal(tx.Nonce(), expectedNonce, "The nonce should be added if not present in the original Tx")
@ -298,7 +298,7 @@ func (s *TransactorSuite) TestBuildAndValidateTransaction() {
}
s.setupBuildTransactionMocks(args, selectedAccount)
tx, err := s.manager.ValidateAndBuildTransaction(chainID, args)
tx, _, err := s.manager.ValidateAndBuildTransaction(chainID, args, -1)
s.NoError(err)
s.Equal(tx.Gas(), uint64(gas), "The gas shouldn't be estimated, but should use the gas from the Tx")
s.Equal(tx.GasPrice(), expectedGasPrice, "The gasPrice should be the same as in the original Tx")
@ -314,7 +314,7 @@ func (s *TransactorSuite) TestBuildAndValidateTransaction() {
}
s.setupBuildTransactionMocks(args, selectedAccount)
tx, err := s.manager.ValidateAndBuildTransaction(chainID, args)
tx, _, err := s.manager.ValidateAndBuildTransaction(chainID, args, -1)
s.NoError(err)
s.Equal(tx.Gas(), expectedGas, "The gas should be estimated if not present in the original Tx")
s.Equal(tx.GasPrice(), expectedGasPrice, "The gasPrice should be estimated if not present in the original Tx")
@ -333,7 +333,7 @@ func (s *TransactorSuite) TestArgsValidation() {
selectedAccount := &account.SelectedExtKey{
Address: account.FromAddress(utils.TestConfig.Account1.WalletAddress),
}
_, err := s.manager.SendTransaction(args, selectedAccount)
_, _, err := s.manager.SendTransaction(args, selectedAccount, -1)
s.EqualError(err, ErrInvalidSendTxArgs.Error())
}
@ -346,14 +346,14 @@ func (s *TransactorSuite) TestAccountMismatch() {
var err error
// missing account
_, err = s.manager.SendTransaction(args, nil)
_, _, err = s.manager.SendTransaction(args, nil, -1)
s.EqualError(err, account.ErrNoAccountSelected.Error())
// mismatched accounts
selectedAccount := &account.SelectedExtKey{
Address: account.FromAddress(utils.TestConfig.Account2.WalletAddress),
}
_, err = s.manager.SendTransaction(args, selectedAccount)
_, _, err = s.manager.SendTransaction(args, selectedAccount, -1)
s.EqualError(err, ErrInvalidTxSender.Error())
}