diff --git a/CHANGELOG.md b/CHANGELOG.md index 35d832e80..b99a9864b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,11 +35,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ # Changelog -## v26.2.0 +## Unreleased -- #554 - [FIX] Add missing checks for transmuter's limiter -- #548 - Return base fee in /quote regardless of simulation success. -- #547 - Add /quote simulation for "out given in" single routes. - #526 - Refactor gas estimation APIs - #524 - Claimbot diff --git a/domain/cosmos/tx/msg_simulator.go b/domain/cosmos/tx/msg_simulator.go index bd4337ad7..0778fffd0 100644 --- a/domain/cosmos/tx/msg_simulator.go +++ b/domain/cosmos/tx/msg_simulator.go @@ -2,7 +2,6 @@ package tx import ( "context" - "errors" cosmosclient "github.com/cosmos/cosmos-sdk/client" txclient "github.com/cosmos/cosmos-sdk/client/tx" @@ -11,12 +10,9 @@ import ( txtypes "github.com/cosmos/cosmos-sdk/types/tx" signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - - "github.com/osmosis-labs/osmosis/v27/app/params" - "github.com/osmosis-labs/sqs/domain" + "github.com/osmosis-labs/osmosis/v26/app/params" + txfeestypes "github.com/osmosis-labs/osmosis/v26/x/txfees/types" "github.com/osmosis-labs/sqs/domain/keyring" - routerrepo "github.com/osmosis-labs/sqs/router/repository" - "google.golang.org/grpc" gogogrpc "github.com/cosmos/gogoproto/grpc" ) @@ -26,6 +22,7 @@ type MsgSimulator interface { BuildTx( ctx context.Context, keyring keyring.Keyring, + txfeesClient txfeestypes.QueryClient, encodingConfig params.EncodingConfig, account *authtypes.BaseAccount, chainID string, @@ -41,24 +38,13 @@ type MsgSimulator interface { chainID string, msgs []sdk.Msg, ) (*txtypes.SimulateResponse, uint64, error) - - // PriceMsgs simulates the execution of the given messages and returns the gas used and the fee coin, - // which is the fee amount in the base denomination. - PriceMsgs( - ctx context.Context, - encodingConfig cosmosclient.TxConfig, - account *authtypes.BaseAccount, - chainID string, - msg ...sdk.Msg, - ) domain.TxFeeInfo } -// NewMsgSimulator creates a new GasCalculator instance. -func NewMsgSimulator(clientCtx gogogrpc.ClientConn, calculateGas CalculateGasFn, memoryRouterRepository routerrepo.RouterRepository) MsgSimulator { +// NewGasCalculator creates a new GasCalculator instance. +func NewGasCalculator(clientCtx gogogrpc.ClientConn, calculateGas CalculateGasFn) MsgSimulator { return &txGasCalulator{ - clientCtx: clientCtx, - calculateGas: calculateGas, - memoryRouterRepository: memoryRouterRepository, + clientCtx: clientCtx, + calculateGas: calculateGas, } } @@ -67,9 +53,8 @@ type CalculateGasFn func(clientCtx gogogrpc.ClientConn, txf txclient.Factory, ms // txGasCalulator is a GasCalculator implementation that uses simulated transactions to calculate gas. type txGasCalulator struct { - clientCtx grpc.ClientConnInterface - calculateGas CalculateGasFn - memoryRouterRepository routerrepo.BaseFeeRepository + clientCtx gogogrpc.ClientConn + calculateGas CalculateGasFn } // BuildTx constructs a transaction using the provided parameters and messages. @@ -77,6 +62,7 @@ type txGasCalulator struct { func (c *txGasCalulator) BuildTx( ctx context.Context, keyring keyring.Keyring, + txfeesClient txfeestypes.QueryClient, encodingConfig params.EncodingConfig, account *authtypes.BaseAccount, chainID string, @@ -93,13 +79,23 @@ func (c *txGasCalulator) BuildTx( return nil, err } - priceInfo := c.PriceMsgs(ctx, encodingConfig.TxConfig, account, chainID, msg...) - if priceInfo.Err != "" { - return nil, errors.New(priceInfo.Err) + _, gas, err := c.SimulateMsgs( + encodingConfig.TxConfig, + account, + chainID, + msg, + ) + if err != nil { + return nil, err } + txBuilder.SetGasLimit(gas) - txBuilder.SetGasLimit(priceInfo.AdjustedGasUsed) - txBuilder.SetFeeAmount(sdk.Coins{priceInfo.FeeCoin}) + feecoin, err := CalculateFeeCoin(ctx, txfeesClient, gas) + if err != nil { + return nil, err + } + + txBuilder.SetFeeAmount(sdk.NewCoins(feecoin)) sigV2 := BuildSignatures(privKey.PubKey(), nil, account.Sequence) err = txBuilder.SetSignatures(sigV2) @@ -147,36 +143,6 @@ func (c *txGasCalulator) SimulateMsgs(encodingConfig cosmosclient.TxConfig, acco return gasResult, adjustedGasUsed, nil } -// PriceMsgs implements MsgSimulator. -func (c *txGasCalulator) PriceMsgs(ctx context.Context, encodingConfig cosmosclient.TxConfig, account *authtypes.BaseAccount, chainID string, msg ...sdk.Msg) domain.TxFeeInfo { - baseFee := c.memoryRouterRepository.GetBaseFee() - if baseFee.CurrentFee.IsNil() || baseFee.CurrentFee.IsZero() { - return domain.TxFeeInfo{Err: "base fee is zero or nil"} - } - if baseFee.Denom == "" { - return domain.TxFeeInfo{Err: "base fee denom is empty"} - } - - _, gasAdjusted, err := c.SimulateMsgs( - encodingConfig, - account, - chainID, - msg, - ) - if err != nil { - return domain.TxFeeInfo{Err: err.Error(), BaseFee: baseFee.CurrentFee} - } - - feeAmount := CalculateFeeAmount(baseFee.CurrentFee, gasAdjusted) - - return domain.TxFeeInfo{ - AdjustedGasUsed: gasAdjusted, - FeeCoin: sdk.Coin{Denom: baseFee.Denom, Amount: feeAmount}, - BaseFee: baseFee.CurrentFee, - Err: "", - } -} - // CalculateGas calculates the gas required for a transaction using the provided transaction factory and messages. func CalculateGas( clientCtx gogogrpc.ClientConn, diff --git a/domain/cosmos/tx/msg_simulator_test.go b/domain/cosmos/tx/msg_simulator_test.go index 7d18904b5..20849e3ab 100644 --- a/domain/cosmos/tx/msg_simulator_test.go +++ b/domain/cosmos/tx/msg_simulator_test.go @@ -7,35 +7,11 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" txtypes "github.com/cosmos/cosmos-sdk/types/tx" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/osmosis-labs/osmosis/osmomath" - "github.com/osmosis-labs/sqs/domain" "github.com/osmosis-labs/sqs/domain/cosmos/tx" "github.com/osmosis-labs/sqs/domain/mocks" - "github.com/osmosis-labs/sqs/log" - routerrepo "github.com/osmosis-labs/sqs/router/repository" "github.com/stretchr/testify/assert" ) -const ( - testChainID = "test-chain" - testKey = "6cf5103c60c939a5f38e383b52239c5296c968579eec1c68a47d70fbf1d19159" - testDenom = "eth" - testBaseFee = "0.1" - testGasUsed = uint64(50) - testAmount = int64(5) -) - -var ( - testAccount = &authtypes.BaseAccount{ - Sequence: 13, - AccountNumber: 1, - } - testMsg = newMsg("sender", "contract", `{"payload": "hello contract"}`) - testTxJSON = []byte(`{"body":{"messages":[{"@type":"/cosmwasm.wasm.v1.MsgExecuteContract","sender":"sender","contract":"contract","msg":{"payload":"hello contract"},"funds":[]}],"memo":"","timeout_height":"0","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[{"public_key":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A+9dbfKKCHgfmiV2XUWelqidYzZhHR+KtNMvcSzWjdPQ"},"mode_info":{"single":{"mode":"SIGN_MODE_DIRECT"}},"sequence":"13"}],"fee":{"amount":[{"denom":"eth","amount":"5"}],"gas_limit":"50","payer":"","granter":""},"tip":null},"signatures":["aRlC8F2MnDA50tNNTJUk7zPvH/xc5c3Av+yaGQEiU0l0AXJxUdzOUxWHiC74D9ltvbsk0HzWbb+2uetCjdQdfA=="]}`) - - testBaseFeeDec = osmomath.MustNewDecFromStr(testBaseFee) -) - func TestSimulateMsgs(t *testing.T) { tests := []struct { name string @@ -49,36 +25,39 @@ func TestSimulateMsgs(t *testing.T) { }{ { name: "Successful simulation", - account: testAccount, - chainID: testChainID, - msgs: []sdk.Msg{testMsg}, + account: &authtypes.BaseAccount{AccountNumber: 1, Sequence: 1}, + chainID: "test-chain", + msgs: []sdk.Msg{newMsg("sender", "contract", `{}`)}, setupMocks: func(calculator mocks.GetCalculateGasMock) tx.CalculateGasFn { - return calculator(&txtypes.SimulateResponse{GasInfo: &sdk.GasInfo{GasUsed: 100000}}, testGasUsed, nil) + return calculator(&txtypes.SimulateResponse{GasInfo: &sdk.GasInfo{GasUsed: 100000}}, 50, nil) }, expectedSimulateResponse: &txtypes.SimulateResponse{GasInfo: &sdk.GasInfo{GasUsed: 100000}}, - expectedGas: testGasUsed, + expectedGas: 50, expectedError: nil, }, { name: "Simulation error", account: &authtypes.BaseAccount{AccountNumber: 2, Sequence: 2}, - chainID: testChainID, + chainID: "test-chain", msgs: []sdk.Msg{}, setupMocks: func(calculator mocks.GetCalculateGasMock) tx.CalculateGasFn { - return calculator(&txtypes.SimulateResponse{}, testGasUsed, assert.AnError) + return calculator(&txtypes.SimulateResponse{}, 3, assert.AnError) }, expectedSimulateResponse: nil, - expectedGas: testGasUsed, + expectedGas: 3, expectedError: assert.AnError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + // Setup the mock calculateGasFnMock := tt.setupMocks(mocks.DefaultGetCalculateGasMock) - routerRepository := routerrepo.New(&log.NoOpLogger{}) - gasCalculator := tx.NewMsgSimulator(nil, calculateGasFnMock, routerRepository) + // Create the gas calculator + gasCalculator := tx.NewGasCalculator(nil, calculateGasFnMock) + + // Call the function result, gas, err := gasCalculator.SimulateMsgs( encodingConfig.TxConfig, tt.account, @@ -86,6 +65,7 @@ func TestSimulateMsgs(t *testing.T) { tt.msgs, ) + // Assert the results assert.Equal(t, tt.expectedSimulateResponse, result) assert.Equal(t, tt.expectedGas, gas) if tt.expectedError != nil { @@ -101,8 +81,7 @@ func TestSimulateMsgs(t *testing.T) { func TestBuildTx(t *testing.T) { testCases := []struct { name string - setupMocks func(calculator mocks.GetCalculateGasMock, keyring *mocks.Keyring) tx.CalculateGasFn - preSetBaseFee domain.BaseFee + setupMocks func(calculator mocks.GetCalculateGasMock, txFeesClient *mocks.TxFeesQueryClient, keyring *mocks.Keyring) tx.CalculateGasFn account *authtypes.BaseAccount chainID string msgs []sdk.Msg @@ -111,25 +90,28 @@ func TestBuildTx(t *testing.T) { }{ { name: "Valid transaction", - setupMocks: func(calculator mocks.GetCalculateGasMock, keyring *mocks.Keyring) tx.CalculateGasFn { - keyring.WithGetKey(testKey) - return calculator(&txtypes.SimulateResponse{GasInfo: &sdk.GasInfo{GasUsed: 100000}}, testGasUsed, nil) + setupMocks: func(calculator mocks.GetCalculateGasMock, txFeesClient *mocks.TxFeesQueryClient, keyring *mocks.Keyring) tx.CalculateGasFn { + keyring.WithGetKey("6cf5103c60c939a5f38e383b52239c5296c968579eec1c68a47d70fbf1d19159") + txFeesClient.WithBaseDenom("eth", nil) + txFeesClient.WithGetEipBaseFee("0.1", nil) + + return calculator(&txtypes.SimulateResponse{GasInfo: &sdk.GasInfo{GasUsed: 100000}}, 50, nil) }, - account: testAccount, - chainID: testChainID, - msgs: []sdk.Msg{testMsg}, - preSetBaseFee: domain.BaseFee{ - Denom: testDenom, - CurrentFee: testBaseFeeDec, + account: &authtypes.BaseAccount{ + Sequence: 13, + AccountNumber: 1, }, - expectedJSON: testTxJSON, + chainID: "test-chain", + msgs: []sdk.Msg{newMsg("sender", "contract", `{"payload": "hello contract"}`)}, + expectedJSON: []byte(`{"body":{"messages":[{"@type":"/cosmwasm.wasm.v1.MsgExecuteContract","sender":"sender","contract":"contract","msg":{"payload":"hello contract"},"funds":[]}],"memo":"","timeout_height":"0","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[{"public_key":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A+9dbfKKCHgfmiV2XUWelqidYzZhHR+KtNMvcSzWjdPQ"},"mode_info":{"single":{"mode":"SIGN_MODE_DIRECT"}},"sequence":"13"}],"fee":{"amount":[{"denom":"eth","amount":"5"}],"gas_limit":"50","payer":"","granter":""},"tip":null},"signatures":["aRlC8F2MnDA50tNNTJUk7zPvH/xc5c3Av+yaGQEiU0l0AXJxUdzOUxWHiC74D9ltvbsk0HzWbb+2uetCjdQdfA=="]}`), expectedError: false, }, { name: "Error building transaction", - setupMocks: func(calculator mocks.GetCalculateGasMock, keyring *mocks.Keyring) tx.CalculateGasFn { - keyring.WithGetKey(testKey) - return calculator(&txtypes.SimulateResponse{}, testGasUsed, assert.AnError) + setupMocks: func(calculator mocks.GetCalculateGasMock, txFeesClient *mocks.TxFeesQueryClient, keyring *mocks.Keyring) tx.CalculateGasFn { + keyring.WithGetKey("6cf5103c60c939a5f38e383b52239c5296c968579eec1c68a47d70fbf1d19159") + + return calculator(&txtypes.SimulateResponse{}, 50, assert.AnError) }, account: &authtypes.BaseAccount{ Sequence: 8, @@ -141,15 +123,19 @@ func TestBuildTx(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { + txFeesClient := mocks.TxFeesQueryClient{} keyring := mocks.Keyring{} - routerRepository := routerrepo.New(&log.NoOpLogger{}) - routerRepository.SetBaseFee(tc.preSetBaseFee) - calculateGasFnMock := tc.setupMocks(mocks.DefaultGetCalculateGasMock, &keyring) - msgSimulator := tx.NewMsgSimulator(nil, calculateGasFnMock, routerRepository) + + // Setup the mock + calculateGasFnMock := tc.setupMocks(mocks.DefaultGetCalculateGasMock, &txFeesClient, &keyring) + + // Create the gas calculator + msgSimulator := tx.NewGasCalculator(nil, calculateGasFnMock) txBuilder, err := msgSimulator.BuildTx( context.Background(), &keyring, + &txFeesClient, encodingConfig, tc.account, tc.chainID, @@ -165,106 +151,9 @@ func TestBuildTx(t *testing.T) { txJSONBytes, err := encodingConfig.TxConfig.TxJSONEncoder()(txBuilder.GetTx()) assert.NoError(t, err) - assert.Equal(t, string(tc.expectedJSON), string(txJSONBytes)) - } - }) - } -} - -func TestPriceMsgs(t *testing.T) { - testCases := []struct { - name string - setupMocks func(calculator mocks.GetCalculateGasMock, keyring *mocks.Keyring) tx.CalculateGasFn - account *authtypes.BaseAccount - chainID string - msgs []sdk.Msg - preSetBaseFee domain.BaseFee - expectedGas uint64 - expectedFeeCoin sdk.Coin - expectedBaseFee osmomath.Dec - expectedError bool - }{ - { - name: "Valid transaction", - setupMocks: func(calculator mocks.GetCalculateGasMock, keyring *mocks.Keyring) tx.CalculateGasFn { - keyring.WithGetKey(testKey) - - return calculator(&txtypes.SimulateResponse{GasInfo: &sdk.GasInfo{GasUsed: 100000}}, testGasUsed, nil) - }, - account: testAccount, - chainID: testChainID, - msgs: []sdk.Msg{testMsg}, - preSetBaseFee: domain.BaseFee{ - Denom: testDenom, - CurrentFee: testBaseFeeDec, - }, - expectedGas: testGasUsed, - expectedFeeCoin: sdk.Coin{Denom: testDenom, Amount: osmomath.NewInt(testAmount)}, - expectedBaseFee: testBaseFeeDec, - expectedError: false, - }, - { - name: "Error building transaction", - setupMocks: func(calculator mocks.GetCalculateGasMock, keyring *mocks.Keyring) tx.CalculateGasFn { - keyring.WithGetKey(testKey) - return calculator(&txtypes.SimulateResponse{}, testGasUsed, assert.AnError) - }, - preSetBaseFee: domain.BaseFee{ - Denom: testDenom, - CurrentFee: testBaseFeeDec, - }, - account: &authtypes.BaseAccount{ - Sequence: 8, - AccountNumber: 51, - }, - expectedFeeCoin: sdk.Coin{}, - expectedBaseFee: osmomath.Dec{}, - expectedError: true, - }, - { - name: "Invalid base fee", - setupMocks: func(calculator mocks.GetCalculateGasMock, keyring *mocks.Keyring) tx.CalculateGasFn { - keyring.WithGetKey(testKey) - - return calculator(&txtypes.SimulateResponse{GasInfo: &sdk.GasInfo{GasUsed: 100000}}, testGasUsed, nil) - }, - account: testAccount, - chainID: testChainID, - msgs: []sdk.Msg{testMsg}, - expectedGas: testGasUsed, - expectedFeeCoin: sdk.Coin{}, - expectedBaseFee: osmomath.Dec{}, - expectedError: true, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - keyring := mocks.Keyring{} - routerRepository := routerrepo.New(&log.NoOpLogger{}) - routerRepository.SetBaseFee(tc.preSetBaseFee) - - calculateGasFnMock := tc.setupMocks(mocks.DefaultGetCalculateGasMock, &keyring) - msgSimulator := tx.NewMsgSimulator(nil, calculateGasFnMock, routerRepository) - - priceInfo := msgSimulator.PriceMsgs( - context.Background(), - encodingConfig.TxConfig, - tc.account, - tc.chainID, - tc.msgs..., - ) - - if tc.expectedError { - assert.NotEmpty(t, priceInfo.Err) - assert.Equal(t, priceInfo.AdjustedGasUsed, uint64(0)) - assert.Equal(t, priceInfo.FeeCoin, sdk.Coin{}) - } else { - assert.Empty(t, priceInfo.Err) - assert.Equal(t, tc.expectedGas, priceInfo.AdjustedGasUsed) - assert.Equal(t, tc.expectedFeeCoin, priceInfo.FeeCoin) - assert.Equal(t, tc.expectedBaseFee, priceInfo.BaseFee) + // Add more specific assertions here based on the expected output + assert.Equal(t, string(tc.expectedJSON), string(txJSONBytes)) } }) } diff --git a/domain/cosmos/tx/tx.go b/domain/cosmos/tx/tx.go index 86cbad499..0805afc6f 100644 --- a/domain/cosmos/tx/tx.go +++ b/domain/cosmos/tx/tx.go @@ -5,9 +5,7 @@ import ( "context" "github.com/osmosis-labs/osmosis/osmomath" - "github.com/osmosis-labs/sqs/domain" - - txfeestypes "github.com/osmosis-labs/osmosis/v27/x/txfees/types" + txfeestypes "github.com/osmosis-labs/osmosis/v26/x/txfees/types" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -33,35 +31,6 @@ func SendTx(ctx context.Context, txServiceClient txtypes.ServiceClient, txBytes return resp.TxResponse, nil } -// SimulateMsgs simulates the execution of the given messages and returns the simulation response, -// adjusted gas used, and any error encountered. It uses the provided gRPC client, encoding config, -// account details, and chain ID to create a transaction factory for the simulation. -func SimulateMsgs( - gasCalculator GasCalculator, - encodingConfig params.EncodingConfig, - account *authtypes.BaseAccount, - chainID string, - msgs []sdk.Msg, -) (*txtypes.SimulateResponse, uint64, error) { - txFactory := txclient.Factory{} - txFactory = txFactory.WithTxConfig(encodingConfig.TxConfig) - txFactory = txFactory.WithAccountNumber(account.AccountNumber) - txFactory = txFactory.WithSequence(account.Sequence) - txFactory = txFactory.WithChainID(chainID) - txFactory = txFactory.WithGasAdjustment(1.15) - - // Estimate transaction - gasResult, adjustedGasUsed, err := gasCalculator.CalculateGas( - txFactory, - msgs..., - ) - if err != nil { - return nil, adjustedGasUsed, err - } - - return gasResult, adjustedGasUsed, nil -} - // BuildSignatures creates a SignatureV2 object using the provided public key, signature, and sequence number. // This is used in the process of building and signing transactions. func BuildSignatures(publicKey cryptotypes.PubKey, signature []byte, sequence uint64) signingtypes.SignatureV2 { diff --git a/domain/mocks/msg_simulator_mock.go b/domain/mocks/msg_simulator_mock.go index 38816554f..f6b59c307 100644 --- a/domain/mocks/msg_simulator_mock.go +++ b/domain/mocks/msg_simulator_mock.go @@ -7,8 +7,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" txtypes "github.com/cosmos/cosmos-sdk/types/tx" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/osmosis-labs/osmosis/v27/app/params" - "github.com/osmosis-labs/sqs/domain" + "github.com/osmosis-labs/osmosis/v26/app/params" + txfeestypes "github.com/osmosis-labs/osmosis/v26/x/txfees/types" sqstx "github.com/osmosis-labs/sqs/domain/cosmos/tx" "github.com/osmosis-labs/sqs/domain/keyring" ) @@ -17,6 +17,7 @@ type MsgSimulatorMock struct { BuildTxFn func( ctx context.Context, keyring keyring.Keyring, + txfeesClient txfeestypes.QueryClient, encodingConfig params.EncodingConfig, account *authtypes.BaseAccount, chainID string, @@ -29,27 +30,20 @@ type MsgSimulatorMock struct { chainID string, msgs []sdk.Msg, ) (*txtypes.SimulateResponse, uint64, error) - - PriceMsgsFn func( - ctx context.Context, - encodingConfig client.TxConfig, - account *authtypes.BaseAccount, - chainID string, - msg ...sdk.Msg, - ) domain.TxFeeInfo } var _ sqstx.MsgSimulator = &MsgSimulatorMock{} func (m *MsgSimulatorMock) BuildTx(ctx context.Context, keyring keyring.Keyring, + txfeesClient txfeestypes.QueryClient, encodingConfig params.EncodingConfig, account *authtypes.BaseAccount, chainID string, msg ...sdk.Msg, ) (client.TxBuilder, error) { if m.BuildTxFn != nil { - return m.BuildTxFn(ctx, keyring, encodingConfig, account, chainID, msg...) + return m.BuildTxFn(ctx, keyring, txfeesClient, encodingConfig, account, chainID, msg...) } panic("BuildTxFn not implemented") } @@ -65,15 +59,3 @@ func (m *MsgSimulatorMock) SimulateMsgs( } panic("SimulateMsgsFn not implemented") } - -// PriceMsgs implements tx.MsgSimulator. -func (m *MsgSimulatorMock) PriceMsgs(ctx context.Context, encodingConfig client.TxConfig, account *authtypes.BaseAccount, chainID string, msg ...interface { - ProtoMessage() - Reset() - String() string -}) domain.TxFeeInfo { - if m.PriceMsgsFn != nil { - return m.PriceMsgsFn(ctx, encodingConfig, account, chainID, msg...) - } - panic("PriceMsgsFn not implemented") -} diff --git a/ingest/usecase/plugins/orderbook/claimbot/config.go b/ingest/usecase/plugins/orderbook/claimbot/config.go index cc25934bd..3046cbc0a 100644 --- a/ingest/usecase/plugins/orderbook/claimbot/config.go +++ b/ingest/usecase/plugins/orderbook/claimbot/config.go @@ -20,7 +20,7 @@ type Config struct { OrderbookUsecase mvc.OrderBookUsecase AccountQueryClient authtypes.QueryClient TxfeesClient txfeestypes.QueryClient - GasCalculator sqstx.GasCalculator + MsgSimulator sqstx.MsgSimulator TxServiceClient txtypes.ServiceClient ChainID string Logger log.Logger @@ -46,7 +46,7 @@ func NewConfig( OrderbookUsecase: orderbookusecase, AccountQueryClient: authtypes.NewQueryClient(grpcClient), TxfeesClient: txfeestypes.NewQueryClient(grpcClient), - GasCalculator: sqstx.NewGasCalculator(grpcClient), + MsgSimulator: sqstx.NewGasCalculator(grpcClient, sqstx.CalculateGas), TxServiceClient: txtypes.NewServiceClient(grpcClient), Logger: logger.Named("claimbot"), ChainID: chainID, diff --git a/ingest/usecase/plugins/orderbook/claimbot/export_test.go b/ingest/usecase/plugins/orderbook/claimbot/export_test.go index ad9ee4597..3a117ac59 100644 --- a/ingest/usecase/plugins/orderbook/claimbot/export_test.go +++ b/ingest/usecase/plugins/orderbook/claimbot/export_test.go @@ -9,6 +9,7 @@ import ( "github.com/osmosis-labs/sqs/domain/mvc" orderbookdomain "github.com/osmosis-labs/sqs/domain/orderbook" + "github.com/osmosis-labs/osmosis/v26/app/params" txfeestypes "github.com/osmosis-labs/osmosis/v26/x/txfees/types" "github.com/osmosis-labs/osmosis/osmomath" @@ -21,6 +22,12 @@ import ( // ProcessedOrderbook is order alias data structure for testing purposes. type ProcessedOrderbook = processedOrderbook +var ( + EncodingConfig = encodingConfig + + DefaultEncodingConfigFn = defaultEncodingConfigFn +) + // ProcessOrderbooksAndGetClaimableOrders is test wrapper for processOrderbooksAndGetClaimableOrders. // This function is exported for testing purposes. func ProcessOrderbooksAndGetClaimableOrders( @@ -34,18 +41,19 @@ func ProcessOrderbooksAndGetClaimableOrders( // SendBatchClaimTx a test wrapper for sendBatchClaimTx. // This function is used only for testing purposes. -func SendBatchClaimTx( +func SendBatchClaimTxInternal( ctx context.Context, keyring keyring.Keyring, txfeesClient txfeestypes.QueryClient, - gasCalculator sqstx.GasCalculator, + msgSimulator sqstx.MsgSimulator, txServiceClient txtypes.ServiceClient, chainID string, account *authtypes.BaseAccount, contractAddress string, claims orderbookdomain.Orders, + getEncodingConfig func() params.EncodingConfig, ) (*sdk.TxResponse, error) { - return sendBatchClaimTx(ctx, keyring, txfeesClient, gasCalculator, txServiceClient, chainID, account, contractAddress, claims) + return sendBatchClaimTxInternal(ctx, keyring, txfeesClient, msgSimulator, txServiceClient, chainID, account, contractAddress, claims, getEncodingConfig) } // PrepareBatchClaimMsg is a test wrapper for prepareBatchClaimMsg. diff --git a/ingest/usecase/plugins/orderbook/claimbot/plugin.go b/ingest/usecase/plugins/orderbook/claimbot/plugin.go index c9042c220..625cbad99 100644 --- a/ingest/usecase/plugins/orderbook/claimbot/plugin.go +++ b/ingest/usecase/plugins/orderbook/claimbot/plugin.go @@ -173,7 +173,7 @@ func (o *claimbot) processOrderbookOrders(ctx context.Context, account *authtype ctx, o.config.Keyring, o.config.TxfeesClient, - o.config.GasCalculator, + o.config.MsgSimulator, o.config.TxServiceClient, o.config.ChainID, account, diff --git a/ingest/usecase/plugins/orderbook/claimbot/tx.go b/ingest/usecase/plugins/orderbook/claimbot/tx.go index 7456ebde0..2ab6eb494 100644 --- a/ingest/usecase/plugins/orderbook/claimbot/tx.go +++ b/ingest/usecase/plugins/orderbook/claimbot/tx.go @@ -9,17 +9,22 @@ import ( "github.com/osmosis-labs/sqs/domain/keyring" orderbookdomain "github.com/osmosis-labs/sqs/domain/orderbook" - "github.com/osmosis-labs/osmosis/v26/app" - txfeestypes "github.com/osmosis-labs/osmosis/v26/x/txfees/types" - wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" sdk "github.com/cosmos/cosmos-sdk/types" txtypes "github.com/cosmos/cosmos-sdk/types/tx" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/osmosis-labs/osmosis/v26/app" + "github.com/osmosis-labs/osmosis/v26/app/params" + txfeestypes "github.com/osmosis-labs/osmosis/v26/x/txfees/types" ) var ( - encodingConfig = app.MakeEncodingConfig() + // Note: we monkey patch the encoding config in tests + encodingConfig params.EncodingConfig = app.MakeEncodingConfig() + + defaultEncodingConfigFn = func() params.EncodingConfig { + return encodingConfig + } ) // sendBatchClaimTx prepares and sends a batch claim transaction to the blockchain. @@ -28,13 +33,32 @@ func sendBatchClaimTx( ctx context.Context, keyring keyring.Keyring, txfeesClient txfeestypes.QueryClient, - gasCalculator sqstx.GasCalculator, + msgSimulator sqstx.MsgSimulator, txServiceClient txtypes.ServiceClient, chainID string, account *authtypes.BaseAccount, contractAddress string, claims orderbookdomain.Orders, ) (*sdk.TxResponse, error) { + return sendBatchClaimTxInternal(ctx, keyring, txfeesClient, msgSimulator, txServiceClient, chainID, account, contractAddress, claims, defaultEncodingConfigFn) +} + +// sendBatchClaimTxInternal is a helper function that prepares and sends a batch claim transaction to the blockchain. +// It takes an encoding config function as a parameter to allow for customization of the encoding config in tests. +func sendBatchClaimTxInternal( + ctx context.Context, + keyring keyring.Keyring, + txfeesClient txfeestypes.QueryClient, + msgSimulator sqstx.MsgSimulator, + txServiceClient txtypes.ServiceClient, + chainID string, + account *authtypes.BaseAccount, + contractAddress string, + claims orderbookdomain.Orders, + getEncodingConfig func() params.EncodingConfig, +) (*sdk.TxResponse, error) { + encodingConfig := getEncodingConfig() + address := keyring.GetAddress().String() msgBytes, err := prepareBatchClaimMsg(claims) @@ -44,7 +68,7 @@ func sendBatchClaimTx( msg := buildExecuteContractMsg(address, contractAddress, msgBytes) - tx, err := sqstx.BuildTx(ctx, keyring, txfeesClient, gasCalculator, encodingConfig, account, chainID, msg) + tx, err := msgSimulator.BuildTx(ctx, keyring, txfeesClient, encodingConfig, account, chainID, msg) if err != nil { return nil, fmt.Errorf("failed to build transaction: %w", err) } diff --git a/ingest/usecase/plugins/orderbook/claimbot/tx_test.go b/ingest/usecase/plugins/orderbook/claimbot/tx_test.go index 79bc6c652..75031f928 100644 --- a/ingest/usecase/plugins/orderbook/claimbot/tx_test.go +++ b/ingest/usecase/plugins/orderbook/claimbot/tx_test.go @@ -4,26 +4,36 @@ import ( "context" "testing" - "github.com/osmosis-labs/sqs/domain/mocks" - orderbookdomain "github.com/osmosis-labs/sqs/domain/orderbook" - "github.com/osmosis-labs/sqs/ingest/usecase/plugins/orderbook/claimbot" - + cosmosclient "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types" txtypes "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/cosmos/cosmos-sdk/x/auth/signing" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - + "github.com/osmosis-labs/osmosis/v26/app" + "github.com/osmosis-labs/osmosis/v26/app/params" + txfeestypes "github.com/osmosis-labs/osmosis/v26/x/txfees/types" + "github.com/osmosis-labs/sqs/domain/keyring" + "github.com/osmosis-labs/sqs/domain/mocks" + orderbookdomain "github.com/osmosis-labs/sqs/domain/orderbook" + "github.com/osmosis-labs/sqs/ingest/usecase/plugins/orderbook/claimbot" "github.com/stretchr/testify/assert" "google.golang.org/grpc" ) func TestSendBatchClaimTx(t *testing.T) { + const mockedTxBytes = "mocked-tx-bytes" + tests := []struct { - name string - chainID string - contractAddress string - claims orderbookdomain.Orders - setupMocks func(*mocks.Keyring, *authtypes.BaseAccount, *mocks.TxFeesQueryClient, *mocks.GasCalculator, *mocks.TxServiceClient) - setSendTxFunc func() []byte + name string + chainID string + contractAddress string + claims orderbookdomain.Orders + setupMocks func(*mocks.Keyring, *authtypes.BaseAccount, *mocks.TxFeesQueryClient, *mocks.MsgSimulatorMock, *mocks.TxServiceClient) + setSendTxFunc func() []byte + + getEncodingConfigFn func() params.EncodingConfig + expectedResponse *sdk.TxResponse expectedError bool }{ @@ -33,17 +43,29 @@ func TestSendBatchClaimTx(t *testing.T) { claims: orderbookdomain.Orders{ {TickId: 13, OrderId: 99}, }, - setupMocks: func(keyringMock *mocks.Keyring, account *authtypes.BaseAccount, txfeesClient *mocks.TxFeesQueryClient, gasCalculator *mocks.GasCalculator, txServiceClient *mocks.TxServiceClient) { + setupMocks: func(keyringMock *mocks.Keyring, account *authtypes.BaseAccount, txfeesClient *mocks.TxFeesQueryClient, msgSimulator *mocks.MsgSimulatorMock, txServiceClient *mocks.TxServiceClient) { keyringMock.WithGetAddress("osmo0address") keyringMock.WithGetKey("6cf5103c60c939a5f38e383b52239c5296c968579eec1c68a47d70fbf1d19159") account = &authtypes.BaseAccount{ AccountNumber: 3, Sequence: 31, } - gasCalculator.WithCalculateGas(nil, 0, assert.AnError) // Fail BuildTx + // Fail BuildTx + msgSimulator.BuildTxFn = func( + ctx context.Context, + keyring keyring.Keyring, + txfeesClient txfeestypes.QueryClient, + encodingConfig params.EncodingConfig, + account *authtypes.BaseAccount, + chainID string, + msg ...sdk.Msg, + ) (cosmosclient.TxBuilder, error) { + return nil, assert.AnError + } }, - expectedResponse: &sdk.TxResponse{}, - expectedError: true, + getEncodingConfigFn: claimbot.DefaultEncodingConfigFn, + expectedResponse: &sdk.TxResponse{}, + expectedError: true, }, { name: "SendTx returns error", @@ -51,10 +73,24 @@ func TestSendBatchClaimTx(t *testing.T) { claims: orderbookdomain.Orders{ {TickId: 13, OrderId: 99}, }, - setupMocks: func(keyringMock *mocks.Keyring, account *authtypes.BaseAccount, txfeesClient *mocks.TxFeesQueryClient, gasCalculator *mocks.GasCalculator, txServiceClient *mocks.TxServiceClient) { + setupMocks: func(keyringMock *mocks.Keyring, account *authtypes.BaseAccount, txfeesClient *mocks.TxFeesQueryClient, msgSimulator *mocks.MsgSimulatorMock, txServiceClient *mocks.TxServiceClient) { keyringMock.WithGetAddress("osmo5address") keyringMock.WithGetKey("6cf5103c60c939a5f38e383b52239c5296c968579eec1c68a47d70fbf1d19159") - gasCalculator.WithCalculateGas(nil, 51, nil) + msgSimulator.BuildTxFn = func( + ctx context.Context, + keyring keyring.Keyring, + txfeesClient txfeestypes.QueryClient, + encodingConfig params.EncodingConfig, + account *authtypes.BaseAccount, + chainID string, + msg ...sdk.Msg, + ) (cosmosclient.TxBuilder, error) { + return &mocks.TxBuilderMock{ + GetTxFn: func() signing.Tx { + return &mocks.TxMock{} + }, + }, nil + } txfeesClient.WithBaseDenom("uosmo", nil) txfeesClient.WithGetEipBaseFee("0.2", nil) account = &authtypes.BaseAccount{ @@ -63,8 +99,9 @@ func TestSendBatchClaimTx(t *testing.T) { } txServiceClient.WithBroadcastTx(nil, assert.AnError) // SendTx returns error }, - expectedResponse: &sdk.TxResponse{}, - expectedError: true, + getEncodingConfigFn: claimbot.DefaultEncodingConfigFn, + expectedResponse: &sdk.TxResponse{}, + expectedError: true, }, { name: "Successful transaction", @@ -74,10 +111,24 @@ func TestSendBatchClaimTx(t *testing.T) { {TickId: 1, OrderId: 100}, {TickId: 2, OrderId: 200}, }, - setupMocks: func(keyringMock *mocks.Keyring, account *authtypes.BaseAccount, txfeesClient *mocks.TxFeesQueryClient, gasCalculator *mocks.GasCalculator, txServiceClient *mocks.TxServiceClient) { + setupMocks: func(keyringMock *mocks.Keyring, account *authtypes.BaseAccount, txfeesClient *mocks.TxFeesQueryClient, msgSimulator *mocks.MsgSimulatorMock, txServiceClient *mocks.TxServiceClient) { keyringMock.WithGetAddress("osmo1address") keyringMock.WithGetKey("6cf5103c60c939a5f38e383b52239c5296c968579eec1c68a47d70fbf1d19159") - gasCalculator.WithCalculateGas(nil, 51, nil) + msgSimulator.BuildTxFn = func( + ctx context.Context, + keyring keyring.Keyring, + txfeesClient txfeestypes.QueryClient, + encodingConfig params.EncodingConfig, + account *authtypes.BaseAccount, + chainID string, + msg ...sdk.Msg, + ) (cosmosclient.TxBuilder, error) { + return &mocks.TxBuilderMock{ + GetTxFn: func() signing.Tx { + return &mocks.TxMock{} + }, + }, nil + } txfeesClient.WithBaseDenom("uosmo", nil) txfeesClient.WithGetEipBaseFee("0.15", nil) account = &authtypes.BaseAccount{ @@ -93,8 +144,21 @@ func TestSendBatchClaimTx(t *testing.T) { }, nil } }, + + getEncodingConfigFn: func() params.EncodingConfig { + encoding := app.MakeEncodingConfig() + encoding.TxConfig = &mocks.TxConfigMock{ + TxEncoderFn: func() types.TxEncoder { + return func(tx types.Tx) ([]byte, error) { + return []byte(mockedTxBytes), nil + } + }, + } + return encoding + }, + expectedResponse: &sdk.TxResponse{ - Data: "\n\x90\x01\n\x8d\x01\n$/cosmwasm.wasm.v1.MsgExecuteContract\x12e\n\x1fosmo1daek6me3v9jxgun9wdes7m4n5q\x12\x14osmo1contractaddress\x1a,{\"batch_claim\":{\"orders\":[[1,100],[2,200]]}}\x12`\nN\nF\n\x1f/cosmos.crypto.secp256k1.PubKey\x12#\n!\x03\xef]m\xf2\x8a\bx\x1f\x9a%v]E\x9e\x96\xa8\x9dc6a\x1d\x1f\x8a\xb4\xd3/q,֍\xd3\xd0\x12\x04\n\x02\b\x01\x12\x0e\n\n\n\x05uosmo\x12\x018\x103\x1a@\x1dI\xb5/D\xd0L\v2\xacg\x91\xb3;b+\xdb\xf6\xe0\x1c\x92\xee\xb8d\xc4&%<ڵ\x81\xd6u\xeb-\xf0ੌ\xf5\xa8);\x19\xfc%@\r\xfb2\x05AI\x13\xf3)=\n\xcf~\xb0\"\xf0\xb1", + Data: string(mockedTxBytes), }, expectedError: false, }, @@ -102,16 +166,18 @@ func TestSendBatchClaimTx(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() keyring := mocks.Keyring{} account := authtypes.BaseAccount{} txFeesClient := mocks.TxFeesQueryClient{} - gasCalculator := mocks.GasCalculator{} txServiceClient := mocks.TxServiceClient{} - tt.setupMocks(&keyring, &account, &txFeesClient, &gasCalculator, &txServiceClient) + txSimulatorMock := mocks.MsgSimulatorMock{} + + tt.setupMocks(&keyring, &account, &txFeesClient, &txSimulatorMock, &txServiceClient) - response, err := claimbot.SendBatchClaimTx(ctx, &keyring, &txFeesClient, &gasCalculator, &txServiceClient, tt.chainID, &account, tt.contractAddress, tt.claims) + response, err := claimbot.SendBatchClaimTxInternal(ctx, &keyring, &txFeesClient, &txSimulatorMock, &txServiceClient, tt.chainID, &account, tt.contractAddress, tt.claims, tt.getEncodingConfigFn) if tt.expectedError { assert.Error(t, err) } else {