test(wallet)_: created Transactor interface

- Moved some methods from Transactor to users of it to clean interface.
- Mocked Bridge interface and Transactor interface for tests
- Wrote unit tests for SendTransaction
This commit is contained in:
Ivan Belyakov 2024-05-26 10:31:13 +02:00 committed by IvanBelyakoff
parent 4d1149100f
commit a135b27980
22 changed files with 674 additions and 101 deletions

View File

@ -342,6 +342,8 @@ mock: ##@other Regenerate mocks
mockgen -package=fake -destination=transactions/fake/mock.go -source=transactions/fake/txservice.go mockgen -package=fake -destination=transactions/fake/mock.go -source=transactions/fake/txservice.go
mockgen -package=status -destination=services/status/account_mock.go -source=services/status/service.go mockgen -package=status -destination=services/status/account_mock.go -source=services/status/service.go
mockgen -package=peer -destination=services/peer/discoverer_mock.go -source=services/peer/service.go mockgen -package=peer -destination=services/peer/discoverer_mock.go -source=services/peer/service.go
mockgen -package=mock_transactor -destination=transactions/mock_transactor/transactor.go -source=transactions/transactor.go
mockgen -package=mock_bridge -destination=services/wallet/bridge/mock_bridge/bridge.go -source=services/wallet/bridge/bridge.go
docker-test: ##@tests Run tests in a docker container with golang. docker-test: ##@tests Run tests in a docker container with golang.
docker run --privileged --rm -it -v "$(PWD):$(DOCKER_TEST_WORKDIR)" -w "$(DOCKER_TEST_WORKDIR)" $(DOCKER_TEST_IMAGE) go test ${ARGS} docker run --privileged --rm -it -v "$(PWD):$(DOCKER_TEST_WORKDIR)" -w "$(DOCKER_TEST_WORKDIR)" $(DOCKER_TEST_IMAGE) go test ${ARGS}

View File

@ -18,6 +18,7 @@ import (
"github.com/imdario/mergo" "github.com/imdario/mergo"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
ethcrypto "github.com/ethereum/go-ethereum/crypto" ethcrypto "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
@ -1950,7 +1951,12 @@ func (b *GethStatusBackend) SendTransactionWithChainID(chainID uint64, sendArgs
} }
func (b *GethStatusBackend) SendTransactionWithSignature(sendArgs transactions.SendTxArgs, sig []byte) (hash types.Hash, err error) { func (b *GethStatusBackend) SendTransactionWithSignature(sendArgs transactions.SendTxArgs, sig []byte) (hash types.Hash, err error) {
return b.transactor.BuildTransactionAndSendWithSignature(b.transactor.NetworkID(), sendArgs, sig) txWithSignature, err := b.transactor.BuildTransactionWithSignature(b.transactor.NetworkID(), sendArgs, sig)
if err != nil {
return hash, err
}
return b.transactor.SendTransactionWithSignature(common.Address(sendArgs.From), sendArgs.Symbol, sendArgs.MultiTransactionID, txWithSignature)
} }
// HashTransaction validate the transaction and returns new sendArgs and the transaction hash. // HashTransaction validate the transaction and returns new sendArgs and the transaction hash.

View File

@ -602,13 +602,13 @@ func (api *API) SendTransactionWithSignature(ctx context.Context, chainID uint64
if err != nil { if err != nil {
return hash, err return hash, err
} }
return api.s.transactionManager.SendTransactionWithSignature(chainID, txType, params, sig) return api.s.transactionManager.SendTransactionWithSignature(chainID, params, sig)
} }
func (api *API) CreateMultiTransaction(ctx context.Context, multiTransactionCommand *transfer.MultiTransactionCommand, data []*bridge.TransactionBridge, password string) (*transfer.MultiTransactionCommandResult, error) { func (api *API) CreateMultiTransaction(ctx context.Context, multiTransactionCommand *transfer.MultiTransactionCommand, data []*bridge.TransactionBridge, password string) (*transfer.MultiTransactionCommandResult, error) {
log.Debug("[WalletAPI:: CreateMultiTransaction] create multi transaction") log.Debug("[WalletAPI:: CreateMultiTransaction] create multi transaction")
cmd, err := api.s.transactionManager.CreateMultiTransactionFromCommand(ctx, multiTransactionCommand, data) cmd, err := api.s.transactionManager.CreateMultiTransactionFromCommand(multiTransactionCommand, data)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -43,13 +43,13 @@ type CBridgeTxArgs struct {
type CBridge struct { type CBridge struct {
rpcClient *rpc.Client rpcClient *rpc.Client
httpClient *thirdparty.HTTPClient httpClient *thirdparty.HTTPClient
transactor *transactions.Transactor transactor transactions.TransactorIface
tokenManager *token.Manager tokenManager *token.Manager
prodTransferConfig *cbridge.GetTransferConfigsResponse prodTransferConfig *cbridge.GetTransferConfigsResponse
testTransferConfig *cbridge.GetTransferConfigsResponse testTransferConfig *cbridge.GetTransferConfigsResponse
} }
func NewCbridge(rpcClient *rpc.Client, transactor *transactions.Transactor, tokenManager *token.Manager) *CBridge { func NewCbridge(rpcClient *rpc.Client, transactor transactions.TransactorIface, tokenManager *token.Manager) *CBridge {
return &CBridge{ return &CBridge{
rpcClient: rpcClient, rpcClient: rpcClient,
httpClient: thirdparty.NewHTTPClient(), httpClient: thirdparty.NewHTTPClient(),

View File

@ -30,10 +30,10 @@ type ERC1155TransferTxArgs struct {
type ERC1155TransferBridge struct { type ERC1155TransferBridge struct {
rpcClient *rpc.Client rpcClient *rpc.Client
transactor *transactions.Transactor transactor transactions.TransactorIface
} }
func NewERC1155TransferBridge(rpcClient *rpc.Client, transactor *transactions.Transactor) *ERC1155TransferBridge { func NewERC1155TransferBridge(rpcClient *rpc.Client, transactor transactions.TransactorIface) *ERC1155TransferBridge {
return &ERC1155TransferBridge{rpcClient: rpcClient, transactor: transactor} return &ERC1155TransferBridge{rpcClient: rpcClient, transactor: transactor}
} }

View File

@ -29,10 +29,10 @@ type ERC721TransferTxArgs struct {
type ERC721TransferBridge struct { type ERC721TransferBridge struct {
rpcClient *rpc.Client rpcClient *rpc.Client
transactor *transactions.Transactor transactor transactions.TransactorIface
} }
func NewERC721TransferBridge(rpcClient *rpc.Client, transactor *transactions.Transactor) *ERC721TransferBridge { func NewERC721TransferBridge(rpcClient *rpc.Client, transactor transactions.TransactorIface) *ERC721TransferBridge {
return &ERC721TransferBridge{rpcClient: rpcClient, transactor: transactor} return &ERC721TransferBridge{rpcClient: rpcClient, transactor: transactor}
} }

View File

@ -107,14 +107,14 @@ func (bf *BonderFee) UnmarshalJSON(data []byte) error {
} }
type HopBridge struct { type HopBridge struct {
transactor *transactions.Transactor transactor transactions.TransactorIface
httpClient *thirdparty.HTTPClient httpClient *thirdparty.HTTPClient
tokenManager *token.Manager tokenManager *token.Manager
contractMaker *contracts.ContractMaker contractMaker *contracts.ContractMaker
bonderFee *BonderFee bonderFee *BonderFee
} }
func NewHopBridge(rpcClient *rpc.Client, transactor *transactions.Transactor, tokenManager *token.Manager) *HopBridge { func NewHopBridge(rpcClient *rpc.Client, transactor transactions.TransactorIface, tokenManager *token.Manager) *HopBridge {
return &HopBridge{ return &HopBridge{
contractMaker: &contracts.ContractMaker{RPCClient: rpcClient}, contractMaker: &contracts.ContractMaker{RPCClient: rpcClient},
httpClient: thirdparty.NewHTTPClient(), httpClient: thirdparty.NewHTTPClient(),

View File

@ -0,0 +1,2 @@
# To generate mocks, from status-go root directory:
mockgen -source=services/wallet/bridge/bridge.go -destination=services/wallet/bridge/mock_bridge/bridge.go -package=mock_bridge

View File

@ -0,0 +1,192 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: services/wallet/bridge/bridge.go
// Package mock_bridge is a generated GoMock package.
package mock_bridge
import (
big "math/big"
reflect "reflect"
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"
bridge "github.com/status-im/status-go/services/wallet/bridge"
token "github.com/status-im/status-go/services/wallet/token"
)
// MockBridge is a mock of Bridge interface.
type MockBridge struct {
ctrl *gomock.Controller
recorder *MockBridgeMockRecorder
}
// MockBridgeMockRecorder is the mock recorder for MockBridge.
type MockBridgeMockRecorder struct {
mock *MockBridge
}
// NewMockBridge creates a new mock instance.
func NewMockBridge(ctrl *gomock.Controller) *MockBridge {
mock := &MockBridge{ctrl: ctrl}
mock.recorder = &MockBridgeMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockBridge) EXPECT() *MockBridgeMockRecorder {
return m.recorder
}
// AvailableFor mocks base method.
func (m *MockBridge) AvailableFor(from, to *params.Network, token, toToken *token.Token) (bool, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AvailableFor", from, to, token, toToken)
ret0, _ := ret[0].(bool)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// AvailableFor indicates an expected call of AvailableFor.
func (mr *MockBridgeMockRecorder) AvailableFor(from, to, token, toToken interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AvailableFor", reflect.TypeOf((*MockBridge)(nil).AvailableFor), from, to, token, toToken)
}
// BuildTransaction mocks base method.
func (m *MockBridge) BuildTransaction(sendArgs *bridge.TransactionBridge) (*types.Transaction, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "BuildTransaction", sendArgs)
ret0, _ := ret[0].(*types.Transaction)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// BuildTransaction indicates an expected call of BuildTransaction.
func (mr *MockBridgeMockRecorder) BuildTransaction(sendArgs interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BuildTransaction", reflect.TypeOf((*MockBridge)(nil).BuildTransaction), sendArgs)
}
// BuildTx mocks base method.
func (m *MockBridge) BuildTx(fromNetwork, toNetwork *params.Network, fromAddress, toAddress common.Address, token *token.Token, amountIn, bonderFee *big.Int) (*types.Transaction, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "BuildTx", fromNetwork, toNetwork, fromAddress, toAddress, token, amountIn, bonderFee)
ret0, _ := ret[0].(*types.Transaction)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// BuildTx indicates an expected call of BuildTx.
func (mr *MockBridgeMockRecorder) BuildTx(fromNetwork, toNetwork, fromAddress, toAddress, token, amountIn, bonderFee interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BuildTx", reflect.TypeOf((*MockBridge)(nil).BuildTx), fromNetwork, toNetwork, fromAddress, toAddress, token, amountIn, bonderFee)
}
// CalculateAmountOut mocks base method.
func (m *MockBridge) CalculateAmountOut(from, to *params.Network, amountIn *big.Int, symbol string) (*big.Int, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CalculateAmountOut", from, to, amountIn, symbol)
ret0, _ := ret[0].(*big.Int)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CalculateAmountOut indicates an expected call of CalculateAmountOut.
func (mr *MockBridgeMockRecorder) CalculateAmountOut(from, to, amountIn, symbol interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CalculateAmountOut", reflect.TypeOf((*MockBridge)(nil).CalculateAmountOut), from, to, amountIn, symbol)
}
// CalculateFees mocks base method.
func (m *MockBridge) CalculateFees(from, to *params.Network, token *token.Token, amountIn *big.Int) (*big.Int, *big.Int, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CalculateFees", from, to, token, amountIn)
ret0, _ := ret[0].(*big.Int)
ret1, _ := ret[1].(*big.Int)
ret2, _ := ret[2].(error)
return ret0, ret1, ret2
}
// CalculateFees indicates an expected call of CalculateFees.
func (mr *MockBridgeMockRecorder) CalculateFees(from, to, token, amountIn interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CalculateFees", reflect.TypeOf((*MockBridge)(nil).CalculateFees), from, to, token, amountIn)
}
// EstimateGas mocks base method.
func (m *MockBridge) EstimateGas(fromNetwork, toNetwork *params.Network, from, to common.Address, token, toToken *token.Token, amountIn *big.Int) (uint64, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "EstimateGas", fromNetwork, toNetwork, from, to, token, toToken, amountIn)
ret0, _ := ret[0].(uint64)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// EstimateGas indicates an expected call of EstimateGas.
func (mr *MockBridgeMockRecorder) EstimateGas(fromNetwork, toNetwork, from, to, token, toToken, amountIn interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EstimateGas", reflect.TypeOf((*MockBridge)(nil).EstimateGas), fromNetwork, toNetwork, from, to, token, toToken, amountIn)
}
// GetContractAddress mocks base method.
func (m *MockBridge) GetContractAddress(network *params.Network, token *token.Token) (common.Address, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetContractAddress", network, token)
ret0, _ := ret[0].(common.Address)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetContractAddress indicates an expected call of GetContractAddress.
func (mr *MockBridgeMockRecorder) GetContractAddress(network, token interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetContractAddress", reflect.TypeOf((*MockBridge)(nil).GetContractAddress), network, token)
}
// Name mocks base method.
func (m *MockBridge) Name() string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Name")
ret0, _ := ret[0].(string)
return ret0
}
// Name indicates an expected call of Name.
func (mr *MockBridgeMockRecorder) Name() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockBridge)(nil).Name))
}
// PackTxInputData mocks base method.
func (m *MockBridge) PackTxInputData(contractType string, fromNetwork, toNetwork *params.Network, from, to common.Address, token *token.Token, amountIn *big.Int) ([]byte, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "PackTxInputData", contractType, fromNetwork, toNetwork, from, to, token, amountIn)
ret0, _ := ret[0].([]byte)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// PackTxInputData indicates an expected call of PackTxInputData.
func (mr *MockBridgeMockRecorder) PackTxInputData(contractType, fromNetwork, toNetwork, from, to, token, amountIn interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PackTxInputData", reflect.TypeOf((*MockBridge)(nil).PackTxInputData), contractType, fromNetwork, toNetwork, from, to, token, amountIn)
}
// Send mocks base method.
func (m *MockBridge) Send(sendArgs *bridge.TransactionBridge, verifiedAccount *account.SelectedExtKey) (types0.Hash, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Send", sendArgs, verifiedAccount)
ret0, _ := ret[0].(types0.Hash)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Send indicates an expected call of Send.
func (mr *MockBridgeMockRecorder) Send(sendArgs, verifiedAccount interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Send", reflect.TypeOf((*MockBridge)(nil).Send), sendArgs, verifiedAccount)
}

View File

@ -28,10 +28,10 @@ type SwapTxArgs struct {
type SwapParaswap struct { type SwapParaswap struct {
paraswapClient *paraswap.ClientV5 paraswapClient *paraswap.ClientV5
priceRoute paraswap.Route priceRoute paraswap.Route
transactor *transactions.Transactor transactor transactions.TransactorIface
} }
func NewSwapParaswap(rpcClient *rpc.Client, transactor *transactions.Transactor, tokenManager *walletToken.Manager) *SwapParaswap { func NewSwapParaswap(rpcClient *rpc.Client, transactor transactions.TransactorIface, tokenManager *walletToken.Manager) *SwapParaswap {
return &SwapParaswap{ return &SwapParaswap{
paraswapClient: paraswap.NewClientV5(walletCommon.EthereumMainnet), paraswapClient: paraswap.NewClientV5(walletCommon.EthereumMainnet),
transactor: transactor, transactor: transactor,

View File

@ -21,10 +21,10 @@ import (
type TransferBridge struct { type TransferBridge struct {
rpcClient *rpc.Client rpcClient *rpc.Client
transactor *transactions.Transactor transactor transactions.TransactorIface
} }
func NewTransferBridge(rpcClient *rpc.Client, transactor *transactions.Transactor) *TransferBridge { func NewTransferBridge(rpcClient *rpc.Client, transactor transactions.TransactorIface) *TransferBridge {
return &TransferBridge{rpcClient: rpcClient, transactor: transactor} return &TransferBridge{rpcClient: rpcClient, transactor: transactor}
} }

View File

@ -178,7 +178,7 @@ func (tm *TransactionManager) removeMultiTransactionByAddress(address common.Add
details := NewMultiTxDetails() details := NewMultiTxDetails()
details.FromAddress = address details.FromAddress = address
mtxs, err := tm.storage.ReadMultiTransactionsByDetails(details) mtxs, err := tm.storage.ReadMultiTransactions(details)
ids := make([]wallet_common.MultiTransactionIDType, 0) ids := make([]wallet_common.MultiTransactionIDType, 0)
for _, mtx := range mtxs { for _, mtx := range mtxs {

View File

@ -9,20 +9,38 @@ import (
wallet_common "github.com/status-im/status-go/services/wallet/common" wallet_common "github.com/status-im/status-go/services/wallet/common"
) )
// DO NOT CREATE IT MANUALLY! Use NewMultiTxDetails() instead // Since we already use MultitransactionIDType in DB, and its default value is 0 (Send)
// this type is used to with default value 0 to represent invalid type to avoid bugs
// when devs forget to call NewMultiTxDetails()
type MultiTransactionDBType MultiTransactionType
const (
MultiTransactionDBTypeInvalid = 0
MultiTransactionDBSend = iota
MultiTransactionDBSwap
MultiTransactionDBBridge
)
func mtDBTypeToMTType(mtDBType MultiTransactionDBType) MultiTransactionType {
if mtDBType == MultiTransactionDBTypeInvalid {
return MultiTransactionTypeInvalid
}
return MultiTransactionType(mtDBType - 1)
}
type MultiTxDetails struct { type MultiTxDetails struct {
IDs []wallet_common.MultiTransactionIDType
AnyAddress common.Address AnyAddress common.Address
FromAddress common.Address FromAddress common.Address
ToAddress common.Address ToAddress common.Address
ToChainID uint64 ToChainID uint64
CrossTxID string CrossTxID string
Type MultiTransactionType Type MultiTransactionDBType
} }
func NewMultiTxDetails() *MultiTxDetails { func NewMultiTxDetails() *MultiTxDetails {
details := &MultiTxDetails{} return &MultiTxDetails{}
details.Type = MultiTransactionTypeInvalid
return details
} }
type MultiTransactionDB struct { type MultiTransactionDB struct {
@ -65,34 +83,7 @@ func (mtDB *MultiTransactionDB) CreateMultiTransaction(multiTransaction *MultiTr
return err return err
} }
func (mtDB *MultiTransactionDB) ReadMultiTransactions(ids []wallet_common.MultiTransactionIDType) ([]*MultiTransaction, error) { func (mtDB *MultiTransactionDB) ReadMultiTransactions(details *MultiTxDetails) ([]*MultiTransaction, error) {
placeholders := make([]string, len(ids))
args := make([]interface{}, len(ids))
for i, v := range ids {
placeholders[i] = "?"
args[i] = v
}
stmt, err := mtDB.db.Prepare(fmt.Sprintf(`SELECT %s
FROM multi_transactions
WHERE id in (%s)`,
selectMultiTransactionColumns,
strings.Join(placeholders, ",")))
if err != nil {
return nil, err
}
defer stmt.Close()
rows, err := stmt.Query(args...)
if err != nil {
return nil, err
}
defer rows.Close()
return rowsToMultiTransactions(rows)
}
func (mtDB *MultiTransactionDB) ReadMultiTransactionsByDetails(details *MultiTxDetails) ([]*MultiTransaction, error) {
if details == nil { if details == nil {
return nil, fmt.Errorf("details is nil") return nil, fmt.Errorf("details is nil")
} }
@ -101,6 +92,14 @@ func (mtDB *MultiTransactionDB) ReadMultiTransactionsByDetails(details *MultiTxD
args := []interface{}{} args := []interface{}{}
if len(details.IDs) > 0 {
placeholders := make([]string, len(details.IDs))
for i, v := range details.IDs {
placeholders[i] = "?"
args = append(args, v)
}
whereClause += fmt.Sprintf("id in (%s) AND ", strings.Join(placeholders, ","))
}
if (details.AnyAddress != common.Address{}) { if (details.AnyAddress != common.Address{}) {
whereClause += "(from_address=? OR to_address=?) AND " whereClause += "(from_address=? OR to_address=?) AND "
args = append(args, details.AnyAddress, details.AnyAddress) args = append(args, details.AnyAddress, details.AnyAddress)
@ -121,9 +120,9 @@ func (mtDB *MultiTransactionDB) ReadMultiTransactionsByDetails(details *MultiTxD
whereClause += "cross_tx_id=? AND " whereClause += "cross_tx_id=? AND "
args = append(args, details.CrossTxID) args = append(args, details.CrossTxID)
} }
if details.Type != MultiTransactionTypeInvalid { if details.Type != MultiTransactionDBTypeInvalid {
whereClause += "type=? AND " whereClause += "type=? AND "
args = append(args, details.Type) args = append(args, mtDBTypeToMTType(details.Type))
} }
stmt, err := mtDB.db.Prepare(fmt.Sprintf(`SELECT %s stmt, err := mtDB.db.Prepare(fmt.Sprintf(`SELECT %s

View File

@ -32,7 +32,9 @@ func TestCreateMultiTransaction(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
// Add assertions here to verify the result of the CreateMultiTransaction method // Add assertions here to verify the result of the CreateMultiTransaction method
mtx, err := mtDB.ReadMultiTransactions([]wallet_common.MultiTransactionIDType{multiTransaction.ID}) details := NewMultiTxDetails()
details.IDs = []wallet_common.MultiTransactionIDType{multiTransaction.ID}
mtx, err := mtDB.ReadMultiTransactions(details)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, mtx, 1) require.Len(t, mtx, 1)
require.True(t, areMultiTransactionsEqual(&multiTransaction, mtx[0])) require.True(t, areMultiTransactionsEqual(&multiTransaction, mtx[0]))
@ -50,6 +52,9 @@ func TestReadMultiTransactions(t *testing.T) {
tr3 := generateTestTransfer(3) tr3 := generateTestTransfer(3)
mt3 := GenerateTestSwapMultiTransaction(tr3, "SNT", 100) mt3 := GenerateTestSwapMultiTransaction(tr3, "SNT", 100)
require.NotEqual(t, mt1.ID, mt2.ID)
require.NotEqual(t, mt1.ID, mt3.ID)
err := mtDB.CreateMultiTransaction(&mt1) err := mtDB.CreateMultiTransaction(&mt1)
require.NoError(t, err) require.NoError(t, err)
err = mtDB.CreateMultiTransaction(&mt2) err = mtDB.CreateMultiTransaction(&mt2)
@ -58,8 +63,9 @@ func TestReadMultiTransactions(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
// Read multi transactions // Read multi transactions
ids := []wallet_common.MultiTransactionIDType{mt1.ID, mt2.ID, mt3.ID} details := NewMultiTxDetails()
mtx, err := mtDB.ReadMultiTransactions(ids) details.IDs = []wallet_common.MultiTransactionIDType{mt1.ID, mt2.ID, mt3.ID}
mtx, err := mtDB.ReadMultiTransactions(details)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, mtx, 3) require.Len(t, mtx, 3)
require.True(t, areMultiTransactionsEqual(&mt1, mtx[0])) require.True(t, areMultiTransactionsEqual(&mt1, mtx[0]))
@ -96,7 +102,9 @@ func TestUpdateMultiTransaction(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
// Read the updated multi transaction // Read the updated multi transaction
mtx, err := mtDB.ReadMultiTransactions([]wallet_common.MultiTransactionIDType{multiTransaction.ID}) details := NewMultiTxDetails()
details.IDs = []wallet_common.MultiTransactionIDType{multiTransaction.ID}
mtx, err := mtDB.ReadMultiTransactions(details)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, mtx, 1) require.Len(t, mtx, 1)
require.True(t, areMultiTransactionsEqual(&multiTransaction, mtx[0])) require.True(t, areMultiTransactionsEqual(&multiTransaction, mtx[0]))
@ -118,7 +126,8 @@ func TestDeleteMultiTransaction(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
// Read the deleted multi transaction // Read the deleted multi transaction
mtx, err := mtDB.ReadMultiTransactions([]wallet_common.MultiTransactionIDType{multiTransaction.ID}) mtx, err := mtDB.ReadMultiTransactions(&MultiTxDetails{
IDs: []wallet_common.MultiTransactionIDType{multiTransaction.ID}})
require.NoError(t, err) require.NoError(t, err)
require.Len(t, mtx, 0) require.Len(t, mtx, 0)
} }

View File

@ -37,6 +37,11 @@ type TestTransfer struct {
Token *token.Token Token *token.Token
} }
type TestCollectibleTransfer struct {
TestTransfer
TestCollectible
}
func SeedToToken(seed int) *token.Token { func SeedToToken(seed int) *token.Token {
tokenIndex := seed % len(TestTokens) tokenIndex := seed % len(TestTokens)
return TestTokens[tokenIndex] return TestTokens[tokenIndex]
@ -79,6 +84,28 @@ func generateTestTransfer(seed int) TestTransfer {
} }
} }
// Will be used in tests to generate a collectible transfer
// nolint:unused
func generateTestCollectibleTransfer(seed int) TestCollectibleTransfer {
collectibleIndex := seed % len(TestCollectibles)
collectible := TestCollectibles[collectibleIndex]
tr := TestCollectibleTransfer{
TestTransfer: TestTransfer{
TestTransaction: generateTestTransaction(seed),
To: eth_common.HexToAddress(fmt.Sprintf("0x3%d", seed)),
Value: int64(seed),
Token: &token.Token{
Address: collectible.TokenAddress,
Name: "Collectible",
ChainID: uint64(collectible.ChainID),
},
},
TestCollectible: collectible,
}
tr.TestTransaction.ChainID = collectible.ChainID
return tr
}
func GenerateTestSendMultiTransaction(tr TestTransfer) MultiTransaction { func GenerateTestSendMultiTransaction(tr TestTransfer) MultiTransaction {
return MultiTransaction{ return MultiTransaction{
ID: multiTransactionIDGenerator(), ID: multiTransactionIDGenerator(),
@ -422,21 +449,13 @@ func (s *InMemMultiTransactionStorage) DeleteMultiTransaction(id common.MultiTra
return nil return nil
} }
func (s *InMemMultiTransactionStorage) ReadMultiTransactions(ids []common.MultiTransactionIDType) ([]*MultiTransaction, error) { func (s *InMemMultiTransactionStorage) ReadMultiTransactions(details *MultiTxDetails) ([]*MultiTransaction, error) {
var multiTxs []*MultiTransaction
for _, id := range ids {
multiTx, ok := s.storage[id]
if !ok {
continue
}
multiTxs = append(multiTxs, multiTx)
}
return multiTxs, nil
}
func (s *InMemMultiTransactionStorage) ReadMultiTransactionsByDetails(details *MultiTxDetails) ([]*MultiTransaction, error) {
var multiTxs []*MultiTransaction var multiTxs []*MultiTransaction
for _, multiTx := range s.storage { for _, multiTx := range s.storage {
if len(details.IDs) > 0 && !testutils.SliceContains(details.IDs, multiTx.ID) {
continue
}
if (details.AnyAddress != eth_common.Address{}) && if (details.AnyAddress != eth_common.Address{}) &&
(multiTx.FromAddress != details.AnyAddress && multiTx.ToAddress != details.AnyAddress) { (multiTx.FromAddress != details.AnyAddress && multiTx.ToAddress != details.AnyAddress) {
continue continue
@ -454,7 +473,7 @@ func (s *InMemMultiTransactionStorage) ReadMultiTransactionsByDetails(details *M
continue continue
} }
if details.Type != MultiTransactionTypeInvalid && multiTx.Type != details.Type { if details.Type != MultiTransactionDBTypeInvalid && multiTx.Type != mtDBTypeToMTType(details.Type) {
continue continue
} }

View File

@ -36,7 +36,7 @@ type TransactionDescription struct {
type TransactionManager struct { type TransactionManager struct {
storage MultiTransactionStorage storage MultiTransactionStorage
gethManager *account.GethManager gethManager *account.GethManager
transactor *transactions.Transactor transactor transactions.TransactorIface
config *params.NodeConfig config *params.NodeConfig
accountsDB *accounts.Database accountsDB *accounts.Database
pendingTracker *transactions.PendingTxTracker pendingTracker *transactions.PendingTxTracker
@ -49,8 +49,7 @@ type TransactionManager struct {
type MultiTransactionStorage interface { type MultiTransactionStorage interface {
CreateMultiTransaction(tx *MultiTransaction) error CreateMultiTransaction(tx *MultiTransaction) error
ReadMultiTransactions(ids []wallet_common.MultiTransactionIDType) ([]*MultiTransaction, error) ReadMultiTransactions(details *MultiTxDetails) ([]*MultiTransaction, error)
ReadMultiTransactionsByDetails(details *MultiTxDetails) ([]*MultiTransaction, error)
UpdateMultiTransaction(tx *MultiTransaction) error UpdateMultiTransaction(tx *MultiTransaction) error
DeleteMultiTransaction(id wallet_common.MultiTransactionIDType) error DeleteMultiTransaction(id wallet_common.MultiTransactionIDType) error
} }
@ -58,7 +57,7 @@ type MultiTransactionStorage interface {
func NewTransactionManager( func NewTransactionManager(
storage MultiTransactionStorage, storage MultiTransactionStorage,
gethManager *account.GethManager, gethManager *account.GethManager,
transactor *transactions.Transactor, transactor transactions.TransactorIface,
config *params.NodeConfig, config *params.NodeConfig,
accountsDB *accounts.Database, accountsDB *accounts.Database,
pendingTxManager *transactions.PendingTxTracker, pendingTxManager *transactions.PendingTxTracker,

View File

@ -23,7 +23,7 @@ func (tm *TransactionManager) UpdateMultiTransaction(multiTransaction *MultiTran
return tm.storage.UpdateMultiTransaction(multiTransaction) return tm.storage.UpdateMultiTransaction(multiTransaction)
} }
func (tm *TransactionManager) CreateMultiTransactionFromCommand(ctx context.Context, command *MultiTransactionCommand, func (tm *TransactionManager) CreateMultiTransactionFromCommand(command *MultiTransactionCommand,
data []*bridge.TransactionBridge) (*MultiTransaction, error) { data []*bridge.TransactionBridge) (*MultiTransaction, error) {
multiTransaction := multiTransactionFromCommand(command) multiTransaction := multiTransactionFromCommand(command)
@ -107,7 +107,7 @@ func (tm *TransactionManager) ProceedWithTransactionsSignatures(ctx context.Cont
} }
func (tm *TransactionManager) GetMultiTransactions(ctx context.Context, ids []wallet_common.MultiTransactionIDType) ([]*MultiTransaction, error) { func (tm *TransactionManager) GetMultiTransactions(ctx context.Context, ids []wallet_common.MultiTransactionIDType) ([]*MultiTransaction, error) {
return tm.storage.ReadMultiTransactions(ids) return tm.storage.ReadMultiTransactions(&MultiTxDetails{IDs: ids})
} }
func (tm *TransactionManager) GetBridgeOriginMultiTransaction(ctx context.Context, toChainID uint64, crossTxID string) (*MultiTransaction, error) { func (tm *TransactionManager) GetBridgeOriginMultiTransaction(ctx context.Context, toChainID uint64, crossTxID string) (*MultiTransaction, error) {
@ -115,7 +115,7 @@ func (tm *TransactionManager) GetBridgeOriginMultiTransaction(ctx context.Contex
details.ToChainID = toChainID details.ToChainID = toChainID
details.CrossTxID = crossTxID details.CrossTxID = crossTxID
multiTxs, err := tm.storage.ReadMultiTransactionsByDetails(details) multiTxs, err := tm.storage.ReadMultiTransactions(details)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -135,7 +135,7 @@ func (tm *TransactionManager) GetBridgeDestinationMultiTransaction(ctx context.C
details.ToChainID = toChainID details.ToChainID = toChainID
details.CrossTxID = crossTxID details.CrossTxID = crossTxID
multiTxs, err := tm.storage.ReadMultiTransactionsByDetails(details) multiTxs, err := tm.storage.ReadMultiTransactions(details)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -0,0 +1,166 @@
package transfer
import (
"context"
"math/big"
"testing"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"
"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/eth-node/types"
"github.com/status-im/status-go/rpc"
"github.com/status-im/status-go/services/wallet/bridge"
"github.com/status-im/status-go/services/wallet/bridge/mock_bridge"
"github.com/status-im/status-go/transactions"
"github.com/status-im/status-go/transactions/mock_transactor"
)
func deepCopy(tx *transactions.SendTxArgs) *transactions.SendTxArgs {
return &transactions.SendTxArgs{
From: tx.From,
To: tx.To,
Value: tx.Value,
Data: tx.Data,
}
}
func deepCopyTransactionBridgeWithTransferTx(tx *bridge.TransactionBridge) *bridge.TransactionBridge {
return &bridge.TransactionBridge{
BridgeName: tx.BridgeName,
ChainID: tx.ChainID,
TransferTx: deepCopy(tx.TransferTx),
HopTx: tx.HopTx,
CbridgeTx: tx.CbridgeTx,
ERC721TransferTx: tx.ERC721TransferTx,
ERC1155TransferTx: tx.ERC1155TransferTx,
SwapTx: tx.SwapTx,
}
}
func setupTransactionManager(t *testing.T) (*TransactionManager, *mock_transactor.MockTransactorIface, *gomock.Controller) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
// Create a mock transactor
transactor := mock_transactor.NewMockTransactorIface(ctrl)
// Create a new instance of the TransactionManager
tm := NewTransactionManager(NewInMemMultiTransactionStorage(), nil, transactor, nil, nil, nil, nil)
return tm, transactor, ctrl
}
func setupAccount(_ *testing.T, address common.Address) *account.SelectedExtKey {
// Dummy account
return &account.SelectedExtKey{
Address: types.Address(address),
AccountKey: &types.Key{},
}
}
func setupTransactionData(_ *testing.T, transactor transactions.TransactorIface) (*MultiTransaction, []*bridge.TransactionBridge, map[string]bridge.Bridge, []*bridge.TransactionBridge) {
SetMultiTransactionIDGenerator(StaticIDCounter())
// Create mock data for the test
ethTransfer := generateTestTransfer(0)
multiTransaction := GenerateTestSendMultiTransaction(ethTransfer)
// Initialize the bridges
var rpcClient *rpc.Client = nil
bridges := make(map[string]bridge.Bridge)
transferBridge := bridge.NewTransferBridge(rpcClient, transactor)
bridges[transferBridge.Name()] = transferBridge
data := []*bridge.TransactionBridge{
{
ChainID: 1,
BridgeName: transferBridge.Name(),
TransferTx: &transactions.SendTxArgs{
From: types.Address(ethTransfer.From),
To: (*types.Address)(&ethTransfer.To),
Value: (*hexutil.Big)(big.NewInt(ethTransfer.Value / 3)),
Data: types.HexBytes("0x0"),
// Symbol: multiTransaction.FromAsset, // This will be set by transaction manager
// MultiTransactionID: multiTransaction.ID, // This will be set by transaction manager
},
},
{
ChainID: 420,
BridgeName: transferBridge.Name(),
TransferTx: &transactions.SendTxArgs{
From: types.Address(ethTransfer.From),
To: (*types.Address)(&ethTransfer.To),
Value: (*hexutil.Big)(big.NewInt(ethTransfer.Value * 2 / 3)),
Data: types.HexBytes("0x0"),
// Symbol: multiTransaction.FromAsset, // This will be set by transaction manager
// MultiTransactionID: multiTransaction.ID, // This will be set by transaction manager
},
},
}
expectedData := make([]*bridge.TransactionBridge, 0)
for _, tx := range data {
txCopy := deepCopyTransactionBridgeWithTransferTx(tx)
updateDataFromMultiTx([]*bridge.TransactionBridge{txCopy}, &multiTransaction)
expectedData = append(expectedData, txCopy)
}
return &multiTransaction, data, bridges, expectedData
}
func TestSendTransactionsETHSuccess(t *testing.T) {
tm, transactor, _ := setupTransactionManager(t)
account := setupAccount(t, common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678"))
multiTransaction, data, bridges, expectedData := setupTransactionData(t, transactor)
// 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)
}
// Call the SendTransactions method
_, err := tm.SendTransactions(context.Background(), multiTransaction, data, bridges, account)
require.NoError(t, err)
}
func TestSendTransactionsETHFailOnBridge(t *testing.T) {
tm, transactor, ctrl := setupTransactionManager(t)
account := setupAccount(t, common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678"))
multiTransaction, data, _, _ := setupTransactionData(t, transactor)
// Initialize the bridges
bridges := make(map[string]bridge.Bridge)
transferBridge := mock_bridge.NewMockBridge(ctrl)
// Set bridge name for the mock to the one used in data
transferBridge.EXPECT().Name().Return(data[0].BridgeName).AnyTimes()
bridges[transferBridge.Name()] = transferBridge
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)
// Call the SendTransactions method
_, err := tm.SendTransactions(context.Background(), multiTransaction, data, bridges, account)
require.Error(t, expectedErr, err)
}
func TestSendTransactionsETHFailOnTransactor(t *testing.T) {
tm, transactor, _ := setupTransactionManager(t)
account := setupAccount(t, common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678"))
multiTransaction, data, bridges, expectedData := setupTransactionData(t, transactor)
// 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)
// Call the SendTransactions method
_, err := tm.SendTransactions(context.Background(), multiTransaction, data, bridges, account)
require.Error(t, expectedErr, err)
}

View File

@ -0,0 +1,2 @@
# To generate mocks, from status-go root directory:
mockgen -source=transactions/transactor.go -destination=transactions/mock_transactor/transactor.go -package=mock_transactor

View File

@ -0,0 +1,177 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: transactions/transactor.go
// Package mock_transactor is a generated GoMock package.
package mock_transactor
import (
big "math/big"
reflect "reflect"
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"
rpc "github.com/status-im/status-go/rpc"
common0 "github.com/status-im/status-go/services/wallet/common"
transactions "github.com/status-im/status-go/transactions"
)
// MockTransactorIface is a mock of TransactorIface interface.
type MockTransactorIface struct {
ctrl *gomock.Controller
recorder *MockTransactorIfaceMockRecorder
}
// MockTransactorIfaceMockRecorder is the mock recorder for MockTransactorIface.
type MockTransactorIfaceMockRecorder struct {
mock *MockTransactorIface
}
// NewMockTransactorIface creates a new mock instance.
func NewMockTransactorIface(ctrl *gomock.Controller) *MockTransactorIface {
mock := &MockTransactorIface{ctrl: ctrl}
mock.recorder = &MockTransactorIfaceMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockTransactorIface) EXPECT() *MockTransactorIfaceMockRecorder {
return m.recorder
}
// AddSignatureToTransaction mocks base method.
func (m *MockTransactorIface) AddSignatureToTransaction(chainID uint64, tx *types.Transaction, sig []byte) (*types.Transaction, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AddSignatureToTransaction", chainID, tx, sig)
ret0, _ := ret[0].(*types.Transaction)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// AddSignatureToTransaction indicates an expected call of AddSignatureToTransaction.
func (mr *MockTransactorIfaceMockRecorder) AddSignatureToTransaction(chainID, tx, sig interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddSignatureToTransaction", reflect.TypeOf((*MockTransactorIface)(nil).AddSignatureToTransaction), chainID, tx, sig)
}
// BuildTransactionWithSignature mocks base method.
func (m *MockTransactorIface) BuildTransactionWithSignature(chainID uint64, args transactions.SendTxArgs, sig []byte) (*types.Transaction, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "BuildTransactionWithSignature", chainID, args, sig)
ret0, _ := ret[0].(*types.Transaction)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// BuildTransactionWithSignature indicates an expected call of BuildTransactionWithSignature.
func (mr *MockTransactorIfaceMockRecorder) BuildTransactionWithSignature(chainID, args, sig interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BuildTransactionWithSignature", reflect.TypeOf((*MockTransactorIface)(nil).BuildTransactionWithSignature), chainID, args, sig)
}
// EstimateGas mocks base method.
func (m *MockTransactorIface) EstimateGas(network *params.Network, from, to common.Address, value *big.Int, input []byte) (uint64, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "EstimateGas", network, from, to, value, input)
ret0, _ := ret[0].(uint64)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// EstimateGas indicates an expected call of EstimateGas.
func (mr *MockTransactorIfaceMockRecorder) EstimateGas(network, from, to, value, input interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EstimateGas", reflect.TypeOf((*MockTransactorIface)(nil).EstimateGas), network, from, to, value, input)
}
// NextNonce mocks base method.
func (m *MockTransactorIface) NextNonce(rpcClient *rpc.Client, chainID uint64, from types0.Address) (uint64, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "NextNonce", rpcClient, chainID, from)
ret0, _ := ret[0].(uint64)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// NextNonce indicates an expected call of NextNonce.
func (mr *MockTransactorIfaceMockRecorder) NextNonce(rpcClient, chainID, from interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NextNonce", reflect.TypeOf((*MockTransactorIface)(nil).NextNonce), rpcClient, chainID, from)
}
// SendRawTransaction mocks base method.
func (m *MockTransactorIface) SendRawTransaction(chainID uint64, rawTx string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SendRawTransaction", chainID, rawTx)
ret0, _ := ret[0].(error)
return ret0
}
// SendRawTransaction indicates an expected call of SendRawTransaction.
func (mr *MockTransactorIfaceMockRecorder) SendRawTransaction(chainID, rawTx interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendRawTransaction", reflect.TypeOf((*MockTransactorIface)(nil).SendRawTransaction), chainID, rawTx)
}
// SendTransaction mocks base method.
func (m *MockTransactorIface) SendTransaction(sendArgs transactions.SendTxArgs, verifiedAccount *account.SelectedExtKey) (types0.Hash, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SendTransaction", sendArgs, verifiedAccount)
ret0, _ := ret[0].(types0.Hash)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// SendTransaction indicates an expected call of SendTransaction.
func (mr *MockTransactorIfaceMockRecorder) SendTransaction(sendArgs, verifiedAccount interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendTransaction", reflect.TypeOf((*MockTransactorIface)(nil).SendTransaction), sendArgs, verifiedAccount)
}
// SendTransactionWithChainID mocks base method.
func (m *MockTransactorIface) SendTransactionWithChainID(chainID uint64, sendArgs transactions.SendTxArgs, verifiedAccount *account.SelectedExtKey) (types0.Hash, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SendTransactionWithChainID", chainID, sendArgs, verifiedAccount)
ret0, _ := ret[0].(types0.Hash)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// SendTransactionWithChainID indicates an expected call of SendTransactionWithChainID.
func (mr *MockTransactorIfaceMockRecorder) SendTransactionWithChainID(chainID, sendArgs, 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)
}
// SendTransactionWithSignature mocks base method.
func (m *MockTransactorIface) SendTransactionWithSignature(from common.Address, symbol string, multiTransactionID common0.MultiTransactionIDType, tx *types.Transaction) (types0.Hash, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SendTransactionWithSignature", from, symbol, multiTransactionID, tx)
ret0, _ := ret[0].(types0.Hash)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// SendTransactionWithSignature indicates an expected call of SendTransactionWithSignature.
func (mr *MockTransactorIfaceMockRecorder) SendTransactionWithSignature(from, symbol, multiTransactionID, tx interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendTransactionWithSignature", reflect.TypeOf((*MockTransactorIface)(nil).SendTransactionWithSignature), from, symbol, multiTransactionID, tx)
}
// ValidateAndBuildTransaction mocks base method.
func (m *MockTransactorIface) ValidateAndBuildTransaction(chainID uint64, sendArgs transactions.SendTxArgs) (*types.Transaction, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ValidateAndBuildTransaction", chainID, sendArgs)
ret0, _ := ret[0].(*types.Transaction)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// ValidateAndBuildTransaction indicates an expected call of ValidateAndBuildTransaction.
func (mr *MockTransactorIfaceMockRecorder) ValidateAndBuildTransaction(chainID, sendArgs interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateAndBuildTransaction", reflect.TypeOf((*MockTransactorIface)(nil).ValidateAndBuildTransaction), chainID, sendArgs)
}

View File

@ -44,6 +44,19 @@ func (e *ErrBadNonce) Error() string {
return fmt.Sprintf("bad nonce. expected %d, got %d", e.expectedNonce, e.nonce) return fmt.Sprintf("bad nonce. expected %d, got %d", e.expectedNonce, e.nonce)
} }
// Transactor is an interface that defines the methods for validating and sending transactions.
type TransactorIface interface {
NextNonce(rpcClient *rpc.Client, 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)
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)
SendTransactionWithSignature(from common.Address, symbol string, multiTransactionID wallet_common.MultiTransactionIDType, tx *gethtypes.Transaction) (hash types.Hash, err error)
}
// Transactor validates, signs transactions. // Transactor validates, signs transactions.
// It uses upstream to propagate transactions to the Ethereum network. // It uses upstream to propagate transactions to the Ethereum network.
type Transactor struct { type Transactor struct {
@ -218,29 +231,9 @@ func (t *Transactor) SendTransactionWithSignature(from common.Address, symbol st
return t.sendTransaction(rpcWrapper, from, symbol, multiTransactionID, tx) return t.sendTransaction(rpcWrapper, from, symbol, multiTransactionID, tx)
} }
func (t *Transactor) AddSignatureToTransactionAndSend(chainID uint64, from common.Address, symbol string, // BuildTransactionAndSendWithSignature receive a transaction and a signature, serialize them together
multiTransactionID wallet_common.MultiTransactionIDType, tx *gethtypes.Transaction, sig []byte) (hash types.Hash, err error) {
txWithSignature, err := t.AddSignatureToTransaction(chainID, tx, sig)
if err != nil {
return hash, err
}
return t.SendTransactionWithSignature(from, symbol, multiTransactionID, txWithSignature)
}
// BuildTransactionAndSendWithSignature receive a transaction and a signature, serialize them together and propage it to the network.
// It's different from eth_sendRawTransaction because it receives a signature and not a serialized transaction with signature. // It's different from eth_sendRawTransaction because it receives a signature and not a serialized transaction with signature.
// Since the transactions is already signed, we assume it was validated and used the right nonce. // Since the transactions is already signed, we assume it was validated and used the right nonce.
func (t *Transactor) BuildTransactionAndSendWithSignature(chainID uint64, args SendTxArgs, sig []byte) (hash types.Hash, err error) {
txWithSignature, err := t.BuildTransactionWithSignature(chainID, args, sig)
if err != nil {
return hash, err
}
hash, err = t.SendTransactionWithSignature(common.Address(args.From), args.Symbol, args.MultiTransactionID, txWithSignature)
return hash, err
}
func (t *Transactor) BuildTransactionWithSignature(chainID uint64, args SendTxArgs, sig []byte) (*gethtypes.Transaction, error) { func (t *Transactor) BuildTransactionWithSignature(chainID uint64, args SendTxArgs, sig []byte) (*gethtypes.Transaction, error) {
if !args.Valid() { if !args.Valid() {
return nil, ErrInvalidSendTxArgs return nil, ErrInvalidSendTxArgs

View File

@ -313,11 +313,18 @@ func (s *TransactorSuite) TestSendTransactionWithSignature() {
Return(common.Hash{}, nil) Return(common.Hash{}, nil)
} }
_, err = s.manager.BuildTransactionAndSendWithSignature(s.nodeConfig.NetworkID, args, sig) tx, err = s.manager.BuildTransactionWithSignature(s.nodeConfig.NetworkID, args, sig)
if scenario.expectError { if scenario.expectError {
s.Error(err) s.Error(err)
} else { } else {
s.NoError(err) s.NoError(err)
_, err = s.manager.SendTransactionWithSignature(common.Address(args.From), args.Symbol, args.MultiTransactionID, tx)
if scenario.expectError {
s.Error(err)
} else {
s.NoError(err)
}
} }
}) })
} }
@ -325,7 +332,7 @@ func (s *TransactorSuite) TestSendTransactionWithSignature() {
func (s *TransactorSuite) TestSendTransactionWithSignature_InvalidSignature() { func (s *TransactorSuite) TestSendTransactionWithSignature_InvalidSignature() {
args := SendTxArgs{} args := SendTxArgs{}
_, err := s.manager.BuildTransactionAndSendWithSignature(1, args, []byte{}) _, err := s.manager.BuildTransactionWithSignature(1, args, []byte{})
s.Equal(ErrInvalidSignatureSize, err) s.Equal(ErrInvalidSignatureSize, err)
} }