From ecf3e476cf393384df582141fcedd0411ba6f470 Mon Sep 17 00:00:00 2001 From: Unique Divine <51418232+Unique-Divine@users.noreply.github.com> Date: Thu, 31 Oct 2024 14:15:01 -0500 Subject: [PATCH 1/2] fix(evm): Use a NibiruBankKeeper to automatically record ether balance changes on the StateDB journal (#2095) * statedb: add cacheing for multistore before precompile runs * messy, working first version that allows for precompile reversion * wip!: Save checkpoint. 1. Created NibiruBankKeeper with safety around NIBI transfers inside of EthereumTx. 2. The "PrecompileCalled" JournalChange now has a propery implementation and a strong test case to show that reverting the precompile calls works as intended. 3. Remove unneeded functions created for testing with low-level struct fields. * chore: changelog * finalize bank keeper changes * chore changelog * reset cache * finalize bank keeper changes * revert to previous commit 7f904a07ac4d5555d8c088411024fc50ff65d085 * fix strange ignored file issue * fix strange ignored file issue * remove new bank keeper * chore: comments from self-review * chore: changelo g * fix(deps): update bank module to remove concrete type assertion on AppModule.RegisterServices This makes it possible to plug in an alternative bankkeeper.Keeper implementation instead of bankkeeper.BaseKeeper. * chore: merge conflicts * revert experiment * finishing touch * fix incorrect arugmetn * feat(evmtest-erc20: Add optional descriptions to the assertions --- CHANGELOG.md | 22 ++- app/ante/gas_wanted_test.go | 2 +- app/ante/handler_opts.go | 2 +- app/evmante/evmante_can_transfer.go | 3 +- app/evmante/evmante_can_transfer_test.go | 4 +- app/evmante/evmante_emit_event_test.go | 4 +- app/evmante/evmante_gas_consume_test.go | 4 +- app/evmante/evmante_handler.go | 18 +- app/evmante/evmante_handler_test.go | 2 +- .../evmante_increment_sender_seq_test.go | 4 +- app/evmante/evmante_mempool_fees_test.go | 2 +- app/evmante/evmante_setup_ctx_test.go | 4 +- app/evmante/evmante_sigverify_test.go | 4 +- app/evmante/evmante_validate_basic_test.go | 4 +- app/evmante/evmante_verify_eth_acc_test.go | 4 +- app/keepers.go | 29 ++-- app/keepers/all_keepers.go | 2 +- go.mod | 2 +- go.sum | 4 +- .../TestNativeSendThenPrecompileSend.json | 4 +- .../TestNativeSendThenPrecompileSend.sol | 21 ++- x/evm/evmmodule/genesis_test.go | 4 +- x/evm/evmtest/erc20.go | 70 +++++++- x/evm/evmtest/eth_test.go | 13 +- x/evm/evmtest/smart_contract_test.go | 4 +- x/evm/evmtest/test_deps.go | 4 +- x/evm/evmtest/tx.go | 15 +- x/evm/keeper/bank_extension.go | 155 ++++++++++++++++++ x/evm/keeper/erc20.go | 4 +- x/evm/keeper/funtoken_from_coin.go | 2 +- x/evm/keeper/funtoken_from_coin_test.go | 133 +++++++++------ x/evm/keeper/funtoken_from_erc20.go | 4 +- x/evm/keeper/funtoken_from_erc20_test.go | 2 +- x/evm/keeper/gas_fees.go | 4 +- x/evm/keeper/grpc_query_test.go | 4 +- x/evm/keeper/keeper.go | 9 +- x/evm/keeper/msg_ethereum_tx_test.go | 8 +- x/evm/keeper/msg_server.go | 22 +-- x/evm/keeper/statedb.go | 20 +-- x/evm/keeper/statedb_test.go | 6 +- x/evm/precompile/funtoken.go | 82 ++------- x/evm/precompile/funtoken_test.go | 4 +- x/evm/precompile/wasm.go | 8 + x/evm/statedb/statedb.go | 4 + x/evm/statedb/statedb_test.go | 36 ++-- 45 files changed, 496 insertions(+), 266 deletions(-) create mode 100644 x/evm/keeper/bank_extension.go diff --git a/CHANGELOG.md b/CHANGELOG.md index dade1db65..884cc7e67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,7 +68,12 @@ consistent setup and dynamic gas calculations, addressing the following tickets. - - [#2088](https://github.com/NibiruChain/nibiru/pull/2088) - refactor(evm): remove outdated comment and improper error message text - [#2089](https://github.com/NibiruChain/nibiru/pull/2089) - better handling of gas consumption within erc20 contract execution +- [#2090](https://github.com/NibiruChain/nibiru/pull/2090) - fix(evm): Account +for (1) ERC20 transfers with tokens that return false success values instead of +throwing an error and (2) ERC20 transfers with other operations that don't bring +about the expected resulting balance for the transfer recipient. - [#2091](https://github.com/NibiruChain/nibiru/pull/2091) - feat(evm): add fun token creation fee validation +- [#2092](https://github.com/NibiruChain/nibiru/pull/2092) - feat(evm): add validation for wasm multi message execution - [#2094](https://github.com/NibiruChain/nibiru/pull/2094) - fix(evm): Following from the changs in #2086, this pull request implements a new `JournalChange` struct that saves a deep copy of the state multi store before each @@ -78,12 +83,17 @@ non-EVM and EVM state will be in sync even if there are complex, multi-step Ethereum transactions, such as in the case of an EthereumTx that influences the `StateDB`, then calls a precompile that also changes non-EVM state, and then EVM reverts inside of a try-catch. -- [#2098](https://github.com/NibiruChain/nibiru/pull/2098) - test(evm): statedb tests for race conditions within funtoken precompile -- [#2090](https://github.com/NibiruChain/nibiru/pull/2090) - fix(evm): Account -for (1) ERC20 transfers with tokens that return false success values instead of -throwing an error and (2) ERC20 transfers with other operations that don't bring -about the expected resulting balance for the transfer recipient. -- [#2092](https://github.com/NibiruChain/nibiru/pull/2092) - feat(evm): add validation for wasm multi message execution +- [#2095](https://github.com/NibiruChain/nibiru/pull/2095) - fix(evm): This +change records NIBI (ether) transfers on the `StateDB` during precompiled +contract calls using the `NibiruBankKeeper`, which is struct extension of +the `bankkeeper.BaseKeeper` that is used throughout Nibiru. +The `NibiruBankKeeper` holds a reference to the current EVM `StateDB` and records +balance changes in wei as journal changes automatically. This guarantees that +commits and reversions of the `StateDB` do not misalign with the state of the +Bank module. This code change uses the `NibiruBankKeeper` on all modules that +depend on x/bank, such as the EVM and Wasm modules. +- [#2098](https://github.com/NibiruChain/nibiru/pull/2098) - test(evm): statedb +tests for race conditions within funtoken precompile - [#2068](https://github.com/NibiruChain/nibiru/pull/2068) - feat: enable wasm light clients on IBC (08-wasm) - [#2101](https://github.com/NibiruChain/nibiru/pull/2101) - fix(evm): tx receipt proper marshalling diff --git a/app/ante/gas_wanted_test.go b/app/ante/gas_wanted_test.go index d892a8217..92b5d1343 100644 --- a/app/ante/gas_wanted_test.go +++ b/app/ante/gas_wanted_test.go @@ -83,7 +83,7 @@ func (s *AnteTestSuite) TestGasWantedDecorator() { for _, tc := range testCases { s.Run(tc.name, func() { deps := evmtest.NewTestDeps() - stateDB := deps.StateDB() + stateDB := deps.NewStateDB() anteDec := ante.AnteDecoratorGasWanted{} tx := tc.txSetup(&deps) diff --git a/app/ante/handler_opts.go b/app/ante/handler_opts.go index b516653c8..9c1d88301 100644 --- a/app/ante/handler_opts.go +++ b/app/ante/handler_opts.go @@ -20,7 +20,7 @@ type AnteHandlerOptions struct { IBCKeeper *ibckeeper.Keeper DevGasKeeper *devgaskeeper.Keeper DevGasBankKeeper devgasante.BankKeeper - EvmKeeper evmkeeper.Keeper + EvmKeeper *evmkeeper.Keeper AccountKeeper authkeeper.AccountKeeper TxCounterStoreKey types.StoreKey diff --git a/app/evmante/evmante_can_transfer.go b/app/evmante/evmante_can_transfer.go index 8af24d2f2..0f8cd0d06 100644 --- a/app/evmante/evmante_can_transfer.go +++ b/app/evmante/evmante_can_transfer.go @@ -72,9 +72,8 @@ func (ctd CanTransferDecorator) AnteHandle( BaseFeeWei: baseFeeWeiPerGas, } - stateDB := statedb.New( + stateDB := ctd.NewStateDB( ctx, - ctd.EVMKeeper, statedb.NewEmptyTxConfig(gethcommon.BytesToHash(ctx.HeaderHash().Bytes())), ) evmInstance := ctd.EVMKeeper.NewEVM(ctx, coreMsg, cfg, evm.NewNoOpTracer(), stateDB) diff --git a/app/evmante/evmante_can_transfer_test.go b/app/evmante/evmante_can_transfer_test.go index 2fa71c674..381597624 100644 --- a/app/evmante/evmante_can_transfer_test.go +++ b/app/evmante/evmante_can_transfer_test.go @@ -88,8 +88,8 @@ func (s *TestSuite) TestCanTransferDecorator() { for _, tc := range testCases { s.Run(tc.name, func() { deps := evmtest.NewTestDeps() - stateDB := deps.StateDB() - anteDec := evmante.CanTransferDecorator{&deps.App.AppKeepers.EvmKeeper} + stateDB := deps.NewStateDB() + anteDec := evmante.CanTransferDecorator{deps.App.AppKeepers.EvmKeeper} tx := tc.txSetup(&deps) if tc.ctxSetup != nil { diff --git a/app/evmante/evmante_emit_event_test.go b/app/evmante/evmante_emit_event_test.go index 855165450..20ff36f5d 100644 --- a/app/evmante/evmante_emit_event_test.go +++ b/app/evmante/evmante_emit_event_test.go @@ -41,8 +41,8 @@ func (s *TestSuite) TestEthEmitEventDecorator() { for _, tc := range testCases { s.Run(tc.name, func() { deps := evmtest.NewTestDeps() - stateDB := deps.StateDB() - anteDec := evmante.NewEthEmitEventDecorator(&deps.App.AppKeepers.EvmKeeper) + stateDB := deps.NewStateDB() + anteDec := evmante.NewEthEmitEventDecorator(deps.App.AppKeepers.EvmKeeper) tx := tc.txSetup(&deps) s.Require().NoError(stateDB.Commit()) diff --git a/app/evmante/evmante_gas_consume_test.go b/app/evmante/evmante_gas_consume_test.go index 1e3c6b1fe..3291c3349 100644 --- a/app/evmante/evmante_gas_consume_test.go +++ b/app/evmante/evmante_gas_consume_test.go @@ -59,9 +59,9 @@ func (s *TestSuite) TestAnteDecEthGasConsume() { for _, tc := range testCases { s.Run(tc.name, func() { deps := evmtest.NewTestDeps() - stateDB := deps.StateDB() + stateDB := deps.NewStateDB() anteDec := evmante.NewAnteDecEthGasConsume( - &deps.App.AppKeepers.EvmKeeper, tc.maxGasWanted, + deps.App.AppKeepers.EvmKeeper, tc.maxGasWanted, ) tc.beforeTxSetup(&deps, stateDB) diff --git a/app/evmante/evmante_handler.go b/app/evmante/evmante_handler.go index 787be312e..a9c2f7d0f 100644 --- a/app/evmante/evmante_handler.go +++ b/app/evmante/evmante_handler.go @@ -13,16 +13,16 @@ func NewAnteHandlerEVM( ) sdk.AnteHandler { return sdk.ChainAnteDecorators( // outermost AnteDecorator. SetUpContext must be called first - NewEthSetUpContextDecorator(&options.EvmKeeper), - NewMempoolGasPriceDecorator(&options.EvmKeeper), - NewEthValidateBasicDecorator(&options.EvmKeeper), - NewEthSigVerificationDecorator(&options.EvmKeeper), - NewAnteDecVerifyEthAcc(&options.EvmKeeper, options.AccountKeeper), - CanTransferDecorator{&options.EvmKeeper}, - NewAnteDecEthGasConsume(&options.EvmKeeper, options.MaxTxGasWanted), - NewAnteDecEthIncrementSenderSequence(&options.EvmKeeper, options.AccountKeeper), + NewEthSetUpContextDecorator(options.EvmKeeper), + NewMempoolGasPriceDecorator(options.EvmKeeper), + NewEthValidateBasicDecorator(options.EvmKeeper), + NewEthSigVerificationDecorator(options.EvmKeeper), + NewAnteDecVerifyEthAcc(options.EvmKeeper, options.AccountKeeper), + CanTransferDecorator{options.EvmKeeper}, + NewAnteDecEthGasConsume(options.EvmKeeper, options.MaxTxGasWanted), + NewAnteDecEthIncrementSenderSequence(options.EvmKeeper, options.AccountKeeper), ante.AnteDecoratorGasWanted{}, // emit eth tx hash and index at the very last ante handler. - NewEthEmitEventDecorator(&options.EvmKeeper), + NewEthEmitEventDecorator(options.EvmKeeper), ) } diff --git a/app/evmante/evmante_handler_test.go b/app/evmante/evmante_handler_test.go index 62e7afb0d..5ab7e8a1f 100644 --- a/app/evmante/evmante_handler_test.go +++ b/app/evmante/evmante_handler_test.go @@ -69,7 +69,7 @@ func (s *TestSuite) TestAnteHandlerEVM() { for _, tc := range testCases { s.Run(tc.name, func() { deps := evmtest.NewTestDeps() - stateDB := deps.StateDB() + stateDB := deps.NewStateDB() anteHandlerEVM := evmante.NewAnteHandlerEVM( ante.AnteHandlerOptions{ diff --git a/app/evmante/evmante_increment_sender_seq_test.go b/app/evmante/evmante_increment_sender_seq_test.go index ac358cbb0..b4503e675 100644 --- a/app/evmante/evmante_increment_sender_seq_test.go +++ b/app/evmante/evmante_increment_sender_seq_test.go @@ -66,8 +66,8 @@ func (s *TestSuite) TestAnteDecEthIncrementSenderSequence() { for _, tc := range testCases { s.Run(tc.name, func() { deps := evmtest.NewTestDeps() - stateDB := deps.StateDB() - anteDec := evmante.NewAnteDecEthIncrementSenderSequence(&deps.App.EvmKeeper, deps.App.AccountKeeper) + stateDB := deps.NewStateDB() + anteDec := evmante.NewAnteDecEthIncrementSenderSequence(deps.App.EvmKeeper, deps.App.AccountKeeper) if tc.beforeTxSetup != nil { tc.beforeTxSetup(&deps, stateDB) diff --git a/app/evmante/evmante_mempool_fees_test.go b/app/evmante/evmante_mempool_fees_test.go index ef7b34e64..892bd9e57 100644 --- a/app/evmante/evmante_mempool_fees_test.go +++ b/app/evmante/evmante_mempool_fees_test.go @@ -82,7 +82,7 @@ func (s *TestSuite) TestMempoolGasFeeDecorator() { for _, tc := range testCases { s.Run(tc.name, func() { deps := evmtest.NewTestDeps() - anteDec := evmante.NewMempoolGasPriceDecorator(&deps.App.AppKeepers.EvmKeeper) + anteDec := evmante.NewMempoolGasPriceDecorator(deps.App.AppKeepers.EvmKeeper) tx := tc.txSetup(&deps) diff --git a/app/evmante/evmante_setup_ctx_test.go b/app/evmante/evmante_setup_ctx_test.go index 9df86ba17..028fceb52 100644 --- a/app/evmante/evmante_setup_ctx_test.go +++ b/app/evmante/evmante_setup_ctx_test.go @@ -12,8 +12,8 @@ import ( func (s *TestSuite) TestEthSetupContextDecorator() { deps := evmtest.NewTestDeps() - stateDB := deps.StateDB() - anteDec := evmante.NewEthSetUpContextDecorator(&deps.App.EvmKeeper) + stateDB := deps.NewStateDB() + anteDec := evmante.NewEthSetUpContextDecorator(deps.App.EvmKeeper) s.Require().NoError(stateDB.Commit()) tx := evmtest.HappyCreateContractTx(&deps) diff --git a/app/evmante/evmante_sigverify_test.go b/app/evmante/evmante_sigverify_test.go index 63b290140..d6a7998b1 100644 --- a/app/evmante/evmante_sigverify_test.go +++ b/app/evmante/evmante_sigverify_test.go @@ -66,8 +66,8 @@ func (s *TestSuite) TestEthSigVerificationDecorator() { for _, tc := range testCases { s.Run(tc.name, func() { deps := evmtest.NewTestDeps() - stateDB := deps.StateDB() - anteDec := evmante.NewEthSigVerificationDecorator(&deps.App.AppKeepers.EvmKeeper) + stateDB := deps.NewStateDB() + anteDec := evmante.NewEthSigVerificationDecorator(deps.App.AppKeepers.EvmKeeper) tx := tc.txSetup(&deps) s.Require().NoError(stateDB.Commit()) diff --git a/app/evmante/evmante_validate_basic_test.go b/app/evmante/evmante_validate_basic_test.go index 3f1263dee..2aa7910dd 100644 --- a/app/evmante/evmante_validate_basic_test.go +++ b/app/evmante/evmante_validate_basic_test.go @@ -198,8 +198,8 @@ func (s *TestSuite) TestEthValidateBasicDecorator() { for _, tc := range testCases { s.Run(tc.name, func() { deps := evmtest.NewTestDeps() - stateDB := deps.StateDB() - anteDec := evmante.NewEthValidateBasicDecorator(&deps.App.AppKeepers.EvmKeeper) + stateDB := deps.NewStateDB() + anteDec := evmante.NewEthValidateBasicDecorator(deps.App.AppKeepers.EvmKeeper) tx := tc.txSetup(&deps) s.Require().NoError(stateDB.Commit()) diff --git a/app/evmante/evmante_verify_eth_acc_test.go b/app/evmante/evmante_verify_eth_acc_test.go index 6d7f9aeda..2af951aa5 100644 --- a/app/evmante/evmante_verify_eth_acc_test.go +++ b/app/evmante/evmante_verify_eth_acc_test.go @@ -64,8 +64,8 @@ func (s *TestSuite) TestAnteDecoratorVerifyEthAcc_CheckTx() { for _, tc := range testCases { s.Run(tc.name, func() { deps := evmtest.NewTestDeps() - stateDB := deps.StateDB() - anteDec := evmante.NewAnteDecVerifyEthAcc(&deps.App.AppKeepers.EvmKeeper, &deps.App.AppKeepers.AccountKeeper) + stateDB := deps.NewStateDB() + anteDec := evmante.NewAnteDecVerifyEthAcc(deps.App.AppKeepers.EvmKeeper, &deps.App.AppKeepers.AccountKeeper) tc.beforeTxSetup(&deps, stateDB) tx := tc.txSetup(&deps) diff --git a/app/keepers.go b/app/keepers.go index 156ef6413..e74b89167 100644 --- a/app/keepers.go +++ b/app/keepers.go @@ -146,7 +146,6 @@ type AppKeepers struct { } type privateKeepers struct { - bankBaseKeeper bankkeeper.BaseKeeper capabilityKeeper *capabilitykeeper.Keeper slashingKeeper slashingkeeper.Keeper crisisKeeper crisiskeeper.Keeper @@ -271,14 +270,17 @@ func (app *NibiruApp) InitKeepers( govModuleAddr, ) - app.bankBaseKeeper = bankkeeper.NewBaseKeeper( - appCodec, - keys[banktypes.StoreKey], - app.AccountKeeper, - BlockedAddresses(), - govModuleAddr, - ) - app.BankKeeper = app.bankBaseKeeper + nibiruBankKeeper := &evmkeeper.NibiruBankKeeper{ + BaseKeeper: bankkeeper.NewBaseKeeper( + appCodec, + keys[banktypes.StoreKey], + app.AccountKeeper, + BlockedAddresses(), + govModuleAddr, + ), + StateDB: nil, + } + app.BankKeeper = nibiruBankKeeper app.StakingKeeper = stakingkeeper.NewKeeper( appCodec, keys[stakingtypes.StoreKey], @@ -374,16 +376,17 @@ func (app *NibiruApp) InitKeepers( ), ) - app.EvmKeeper = evmkeeper.NewKeeper( + evmKeeper := evmkeeper.NewKeeper( appCodec, keys[evm.StoreKey], tkeys[evm.TransientKey], authtypes.NewModuleAddress(govtypes.ModuleName), app.AccountKeeper, - app.BankKeeper, + nibiruBankKeeper, app.StakingKeeper, cast.ToString(appOpts.Get("evm.tracer")), ) + app.EvmKeeper = &evmKeeper // ---------------------------------- IBC keepers @@ -631,7 +634,7 @@ func (app *NibiruApp) initAppModules( ), auth.NewAppModule(appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts, app.GetSubspace(authtypes.ModuleName)), vesting.NewAppModule(app.AccountKeeper, app.BankKeeper), - bank.NewAppModule(appCodec, app.bankBaseKeeper, app.AccountKeeper, app.GetSubspace(banktypes.ModuleName)), + bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper, app.GetSubspace(banktypes.ModuleName)), capability.NewAppModule(appCodec, *app.capabilityKeeper, false), feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), gov.NewAppModule(appCodec, &app.GovKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(govtypes.ModuleName)), @@ -657,7 +660,7 @@ func (app *NibiruApp) initAppModules( ica.NewAppModule(&app.icaControllerKeeper, &app.icaHostKeeper), ibcwasm.NewAppModule(app.WasmClientKeeper), - evmmodule.NewAppModule(&app.EvmKeeper, app.AccountKeeper), + evmmodule.NewAppModule(app.EvmKeeper, app.AccountKeeper), // wasm wasm.NewAppModule( diff --git a/app/keepers/all_keepers.go b/app/keepers/all_keepers.go index 25303860f..0ebc82afd 100644 --- a/app/keepers/all_keepers.go +++ b/app/keepers/all_keepers.go @@ -64,7 +64,7 @@ type PublicKeepers struct { SudoKeeper keeper.Keeper DevGasKeeper devgaskeeper.Keeper TokenFactoryKeeper tokenfactorykeeper.Keeper - EvmKeeper evmkeeper.Keeper + EvmKeeper *evmkeeper.Keeper // WASM keepers WasmKeeper wasmkeeper.Keeper diff --git a/go.mod b/go.mod index 99e351a4e..c4546c826 100644 --- a/go.mod +++ b/go.mod @@ -246,7 +246,7 @@ replace ( cosmossdk.io/api => cosmossdk.io/api v0.3.1 github.com/CosmWasm/wasmd => github.com/NibiruChain/wasmd v0.44.0-nibiru - github.com/cosmos/cosmos-sdk => github.com/NibiruChain/cosmos-sdk v0.47.11-nibiru + github.com/cosmos/cosmos-sdk => github.com/NibiruChain/cosmos-sdk v0.47.11-nibiru.2 github.com/cosmos/iavl => github.com/cosmos/iavl v0.20.0 diff --git a/go.sum b/go.sum index 616630872..a4fec943d 100644 --- a/go.sum +++ b/go.sum @@ -235,8 +235,8 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/NibiruChain/collections v0.5.0 h1:33pXpVTe1PK/tfdZlAJF1JF7AdzGNARG+iL9G/z3X7k= github.com/NibiruChain/collections v0.5.0/go.mod h1:43L6yjuF0BMre/mw4gqn/kUOZz1c2Y3huZ/RQfBFrOQ= -github.com/NibiruChain/cosmos-sdk v0.47.11-nibiru h1:PgFpxDe+7+OzWHs4zXlml5j2i9sGq2Zpd3ndYQG29/0= -github.com/NibiruChain/cosmos-sdk v0.47.11-nibiru/go.mod h1:ADjORYzUQqQv/FxDi0H0K5gW/rAk1CiDR3ZKsExfJV0= +github.com/NibiruChain/cosmos-sdk v0.47.11-nibiru.2 h1:HtLNrkp0HhgxtbpdNwsasOod4uUNFpuwYpyceFtbj3E= +github.com/NibiruChain/cosmos-sdk v0.47.11-nibiru.2/go.mod h1:ADjORYzUQqQv/FxDi0H0K5gW/rAk1CiDR3ZKsExfJV0= github.com/NibiruChain/go-ethereum v1.10.27-nibiru h1:o6lRFt57izoYwzN5cG8tnnBtJcaO3X7MjjN7PGGNCFg= github.com/NibiruChain/go-ethereum v1.10.27-nibiru/go.mod h1:kvvL3nDceUcB+1qGUBAsVf5dW23RBR77fqxgx2PGNrQ= github.com/NibiruChain/wasmd v0.44.0-nibiru h1:b+stNdbMFsl0+o4KedXyF83qRnEpB/jCiTGZZgv2h2U= diff --git a/x/evm/embeds/artifacts/contracts/TestNativeSendThenPrecompileSend.sol/TestNativeSendThenPrecompileSend.json b/x/evm/embeds/artifacts/contracts/TestNativeSendThenPrecompileSend.sol/TestNativeSendThenPrecompileSend.json index 5b0046040..e4a2ccfb1 100644 --- a/x/evm/embeds/artifacts/contracts/TestNativeSendThenPrecompileSend.sol/TestNativeSendThenPrecompileSend.json +++ b/x/evm/embeds/artifacts/contracts/TestNativeSendThenPrecompileSend.sol/TestNativeSendThenPrecompileSend.json @@ -43,8 +43,8 @@ "type": "function" } ], - "bytecode": "0x608060405234801561001057600080fd5b50604051610877380380610877833981810160405281019061003291906100db565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050610108565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006100a88261007d565b9050919050565b6100b88161009d565b81146100c357600080fd5b50565b6000815190506100d5816100af565b92915050565b6000602082840312156100f1576100f0610078565b5b60006100ff848285016100c6565b91505092915050565b610760806101176000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063a4de557414610030575b600080fd5b61004a6004803603810190610045919061043c565b61004c565b005b60008473ffffffffffffffffffffffffffffffffffffffff166108fc859081150290604051600060405180830381858888f193505050509050806100c5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bc9061051c565b60405180910390fd5b600061080073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff168486604051602401610115939291906105da565b6040516020818303038152906040527f03003bc5000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161019f919061065f565b6000604051808303816000865af19150503d80600081146101dc576040519150601f19603f3d011682016040523d82523d6000602084013e6101e1565b606091505b50509050806040516020016101f5906106f3565b60405160208183030381529060405290610245576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161023c9190610708565b60405180910390fd5b50505050505050565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061028d82610262565b9050919050565b61029d81610282565b81146102a857600080fd5b50565b6000813590506102ba81610294565b92915050565b6000819050919050565b6102d3816102c0565b81146102de57600080fd5b50565b6000813590506102f0816102ca565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61034982610300565b810181811067ffffffffffffffff8211171561036857610367610311565b5b80604052505050565b600061037b61024e565b90506103878282610340565b919050565b600067ffffffffffffffff8211156103a7576103a6610311565b5b6103b082610300565b9050602081019050919050565b82818337600083830152505050565b60006103df6103da8461038c565b610371565b9050828152602081018484840111156103fb576103fa6102fb565b5b6104068482856103bd565b509392505050565b600082601f830112610423576104226102f6565b5b81356104338482602086016103cc565b91505092915050565b6000806000806080858703121561045657610455610258565b5b6000610464878288016102ab565b9450506020610475878288016102e1565b935050604085013567ffffffffffffffff8111156104965761049561025d565b5b6104a28782880161040e565b92505060606104b3878288016102e1565b91505092959194509250565b600082825260208201905092915050565b7f4661696c656420746f2073656e64206e617469766520746f6b656e0000000000600082015250565b6000610506601b836104bf565b9150610511826104d0565b602082019050919050565b60006020820190508181036000830152610535816104f9565b9050919050565b600061054782610262565b9050919050565b6105578161053c565b82525050565b610566816102c0565b82525050565b600081519050919050565b60005b8381101561059557808201518184015260208101905061057a565b60008484015250505050565b60006105ac8261056c565b6105b681856104bf565b93506105c6818560208601610577565b6105cf81610300565b840191505092915050565b60006060820190506105ef600083018661054e565b6105fc602083018561055d565b818103604083015261060e81846105a1565b9050949350505050565b600081519050919050565b600081905092915050565b600061063982610618565b6106438185610623565b9350610653818560208601610577565b80840191505092915050565b600061066b828461062e565b915081905092915050565b600081905092915050565b7f4661696c656420746f2063616c6c20707265636f6d70696c652062616e6b536560008201527f6e64000000000000000000000000000000000000000000000000000000000000602082015250565b60006106dd602283610676565b91506106e882610681565b602282019050919050565b60006106fe826106d0565b9150819050919050565b6000602082019050818103600083015261072281846105a1565b90509291505056fea2646970667358221220bd148fba67bf9e1966835ecfba5be560625fcf8c88f7890050149168488a782364736f6c63430008180033", - "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061002b5760003560e01c8063a4de557414610030575b600080fd5b61004a6004803603810190610045919061043c565b61004c565b005b60008473ffffffffffffffffffffffffffffffffffffffff166108fc859081150290604051600060405180830381858888f193505050509050806100c5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bc9061051c565b60405180910390fd5b600061080073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff168486604051602401610115939291906105da565b6040516020818303038152906040527f03003bc5000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161019f919061065f565b6000604051808303816000865af19150503d80600081146101dc576040519150601f19603f3d011682016040523d82523d6000602084013e6101e1565b606091505b50509050806040516020016101f5906106f3565b60405160208183030381529060405290610245576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161023c9190610708565b60405180910390fd5b50505050505050565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061028d82610262565b9050919050565b61029d81610282565b81146102a857600080fd5b50565b6000813590506102ba81610294565b92915050565b6000819050919050565b6102d3816102c0565b81146102de57600080fd5b50565b6000813590506102f0816102ca565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61034982610300565b810181811067ffffffffffffffff8211171561036857610367610311565b5b80604052505050565b600061037b61024e565b90506103878282610340565b919050565b600067ffffffffffffffff8211156103a7576103a6610311565b5b6103b082610300565b9050602081019050919050565b82818337600083830152505050565b60006103df6103da8461038c565b610371565b9050828152602081018484840111156103fb576103fa6102fb565b5b6104068482856103bd565b509392505050565b600082601f830112610423576104226102f6565b5b81356104338482602086016103cc565b91505092915050565b6000806000806080858703121561045657610455610258565b5b6000610464878288016102ab565b9450506020610475878288016102e1565b935050604085013567ffffffffffffffff8111156104965761049561025d565b5b6104a28782880161040e565b92505060606104b3878288016102e1565b91505092959194509250565b600082825260208201905092915050565b7f4661696c656420746f2073656e64206e617469766520746f6b656e0000000000600082015250565b6000610506601b836104bf565b9150610511826104d0565b602082019050919050565b60006020820190508181036000830152610535816104f9565b9050919050565b600061054782610262565b9050919050565b6105578161053c565b82525050565b610566816102c0565b82525050565b600081519050919050565b60005b8381101561059557808201518184015260208101905061057a565b60008484015250505050565b60006105ac8261056c565b6105b681856104bf565b93506105c6818560208601610577565b6105cf81610300565b840191505092915050565b60006060820190506105ef600083018661054e565b6105fc602083018561055d565b818103604083015261060e81846105a1565b9050949350505050565b600081519050919050565b600081905092915050565b600061063982610618565b6106438185610623565b9350610653818560208601610577565b80840191505092915050565b600061066b828461062e565b915081905092915050565b600081905092915050565b7f4661696c656420746f2063616c6c20707265636f6d70696c652062616e6b536560008201527f6e64000000000000000000000000000000000000000000000000000000000000602082015250565b60006106dd602283610676565b91506106e882610681565b602282019050919050565b60006106fe826106d0565b9150819050919050565b6000602082019050818103600083015261072281846105a1565b90509291505056fea2646970667358221220bd148fba67bf9e1966835ecfba5be560625fcf8c88f7890050149168488a782364736f6c63430008180033", + "bytecode": "0x608060405234801561001057600080fd5b50604051610afc380380610afc833981810160405281019061003291906100db565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050610108565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006100a88261007d565b9050919050565b6100b88161009d565b81146100c357600080fd5b50565b6000815190506100d5816100af565b92915050565b6000602082840312156100f1576100f0610078565b5b60006100ff848285016100c6565b91505092915050565b6109e5806101176000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063a4de557414610030575b600080fd5b61004a600480360381019061004591906105f9565b61004c565b005b60008473ffffffffffffffffffffffffffffffffffffffff166108fc859081150290604051600060405180830381858888f193505050509050806100c5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bc906106d9565b60405180910390fd5b600061080073ffffffffffffffffffffffffffffffffffffffff166303003bc560008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1685876040518463ffffffff1660e01b815260040161012693929190610797565b6020604051808303816000875af1158015610145573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061016991906107ea565b9050828114610177826101ea565b610180856101ea565b604051602001610191929190610911565b604051602081830303815290604052906101e1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101d8919061095e565b60405180910390fd5b50505050505050565b6060600060016101f9846102b8565b01905060008167ffffffffffffffff811115610218576102176104ce565b5b6040519080825280601f01601f19166020018201604052801561024a5781602001600182028036833780820191505090505b509050600082602001820190505b6001156102ad578080600190039150507f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a85816102a1576102a0610980565b5b04945060008503610258575b819350505050919050565b600080600090507a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008310610316577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000838161030c5761030b610980565b5b0492506040810190505b6d04ee2d6d415b85acef81000000008310610353576d04ee2d6d415b85acef8100000000838161034957610348610980565b5b0492506020810190505b662386f26fc10000831061038257662386f26fc10000838161037857610377610980565b5b0492506010810190505b6305f5e10083106103ab576305f5e10083816103a1576103a0610980565b5b0492506008810190505b61271083106103d05761271083816103c6576103c5610980565b5b0492506004810190505b606483106103f357606483816103e9576103e8610980565b5b0492506002810190505b600a8310610402576001810190505b80915050919050565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061044a8261041f565b9050919050565b61045a8161043f565b811461046557600080fd5b50565b60008135905061047781610451565b92915050565b6000819050919050565b6104908161047d565b811461049b57600080fd5b50565b6000813590506104ad81610487565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610506826104bd565b810181811067ffffffffffffffff82111715610525576105246104ce565b5b80604052505050565b600061053861040b565b905061054482826104fd565b919050565b600067ffffffffffffffff821115610564576105636104ce565b5b61056d826104bd565b9050602081019050919050565b82818337600083830152505050565b600061059c61059784610549565b61052e565b9050828152602081018484840111156105b8576105b76104b8565b5b6105c384828561057a565b509392505050565b600082601f8301126105e0576105df6104b3565b5b81356105f0848260208601610589565b91505092915050565b6000806000806080858703121561061357610612610415565b5b600061062187828801610468565b94505060206106328782880161049e565b935050604085013567ffffffffffffffff8111156106535761065261041a565b5b61065f878288016105cb565b92505060606106708782880161049e565b91505092959194509250565b600082825260208201905092915050565b7f4661696c656420746f2073656e64206e617469766520746f6b656e0000000000600082015250565b60006106c3601b8361067c565b91506106ce8261068d565b602082019050919050565b600060208201905081810360008301526106f2816106b6565b9050919050565b60006107048261041f565b9050919050565b610714816106f9565b82525050565b6107238161047d565b82525050565b600081519050919050565b60005b83811015610752578082015181840152602081019050610737565b60008484015250505050565b600061076982610729565b610773818561067c565b9350610783818560208601610734565b61078c816104bd565b840191505092915050565b60006060820190506107ac600083018661070b565b6107b9602083018561071a565b81810360408301526107cb818461075e565b9050949350505050565b6000815190506107e481610487565b92915050565b600060208284031215610800576107ff610415565b5b600061080e848285016107d5565b91505092915050565b600081905092915050565b7f4946756e546f6b656e2e62616e6b53656e64207375636365656465642062757460008201527f207472616e73666572726564207468652077726f6e6720616d6f756e74000000602082015250565b600061087e603d83610817565b915061088982610822565b603d82019050919050565b7f73656e74416d6f756e7420000000000000000000000000000000000000000000815250565b60006108c582610729565b6108cf8185610817565b93506108df818560208601610734565b80840191505092915050565b7f6578706563746564200000000000000000000000000000000000000000000000815250565b600061091c82610871565b915061092782610894565b600b8201915061093782856108ba565b9150610942826108eb565b60098201915061095282846108ba565b91508190509392505050565b60006020820190508181036000830152610978818461075e565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fdfea2646970667358221220df7092cb424bd549df3b9d958db66a63c029a1e42d78f468fb93c7574ccea23864736f6c63430008180033", + "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061002b5760003560e01c8063a4de557414610030575b600080fd5b61004a600480360381019061004591906105f9565b61004c565b005b60008473ffffffffffffffffffffffffffffffffffffffff166108fc859081150290604051600060405180830381858888f193505050509050806100c5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bc906106d9565b60405180910390fd5b600061080073ffffffffffffffffffffffffffffffffffffffff166303003bc560008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1685876040518463ffffffff1660e01b815260040161012693929190610797565b6020604051808303816000875af1158015610145573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061016991906107ea565b9050828114610177826101ea565b610180856101ea565b604051602001610191929190610911565b604051602081830303815290604052906101e1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101d8919061095e565b60405180910390fd5b50505050505050565b6060600060016101f9846102b8565b01905060008167ffffffffffffffff811115610218576102176104ce565b5b6040519080825280601f01601f19166020018201604052801561024a5781602001600182028036833780820191505090505b509050600082602001820190505b6001156102ad578080600190039150507f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a85816102a1576102a0610980565b5b04945060008503610258575b819350505050919050565b600080600090507a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008310610316577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000838161030c5761030b610980565b5b0492506040810190505b6d04ee2d6d415b85acef81000000008310610353576d04ee2d6d415b85acef8100000000838161034957610348610980565b5b0492506020810190505b662386f26fc10000831061038257662386f26fc10000838161037857610377610980565b5b0492506010810190505b6305f5e10083106103ab576305f5e10083816103a1576103a0610980565b5b0492506008810190505b61271083106103d05761271083816103c6576103c5610980565b5b0492506004810190505b606483106103f357606483816103e9576103e8610980565b5b0492506002810190505b600a8310610402576001810190505b80915050919050565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061044a8261041f565b9050919050565b61045a8161043f565b811461046557600080fd5b50565b60008135905061047781610451565b92915050565b6000819050919050565b6104908161047d565b811461049b57600080fd5b50565b6000813590506104ad81610487565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610506826104bd565b810181811067ffffffffffffffff82111715610525576105246104ce565b5b80604052505050565b600061053861040b565b905061054482826104fd565b919050565b600067ffffffffffffffff821115610564576105636104ce565b5b61056d826104bd565b9050602081019050919050565b82818337600083830152505050565b600061059c61059784610549565b61052e565b9050828152602081018484840111156105b8576105b76104b8565b5b6105c384828561057a565b509392505050565b600082601f8301126105e0576105df6104b3565b5b81356105f0848260208601610589565b91505092915050565b6000806000806080858703121561061357610612610415565b5b600061062187828801610468565b94505060206106328782880161049e565b935050604085013567ffffffffffffffff8111156106535761065261041a565b5b61065f878288016105cb565b92505060606106708782880161049e565b91505092959194509250565b600082825260208201905092915050565b7f4661696c656420746f2073656e64206e617469766520746f6b656e0000000000600082015250565b60006106c3601b8361067c565b91506106ce8261068d565b602082019050919050565b600060208201905081810360008301526106f2816106b6565b9050919050565b60006107048261041f565b9050919050565b610714816106f9565b82525050565b6107238161047d565b82525050565b600081519050919050565b60005b83811015610752578082015181840152602081019050610737565b60008484015250505050565b600061076982610729565b610773818561067c565b9350610783818560208601610734565b61078c816104bd565b840191505092915050565b60006060820190506107ac600083018661070b565b6107b9602083018561071a565b81810360408301526107cb818461075e565b9050949350505050565b6000815190506107e481610487565b92915050565b600060208284031215610800576107ff610415565b5b600061080e848285016107d5565b91505092915050565b600081905092915050565b7f4946756e546f6b656e2e62616e6b53656e64207375636365656465642062757460008201527f207472616e73666572726564207468652077726f6e6720616d6f756e74000000602082015250565b600061087e603d83610817565b915061088982610822565b603d82019050919050565b7f73656e74416d6f756e7420000000000000000000000000000000000000000000815250565b60006108c582610729565b6108cf8185610817565b93506108df818560208601610734565b80840191505092915050565b7f6578706563746564200000000000000000000000000000000000000000000000815250565b600061091c82610871565b915061092782610894565b600b8201915061093782856108ba565b9150610942826108eb565b60098201915061095282846108ba565b91508190509392505050565b60006020820190508181036000830152610978818461075e565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fdfea2646970667358221220df7092cb424bd549df3b9d958db66a63c029a1e42d78f468fb93c7574ccea23864736f6c63430008180033", "linkReferences": {}, "deployedLinkReferences": {} } diff --git a/x/evm/embeds/contracts/TestNativeSendThenPrecompileSend.sol b/x/evm/embeds/contracts/TestNativeSendThenPrecompileSend.sol index da9cb471b..45ec757b9 100644 --- a/x/evm/embeds/contracts/TestNativeSendThenPrecompileSend.sol +++ b/x/evm/embeds/contracts/TestNativeSendThenPrecompileSend.sol @@ -20,15 +20,20 @@ contract TestNativeSendThenPrecompileSend { bool isSent = nativeRecipient.send(nativeAmount); require(isSent, "Failed to send native token"); - (bool success, ) = FUNTOKEN_PRECOMPILE_ADDRESS.call( - abi.encodeWithSignature( - "bankSend(address,uint256,string)", - erc20, - precompileAmount, - precompileRecipient + uint256 sentAmount = FUNTOKEN_PRECOMPILE.bankSend( + erc20, + precompileAmount, + precompileRecipient + ); + require( + sentAmount == precompileAmount, + string.concat( + "IFunToken.bankSend succeeded but transferred the wrong amount", + "sentAmount ", + Strings.toString(sentAmount), + "expected ", + Strings.toString(precompileAmount) ) ); - - require(success, string.concat("Failed to call precompile bankSend")); } } diff --git a/x/evm/evmmodule/genesis_test.go b/x/evm/evmmodule/genesis_test.go index 690745e84..72b884082 100644 --- a/x/evm/evmmodule/genesis_test.go +++ b/x/evm/evmmodule/genesis_test.go @@ -89,13 +89,13 @@ func (s *Suite) TestExportInitGenesis() { s.Require().NoError(err) // Export genesis - evmGenesisState := evmmodule.ExportGenesis(deps.Ctx, &deps.EvmKeeper, deps.App.AccountKeeper) + evmGenesisState := evmmodule.ExportGenesis(deps.Ctx, deps.EvmKeeper, deps.App.AccountKeeper) authGenesisState := deps.App.AccountKeeper.ExportGenesis(deps.Ctx) // Init genesis from the exported state deps = evmtest.NewTestDeps() deps.App.AccountKeeper.InitGenesis(deps.Ctx, *authGenesisState) - evmmodule.InitGenesis(deps.Ctx, &deps.EvmKeeper, deps.App.AccountKeeper, *evmGenesisState) + evmmodule.InitGenesis(deps.Ctx, deps.EvmKeeper, deps.App.AccountKeeper, *evmGenesisState) // Verify erc20 balances for users A, B and sender balance, err := deps.EvmKeeper.ERC20().BalanceOf(erc20Addr, toUserA, deps.Ctx) diff --git a/x/evm/evmtest/erc20.go b/x/evm/evmtest/erc20.go index d8798f71d..3d7dd4d76 100644 --- a/x/evm/evmtest/erc20.go +++ b/x/evm/evmtest/erc20.go @@ -21,9 +21,7 @@ func AssertERC20BalanceEqual( erc20, account gethcommon.Address, expectedBalance *big.Int, ) { - actualBalance, err := deps.EvmKeeper.ERC20().BalanceOf(erc20, account, deps.Ctx) - assert.NoError(t, err) - assert.Equal(t, expectedBalance.String(), actualBalance.String(), "expected %s, got %s", expectedBalance, actualBalance) + AssertERC20BalanceEqualWithDescription(t, deps, erc20, account, expectedBalance, "") } // CreateFunTokenForBankCoin: Uses the "TestDeps.Sender" account to create a @@ -95,9 +93,9 @@ func AssertBankBalanceEqual( account gethcommon.Address, expectedBalance *big.Int, ) { - bech32Addr := eth.EthAddrToNibiruAddr(account) - actualBalance := deps.App.BankKeeper.GetBalance(deps.Ctx, bech32Addr, denom).Amount.BigInt() - assert.Zero(t, expectedBalance.Cmp(actualBalance), "expected %s, got %s", expectedBalance, actualBalance) + AssertBankBalanceEqualWithDescription( + t, deps, denom, account, expectedBalance, "", + ) } // BigPow multiplies "amount" by 10 to the "pow10Exp". @@ -105,3 +103,63 @@ func BigPow(amount *big.Int, pow10Exp uint8) (powAmount *big.Int) { pow10 := new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(pow10Exp)), nil) return new(big.Int).Mul(amount, pow10) } + +type FunTokenBalanceAssert struct { + FunToken evm.FunToken + Account gethcommon.Address + BalanceBank *big.Int + BalanceERC20 *big.Int + Description string +} + +func (bals FunTokenBalanceAssert) Assert(t *testing.T, deps TestDeps) { + AssertERC20BalanceEqualWithDescription( + t, deps, bals.FunToken.Erc20Addr.Address, bals.Account, bals.BalanceERC20, + bals.Description, + ) + AssertBankBalanceEqualWithDescription( + t, deps, bals.FunToken.BankDenom, bals.Account, bals.BalanceBank, + bals.Description, + ) +} + +func AssertERC20BalanceEqualWithDescription( + t *testing.T, + deps TestDeps, + erc20, account gethcommon.Address, + expectedBalance *big.Int, + description string, +) { + actualBalance, err := deps.EvmKeeper.ERC20().BalanceOf(erc20, account, deps.Ctx) + var errSuffix string + if description == "" { + errSuffix = description + } else { + errSuffix = ": " + description + } + assert.NoError(t, err, errSuffix) + assert.Equalf(t, expectedBalance.String(), actualBalance.String(), + "expected %s, got %s", expectedBalance, actualBalance, + errSuffix, + ) +} + +func AssertBankBalanceEqualWithDescription( + t *testing.T, + deps TestDeps, + denom string, + account gethcommon.Address, + expectedBalance *big.Int, + description string, +) { + bech32Addr := eth.EthAddrToNibiruAddr(account) + actualBalance := deps.App.BankKeeper.GetBalance(deps.Ctx, bech32Addr, denom).Amount.BigInt() + var errSuffix string + if description == "" { + errSuffix = description + } else { + errSuffix = ": " + description + } + assert.Equalf(t, expectedBalance.String(), actualBalance.String(), + "expected %s, got %s", expectedBalance, actualBalance, errSuffix) +} diff --git a/x/evm/evmtest/eth_test.go b/x/evm/evmtest/eth_test.go index 612beb05d..1d5feee47 100644 --- a/x/evm/evmtest/eth_test.go +++ b/x/evm/evmtest/eth_test.go @@ -36,11 +36,10 @@ func (s *Suite) TestSampleFns() { func (s *Suite) TestERC20Helpers() { deps := evmtest.NewTestDeps() funtoken := evmtest.CreateFunTokenForBankCoin(&deps, "token", &s.Suite) - - evmtest.AssertERC20BalanceEqual( - s.T(), deps, - funtoken.Erc20Addr.Address, - deps.Sender.EthAddr, - big.NewInt(0), - ) + evmtest.FunTokenBalanceAssert{ + FunToken: funtoken, + Account: deps.Sender.EthAddr, + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(0), + }.Assert(s.T(), deps) } diff --git a/x/evm/evmtest/smart_contract_test.go b/x/evm/evmtest/smart_contract_test.go index d5ca3434a..a78dbcf8e 100644 --- a/x/evm/evmtest/smart_contract_test.go +++ b/x/evm/evmtest/smart_contract_test.go @@ -17,7 +17,7 @@ func (s *Suite) TestCreateContractTxMsg() { EthAcc: ethAcc, EthChainIDInt: deps.EvmKeeper.EthChainID(deps.Ctx), GasPrice: big.NewInt(1), - Nonce: deps.StateDB().GetNonce(ethAcc.EthAddr), + Nonce: deps.NewStateDB().GetNonce(ethAcc.EthAddr), } ethTxMsg, err := evmtest.CreateContractMsgEthereumTx(args) @@ -33,7 +33,7 @@ func (s *Suite) TestExecuteContractTxMsg() { EthAcc: ethAcc, EthChainIDInt: deps.EvmKeeper.EthChainID(deps.Ctx), GasPrice: big.NewInt(1), - Nonce: deps.StateDB().GetNonce(ethAcc.EthAddr), + Nonce: deps.NewStateDB().GetNonce(ethAcc.EthAddr), ContractAddress: &contractAddress, Data: nil, } diff --git a/x/evm/evmtest/test_deps.go b/x/evm/evmtest/test_deps.go index 44fb34c31..6d2e830af 100644 --- a/x/evm/evmtest/test_deps.go +++ b/x/evm/evmtest/test_deps.go @@ -22,7 +22,7 @@ type TestDeps struct { App *app.NibiruApp Ctx sdk.Context EncCfg codec.EncodingConfig - EvmKeeper keeper.Keeper + EvmKeeper *keeper.Keeper GenState *evm.GenesisState Sender EthPrivKeyAcc } @@ -45,7 +45,7 @@ func NewTestDeps() TestDeps { } } -func (deps TestDeps) StateDB() *statedb.StateDB { +func (deps TestDeps) NewStateDB() *statedb.StateDB { return deps.EvmKeeper.NewStateDB( deps.Ctx, statedb.NewEmptyTxConfig( diff --git a/x/evm/evmtest/tx.go b/x/evm/evmtest/tx.go index 6b876f16d..25bba83f6 100644 --- a/x/evm/evmtest/tx.go +++ b/x/evm/evmtest/tx.go @@ -7,9 +7,8 @@ import ( "math/big" "testing" - sdkmath "cosmossdk.io/math" - "cosmossdk.io/errors" + sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -28,7 +27,7 @@ import ( // ExecuteNibiTransfer executes nibi transfer func ExecuteNibiTransfer(deps *TestDeps, t *testing.T) *evm.MsgEthereumTx { - nonce := deps.StateDB().GetNonce(deps.Sender.EthAddr) + nonce := deps.NewStateDB().GetNonce(deps.Sender.EthAddr) recipient := NewEthPrivAcc().EthAddr txArgs := evm.JsonTxArgs{ @@ -67,7 +66,7 @@ func DeployContract( } bytecodeForCall := append(contract.Bytecode, packedArgs...) - nonce := deps.StateDB().GetNonce(deps.Sender.EthAddr) + nonce := deps.NewStateDB().GetNonce(deps.Sender.EthAddr) ethTxMsg, gethSigner, krSigner, err := GenerateEthTxMsgAndSigner( evm.JsonTxArgs{ Nonce: (*hexutil.Uint64)(&nonce), @@ -124,7 +123,7 @@ func DeployAndExecuteERC20Transfer( "transfer", NewEthPrivAcc().EthAddr, new(big.Int).SetUint64(1000), ) require.NoError(t, err) - nonce = deps.StateDB().GetNonce(deps.Sender.EthAddr) + nonce = deps.NewStateDB().GetNonce(deps.Sender.EthAddr) txArgs := evm.JsonTxArgs{ From: &deps.Sender.EthAddr, To: &contractAddr, @@ -149,7 +148,7 @@ func CallContractTx( input []byte, sender EthPrivKeyAcc, ) (ethTxMsg *evm.MsgEthereumTx, resp *evm.MsgEthereumTxResponse, err error) { - nonce := deps.StateDB().GetNonce(sender.EthAddr) + nonce := deps.NewStateDB().GetNonce(sender.EthAddr) ethTxMsg, gethSigner, krSigner, err := GenerateEthTxMsgAndSigner(evm.JsonTxArgs{ From: &sender.EthAddr, To: &contractAddr, @@ -171,6 +170,8 @@ func CallContractTx( return ethTxMsg, resp, err } +var DefaultEthCallGasLimit = srvconfig.DefaultEthCallGasLimit + // GenerateEthTxMsgAndSigner estimates gas, sets gas limit and returns signer for // the tx. // @@ -220,7 +221,7 @@ func TransferWei( deps, gethcore.LegacyTxType, innerTxData, - deps.StateDB().GetNonce(ethAcc.EthAddr), + deps.NewStateDB().GetNonce(ethAcc.EthAddr), &to, amountWei, gethparams.TxGas, diff --git a/x/evm/keeper/bank_extension.go b/x/evm/keeper/bank_extension.go new file mode 100644 index 000000000..da5221cbb --- /dev/null +++ b/x/evm/keeper/bank_extension.go @@ -0,0 +1,155 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + auth "github.com/cosmos/cosmos-sdk/x/auth/types" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/statedb" +) + +var ( + _ bankkeeper.Keeper = &NibiruBankKeeper{} + _ bankkeeper.SendKeeper = &NibiruBankKeeper{} +) + +type NibiruBankKeeper struct { + bankkeeper.BaseKeeper + StateDB *statedb.StateDB +} + +func (evmKeeper *Keeper) NewStateDB( + ctx sdk.Context, txConfig statedb.TxConfig, +) *statedb.StateDB { + stateDB := statedb.New(ctx, evmKeeper, txConfig) + evmKeeper.Bank.StateDB = stateDB + return stateDB +} + +func (bk NibiruBankKeeper) MintCoins( + ctx sdk.Context, + moduleName string, + coins sdk.Coins, +) error { + // Use the embedded function from [bankkeeper.Keeper] + if err := bk.BaseKeeper.MintCoins(ctx, moduleName, coins); err != nil { + return err + } + if findEtherBalanceChangeFromCoins(coins) { + moduleBech32Addr := auth.NewModuleAddress(moduleName) + bk.SyncStateDBWithAccount(ctx, moduleBech32Addr) + } + return nil +} + +func (bk NibiruBankKeeper) BurnCoins( + ctx sdk.Context, + moduleName string, + coins sdk.Coins, +) error { + // Use the embedded function from [bankkeeper.Keeper] + if err := bk.BaseKeeper.BurnCoins(ctx, moduleName, coins); err != nil { + return err + } + if findEtherBalanceChangeFromCoins(coins) { + moduleBech32Addr := auth.NewModuleAddress(moduleName) + bk.SyncStateDBWithAccount(ctx, moduleBech32Addr) + } + return nil +} + +func (bk NibiruBankKeeper) SendCoins( + ctx sdk.Context, + fromAddr sdk.AccAddress, + toAddr sdk.AccAddress, + coins sdk.Coins, +) error { + // Use the embedded function from [bankkeeper.Keeper] + if err := bk.BaseKeeper.SendCoins(ctx, fromAddr, toAddr, coins); err != nil { + return err + } + if findEtherBalanceChangeFromCoins(coins) { + bk.SyncStateDBWithAccount(ctx, fromAddr) + bk.SyncStateDBWithAccount(ctx, toAddr) + } + return nil +} + +func (bk *NibiruBankKeeper) SyncStateDBWithAccount( + ctx sdk.Context, acc sdk.AccAddress, +) { + // If there's no StateDB set, it means we're not in an EthereumTx. + if bk.StateDB == nil { + return + } + balanceWei := evm.NativeToWei( + bk.GetBalance(ctx, acc, evm.EVMBankDenom).Amount.BigInt(), + ) + bk.StateDB.SetBalanceWei(eth.NibiruAddrToEthAddr(acc), balanceWei) +} + +func findEtherBalanceChangeFromCoins(coins sdk.Coins) (found bool) { + for _, c := range coins { + if c.Denom == evm.EVMBankDenom { + return true + } + } + return false +} + +func (bk NibiruBankKeeper) SendCoinsFromAccountToModule( + ctx sdk.Context, + senderAddr sdk.AccAddress, + recipientModule string, + coins sdk.Coins, +) error { + // Use the embedded function from [bankkeeper.Keeper] + if err := bk.BaseKeeper.SendCoinsFromAccountToModule(ctx, senderAddr, recipientModule, coins); err != nil { + return err + } + if findEtherBalanceChangeFromCoins(coins) { + bk.SyncStateDBWithAccount(ctx, senderAddr) + moduleBech32Addr := auth.NewModuleAddress(recipientModule) + bk.SyncStateDBWithAccount(ctx, moduleBech32Addr) + } + return nil +} + +func (bk NibiruBankKeeper) SendCoinsFromModuleToAccount( + ctx sdk.Context, + senderModule string, + recipientAddr sdk.AccAddress, + coins sdk.Coins, +) error { + // Use the embedded function from [bankkeeper.Keeper] + if err := bk.BaseKeeper.SendCoinsFromModuleToAccount(ctx, senderModule, recipientAddr, coins); err != nil { + return err + } + if findEtherBalanceChangeFromCoins(coins) { + moduleBech32Addr := auth.NewModuleAddress(senderModule) + bk.SyncStateDBWithAccount(ctx, moduleBech32Addr) + bk.SyncStateDBWithAccount(ctx, recipientAddr) + } + return nil +} + +func (bk NibiruBankKeeper) SendCoinsFromModuleToModule( + ctx sdk.Context, + senderModule string, + recipientModule string, + coins sdk.Coins, +) error { + // Use the embedded function from [bankkeeper.Keeper] + if err := bk.BaseKeeper.SendCoinsFromModuleToModule(ctx, senderModule, recipientModule, coins); err != nil { + return err + } + if findEtherBalanceChangeFromCoins(coins) { + senderBech32Addr := auth.NewModuleAddress(senderModule) + recipientBech32Addr := auth.NewModuleAddress(recipientModule) + bk.SyncStateDBWithAccount(ctx, senderBech32Addr) + bk.SyncStateDBWithAccount(ctx, recipientBech32Addr) + } + return nil +} diff --git a/x/evm/keeper/erc20.go b/x/evm/keeper/erc20.go index 06d2e5ab3..79c189f1e 100644 --- a/x/evm/keeper/erc20.go +++ b/x/evm/keeper/erc20.go @@ -180,7 +180,9 @@ func (k Keeper) CallContract( if err != nil { return nil, fmt.Errorf("failed to pack ABI args: %w", err) } - evmResp, _, err = k.CallContractWithInput(ctx, fromAcc, contract, commit, contractInput) + evmResp, _, err = k.CallContractWithInput( + ctx, fromAcc, contract, commit, contractInput, + ) return evmResp, err } diff --git a/x/evm/keeper/funtoken_from_coin.go b/x/evm/keeper/funtoken_from_coin.go index 6f0f2efd0..8075107c6 100644 --- a/x/evm/keeper/funtoken_from_coin.go +++ b/x/evm/keeper/funtoken_from_coin.go @@ -23,7 +23,7 @@ func (k *Keeper) createFunTokenFromCoin( } // 2 | Check for denom metadata in bank state - bankMetadata, isFound := k.bankKeeper.GetDenomMetaData(ctx, bankDenom) + bankMetadata, isFound := k.Bank.GetDenomMetaData(ctx, bankDenom) if !isFound { return nil, fmt.Errorf("bank coin denom should have bank metadata for denom \"%s\"", bankDenom) } diff --git a/x/evm/keeper/funtoken_from_coin_test.go b/x/evm/keeper/funtoken_from_coin_test.go index 3b4b17f7a..f69143772 100644 --- a/x/evm/keeper/funtoken_from_coin_test.go +++ b/x/evm/keeper/funtoken_from_coin_test.go @@ -25,7 +25,7 @@ func (s *FunTokenFromCoinSuite) TestCreateFunTokenFromCoin() { deps := evmtest.NewTestDeps() // Compute contract address. FindERC20 should fail - nonce := deps.StateDB().GetNonce(deps.Sender.EthAddr) + nonce := deps.NewStateDB().GetNonce(deps.Sender.EthAddr) contractAddress := crypto.CreateAddress(deps.Sender.EthAddr, nonce) metadata, err := deps.EvmKeeper.FindERC20Metadata(deps.Ctx, contractAddress) s.Require().Error(err) @@ -172,7 +172,7 @@ func (s *FunTokenFromCoinSuite) TestConvertCoinToEvmAndBack() { bankDenom := evm.EVMBankDenom // Initial setup - funTokenErc20Addr := s.fundAndCreateFunToken(deps, 100) + funToken := s.fundAndCreateFunToken(deps, 100) s.T().Log("Convert bank coin to erc-20") _, err := deps.EvmKeeper.ConvertCoinToEvm( @@ -193,7 +193,7 @@ func (s *FunTokenFromCoinSuite) TestConvertCoinToEvmAndBack() { deps.Ctx, &evm.EventConvertCoinToEvm{ Sender: deps.Sender.NibiruAddr.String(), - Erc20ContractAddress: funTokenErc20Addr.String(), + Erc20ContractAddress: funToken.Erc20Addr.String(), ToEthAddr: alice.EthAddr.String(), BankCoin: sdk.NewCoin(bankDenom, sdk.NewInt(10)), }, @@ -208,7 +208,7 @@ func (s *FunTokenFromCoinSuite) TestConvertCoinToEvmAndBack() { s.Require().Equal(sdk.NewInt(90), senderBalance.Amount) // Check 3: erc-20 balance - balance, err := deps.EvmKeeper.ERC20().BalanceOf(funTokenErc20Addr.Address, alice.EthAddr, deps.Ctx) + balance, err := deps.EvmKeeper.ERC20().BalanceOf(funToken.Erc20Addr.Address, alice.EthAddr, deps.Ctx) s.Require().NoError(err) s.Require().Zero(balance.Cmp(big.NewInt(10))) @@ -233,7 +233,7 @@ func (s *FunTokenFromCoinSuite) TestConvertCoinToEvmAndBack() { &precompile.PrecompileAddr_FunToken, true, "bankSend", - funTokenErc20Addr.Address, + funToken.Erc20Addr.Address, big.NewInt(10), deps.Sender.NibiruAddr.String(), ) @@ -248,7 +248,7 @@ func (s *FunTokenFromCoinSuite) TestConvertCoinToEvmAndBack() { s.Require().Equal(sdk.NewInt(100), senderBalance.Amount) // Check 3: erc-20 balance - balance, err = deps.EvmKeeper.ERC20().BalanceOf(funTokenErc20Addr.Address, alice.EthAddr, deps.Ctx) + balance, err = deps.EvmKeeper.ERC20().BalanceOf(funToken.Erc20Addr.Address, alice.EthAddr, deps.Ctx) s.Require().NoError(err) s.Require().Equal("0", balance.String()) @@ -260,7 +260,7 @@ func (s *FunTokenFromCoinSuite) TestConvertCoinToEvmAndBack() { &precompile.PrecompileAddr_FunToken, true, "bankSend", - funTokenErc20Addr.Address, + funToken.Erc20Addr.Address, big.NewInt(10), deps.Sender.NibiruAddr.String(), ) @@ -286,13 +286,14 @@ func (s *FunTokenFromCoinSuite) TestNativeSendThenPrecompileSend() { bankDenom := evm.EVMBankDenom // Initial setup - funTokenErc20Addr := s.fundAndCreateFunToken(deps, 10e6) + sendAmt := big.NewInt(10) + funtoken := s.fundAndCreateFunToken(deps, sendAmt.Int64()) s.T().Log("Deploy Test Contract") deployResp, err := evmtest.DeployContract( &deps, embeds.SmartContract_TestNativeSendThenPrecompileSendJson, - funTokenErc20Addr.Address, + funtoken.Erc20Addr.Address, ) s.Require().NoError(err) @@ -304,7 +305,13 @@ func (s *FunTokenFromCoinSuite) TestNativeSendThenPrecompileSend() { deps.App.BankKeeper, deps.Ctx, testContractNibiAddr, - sdk.NewCoins(sdk.NewCoin(bankDenom, sdk.NewInt(10e6)))), + sdk.NewCoins(sdk.NewCoin(bankDenom, sdk.NewIntFromBigInt(sendAmt)))), + ) + evmtest.AssertBankBalanceEqual( + s.T(), deps, bankDenom, testContractAddr, sendAmt, + ) + evmtest.AssertBankBalanceEqual( + s.T(), deps, bankDenom, evm.EVM_MODULE_ADDRESS, big.NewInt(0), ) s.T().Log("Convert bank coin to erc-20: give test contract 10 WNIBI (erc20)") @@ -312,52 +319,72 @@ func (s *FunTokenFromCoinSuite) TestNativeSendThenPrecompileSend() { sdk.WrapSDKContext(deps.Ctx), &evm.MsgConvertCoinToEvm{ Sender: deps.Sender.NibiruAddr.String(), - BankCoin: sdk.NewCoin(bankDenom, sdk.NewInt(10e6)), + BankCoin: sdk.NewCoin(bankDenom, sdk.NewIntFromBigInt(sendAmt)), ToEthAddr: eth.EIP55Addr{Address: testContractAddr}, }, ) s.Require().NoError(err) + evmtest.FunTokenBalanceAssert{ + FunToken: funtoken, + Account: testContractAddr, + BalanceBank: sendAmt, + BalanceERC20: sendAmt, + }.Assert(s.T(), deps) + evmtest.FunTokenBalanceAssert{ + FunToken: funtoken, + Account: evm.EVM_MODULE_ADDRESS, + BalanceBank: sendAmt, + BalanceERC20: big.NewInt(0), + }.Assert(s.T(), deps) // Alice hex and Alice bech32 is the same address in different representation, // so funds are expected to be available in Alice's bank wallet alice := evmtest.NewEthPrivAcc() + evmtest.FunTokenBalanceAssert{ + FunToken: funtoken, + Account: alice.EthAddr, + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(0), + }.Assert(s.T(), deps) s.T().Log("call test contract") - _, err = deps.EvmKeeper.CallContract( + evmResp, err := deps.EvmKeeper.CallContract( deps.Ctx, embeds.SmartContract_TestNativeSendThenPrecompileSendJson.ABI, deps.Sender.EthAddr, &testContractAddr, true, "nativeSendThenPrecompileSend", - alice.EthAddr, - evm.NativeToWei(big.NewInt(10e6)), // for native evm send: 18 decimals - alice.NibiruAddr.String(), - big.NewInt(10e6), // for precompile bankSend: 6 decimals + []any{ + alice.EthAddr, + evm.NativeToWei(sendAmt), // native send uses wei units + alice.NibiruAddr.String(), + sendAmt, // amount for precompile bankSend + }..., ) s.Require().NoError(err) - - // Check 1: Alice has 20 NIBI in bank - aliceBankBalance := deps.App.BankKeeper.GetBalance(deps.Ctx, alice.NibiruAddr, bankDenom) - s.Require().Equal(sdk.NewInt(20e6), aliceBankBalance.Amount) - - // Check 2: Alice has 0 WNIBI on ERC20 - aliceERC20Balance, err := deps.EvmKeeper.ERC20().BalanceOf(funTokenErc20Addr.Address, alice.EthAddr, deps.Ctx) - s.Require().NoError(err) - s.Require().Zero(big.NewInt(0).Cmp(aliceERC20Balance)) - - // Check 3: test contract has 0 NIBI in bank - testContractBankBalance := deps.App.BankKeeper.GetBalance(deps.Ctx, testContractNibiAddr, bankDenom) - s.Require().Equal(sdk.NewInt(0), testContractBankBalance.Amount) - - // Check 4: test contract has 0 WNIBI on ERC20 - testContractERC20Balance, err := deps.EvmKeeper.ERC20().BalanceOf(funTokenErc20Addr.Address, testContractAddr, deps.Ctx) - s.Require().NoError(err) - s.Require().Zero(big.NewInt(0).Cmp(testContractERC20Balance)) - - // Check 5: module balance has 0 NIBI escrowed - moduleBalance := deps.App.BankKeeper.GetBalance(deps.Ctx, authtypes.NewModuleAddress(evm.ModuleName), bankDenom) - s.Require().Equal(sdk.NewInt(0), moduleBalance.Amount) + s.Empty(evmResp.VmError) + + evmtest.FunTokenBalanceAssert{ + FunToken: funtoken, + Account: alice.EthAddr, + BalanceBank: new(big.Int).Mul(sendAmt, big.NewInt(2)), + BalanceERC20: big.NewInt(0), + }.Assert(s.T(), deps) + + evmtest.FunTokenBalanceAssert{ + FunToken: funtoken, + Account: testContractAddr, + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(0), + }.Assert(s.T(), deps) + + evmtest.FunTokenBalanceAssert{ + FunToken: funtoken, + Account: evm.EVM_MODULE_ADDRESS, + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(0), + }.Assert(s.T(), deps) } // TestERC20TransferThenPrecompileSend @@ -379,13 +406,13 @@ func (s *FunTokenFromCoinSuite) TestERC20TransferThenPrecompileSend() { bankDenom := evm.EVMBankDenom // Initial setup - funTokenErc20Addr := s.fundAndCreateFunToken(deps, 10e6) + funToken := s.fundAndCreateFunToken(deps, 10e6) s.T().Log("Deploy Test Contract") deployResp, err := evmtest.DeployContract( &deps, embeds.SmartContract_TestERC20TransferThenPrecompileSend, - funTokenErc20Addr.Address, + funToken.Erc20Addr.Address, ) s.Require().NoError(err) @@ -425,12 +452,12 @@ func (s *FunTokenFromCoinSuite) TestERC20TransferThenPrecompileSend() { s.Require().Equal(sdk.NewInt(9e6), aliceBankBalance.Amount) // Check 2: Alice has 1 WNIBI on ERC20 - aliceERC20Balance, err := deps.EvmKeeper.ERC20().BalanceOf(funTokenErc20Addr.Address, alice.EthAddr, deps.Ctx) + aliceERC20Balance, err := deps.EvmKeeper.ERC20().BalanceOf(funToken.Erc20Addr.Address, alice.EthAddr, deps.Ctx) s.Require().NoError(err) s.Require().Zero(big.NewInt(1e6).Cmp(aliceERC20Balance)) // Check 3: test contract has 0 WNIBI on ERC20 - testContractERC20Balance, err := deps.EvmKeeper.ERC20().BalanceOf(funTokenErc20Addr.Address, testContractAddr, deps.Ctx) + testContractERC20Balance, err := deps.EvmKeeper.ERC20().BalanceOf(funToken.Erc20Addr.Address, testContractAddr, deps.Ctx) s.Require().NoError(err) s.Require().Zero(big.NewInt(0).Cmp(testContractERC20Balance)) @@ -440,7 +467,7 @@ func (s *FunTokenFromCoinSuite) TestERC20TransferThenPrecompileSend() { } // fundAndCreateFunToken creates initial setup for tests -func (s *FunTokenFromCoinSuite) fundAndCreateFunToken(deps evmtest.TestDeps, unibiAmount int64) eth.EIP55Addr { +func (s *FunTokenFromCoinSuite) fundAndCreateFunToken(deps evmtest.TestDeps, unibiAmount int64) evm.FunToken { bankDenom := evm.EVMBankDenom s.T().Log("Setup: Create a coin in the bank state") @@ -449,12 +476,15 @@ func (s *FunTokenFromCoinSuite) fundAndCreateFunToken(deps evmtest.TestDeps, uni { Denom: bankDenom, Exponent: 0, - Aliases: nil, + }, + { + Denom: "NIBI", + Exponent: 6, }, }, Base: bankDenom, - Display: bankDenom, - Name: bankDenom, + Display: "NIBI", + Name: "NIBI", Symbol: "NIBI", }) @@ -475,7 +505,16 @@ func (s *FunTokenFromCoinSuite) fundAndCreateFunToken(deps evmtest.TestDeps, uni }, ) s.Require().NoError(err) - return createFunTokenResp.FuntokenMapping.Erc20Addr + + erc20Decimals, err := deps.EvmKeeper.LoadERC20Decimals( + deps.Ctx, + embeds.SmartContract_ERC20Minter.ABI, + createFunTokenResp.FuntokenMapping.Erc20Addr.Address, + ) + s.Require().NoError(err) + s.Require().Equal(erc20Decimals, uint8(6)) + + return createFunTokenResp.FuntokenMapping } type FunTokenFromCoinSuite struct { diff --git a/x/evm/keeper/funtoken_from_erc20.go b/x/evm/keeper/funtoken_from_erc20.go index e10f0cca8..f1fa3b0b2 100644 --- a/x/evm/keeper/funtoken_from_erc20.go +++ b/x/evm/keeper/funtoken_from_erc20.go @@ -122,7 +122,7 @@ func (k *Keeper) createFunTokenFromERC20( bankDenom := fmt.Sprintf("erc20/%s", erc20.String()) // 3 | Coin already registered with FunToken? - _, isFound := k.bankKeeper.GetDenomMetaData(ctx, bankDenom) + _, isFound := k.Bank.GetDenomMetaData(ctx, bankDenom) if isFound { return funtoken, fmt.Errorf("bank coin denom already registered with denom \"%s\"", bankDenom) } @@ -137,7 +137,7 @@ func (k *Keeper) createFunTokenFromERC20( if err != nil { return funtoken, fmt.Errorf("failed to validate bank metadata: %w", err) } - k.bankKeeper.SetDenomMetaData(ctx, bankMetadata) + k.Bank.SetDenomMetaData(ctx, bankMetadata) // 5 | Officially create the funtoken mapping funtoken = &evm.FunToken{ diff --git a/x/evm/keeper/funtoken_from_erc20_test.go b/x/evm/keeper/funtoken_from_erc20_test.go index eb209f7ca..53db98b31 100644 --- a/x/evm/keeper/funtoken_from_erc20_test.go +++ b/x/evm/keeper/funtoken_from_erc20_test.go @@ -25,7 +25,7 @@ func (s *FunTokenFromErc20Suite) TestCreateFunTokenFromERC20() { deps := evmtest.NewTestDeps() // assert that the ERC20 contract is not deployed - expectedERC20Addr := crypto.CreateAddress(deps.Sender.EthAddr, deps.StateDB().GetNonce(deps.Sender.EthAddr)) + expectedERC20Addr := crypto.CreateAddress(deps.Sender.EthAddr, deps.NewStateDB().GetNonce(deps.Sender.EthAddr)) _, err := deps.EvmKeeper.FindERC20Metadata(deps.Ctx, expectedERC20Addr) s.Error(err) diff --git a/x/evm/keeper/gas_fees.go b/x/evm/keeper/gas_fees.go index 01c72034c..c373b06ac 100644 --- a/x/evm/keeper/gas_fees.go +++ b/x/evm/keeper/gas_fees.go @@ -60,7 +60,7 @@ func (k *Keeper) RefundGas( // Refund to sender from the fee collector module account. This account // manages the collection of gas fees. - err := k.bankKeeper.SendCoinsFromModuleToAccount( + err := k.Bank.SendCoinsFromModuleToAccount( ctx, authtypes.FeeCollectorName, // sender msgFrom.Bytes(), // recipient @@ -136,7 +136,7 @@ func (k *Keeper) DeductTxCostsFromUserBalance( } // deduct the full gas cost from the user balance - if err := authante.DeductFees(k.bankKeeper, ctx, signerAcc, fees); err != nil { + if err := authante.DeductFees(k.Bank, ctx, signerAcc, fees); err != nil { return errors.Wrapf(err, "failed to deduct full gas cost %s from the user %s balance", fees, from) } diff --git a/x/evm/keeper/grpc_query_test.go b/x/evm/keeper/grpc_query_test.go index b16bea40c..b1d8e0515 100644 --- a/x/evm/keeper/grpc_query_test.go +++ b/x/evm/keeper/grpc_query_test.go @@ -330,7 +330,7 @@ func (s *Suite) TestQueryStorage() { Key: storageKey.String(), } - stateDB := deps.StateDB() + stateDB := deps.NewStateDB() storageValue := gethcommon.BytesToHash([]byte("value")) stateDB.SetState(addr, storageKey, storageValue) @@ -404,7 +404,7 @@ func (s *Suite) TestQueryCode() { Address: addr.Hex(), } - stateDB := deps.StateDB() + stateDB := deps.NewStateDB() contractBytecode := []byte("bytecode") stateDB.SetCode(addr, contractBytecode) s.Require().NoError(stateDB.Commit()) diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index dd31229fd..dd757ced9 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -15,7 +15,6 @@ import ( "github.com/cosmos/cosmos-sdk/codec" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/NibiruChain/nibiru/v2/app/appconst" @@ -41,7 +40,7 @@ type Keeper struct { // this should be the x/gov module account. authority sdk.AccAddress - bankKeeper bankkeeper.Keeper + Bank *NibiruBankKeeper accountKeeper evm.AccountKeeper stakingKeeper evm.StakingKeeper @@ -64,7 +63,7 @@ func NewKeeper( storeKey, transientKey storetypes.StoreKey, authority sdk.AccAddress, accKeeper evm.AccountKeeper, - bankKeeper bankkeeper.Keeper, + bankKeeper *NibiruBankKeeper, stakingKeeper evm.StakingKeeper, tracer string, ) Keeper { @@ -80,7 +79,7 @@ func NewKeeper( EvmState: NewEvmState(cdc, storeKey, transientKey), FunTokens: NewFunTokenState(cdc, storeKey), accountKeeper: accKeeper, - bankKeeper: bankKeeper, + Bank: bankKeeper, stakingKeeper: stakingKeeper, tracer: tracer, } @@ -91,7 +90,7 @@ func NewKeeper( // tokens for EVM execution in EVM denom units. func (k *Keeper) GetEvmGasBalance(ctx sdk.Context, addr gethcommon.Address) (balance *big.Int) { nibiruAddr := sdk.AccAddress(addr.Bytes()) - return k.bankKeeper.GetBalance(ctx, nibiruAddr, evm.EVMBankDenom).Amount.BigInt() + return k.Bank.GetBalance(ctx, nibiruAddr, evm.EVMBankDenom).Amount.BigInt() } func (k Keeper) EthChainID(ctx sdk.Context) *big.Int { diff --git a/x/evm/keeper/msg_ethereum_tx_test.go b/x/evm/keeper/msg_ethereum_tx_test.go index 0c2de7a39..4d9f16345 100644 --- a/x/evm/keeper/msg_ethereum_tx_test.go +++ b/x/evm/keeper/msg_ethereum_tx_test.go @@ -47,7 +47,7 @@ func (s *Suite) TestMsgEthereumTx_CreateContract() { EthAcc: ethAcc, EthChainIDInt: deps.EvmKeeper.EthChainID(deps.Ctx), GasPrice: big.NewInt(1), - Nonce: deps.StateDB().GetNonce(ethAcc.EthAddr), + Nonce: deps.NewStateDB().GetNonce(ethAcc.EthAddr), GasLimit: gasLimit, } ethTxMsg, err := evmtest.CreateContractMsgEthereumTx(args) @@ -87,7 +87,7 @@ func (s *Suite) TestMsgEthereumTx_CreateContract() { EthAcc: ethAcc, EthChainIDInt: deps.EvmKeeper.EthChainID(deps.Ctx), GasPrice: big.NewInt(1), - Nonce: deps.StateDB().GetNonce(ethAcc.EthAddr), + Nonce: deps.NewStateDB().GetNonce(ethAcc.EthAddr), } ethTxMsg, err := evmtest.CreateContractMsgEthereumTx(args) s.NoError(err) @@ -139,7 +139,7 @@ func (s *Suite) TestMsgEthereumTx_ExecuteContract() { EthAcc: ethAcc, EthChainIDInt: deps.EvmKeeper.EthChainID(deps.Ctx), GasPrice: big.NewInt(1), - Nonce: deps.StateDB().GetNonce(ethAcc.EthAddr), + Nonce: deps.NewStateDB().GetNonce(ethAcc.EthAddr), GasLimit: gasLimit, ContractAddress: &contractAddr, Data: input, @@ -205,7 +205,7 @@ func (s *Suite) TestMsgEthereumTx_SimpleTransfer() { &deps, tc.txType, innerTxData, - deps.StateDB().GetNonce(ethAcc.EthAddr), + deps.NewStateDB().GetNonce(ethAcc.EthAddr), &to, big.NewInt(fundedAmount), gethparams.TxGas, diff --git a/x/evm/keeper/msg_server.go b/x/evm/keeper/msg_server.go index ec13c0709..dbc1e8f74 100644 --- a/x/evm/keeper/msg_server.go +++ b/x/evm/keeper/msg_server.go @@ -202,9 +202,10 @@ func (k Keeper) GetHashFn(ctx sdk.Context) vm.GetHashFunc { } } -// ApplyEvmMsg computes the new state by applying the given message against the existing state. -// If the message fails, the VM execution error with the reason will be returned to the client -// and the transaction won't be committed to the store. +// ApplyEvmMsg computes the new state by applying the given message against the +// existing state. If the message fails, the VM execution error with the reason +// will be returned to the client and the transaction won't be committed to the +// store. // // # Reverted state // @@ -455,11 +456,11 @@ func (k Keeper) deductCreateFunTokenFee(ctx sdk.Context, msg *evm.MsgCreateFunTo fee := k.FeeForCreateFunToken(ctx) from := sdk.MustAccAddressFromBech32(msg.Sender) // validation in msg.ValidateBasic - if err := k.bankKeeper.SendCoinsFromAccountToModule( + if err := k.Bank.SendCoinsFromAccountToModule( ctx, from, evm.ModuleName, fee); err != nil { return fmt.Errorf("unable to pay the \"create_fun_token_fee\": %w", err) } - if err := k.bankKeeper.BurnCoins(ctx, evm.ModuleName, fee); err != nil { + if err := k.Bank.BurnCoins(ctx, evm.ModuleName, fee); err != nil { return fmt.Errorf("failed to burn the \"create_fun_token_fee\" after payment: %w", err) } return nil @@ -511,15 +512,14 @@ func (k Keeper) convertCoinToEvmBornCoin( coin sdk.Coin, funTokenMapping evm.FunToken, ) (*evm.MsgConvertCoinToEvmResponse, error) { - // Step 1: Send Bank Coins to the EVM module - err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, sender, evm.ModuleName, sdk.NewCoins(coin)) + // 1 | Send Bank Coins to the EVM module + err := k.Bank.SendCoinsFromAccountToModule(ctx, sender, evm.ModuleName, sdk.NewCoins(coin)) if err != nil { return nil, errors.Wrap(err, "failed to send coins to module account") } + // 2 | Mint ERC20 tokens to the recipient erc20Addr := funTokenMapping.Erc20Addr.Address - - // Step 2: Mint ERC20 tokens to the recipient evmResp, err := k.CallContract( ctx, embeds.SmartContract_ERC20Minter.ABI, @@ -560,7 +560,7 @@ func (k Keeper) convertCoinToEvmBornERC20( ) (*evm.MsgConvertCoinToEvmResponse, error) { erc20Addr := funTokenMapping.Erc20Addr.Address // 1 | Caller transfers Bank Coins to be converted to ERC20 tokens. - if err := k.bankKeeper.SendCoinsFromAccountToModule( + if err := k.Bank.SendCoinsFromAccountToModule( ctx, sender, evm.ModuleName, @@ -594,7 +594,7 @@ func (k Keeper) convertCoinToEvmBornERC20( // on the sum of the FunToken's bank and ERC20 supply, we burn the coins here // in the BC → ERC20 conversion. burnCoin := sdk.NewCoin(coin.Denom, sdk.NewIntFromBigInt(actualSentAmount)) - err = k.bankKeeper.BurnCoins(ctx, evm.ModuleName, sdk.NewCoins(burnCoin)) + err = k.Bank.BurnCoins(ctx, evm.ModuleName, sdk.NewCoins(burnCoin)) if err != nil { return nil, errors.Wrap(err, "failed to burn coins") } diff --git a/x/evm/keeper/statedb.go b/x/evm/keeper/statedb.go index 7aff8c02d..b85a595a5 100644 --- a/x/evm/keeper/statedb.go +++ b/x/evm/keeper/statedb.go @@ -17,13 +17,6 @@ import ( var _ statedb.Keeper = &Keeper{} -func (k *Keeper) NewStateDB( - ctx sdk.Context, - txConfig statedb.TxConfig, -) *statedb.StateDB { - return statedb.New(ctx, k, txConfig) -} - // ---------------------------------------------------------------------------- // StateDB Keeper implementation // ---------------------------------------------------------------------------- @@ -79,27 +72,28 @@ func (k *Keeper) ForEachStorage( func (k *Keeper) SetAccBalance( ctx sdk.Context, addr gethcommon.Address, amountEvmDenom *big.Int, ) error { - nativeAddr := sdk.AccAddress(addr.Bytes()) - balance := k.bankKeeper.GetBalance(ctx, nativeAddr, evm.EVMBankDenom).Amount.BigInt() + addrBech32 := eth.EthAddrToNibiruAddr(addr) + balance := k.Bank.GetBalance(ctx, addrBech32, evm.EVMBankDenom).Amount.BigInt() delta := new(big.Int).Sub(amountEvmDenom, balance) + bk := k.Bank.BaseKeeper switch delta.Sign() { case 1: // mint coins := sdk.NewCoins(sdk.NewCoin(evm.EVMBankDenom, sdkmath.NewIntFromBigInt(delta))) - if err := k.bankKeeper.MintCoins(ctx, evm.ModuleName, coins); err != nil { + if err := bk.MintCoins(ctx, evm.ModuleName, coins); err != nil { return err } - if err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, evm.ModuleName, nativeAddr, coins); err != nil { + if err := bk.SendCoinsFromModuleToAccount(ctx, evm.ModuleName, addrBech32, coins); err != nil { return err } case -1: // burn coins := sdk.NewCoins(sdk.NewCoin(evm.EVMBankDenom, sdkmath.NewIntFromBigInt(new(big.Int).Neg(delta)))) - if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, nativeAddr, evm.ModuleName, coins); err != nil { + if err := bk.SendCoinsFromAccountToModule(ctx, addrBech32, evm.ModuleName, coins); err != nil { return err } - if err := k.bankKeeper.BurnCoins(ctx, evm.ModuleName, coins); err != nil { + if err := bk.BurnCoins(ctx, evm.ModuleName, coins); err != nil { return err } default: diff --git a/x/evm/keeper/statedb_test.go b/x/evm/keeper/statedb_test.go index a918039a8..a9fd8cede 100644 --- a/x/evm/keeper/statedb_test.go +++ b/x/evm/keeper/statedb_test.go @@ -21,7 +21,7 @@ import ( func (s *Suite) TestStateDBBalance() { deps := evmtest.NewTestDeps() { - db := deps.StateDB() + db := deps.NewStateDB() s.Equal("0", db.GetBalance(deps.Sender.EthAddr).String()) s.T().Log("fund account in unibi. See expected wei amount.") @@ -47,7 +47,7 @@ func (s *Suite) TestStateDBBalance() { { err := evmtest.TransferWei(&deps, to, evm.NativeToWei(big.NewInt(12))) s.Require().NoError(err) - db := deps.StateDB() + db := deps.NewStateDB() s.Equal( "30"+strings.Repeat("0", 12), db.GetBalance(deps.Sender.EthAddr).String(), @@ -80,7 +80,7 @@ func (s *Suite) TestStateDBBalance() { ) s.NoError(err) - db := deps.StateDB() + db := deps.NewStateDB() s.Equal( "3"+strings.Repeat("0", 12), db.GetBalance(to).String(), diff --git a/x/evm/precompile/funtoken.go b/x/evm/precompile/funtoken.go index 1d50d99ab..b1fc0239d 100644 --- a/x/evm/precompile/funtoken.go +++ b/x/evm/precompile/funtoken.go @@ -6,18 +6,14 @@ import ( "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" - auth "github.com/cosmos/cosmos-sdk/x/auth/types" - bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" gethabi "github.com/ethereum/go-ethereum/accounts/abi" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" "github.com/NibiruChain/nibiru/v2/app/keepers" - "github.com/NibiruChain/nibiru/v2/eth" "github.com/NibiruChain/nibiru/v2/x/evm" "github.com/NibiruChain/nibiru/v2/x/evm/embeds" evmkeeper "github.com/NibiruChain/nibiru/v2/x/evm/keeper" - "github.com/NibiruChain/nibiru/v2/x/evm/statedb" ) var _ vm.PrecompiledContract = (*precompileFunToken)(nil) @@ -55,6 +51,7 @@ func (p precompileFunToken) Run( if err != nil { return nil, err } + p.evmKeeper.Bank.StateDB = start.StateDB method := start.Method switch PrecompileMethod(method.Name) { @@ -74,14 +71,12 @@ func (p precompileFunToken) Run( func PrecompileFunToken(keepers keepers.PublicKeepers) vm.PrecompiledContract { return precompileFunToken{ - bankKeeper: keepers.BankKeeper, - evmKeeper: keepers.EvmKeeper, + evmKeeper: keepers.EvmKeeper, } } type precompileFunToken struct { - bankKeeper bankkeeper.Keeper - evmKeeper evmkeeper.Keeper + evmKeeper *evmkeeper.Keeper } // bankSend: Implements "IFunToken.bankSend" @@ -151,7 +146,12 @@ func (p precompileFunToken) bankSend( return } } else { - err = SafeMintCoins(ctx, evm.ModuleName, coinToSend, p.bankKeeper, start.StateDB) + // NOTE: The NibiruBankKeeper needs to reference the current [vm.StateDB] before + // any operation that has the potential to use Bank send methods. This will + // guarantee that [evmkeeper.Keeper.SetAccBalance] journal changes are + // recorded if wei (NIBI) is transferred. + p.evmKeeper.Bank.StateDB = start.StateDB + err = p.evmKeeper.Bank.MintCoins(ctx, evm.ModuleName, sdk.NewCoins(coinToSend)) if err != nil { return nil, fmt.Errorf("mint failed for module \"%s\" (%s): contract caller %s: %w", evm.ModuleName, evm.EVM_MODULE_ADDRESS.Hex(), caller.Hex(), err, @@ -160,13 +160,17 @@ func (p precompileFunToken) bankSend( } // Transfer the bank coin - err = SafeSendCoinFromModuleToAccount( + // + // NOTE: The NibiruBankKeeper needs to reference the current [vm.StateDB] before + // any operation that has the potential to use Bank send methods. This will + // guarantee that [evmkeeper.Keeper.SetAccBalance] journal changes are + // recorded if wei (NIBI) is transferred. + p.evmKeeper.Bank.StateDB = start.StateDB + err = p.evmKeeper.Bank.SendCoinsFromModuleToAccount( ctx, evm.ModuleName, toAddr, - coinToSend, - p.bankKeeper, - start.StateDB, + sdk.NewCoins(coinToSend), ) if err != nil { return nil, fmt.Errorf("send failed for module \"%s\" (%s): contract caller %s: %w", @@ -179,58 +183,6 @@ func (p precompileFunToken) bankSend( return method.Outputs.Pack(gotAmount) } -func SafeMintCoins( - ctx sdk.Context, - moduleName string, - amt sdk.Coin, - bk bankkeeper.Keeper, - db *statedb.StateDB, -) error { - err := bk.MintCoins(ctx, evm.ModuleName, sdk.NewCoins(amt)) - if err != nil { - return err - } - if amt.Denom == evm.EVMBankDenom { - evmBech32Addr := auth.NewModuleAddress(evm.ModuleName) - balAfter := bk.GetBalance(ctx, evmBech32Addr, amt.Denom).Amount.BigInt() - db.SetBalanceWei( - evm.EVM_MODULE_ADDRESS, - evm.NativeToWei(balAfter), - ) - } - - return nil -} - -func SafeSendCoinFromModuleToAccount( - ctx sdk.Context, - senderModule string, - recipientAddr sdk.AccAddress, - amt sdk.Coin, - bk bankkeeper.Keeper, - db *statedb.StateDB, -) error { - err := bk.SendCoinsFromModuleToAccount(ctx, senderModule, recipientAddr, sdk.NewCoins(amt)) - if err != nil { - return err - } - if amt.Denom == evm.EVMBankDenom { - evmBech32Addr := auth.NewModuleAddress(evm.ModuleName) - balAfterFrom := bk.GetBalance(ctx, evmBech32Addr, amt.Denom).Amount.BigInt() - db.SetBalanceWei( - evm.EVM_MODULE_ADDRESS, - evm.NativeToWei(balAfterFrom), - ) - - balAfterTo := bk.GetBalance(ctx, recipientAddr, amt.Denom).Amount.BigInt() - db.SetBalanceWei( - eth.NibiruAddrToEthAddr(recipientAddr), - evm.NativeToWei(balAfterTo), - ) - } - return nil -} - func (p precompileFunToken) decomposeBankSendArgs(args []any) ( erc20 gethcommon.Address, amount *big.Int, diff --git a/x/evm/precompile/funtoken_test.go b/x/evm/precompile/funtoken_test.go index fc51a47f5..a17836582 100644 --- a/x/evm/precompile/funtoken_test.go +++ b/x/evm/precompile/funtoken_test.go @@ -120,7 +120,7 @@ func (s *FuntokenSuite) TestHappyPath() { randomAcc := testutil.AccAddress() - s.T().Log("Send using precompile") + s.T().Log("Send NIBI (FunToken) using precompile") amtToSend := int64(420) callArgs := []any{erc20, big.NewInt(amtToSend), randomAcc.String()} input, err := embeds.SmartContract_FunToken.ABI.Pack(string(precompile.FunTokenMethod_BankSend), callArgs...) @@ -134,6 +134,7 @@ func (s *FuntokenSuite) TestHappyPath() { ) s.Require().NoError(err) s.Require().Empty(ethTxResp.VmError) + s.True(deps.App.BankKeeper == deps.App.EvmKeeper.Bank) evmtest.AssertERC20BalanceEqual( s.T(), deps, erc20, deps.Sender.EthAddr, big.NewInt(69_000), @@ -144,6 +145,7 @@ func (s *FuntokenSuite) TestHappyPath() { s.Equal(sdk.NewInt(420).String(), deps.App.BankKeeper.GetBalance(deps.Ctx, randomAcc, funtoken.BankDenom).Amount.String(), ) + s.Require().NotNil(deps.EvmKeeper.Bank.StateDB) s.T().Log("Parse the response contract addr and response bytes") var sentAmt *big.Int diff --git a/x/evm/precompile/wasm.go b/x/evm/precompile/wasm.go index cddfc488e..0c7753564 100644 --- a/x/evm/precompile/wasm.go +++ b/x/evm/precompile/wasm.go @@ -8,6 +8,7 @@ import ( "github.com/NibiruChain/nibiru/v2/app/keepers" "github.com/NibiruChain/nibiru/v2/eth" "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + evmkeeper "github.com/NibiruChain/nibiru/v2/x/evm/keeper" wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" wasm "github.com/CosmWasm/wasmd/x/wasm/types" @@ -42,6 +43,11 @@ func (p precompileWasm) Run( return nil, err } + // NOTE: The NibiruBankKeeper needs to reference the current [vm.StateDB] before + // any operation that has the potential to use Bank send methods. This will + // guarantee that [evmkeeper.Keeper.SetAccBalance] journal changes are + // recorded if wei (NIBI) is transferred. + p.Bank.StateDB = startResult.StateDB switch PrecompileMethod(startResult.Method.Name) { case WasmMethod_execute: bz, err = p.execute(startResult, contract.CallerAddress, readonly) @@ -66,6 +72,7 @@ func (p precompileWasm) Run( } type precompileWasm struct { + *evmkeeper.Keeper Wasm Wasm } @@ -91,6 +98,7 @@ type Wasm struct { func PrecompileWasm(keepers keepers.PublicKeepers) vm.PrecompiledContract { return precompileWasm{ + Keeper: keepers.EvmKeeper, Wasm: Wasm{ wasmkeeper.NewDefaultPermissionKeeper(keepers.WasmKeeper), keepers.WasmKeeper, diff --git a/x/evm/statedb/statedb.go b/x/evm/statedb/statedb.go index 4e93b127a..4c729ef34 100644 --- a/x/evm/statedb/statedb.go +++ b/x/evm/statedb/statedb.go @@ -78,6 +78,10 @@ type StateDB struct { accessList *accessList } +func FromVM(evmObj *vm.EVM) *StateDB { + return evmObj.StateDB.(*StateDB) +} + // New creates a new state from a given trie. func New(ctx sdk.Context, keeper Keeper, txConfig TxConfig) *StateDB { return &StateDB{ diff --git a/x/evm/statedb/statedb_test.go b/x/evm/statedb/statedb_test.go index 7919d3da0..a89444042 100644 --- a/x/evm/statedb/statedb_test.go +++ b/x/evm/statedb/statedb_test.go @@ -82,7 +82,7 @@ func (s *Suite) TestAccount() { s.Require().EqualValues(statedb.NewEmptyAccount(), acct) s.Require().Empty(CollectContractStorage(db)) - db = deps.StateDB() + db = deps.NewStateDB() s.Require().Equal(true, db.Exist(address)) s.Require().Equal(true, db.Empty(address)) s.Require().Equal(big.NewInt(0), db.GetBalance(address)) @@ -104,7 +104,7 @@ func (s *Suite) TestAccount() { s.Require().NoError(db.Commit()) // suicide - db = deps.StateDB() + db = deps.NewStateDB() s.Require().False(db.HasSuicided(address)) s.Require().True(db.Suicide(address)) @@ -119,7 +119,7 @@ func (s *Suite) TestAccount() { s.Require().NoError(db.Commit()) // not accessible from StateDB anymore - db = deps.StateDB() + db = deps.NewStateDB() s.Require().False(db.Exist(address)) s.Require().Empty(CollectContractStorage(db)) }}, @@ -127,7 +127,7 @@ func (s *Suite) TestAccount() { for _, tc := range testCases { s.Run(tc.name, func() { deps := evmtest.NewTestDeps() - db := deps.StateDB() + db := deps.NewStateDB() tc.malleate(&deps, db) }) } @@ -135,7 +135,7 @@ func (s *Suite) TestAccount() { func (s *Suite) TestAccountOverride() { deps := evmtest.NewTestDeps() - db := deps.StateDB() + db := deps.NewStateDB() // test balance carry over when overwritten amount := big.NewInt(1) @@ -168,7 +168,7 @@ func (s *Suite) TestDBError() { } for _, tc := range testCases { deps := evmtest.NewTestDeps() - db := deps.StateDB() + db := deps.NewStateDB() tc.malleate(db) s.Require().NoError(db.Commit()) } @@ -201,7 +201,7 @@ func (s *Suite) TestBalance() { for _, tc := range testCases { s.Run(tc.name, func() { deps := evmtest.NewTestDeps() - db := deps.StateDB() + db := deps.NewStateDB() tc.malleate(db) // check dirty state @@ -254,7 +254,7 @@ func (s *Suite) TestState() { for _, tc := range testCases { s.Run(tc.name, func() { deps := evmtest.NewTestDeps() - db := deps.StateDB() + db := deps.NewStateDB() tc.malleate(db) s.Require().NoError(db.Commit()) @@ -264,7 +264,7 @@ func (s *Suite) TestState() { } // check ForEachStorage - db = deps.StateDB() + db = deps.NewStateDB() collected := CollectContractStorage(db) if len(tc.expStates) > 0 { s.Require().Equal(tc.expStates, collected) @@ -297,7 +297,7 @@ func (s *Suite) TestCode() { for _, tc := range testCases { s.Run(tc.name, func() { deps := evmtest.NewTestDeps() - db := deps.StateDB() + db := deps.NewStateDB() tc.malleate(db) // check dirty state @@ -308,7 +308,7 @@ func (s *Suite) TestCode() { s.Require().NoError(db.Commit()) // check again - db = deps.StateDB() + db = deps.NewStateDB() s.Require().Equal(tc.expCode, db.GetCode(address)) s.Require().Equal(len(tc.expCode), db.GetCodeSize(address)) s.Require().Equal(tc.expCodeHash, db.GetCodeHash(address)) @@ -364,7 +364,7 @@ func (s *Suite) TestRevertSnapshot() { deps := evmtest.NewTestDeps() // do some arbitrary changes to the storage - db := deps.StateDB() + db := deps.NewStateDB() db.SetNonce(address, 1) db.AddBalance(address, big.NewInt(100)) db.SetCode(address, []byte("hello world")) @@ -406,7 +406,7 @@ func (s *Suite) TestNestedSnapshot() { value2 := common.BigToHash(big.NewInt(2)) deps := evmtest.NewTestDeps() - db := deps.StateDB() + db := deps.NewStateDB() rev1 := db.Snapshot() db.SetState(address, key, value1) @@ -424,7 +424,7 @@ func (s *Suite) TestNestedSnapshot() { func (s *Suite) TestInvalidSnapshotId() { deps := evmtest.NewTestDeps() - db := deps.StateDB() + db := deps.NewStateDB() s.Require().Panics(func() { db.RevertToSnapshot(1) @@ -499,7 +499,7 @@ func (s *Suite) TestAccessList() { for _, tc := range testCases { deps := evmtest.NewTestDeps() - db := deps.StateDB() + db := deps.NewStateDB() tc.malleate(db) } } @@ -521,7 +521,7 @@ func (s *Suite) TestLog() { } deps := evmtest.NewTestDeps() - db := statedb.New(deps.Ctx, &deps.App.EvmKeeper, txConfig) + db := statedb.New(deps.Ctx, deps.App.EvmKeeper, txConfig) logData := []byte("hello world") log := &gethcore.Log{ @@ -576,7 +576,7 @@ func (s *Suite) TestRefund() { } for _, tc := range testCases { deps := evmtest.NewTestDeps() - db := deps.StateDB() + db := deps.NewStateDB() if !tc.expPanic { tc.malleate(db) s.Require().Equal(tc.expRefund, db.GetRefund()) @@ -595,7 +595,7 @@ func (s *Suite) TestIterateStorage() { value2 := common.BigToHash(big.NewInt(4)) deps := evmtest.NewTestDeps() - db := deps.StateDB() + db := deps.NewStateDB() db.SetState(address, key1, value1) db.SetState(address, key2, value2) From 7b26a2cf2a0c66ecd52b7445bbd3053c69918225 Mon Sep 17 00:00:00 2001 From: Matthias <97468149+matthiasmatt@users.noreply.github.com> Date: Thu, 31 Oct 2024 20:26:44 +0100 Subject: [PATCH 2/2] feat: add new query for dated prices (#2097) * feat: add new query for dated prices * feat: use ctx to get timestamp for query * chore: changelog * lint: remove dead code * field rename to include block --------- Co-authored-by: Unique-Divine --- CHANGELOG.md | 14 +- app/wasmext/stargate_query.go | 1 + proto/nibiru/oracle/v1/query.proto | 26 +- .../contracts/IOracle.sol/IOracle.json | 16 +- x/evm/embeds/contracts/IOracle.sol | 20 +- x/evm/precompile/oracle.go | 4 +- x/evm/precompile/oracle_test.go | 8 +- x/oracle/keeper/keeper.go | 10 + x/oracle/keeper/querier.go | 18 + x/oracle/keeper/querier_test.go | 105 ++++ x/oracle/types/query.pb.go | 467 ++++++++++++++---- x/oracle/types/query.pb.gw.go | 83 ++++ 12 files changed, 663 insertions(+), 109 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 884cc7e67..9467ff398 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,11 +50,12 @@ Zenith](https://code4rena.com/zenith) Audit, running from 2024-10-07 until period. This section describes code changes that occured after that audit in preparation for a second audit starting in November 2024. -- [#2074](https://github.com/NibiruChain/nibiru/pull/2074) - fix(evm-keeper): better utilize ERC20 metadata during FunToken creation. The bank metadata for a new FunToken mapping ties a connection between the Bank Coin's `DenomUnit` and the ERC20 contract metadata like the name, decimals, and symbol. This change brings parity between EVM wallets, such as MetaMask, and Interchain wallets like Keplr and Leap. +- [#2068](https://github.com/NibiruChain/nibiru/pull/2068) - feat: enable wasm light clients on IBC (08-wasm) +- [#2074](https://github.com/NibiruChain/nibiru/pull/2074) - fix(evm-keeper): better utilize ERC20 metadata during FunToken creation. The bank metadata for a new FunToken mapping ties a connection between the Bank Coin's `DenomUnit` and the ERC20 contract metadata like the name, decimals, and symbol. This change brings parity between EVM wallets, such as MetaMask, and Interchain wallets like Keplr and Leap. - [#2076](https://github.com/NibiruChain/nibiru/pull/2076) - fix(evm-gas-fees): -Use effective gas price in RefundGas and make sure that units are properly -reflected on all occurences of "base fee" in the codebase. This fixes [#2059](https://github.com/NibiruChain/nibiru/issues/2059) -and the [related comments from @Unique-Divine and @berndartmueller](https://github.com/NibiruChain/nibiru/issues/2059#issuecomment-2408625724). + Use effective gas price in RefundGas and make sure that units are properly + reflected on all occurences of "base fee" in the codebase. This fixes [#2059](https://github.com/NibiruChain/nibiru/issues/2059) + and the [related comments from @Unique-Divine and @berndartmueller](https://github.com/NibiruChain/nibiru/issues/2059#issuecomment-2408625724). - [#2084](https://github.com/NibiruChain/nibiru/pull/2084) - feat(evm-forge): foundry support and template for Nibiru EVM develoment - [#2086](https://github.com/NibiruChain/nibiru/pull/2086) - fix(evm-precomples): Fix state consistency in precompile execution by ensuring proper journaling of @@ -92,11 +93,13 @@ balance changes in wei as journal changes automatically. This guarantees that commits and reversions of the `StateDB` do not misalign with the state of the Bank module. This code change uses the `NibiruBankKeeper` on all modules that depend on x/bank, such as the EVM and Wasm modules. +- [#2097](https://github.com/NibiruChain/nibiru/pull/2097) - feat(evm): Add new query to get dated price from the oracle precompile - [#2098](https://github.com/NibiruChain/nibiru/pull/2098) - test(evm): statedb tests for race conditions within funtoken precompile -- [#2068](https://github.com/NibiruChain/nibiru/pull/2068) - feat: enable wasm light clients on IBC (08-wasm) +- [#2100](https://github.com/NibiruChain/nibiru/pull/2100) - refactor: cleanup statedb and precompile sections - [#2101](https://github.com/NibiruChain/nibiru/pull/2101) - fix(evm): tx receipt proper marshalling + #### Nibiru EVM | Before Audit 1 - 2024-10-18 - [#1837](https://github.com/NibiruChain/nibiru/pull/1837) - feat(eth): protos, eth types, and evm module types @@ -175,7 +178,6 @@ tests for race conditions within funtoken precompile - [#2060](https://github.com/NibiruChain/nibiru/pull/2060) - fix(evm-precompiles): add assertNumArgs validation - [#2056](https://github.com/NibiruChain/nibiru/pull/2056) - feat(evm): add oracle precompile - [#2065](https://github.com/NibiruChain/nibiru/pull/2065) - refactor(evm)!: Refactor out dead code from the evm.Params -- [#2100](https://github.com/NibiruChain/nibiru/pull/2100) - refactor: cleanup statedb and precompile sections ### State Machine Breaking (Other) diff --git a/app/wasmext/stargate_query.go b/app/wasmext/stargate_query.go index 2d90b6d58..bd1567175 100644 --- a/app/wasmext/stargate_query.go +++ b/app/wasmext/stargate_query.go @@ -120,6 +120,7 @@ func WasmAcceptedStargateQueries() wasmkeeper.AcceptedStargateQueries { // nibiru oracle "/nibiru.oracle.v1.Query/ExchangeRate": new(oracle.QueryExchangeRateResponse), + "/nibiru.oracle.v1.Query/DatedExchangeRate": new(oracle.QueryDatedExchangeRateResponse), "/nibiru.oracle.v1.Query/ExchangeRateTwap": new(oracle.QueryExchangeRateResponse), "/nibiru.oracle.v1.Query/ExchangeRates": new(oracle.QueryExchangeRatesResponse), "/nibiru.oracle.v1.Query/Actives": new(oracle.QueryActivesResponse), diff --git a/proto/nibiru/oracle/v1/query.proto b/proto/nibiru/oracle/v1/query.proto index 62e1f40be..3be464a1a 100644 --- a/proto/nibiru/oracle/v1/query.proto +++ b/proto/nibiru/oracle/v1/query.proto @@ -22,6 +22,12 @@ service Query { option (google.api.http).get = "/nibiru/oracle/v1beta1/exchange_rate_twap"; } + // DatedExchangeRate returns latest price of a pair + rpc DatedExchangeRate(QueryExchangeRateRequest) + returns (QueryDatedExchangeRateResponse) { + option (google.api.http).get = "/nibiru/oracle/v1beta1/dated_exchange_rate"; + } + // ExchangeRates returns exchange rates of all pairs rpc ExchangeRates(QueryExchangeRatesRequest) returns (QueryExchangeRatesResponse) { @@ -112,7 +118,25 @@ message QueryExchangeRateResponse { // QueryExchangeRatesRequest is the request type for the Query/ExchangeRates RPC // method. -message QueryExchangeRatesRequest {} +message QueryExchangeRatesRequest { +} + +// QueryDatedExchangeRateResponse is the request type for the +// Query/DatedExchangeRate RPC method. +message QueryDatedExchangeRateResponse { + string price = 1 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + + // Block timestamp for the block where the oracle came to consensus for this + // price. This timestamp is a conventional Unix millisecond time, i.e. the + // number of milliseconds elapsed since January 1, 1970 UTC. + int64 block_timestamp_ms = 2; + + // Block height when the oracle came to consensus for this price. + uint64 block_height = 3; +} // QueryExchangeRatesResponse is response type for the // Query/ExchangeRates RPC method. diff --git a/x/evm/embeds/artifacts/contracts/IOracle.sol/IOracle.json b/x/evm/embeds/artifacts/contracts/IOracle.sol/IOracle.json index 39638bdae..887d9433c 100644 --- a/x/evm/embeds/artifacts/contracts/IOracle.sol/IOracle.json +++ b/x/evm/embeds/artifacts/contracts/IOracle.sol/IOracle.json @@ -14,9 +14,19 @@ "name": "queryExchangeRate", "outputs": [ { - "internalType": "string", - "name": "", - "type": "string" + "internalType": "uint256", + "name": "price", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "blockTimeMs", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "blockHeight", + "type": "uint64" } ], "stateMutability": "view", diff --git a/x/evm/embeds/contracts/IOracle.sol b/x/evm/embeds/contracts/IOracle.sol index 7cb0a820a..393584f6f 100644 --- a/x/evm/embeds/contracts/IOracle.sol +++ b/x/evm/embeds/contracts/IOracle.sol @@ -3,12 +3,20 @@ pragma solidity >=0.8.19; /// @notice Oracle interface for querying exchange rates interface IOracle { - /// @notice Queries the exchange rate for a given pair - /// @param pair The asset pair to query. For example, "ubtc:uusd" is the - /// USD price of BTC and "unibi:uusd" is the USD price of NIBI. - /// @return The exchange rate (a decimal value) as a string. - /// @dev This function is view-only and does not modify state. - function queryExchangeRate(string memory pair) external view returns (string memory); + /// @notice Queries the dated exchange rate for a given pair + /// @param pair The asset pair to query. For example, "ubtc:uusd" is the + /// USD price of BTC and "unibi:uusd" is the USD price of NIBI. + /// @return price The exchange rate for the given pair + /// @return blockTimeMs The block time in milliseconds when the price was + /// last updated + /// @return blockHeight The block height when the price was last updated + /// @dev This function is view-only and does not modify state. + function queryExchangeRate( + string memory pair + ) + external + view + returns (uint256 price, uint64 blockTimeMs, uint64 blockHeight); } address constant ORACLE_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000801; diff --git a/x/evm/precompile/oracle.go b/x/evm/precompile/oracle.go index f59b93822..cf461a3c4 100644 --- a/x/evm/precompile/oracle.go +++ b/x/evm/precompile/oracle.go @@ -81,12 +81,12 @@ func (p precompileOracle) queryExchangeRate( return nil, err } - price, err := p.oracleKeeper.GetExchangeRate(ctx, assetPair) + price, blockTime, blockHeight, err := p.oracleKeeper.GetDatedExchangeRate(ctx, assetPair) if err != nil { return nil, err } - return method.Outputs.Pack(price.String()) + return method.Outputs.Pack(price.BigInt(), uint64(blockTime), blockHeight) } func (p precompileOracle) parseQueryExchangeRateArgs(args []any) ( diff --git a/x/evm/precompile/oracle_test.go b/x/evm/precompile/oracle_test.go index efab80118..fa07d72d6 100644 --- a/x/evm/precompile/oracle_test.go +++ b/x/evm/precompile/oracle_test.go @@ -1,7 +1,9 @@ package precompile_test import ( + "math/big" "testing" + "time" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/ethereum/go-ethereum/common" @@ -55,7 +57,9 @@ func (s *OracleSuite) TestOracle_HappyPath() { s.T().Log("Query exchange rate") { + deps.Ctx = deps.Ctx.WithBlockTime(time.Unix(69, 420)).WithBlockHeight(69) deps.App.OracleKeeper.SetPrice(deps.Ctx, "unibi:uusd", sdk.MustNewDecFromStr("0.067")) + input, err := embeds.SmartContract_Oracle.ABI.Pack("queryExchangeRate", "unibi:uusd") s.NoError(err) resp, _, err := deps.EvmKeeper.CallContractWithInput( @@ -68,7 +72,9 @@ func (s *OracleSuite) TestOracle_HappyPath() { s.NoError(err) // Check the response - s.Equal("0.067000000000000000", out[0].(string)) + s.Equal(out[0].(*big.Int), big.NewInt(67000000000000000)) + s.Equal(out[1].(uint64), uint64(69000)) + s.Equal(out[2].(uint64), uint64(69)) } } diff --git a/x/oracle/keeper/keeper.go b/x/oracle/keeper/keeper.go index 32d2b8b63..bbbf31ce9 100644 --- a/x/oracle/keeper/keeper.go +++ b/x/oracle/keeper/keeper.go @@ -187,6 +187,16 @@ func (k Keeper) GetExchangeRate(ctx sdk.Context, pair asset.Pair) (price sdk.Dec return } +func (k Keeper) GetDatedExchangeRate(ctx sdk.Context, pair asset.Pair) (price sdk.Dec, blockTimeMs int64, BlockHeight uint64, err error) { + exchangeRate, err := k.ExchangeRates.Get(ctx, pair) + if err != nil { + return + } + time := ctx.WithBlockHeight(int64(exchangeRate.CreatedBlock)).BlockTime() + + return exchangeRate.ExchangeRate, time.UnixMilli(), exchangeRate.CreatedBlock, nil +} + // SetPrice sets the price for a pair as well as the price snapshot. func (k Keeper) SetPrice(ctx sdk.Context, pair asset.Pair, price sdk.Dec) { k.ExchangeRates.Insert(ctx, pair, types.DatedPrice{ExchangeRate: price, CreatedBlock: uint64(ctx.BlockHeight())}) diff --git a/x/oracle/keeper/querier.go b/x/oracle/keeper/querier.go index 576c5c72b..6cc7fbd2b 100644 --- a/x/oracle/keeper/querier.go +++ b/x/oracle/keeper/querier.go @@ -79,6 +79,24 @@ func (q querier) ExchangeRateTwap(c context.Context, req *types.QueryExchangeRat return &types.QueryExchangeRateResponse{ExchangeRate: twap}, nil } +// get the latest price snapshot from the oracle for a pair +func (q querier) DatedExchangeRate(c context.Context, req *types.QueryExchangeRateRequest) (response *types.QueryDatedExchangeRateResponse, err error) { + if _, err = q.ExchangeRate(c, req); err != nil { + return + } + + ctx := sdk.UnwrapSDKContext(c) + price, blockTime, blockHeight, err := q.Keeper.GetDatedExchangeRate(ctx, req.Pair) + if err != nil { + return &types.QueryDatedExchangeRateResponse{}, err + } + return &types.QueryDatedExchangeRateResponse{ + Price: price, + BlockTimestampMs: blockTime, + BlockHeight: blockHeight, + }, nil +} + // ExchangeRates queries exchange rates of all pairs func (q querier) ExchangeRates(c context.Context, _ *types.QueryExchangeRatesRequest) (*types.QueryExchangeRatesResponse, error) { ctx := sdk.UnwrapSDKContext(c) diff --git a/x/oracle/keeper/querier_test.go b/x/oracle/keeper/querier_test.go index 8db8f2749..45e39ccdc 100644 --- a/x/oracle/keeper/querier_test.go +++ b/x/oracle/keeper/querier_test.go @@ -118,6 +118,111 @@ func TestQueryExchangeRateTwap(t *testing.T) { require.Equal(t, math.LegacyMustNewDecFromStr("1700"), res.ExchangeRate) } +func TestQueryDatedExchangeRate(t *testing.T) { + input := CreateTestFixture(t) + querier := NewQuerier(input.OracleKeeper) + + // Set initial block time and height + initialTime := input.Ctx.BlockTime() + initialHeight := input.Ctx.BlockHeight() + + // Pair 1: BTC/NUSD + pairBTC := asset.Registry.Pair(denoms.BTC, denoms.NUSD) + rateBTC1 := math.LegacyNewDec(1700) + rateBTC2 := math.LegacyNewDec(1800) + + // Pair 2: ETH/NUSD + pairETH := asset.Registry.Pair(denoms.ETH, denoms.NUSD) + rateETH1 := math.LegacyNewDec(100) + rateETH2 := math.LegacyNewDec(110) + + // --- Set first price for BTC/NUSD --- + input.OracleKeeper.SetPrice(input.Ctx, pairBTC, rateBTC1) + testutilevents.RequireContainsTypedEvent( + t, + input.Ctx, + &types.EventPriceUpdate{ + Pair: pairBTC.String(), + Price: rateBTC1, + TimestampMs: input.Ctx.BlockTime().UnixMilli(), + }, + ) + + // Advance time and block height + input.Ctx = input.Ctx.WithBlockTime(initialTime.Add(1 * time.Second)). + WithBlockHeight(initialHeight + 1) + + // --- Set first price for ETH/NUSD --- + input.OracleKeeper.SetPrice(input.Ctx, pairETH, rateETH1) + testutilevents.RequireContainsTypedEvent( + t, + input.Ctx, + &types.EventPriceUpdate{ + Pair: pairETH.String(), + Price: rateETH1, + TimestampMs: input.Ctx.BlockTime().UnixMilli(), + }, + ) + + // Advance time and block height + input.Ctx = input.Ctx.WithBlockTime(initialTime.Add(2 * time.Second)). + WithBlockHeight(initialHeight + 2) + + // --- Set second price for BTC/NUSD --- + input.OracleKeeper.SetPrice(input.Ctx, pairBTC, rateBTC2) + testutilevents.RequireContainsTypedEvent( + t, + input.Ctx, + &types.EventPriceUpdate{ + Pair: pairBTC.String(), + Price: rateBTC2, + TimestampMs: input.Ctx.BlockTime().UnixMilli(), + }, + ) + + // Advance time and block height + input.Ctx = input.Ctx.WithBlockTime(initialTime.Add(3 * time.Second)). + WithBlockHeight(initialHeight + 3) + + // --- Set second price for ETH/NUSD --- + input.OracleKeeper.SetPrice(input.Ctx, pairETH, rateETH2) + testutilevents.RequireContainsTypedEvent( + t, + input.Ctx, + &types.EventPriceUpdate{ + Pair: pairETH.String(), + Price: rateETH2, + TimestampMs: input.Ctx.BlockTime().UnixMilli(), + }, + ) + + // Wrap context for querying + ctx := sdk.WrapSDKContext(input.Ctx) + + // --- Query latest snapshot for BTC/NUSD --- + resBTC, err := querier.DatedExchangeRate( + ctx, + &types.QueryExchangeRateRequest{Pair: pairBTC}, + ) + require.NoError(t, err) + require.Equal(t, rateBTC2, resBTC.Price) + require.Equal(t, input.Ctx.BlockTime().UnixMilli(), resBTC.BlockTimestampMs) + + // --- Query latest snapshot for ETH/NUSD --- + resETH, err := querier.DatedExchangeRate( + ctx, + &types.QueryExchangeRateRequest{Pair: pairETH}, + ) + require.NoError(t, err) + require.Equal(t, rateETH2, resETH.Price) + require.Equal(t, input.Ctx.BlockTime().UnixMilli(), resETH.BlockTimestampMs) + + // --- Query a pair with no snapshots (should return an error) --- + pairATOM := asset.Registry.Pair(denoms.ATOM, denoms.NUSD) + _, err = querier.DatedExchangeRate(ctx, &types.QueryExchangeRateRequest{Pair: pairATOM}) + require.Error(t, err) +} + func TestCalcTwap(t *testing.T) { tests := []struct { name string diff --git a/x/oracle/types/query.pb.go b/x/oracle/types/query.pb.go index d1462c82e..4149e28c4 100644 --- a/x/oracle/types/query.pb.go +++ b/x/oracle/types/query.pb.go @@ -150,6 +150,65 @@ func (m *QueryExchangeRatesRequest) XXX_DiscardUnknown() { var xxx_messageInfo_QueryExchangeRatesRequest proto.InternalMessageInfo +// QueryDatedExchangeRateResponse is the request type for the +// Query/DatedExchangeRate RPC method. +type QueryDatedExchangeRateResponse struct { + Price github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,1,opt,name=price,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"price"` + // Block timestamp for the block where the oracle came to consensus for this + // price. This timestamp is a conventional Unix millisecond time, i.e. the + // number of milliseconds elapsed since January 1, 1970 UTC. + BlockTimestampMs int64 `protobuf:"varint,2,opt,name=block_timestamp_ms,json=blockTimestampMs,proto3" json:"block_timestamp_ms,omitempty"` + // Block height when the oracle came to consensus for this price. + BlockHeight uint64 `protobuf:"varint,3,opt,name=block_height,json=blockHeight,proto3" json:"block_height,omitempty"` +} + +func (m *QueryDatedExchangeRateResponse) Reset() { *m = QueryDatedExchangeRateResponse{} } +func (m *QueryDatedExchangeRateResponse) String() string { return proto.CompactTextString(m) } +func (*QueryDatedExchangeRateResponse) ProtoMessage() {} +func (*QueryDatedExchangeRateResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_16aef2382d1249a8, []int{3} +} +func (m *QueryDatedExchangeRateResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDatedExchangeRateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDatedExchangeRateResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryDatedExchangeRateResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDatedExchangeRateResponse.Merge(m, src) +} +func (m *QueryDatedExchangeRateResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryDatedExchangeRateResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDatedExchangeRateResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDatedExchangeRateResponse proto.InternalMessageInfo + +func (m *QueryDatedExchangeRateResponse) GetBlockTimestampMs() int64 { + if m != nil { + return m.BlockTimestampMs + } + return 0 +} + +func (m *QueryDatedExchangeRateResponse) GetBlockHeight() uint64 { + if m != nil { + return m.BlockHeight + } + return 0 +} + // QueryExchangeRatesResponse is response type for the // Query/ExchangeRates RPC method. type QueryExchangeRatesResponse struct { @@ -162,7 +221,7 @@ func (m *QueryExchangeRatesResponse) Reset() { *m = QueryExchangeRatesRe func (m *QueryExchangeRatesResponse) String() string { return proto.CompactTextString(m) } func (*QueryExchangeRatesResponse) ProtoMessage() {} func (*QueryExchangeRatesResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_16aef2382d1249a8, []int{3} + return fileDescriptor_16aef2382d1249a8, []int{4} } func (m *QueryExchangeRatesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -206,7 +265,7 @@ func (m *QueryActivesRequest) Reset() { *m = QueryActivesRequest{} } func (m *QueryActivesRequest) String() string { return proto.CompactTextString(m) } func (*QueryActivesRequest) ProtoMessage() {} func (*QueryActivesRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_16aef2382d1249a8, []int{4} + return fileDescriptor_16aef2382d1249a8, []int{5} } func (m *QueryActivesRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -246,7 +305,7 @@ func (m *QueryActivesResponse) Reset() { *m = QueryActivesResponse{} } func (m *QueryActivesResponse) String() string { return proto.CompactTextString(m) } func (*QueryActivesResponse) ProtoMessage() {} func (*QueryActivesResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_16aef2382d1249a8, []int{5} + return fileDescriptor_16aef2382d1249a8, []int{6} } func (m *QueryActivesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -284,7 +343,7 @@ func (m *QueryVoteTargetsRequest) Reset() { *m = QueryVoteTargetsRequest func (m *QueryVoteTargetsRequest) String() string { return proto.CompactTextString(m) } func (*QueryVoteTargetsRequest) ProtoMessage() {} func (*QueryVoteTargetsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_16aef2382d1249a8, []int{6} + return fileDescriptor_16aef2382d1249a8, []int{7} } func (m *QueryVoteTargetsRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -325,7 +384,7 @@ func (m *QueryVoteTargetsResponse) Reset() { *m = QueryVoteTargetsRespon func (m *QueryVoteTargetsResponse) String() string { return proto.CompactTextString(m) } func (*QueryVoteTargetsResponse) ProtoMessage() {} func (*QueryVoteTargetsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_16aef2382d1249a8, []int{7} + return fileDescriptor_16aef2382d1249a8, []int{8} } func (m *QueryVoteTargetsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -365,7 +424,7 @@ func (m *QueryFeederDelegationRequest) Reset() { *m = QueryFeederDelegat func (m *QueryFeederDelegationRequest) String() string { return proto.CompactTextString(m) } func (*QueryFeederDelegationRequest) ProtoMessage() {} func (*QueryFeederDelegationRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_16aef2382d1249a8, []int{8} + return fileDescriptor_16aef2382d1249a8, []int{9} } func (m *QueryFeederDelegationRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -405,7 +464,7 @@ func (m *QueryFeederDelegationResponse) Reset() { *m = QueryFeederDelega func (m *QueryFeederDelegationResponse) String() string { return proto.CompactTextString(m) } func (*QueryFeederDelegationResponse) ProtoMessage() {} func (*QueryFeederDelegationResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_16aef2382d1249a8, []int{9} + return fileDescriptor_16aef2382d1249a8, []int{10} } func (m *QueryFeederDelegationResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -452,7 +511,7 @@ func (m *QueryMissCounterRequest) Reset() { *m = QueryMissCounterRequest func (m *QueryMissCounterRequest) String() string { return proto.CompactTextString(m) } func (*QueryMissCounterRequest) ProtoMessage() {} func (*QueryMissCounterRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_16aef2382d1249a8, []int{10} + return fileDescriptor_16aef2382d1249a8, []int{11} } func (m *QueryMissCounterRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -492,7 +551,7 @@ func (m *QueryMissCounterResponse) Reset() { *m = QueryMissCounterRespon func (m *QueryMissCounterResponse) String() string { return proto.CompactTextString(m) } func (*QueryMissCounterResponse) ProtoMessage() {} func (*QueryMissCounterResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_16aef2382d1249a8, []int{11} + return fileDescriptor_16aef2382d1249a8, []int{12} } func (m *QueryMissCounterResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -539,7 +598,7 @@ func (m *QueryAggregatePrevoteRequest) Reset() { *m = QueryAggregatePrev func (m *QueryAggregatePrevoteRequest) String() string { return proto.CompactTextString(m) } func (*QueryAggregatePrevoteRequest) ProtoMessage() {} func (*QueryAggregatePrevoteRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_16aef2382d1249a8, []int{12} + return fileDescriptor_16aef2382d1249a8, []int{13} } func (m *QueryAggregatePrevoteRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -580,7 +639,7 @@ func (m *QueryAggregatePrevoteResponse) Reset() { *m = QueryAggregatePre func (m *QueryAggregatePrevoteResponse) String() string { return proto.CompactTextString(m) } func (*QueryAggregatePrevoteResponse) ProtoMessage() {} func (*QueryAggregatePrevoteResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_16aef2382d1249a8, []int{13} + return fileDescriptor_16aef2382d1249a8, []int{14} } func (m *QueryAggregatePrevoteResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -625,7 +684,7 @@ func (m *QueryAggregatePrevotesRequest) Reset() { *m = QueryAggregatePre func (m *QueryAggregatePrevotesRequest) String() string { return proto.CompactTextString(m) } func (*QueryAggregatePrevotesRequest) ProtoMessage() {} func (*QueryAggregatePrevotesRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_16aef2382d1249a8, []int{14} + return fileDescriptor_16aef2382d1249a8, []int{15} } func (m *QueryAggregatePrevotesRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -666,7 +725,7 @@ func (m *QueryAggregatePrevotesResponse) Reset() { *m = QueryAggregatePr func (m *QueryAggregatePrevotesResponse) String() string { return proto.CompactTextString(m) } func (*QueryAggregatePrevotesResponse) ProtoMessage() {} func (*QueryAggregatePrevotesResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_16aef2382d1249a8, []int{15} + return fileDescriptor_16aef2382d1249a8, []int{16} } func (m *QueryAggregatePrevotesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -713,7 +772,7 @@ func (m *QueryAggregateVoteRequest) Reset() { *m = QueryAggregateVoteReq func (m *QueryAggregateVoteRequest) String() string { return proto.CompactTextString(m) } func (*QueryAggregateVoteRequest) ProtoMessage() {} func (*QueryAggregateVoteRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_16aef2382d1249a8, []int{16} + return fileDescriptor_16aef2382d1249a8, []int{17} } func (m *QueryAggregateVoteRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -754,7 +813,7 @@ func (m *QueryAggregateVoteResponse) Reset() { *m = QueryAggregateVoteRe func (m *QueryAggregateVoteResponse) String() string { return proto.CompactTextString(m) } func (*QueryAggregateVoteResponse) ProtoMessage() {} func (*QueryAggregateVoteResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_16aef2382d1249a8, []int{17} + return fileDescriptor_16aef2382d1249a8, []int{18} } func (m *QueryAggregateVoteResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -799,7 +858,7 @@ func (m *QueryAggregateVotesRequest) Reset() { *m = QueryAggregateVotesR func (m *QueryAggregateVotesRequest) String() string { return proto.CompactTextString(m) } func (*QueryAggregateVotesRequest) ProtoMessage() {} func (*QueryAggregateVotesRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_16aef2382d1249a8, []int{18} + return fileDescriptor_16aef2382d1249a8, []int{19} } func (m *QueryAggregateVotesRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -840,7 +899,7 @@ func (m *QueryAggregateVotesResponse) Reset() { *m = QueryAggregateVotes func (m *QueryAggregateVotesResponse) String() string { return proto.CompactTextString(m) } func (*QueryAggregateVotesResponse) ProtoMessage() {} func (*QueryAggregateVotesResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_16aef2382d1249a8, []int{19} + return fileDescriptor_16aef2382d1249a8, []int{20} } func (m *QueryAggregateVotesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -884,7 +943,7 @@ func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } func (*QueryParamsRequest) ProtoMessage() {} func (*QueryParamsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_16aef2382d1249a8, []int{20} + return fileDescriptor_16aef2382d1249a8, []int{21} } func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -923,7 +982,7 @@ func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } func (*QueryParamsResponse) ProtoMessage() {} func (*QueryParamsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_16aef2382d1249a8, []int{21} + return fileDescriptor_16aef2382d1249a8, []int{22} } func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -963,6 +1022,7 @@ func init() { proto.RegisterType((*QueryExchangeRateRequest)(nil), "nibiru.oracle.v1.QueryExchangeRateRequest") proto.RegisterType((*QueryExchangeRateResponse)(nil), "nibiru.oracle.v1.QueryExchangeRateResponse") proto.RegisterType((*QueryExchangeRatesRequest)(nil), "nibiru.oracle.v1.QueryExchangeRatesRequest") + proto.RegisterType((*QueryDatedExchangeRateResponse)(nil), "nibiru.oracle.v1.QueryDatedExchangeRateResponse") proto.RegisterType((*QueryExchangeRatesResponse)(nil), "nibiru.oracle.v1.QueryExchangeRatesResponse") proto.RegisterType((*QueryActivesRequest)(nil), "nibiru.oracle.v1.QueryActivesRequest") proto.RegisterType((*QueryActivesResponse)(nil), "nibiru.oracle.v1.QueryActivesResponse") @@ -987,77 +1047,84 @@ func init() { func init() { proto.RegisterFile("nibiru/oracle/v1/query.proto", fileDescriptor_16aef2382d1249a8) } var fileDescriptor_16aef2382d1249a8 = []byte{ - // 1108 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x97, 0xc1, 0x6f, 0x1b, 0x45, - 0x14, 0xc6, 0x3d, 0x10, 0x52, 0x78, 0x8e, 0xd3, 0x64, 0x08, 0x22, 0xdd, 0x26, 0x76, 0x59, 0x9a, - 0xa8, 0x6d, 0x9a, 0x5d, 0x9c, 0x54, 0x45, 0x81, 0x22, 0x70, 0x12, 0x2a, 0x15, 0xb5, 0x10, 0x4c, - 0x15, 0xa1, 0x5e, 0xac, 0xf1, 0x7a, 0xba, 0x59, 0x61, 0xef, 0x6c, 0x77, 0xd6, 0xa6, 0x51, 0xe1, - 0x52, 0x01, 0xe2, 0x88, 0x84, 0x10, 0x37, 0xe8, 0x05, 0x09, 0x71, 0x06, 0xee, 0x70, 0xea, 0xb1, - 0x12, 0x17, 0xc4, 0xa1, 0xa0, 0x84, 0x03, 0x7f, 0x06, 0xda, 0xd9, 0xf1, 0x7a, 0xd7, 0xeb, 0x51, - 0x16, 0x97, 0x9e, 0x12, 0xcd, 0x7b, 0xfb, 0xde, 0xef, 0x7d, 0x33, 0x3b, 0x9f, 0x17, 0x16, 0x5c, - 0xa7, 0xe9, 0xf8, 0x5d, 0x93, 0xf9, 0xc4, 0x6a, 0x53, 0xb3, 0x57, 0x35, 0x6f, 0x75, 0xa9, 0xbf, - 0x6f, 0x78, 0x3e, 0x0b, 0x18, 0x9e, 0x89, 0xa2, 0x46, 0x14, 0x35, 0x7a, 0x55, 0x6d, 0xce, 0x66, - 0x36, 0x13, 0x41, 0x33, 0xfc, 0x2f, 0xca, 0xd3, 0x16, 0x6c, 0xc6, 0xec, 0x36, 0x35, 0x89, 0xe7, - 0x98, 0xc4, 0x75, 0x59, 0x40, 0x02, 0x87, 0xb9, 0x5c, 0x46, 0x17, 0x33, 0x3d, 0x64, 0xbd, 0x28, - 0x5c, 0xb6, 0x18, 0xef, 0x30, 0x6e, 0x36, 0x09, 0x0f, 0x83, 0x4d, 0x1a, 0x90, 0xaa, 0x69, 0x31, - 0xc7, 0x8d, 0xe2, 0x7a, 0x0f, 0xe6, 0xdf, 0x0d, 0x99, 0xde, 0xbc, 0x6d, 0xed, 0x11, 0xd7, 0xa6, - 0x75, 0x12, 0xd0, 0x3a, 0xbd, 0xd5, 0xa5, 0x3c, 0xc0, 0x3b, 0x30, 0xe1, 0x11, 0xc7, 0x9f, 0x47, - 0xa7, 0xd0, 0x99, 0x67, 0x36, 0x2f, 0xdd, 0x7f, 0x58, 0x29, 0xfc, 0xf1, 0xb0, 0x72, 0xc1, 0x76, - 0x82, 0xbd, 0x6e, 0xd3, 0xb0, 0x58, 0xc7, 0x7c, 0x5b, 0xf4, 0xde, 0xda, 0x23, 0x8e, 0x6b, 0x4a, - 0x8e, 0xde, 0x9a, 0x79, 0xdb, 0xb4, 0x58, 0xa7, 0xc3, 0x5c, 0x93, 0x70, 0x4e, 0x03, 0x63, 0x87, - 0x38, 0x7e, 0x5d, 0x54, 0x7a, 0xe5, 0xe9, 0xcf, 0xef, 0x55, 0x0a, 0xff, 0xdc, 0xab, 0x14, 0x74, - 0x0f, 0x4e, 0x8c, 0xe8, 0xcb, 0x3d, 0xe6, 0x72, 0x8a, 0xdf, 0x83, 0x12, 0x95, 0xeb, 0x0d, 0x9f, - 0x04, 0x54, 0x12, 0x18, 0x92, 0x60, 0x39, 0x41, 0x20, 0xc7, 0x8b, 0xfe, 0xac, 0xf2, 0xd6, 0x07, - 0x66, 0xb0, 0xef, 0x51, 0x6e, 0x6c, 0x53, 0xab, 0x3e, 0x45, 0x13, 0xc5, 0xf5, 0x93, 0x23, 0x3a, - 0x72, 0x39, 0xaa, 0xfe, 0x09, 0x02, 0x6d, 0x54, 0x54, 0x02, 0xdd, 0x84, 0xe9, 0x14, 0x10, 0x9f, - 0x47, 0xa7, 0x9e, 0x3c, 0x53, 0x5c, 0x7b, 0xd1, 0x18, 0xde, 0x43, 0x23, 0x59, 0xe0, 0x7a, 0xd7, - 0x6b, 0xd3, 0x4d, 0x2d, 0xc4, 0xfe, 0xe1, 0xcf, 0x0a, 0xce, 0x84, 0x78, 0xbd, 0x94, 0x44, 0xe4, - 0xfa, 0x73, 0xf0, 0xac, 0xa0, 0xa8, 0x59, 0x81, 0xd3, 0x1b, 0xd0, 0xb9, 0x30, 0x97, 0x5e, 0x96, - 0x58, 0xbb, 0x70, 0x8c, 0x44, 0x4b, 0x82, 0xe7, 0x51, 0xf7, 0xa8, 0x5f, 0x4c, 0x3f, 0x01, 0xcf, - 0x8b, 0x7e, 0xbb, 0x2c, 0xa0, 0xd7, 0x89, 0x6f, 0xd3, 0x20, 0x46, 0xb9, 0x23, 0xcf, 0x4b, 0x2a, - 0x24, 0x71, 0x1a, 0x30, 0xd5, 0x63, 0x01, 0x6d, 0x04, 0xd1, 0xfa, 0xff, 0xc2, 0x54, 0xec, 0x0d, - 0x1a, 0xe9, 0xef, 0xc0, 0x82, 0x68, 0x7e, 0x99, 0xd2, 0x16, 0xf5, 0xb7, 0x69, 0x9b, 0xda, 0xe2, - 0x5d, 0xe8, 0x1f, 0xd8, 0x25, 0x98, 0xee, 0x91, 0xb6, 0xd3, 0x22, 0x01, 0xf3, 0x1b, 0xa4, 0xd5, - 0x92, 0x47, 0xb7, 0x5e, 0x8a, 0x57, 0x6b, 0xad, 0x56, 0xf2, 0x14, 0xbe, 0x01, 0x8b, 0x8a, 0x82, - 0x72, 0xa4, 0x0a, 0x14, 0x6f, 0x8a, 0x58, 0xb2, 0x1c, 0x44, 0x4b, 0x61, 0x2d, 0xfd, 0x2d, 0x29, - 0xd5, 0x35, 0x87, 0xf3, 0x2d, 0xd6, 0x75, 0x03, 0xea, 0x8f, 0x4d, 0xf3, 0x9a, 0xd4, 0x36, 0x55, - 0x4b, 0x82, 0xbc, 0x00, 0x53, 0x1d, 0x87, 0xf3, 0x86, 0x15, 0xad, 0x8b, 0x52, 0x13, 0xf5, 0x62, - 0x67, 0x90, 0x1a, 0xab, 0x53, 0xb3, 0x6d, 0x3f, 0x9c, 0x83, 0xee, 0xf8, 0x34, 0x54, 0x6f, 0x6c, - 0x9e, 0xbb, 0x48, 0xca, 0x93, 0xad, 0x28, 0xa9, 0x08, 0xcc, 0x92, 0x7e, 0xac, 0xe1, 0x45, 0x41, - 0x51, 0xb5, 0xb8, 0x66, 0x64, 0x5f, 0x8d, 0xb8, 0x4c, 0xf2, 0x45, 0x90, 0x25, 0x37, 0x27, 0xc2, - 0x63, 0x52, 0x9f, 0x21, 0x43, 0xad, 0xf4, 0x8a, 0x82, 0x21, 0x3e, 0x91, 0x9f, 0x22, 0x28, 0xab, - 0x32, 0x24, 0xa6, 0x05, 0x38, 0x83, 0xd9, 0x7f, 0x85, 0xc7, 0xe3, 0x9c, 0x1d, 0xe6, 0xe4, 0xfa, - 0x55, 0x79, 0xbf, 0xc4, 0x4f, 0xef, 0x3e, 0x8a, 0xf6, 0x3d, 0x79, 0x1f, 0x0d, 0x55, 0x93, 0x03, - 0xbd, 0x0f, 0xd3, 0x83, 0x81, 0x12, 0xa2, 0xaf, 0xe4, 0x1c, 0x66, 0x77, 0x30, 0x49, 0x89, 0x24, - 0x3b, 0xe8, 0x0b, 0xa3, 0xfa, 0xc6, 0x5a, 0xef, 0xc3, 0xc9, 0x91, 0x51, 0x89, 0x75, 0x03, 0x8e, - 0xa7, 0xb1, 0xfa, 0x22, 0x8f, 0xc1, 0x35, 0x9d, 0xe2, 0xe2, 0xfa, 0x1c, 0x60, 0xd1, 0x7a, 0x87, - 0xf8, 0xa4, 0x13, 0x03, 0x5d, 0x93, 0x17, 0x66, 0x7f, 0x55, 0x82, 0x5c, 0x84, 0x49, 0x4f, 0xac, - 0x48, 0x5d, 0xe6, 0xb3, 0xfd, 0xa3, 0x27, 0x64, 0x33, 0x99, 0xbd, 0xf6, 0xeb, 0x71, 0x78, 0x4a, - 0xd4, 0xc3, 0x5f, 0x21, 0x98, 0x4a, 0x92, 0xe1, 0x73, 0xd9, 0x12, 0x2a, 0xe3, 0xd4, 0x56, 0x72, - 0xe5, 0x46, 0xac, 0xfa, 0xf9, 0xbb, 0xbf, 0xfd, 0xfd, 0xe5, 0x13, 0xcb, 0xf8, 0xb4, 0x39, 0xec, - 0xe4, 0x91, 0x59, 0xa7, 0x8c, 0x07, 0x7f, 0x83, 0x60, 0x26, 0xe5, 0x23, 0x1f, 0x12, 0xef, 0xf1, - 0xb1, 0x55, 0x05, 0xdb, 0x0a, 0x3e, 0x9b, 0x87, 0xad, 0x11, 0x84, 0x2c, 0xdf, 0x22, 0x28, 0xa5, - 0x4c, 0x14, 0xe7, 0xe9, 0xd8, 0xdf, 0x50, 0xed, 0x7c, 0xbe, 0x64, 0xc9, 0xb7, 0x2e, 0xf8, 0x56, - 0xf1, 0x8a, 0x82, 0x2f, 0xfc, 0xd1, 0xc1, 0xd3, 0x94, 0x1c, 0x7f, 0x86, 0xe0, 0x98, 0x74, 0x52, - 0xbc, 0xa4, 0x68, 0x97, 0x36, 0x60, 0x6d, 0xf9, 0xa8, 0xb4, 0x9c, 0x7b, 0x19, 0xf1, 0x48, 0x9b, - 0xc5, 0x5f, 0x23, 0x28, 0x26, 0x7c, 0x14, 0x9f, 0x55, 0x74, 0xc9, 0xda, 0xb0, 0x76, 0x2e, 0x4f, - 0x6a, 0xce, 0x4d, 0x8c, 0xa0, 0x92, 0xce, 0x8d, 0x7f, 0x46, 0x30, 0x33, 0xec, 0x89, 0xd8, 0x50, - 0xf4, 0x54, 0xb8, 0xb1, 0x66, 0xe6, 0xce, 0x97, 0xa0, 0x35, 0x01, 0xfa, 0x2a, 0xde, 0x50, 0x80, - 0xc6, 0x77, 0x25, 0x37, 0xef, 0xa4, 0x6f, 0xd3, 0x8f, 0xcd, 0xc8, 0x92, 0xf1, 0x77, 0x08, 0x8a, - 0x09, 0xfb, 0x54, 0x4a, 0x9a, 0xb5, 0x6b, 0xa5, 0xa4, 0x23, 0xdc, 0x58, 0x7f, 0x5d, 0x90, 0x6e, - 0xe0, 0x97, 0xc7, 0x20, 0x0d, 0x2d, 0x1b, 0xff, 0x82, 0x60, 0x66, 0xd8, 0xaf, 0x94, 0x02, 0x2b, - 0x0c, 0x5d, 0x29, 0xb0, 0xca, 0xae, 0xf5, 0xab, 0x02, 0xfb, 0x32, 0xde, 0x1e, 0x03, 0x3b, 0x63, - 0xa0, 0xf8, 0x47, 0x04, 0xb3, 0x19, 0xcf, 0xc5, 0x79, 0xa1, 0xe2, 0xa3, 0xfc, 0x52, 0xfe, 0x07, - 0xe4, 0x18, 0x97, 0xc4, 0x18, 0x17, 0xf1, 0x85, 0xa3, 0xc7, 0xc8, 0xda, 0x3e, 0xfe, 0x09, 0x41, - 0x29, 0xe5, 0x5f, 0xca, 0x0b, 0x6a, 0x94, 0x93, 0x2b, 0x2f, 0xa8, 0x91, 0x46, 0xad, 0x5f, 0x11, - 0xa8, 0x5b, 0xb8, 0xa6, 0x46, 0x6d, 0x39, 0x47, 0x2a, 0x2e, 0xe4, 0xfe, 0x1e, 0xc1, 0x74, 0xda, - 0x77, 0x71, 0x2e, 0x96, 0x58, 0xe8, 0xd5, 0x9c, 0xd9, 0x12, 0x7d, 0x43, 0xa0, 0xaf, 0xe3, 0xea, - 0x7f, 0x51, 0x39, 0x92, 0xf8, 0x23, 0x98, 0x8c, 0xec, 0x15, 0x9f, 0x56, 0xf4, 0x4c, 0xb9, 0xb8, - 0xb6, 0x74, 0x44, 0x96, 0x24, 0x5a, 0x12, 0x44, 0x15, 0xbc, 0xa8, 0xbc, 0xc8, 0x84, 0xa5, 0x5f, - 0xb9, 0x7f, 0x50, 0x46, 0x0f, 0x0e, 0xca, 0xe8, 0xaf, 0x83, 0x32, 0xfa, 0xe2, 0xb0, 0x5c, 0x78, - 0x70, 0x58, 0x2e, 0xfc, 0x7e, 0x58, 0x2e, 0xdc, 0x30, 0x73, 0x7c, 0x82, 0xc8, 0x9a, 0xe2, 0x2b, - 0xb2, 0x39, 0x29, 0x3e, 0x92, 0xd7, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xa4, 0xa3, 0x89, 0xf1, - 0xc9, 0x0f, 0x00, 0x00, + // 1218 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x98, 0xcf, 0x6f, 0x1b, 0x45, + 0x14, 0xc7, 0x3d, 0x4d, 0x9a, 0xc2, 0x73, 0x1c, 0x9c, 0x21, 0x08, 0xd7, 0x4d, 0xec, 0x74, 0x69, + 0xa2, 0x34, 0x3f, 0x76, 0x49, 0x52, 0x15, 0x05, 0x8a, 0x20, 0x3f, 0xa8, 0x28, 0x6a, 0x20, 0x98, + 0x28, 0x42, 0xbd, 0x58, 0xe3, 0xf5, 0x74, 0xb3, 0xaa, 0xbd, 0xbb, 0xdd, 0x19, 0x9b, 0x46, 0x85, + 0x4b, 0x05, 0x88, 0x23, 0x12, 0x42, 0xdc, 0xa0, 0x17, 0x24, 0xc4, 0x99, 0x1f, 0x67, 0x6e, 0x3d, + 0x56, 0xe2, 0x82, 0x38, 0x14, 0x94, 0x70, 0x40, 0xfc, 0x15, 0x68, 0x67, 0xc7, 0xce, 0xae, 0xd7, + 0xa3, 0x6c, 0x5d, 0x38, 0xb5, 0x9a, 0xf7, 0xf6, 0xbd, 0xcf, 0x7c, 0x35, 0x33, 0xef, 0xeb, 0xc0, + 0xa4, 0x63, 0xd7, 0x6c, 0xbf, 0x65, 0xb8, 0x3e, 0x31, 0x1b, 0xd4, 0x68, 0x2f, 0x1b, 0xb7, 0x5b, + 0xd4, 0x3f, 0xd0, 0x3d, 0xdf, 0xe5, 0x2e, 0xce, 0x87, 0x51, 0x3d, 0x8c, 0xea, 0xed, 0xe5, 0xe2, + 0x84, 0xe5, 0x5a, 0xae, 0x08, 0x1a, 0xc1, 0xff, 0xc2, 0xbc, 0xe2, 0xa4, 0xe5, 0xba, 0x56, 0x83, + 0x1a, 0xc4, 0xb3, 0x0d, 0xe2, 0x38, 0x2e, 0x27, 0xdc, 0x76, 0x1d, 0x26, 0xa3, 0x53, 0x89, 0x1e, + 0xb2, 0x5e, 0x18, 0x2e, 0x99, 0x2e, 0x6b, 0xba, 0xcc, 0xa8, 0x11, 0x16, 0x04, 0x6b, 0x94, 0x93, + 0x65, 0xc3, 0x74, 0x6d, 0x27, 0x8c, 0x6b, 0x6d, 0x28, 0xbc, 0x1b, 0x30, 0xbd, 0x71, 0xc7, 0xdc, + 0x27, 0x8e, 0x45, 0x2b, 0x84, 0xd3, 0x0a, 0xbd, 0xdd, 0xa2, 0x8c, 0xe3, 0x1d, 0x18, 0xf6, 0x88, + 0xed, 0x17, 0xd0, 0x34, 0x9a, 0x7b, 0x7a, 0xe3, 0xca, 0x83, 0x47, 0xe5, 0xcc, 0xef, 0x8f, 0xca, + 0x97, 0x2c, 0x9b, 0xef, 0xb7, 0x6a, 0xba, 0xe9, 0x36, 0x8d, 0xb7, 0x45, 0xef, 0xcd, 0x7d, 0x62, + 0x3b, 0x86, 0xe4, 0x68, 0xaf, 0x18, 0x77, 0x0c, 0xd3, 0x6d, 0x36, 0x5d, 0xc7, 0x20, 0x8c, 0x51, + 0xae, 0xef, 0x10, 0xdb, 0xaf, 0x88, 0x4a, 0x2f, 0x3f, 0xf5, 0xd9, 0xfd, 0x72, 0xe6, 0xef, 0xfb, + 0xe5, 0x8c, 0xe6, 0xc1, 0xd9, 0x3e, 0x7d, 0x99, 0xe7, 0x3a, 0x8c, 0xe2, 0xf7, 0x20, 0x47, 0xe5, + 0x7a, 0xd5, 0x27, 0x9c, 0x4a, 0x02, 0x5d, 0x12, 0xcc, 0x46, 0x08, 0xe4, 0xf6, 0xc2, 0x7f, 0x96, + 0x58, 0xfd, 0x96, 0xc1, 0x0f, 0x3c, 0xca, 0xf4, 0x2d, 0x6a, 0x56, 0x46, 0x69, 0xa4, 0xb8, 0x76, + 0xae, 0x4f, 0x47, 0x26, 0xb7, 0xaa, 0xfd, 0x8c, 0xa0, 0x24, 0xa2, 0x5b, 0x84, 0xd3, 0x7a, 0x5f, + 0xa8, 0x2d, 0x38, 0xed, 0xf9, 0xb6, 0x39, 0x28, 0x4c, 0xf8, 0x31, 0x5e, 0x04, 0x5c, 0x6b, 0xb8, + 0xe6, 0xad, 0x2a, 0xb7, 0x9b, 0x94, 0x71, 0xd2, 0xf4, 0xaa, 0x4d, 0x56, 0x38, 0x35, 0x8d, 0xe6, + 0x86, 0x2a, 0x79, 0x11, 0xd9, 0xed, 0x04, 0xb6, 0x19, 0x3e, 0x0f, 0xa3, 0x61, 0xf6, 0x3e, 0xb5, + 0xad, 0x7d, 0x5e, 0x18, 0x9a, 0x46, 0x73, 0xc3, 0x95, 0xac, 0x58, 0x7b, 0x53, 0x2c, 0x69, 0x1f, + 0x23, 0x28, 0xf6, 0xdb, 0x97, 0xa4, 0xbe, 0x09, 0x63, 0x31, 0x29, 0x59, 0x01, 0x4d, 0x0f, 0xcd, + 0x65, 0x57, 0x5e, 0xd0, 0x7b, 0x4f, 0x9f, 0x1e, 0x2d, 0xb0, 0xdb, 0xf2, 0x1a, 0x74, 0xa3, 0x18, + 0xec, 0xf1, 0xfb, 0x3f, 0xca, 0x38, 0x11, 0x62, 0x95, 0x5c, 0x54, 0x5c, 0xa6, 0x3d, 0x07, 0xcf, + 0x0a, 0x8a, 0x75, 0x93, 0xdb, 0xed, 0x63, 0x5d, 0x1d, 0x98, 0x88, 0x2f, 0x4b, 0xac, 0x3d, 0x38, + 0x43, 0xc2, 0x25, 0xc1, 0xf3, 0xa4, 0xa7, 0xab, 0x53, 0x4c, 0x3b, 0x0b, 0xcf, 0x8b, 0x7e, 0x7b, + 0x2e, 0xa7, 0xbb, 0xc4, 0xb7, 0x28, 0xef, 0xa2, 0xdc, 0x95, 0x27, 0x3d, 0x16, 0x92, 0x38, 0x55, + 0x18, 0x6d, 0xbb, 0x9c, 0x56, 0x79, 0xb8, 0xfe, 0x9f, 0x30, 0x65, 0xdb, 0xc7, 0x8d, 0xb4, 0x77, + 0x60, 0x52, 0x34, 0xbf, 0x4a, 0x69, 0x9d, 0xfa, 0x5b, 0xb4, 0x41, 0x2d, 0x71, 0x8b, 0x3b, 0x57, + 0x6d, 0x06, 0xc6, 0xda, 0xa4, 0x61, 0xd7, 0x09, 0x77, 0xfd, 0x2a, 0xa9, 0xd7, 0xe5, 0xa5, 0xab, + 0xe4, 0xba, 0xab, 0xeb, 0xf5, 0x7a, 0xf4, 0xfe, 0xbc, 0x0e, 0x53, 0x8a, 0x82, 0x72, 0x4b, 0x65, + 0xc8, 0xde, 0x14, 0xb1, 0x68, 0x39, 0x08, 0x97, 0x82, 0x5a, 0xda, 0x5b, 0x52, 0xaa, 0x6d, 0x9b, + 0xb1, 0x4d, 0xb7, 0xe5, 0x70, 0xea, 0x0f, 0x4c, 0xf3, 0xaa, 0xd4, 0x36, 0x56, 0x4b, 0x82, 0x9c, + 0x87, 0xd1, 0xa6, 0xcd, 0x58, 0xd5, 0x0c, 0xd7, 0x45, 0xa9, 0xe1, 0x4a, 0xb6, 0x79, 0x9c, 0xda, + 0x55, 0x67, 0xdd, 0xb2, 0xfc, 0x60, 0x1f, 0x74, 0xc7, 0xa7, 0x81, 0x7a, 0x03, 0xf3, 0xdc, 0x43, + 0x52, 0x9e, 0x64, 0x45, 0x49, 0x45, 0x60, 0x9c, 0x74, 0x62, 0x55, 0x2f, 0x0c, 0x8a, 0xaa, 0xd9, + 0x15, 0x3d, 0x79, 0x35, 0xba, 0x65, 0xa2, 0x17, 0x41, 0x96, 0xdc, 0x18, 0x0e, 0x8e, 0x49, 0x25, + 0x4f, 0x7a, 0x5a, 0x69, 0x65, 0x05, 0x43, 0xf7, 0x44, 0x7e, 0xd2, 0x79, 0x74, 0xfa, 0x64, 0x48, + 0x4c, 0x13, 0x70, 0x02, 0xb3, 0x73, 0x85, 0x07, 0xe3, 0x1c, 0xef, 0xe5, 0x64, 0xda, 0x75, 0xf9, + 0x32, 0x76, 0xbf, 0xde, 0x7b, 0x12, 0xed, 0xdb, 0xf2, 0x3d, 0xea, 0xa9, 0x26, 0x37, 0xf4, 0x3e, + 0x8c, 0x1d, 0x6f, 0x28, 0x22, 0xfa, 0x42, 0xca, 0xcd, 0xec, 0x1d, 0xef, 0x24, 0x47, 0xa2, 0x1d, + 0xb4, 0xc9, 0x7e, 0x7d, 0xbb, 0x5a, 0x1f, 0xc0, 0xb9, 0xbe, 0x51, 0x89, 0x75, 0x03, 0x9e, 0x89, + 0x63, 0x75, 0x44, 0x1e, 0x80, 0x6b, 0x2c, 0xc6, 0xc5, 0xb4, 0x09, 0xc0, 0xa2, 0xf5, 0x0e, 0xf1, + 0x49, 0xb3, 0x0b, 0xb4, 0x2d, 0x1f, 0xcc, 0xce, 0xaa, 0x04, 0xb9, 0x0c, 0x23, 0x9e, 0x58, 0x91, + 0xba, 0x14, 0x92, 0xfd, 0xc3, 0x2f, 0x64, 0x33, 0x99, 0xbd, 0xf2, 0x4f, 0x1e, 0x4e, 0x8b, 0x7a, + 0xf8, 0x4b, 0x04, 0xa3, 0x51, 0x32, 0x3c, 0x9f, 0x2c, 0xa1, 0x1a, 0xf9, 0xc5, 0x85, 0x54, 0xb9, + 0x21, 0xab, 0xb6, 0x78, 0xef, 0xd7, 0xbf, 0xbe, 0x38, 0x35, 0x8b, 0x2f, 0x18, 0xbd, 0x1e, 0x24, + 0xb4, 0x19, 0xb1, 0xc1, 0x83, 0xbf, 0x46, 0x90, 0x8f, 0xcd, 0x91, 0x0f, 0x88, 0xf7, 0xff, 0xb1, + 0x2d, 0x0b, 0xb6, 0x05, 0x7c, 0x31, 0x0d, 0x5b, 0x95, 0x07, 0x2c, 0xdf, 0x22, 0x18, 0x4f, 0x8c, + 0xff, 0xc7, 0x22, 0x7c, 0x51, 0x91, 0xab, 0x34, 0x15, 0xda, 0x8a, 0xc0, 0x5c, 0xc4, 0xf3, 0x0a, + 0xcc, 0x7a, 0xf0, 0x65, 0x35, 0x2e, 0xe4, 0x37, 0x08, 0x72, 0xb1, 0x61, 0x8f, 0xd3, 0x28, 0xd3, + 0x39, 0x78, 0xc5, 0xc5, 0x74, 0xc9, 0x12, 0x70, 0x55, 0x00, 0x2e, 0xe1, 0x05, 0x05, 0x60, 0x60, + 0xeb, 0x58, 0x5c, 0x4d, 0x86, 0x3f, 0x45, 0x70, 0x46, 0x4e, 0x7c, 0x3c, 0xa3, 0x68, 0x17, 0x37, + 0x0a, 0xc5, 0xd9, 0x93, 0xd2, 0x52, 0x9e, 0xb9, 0x90, 0x47, 0xda, 0x01, 0xfc, 0x15, 0x82, 0x6c, + 0x64, 0xde, 0xe3, 0x8b, 0x8a, 0x2e, 0x49, 0xbb, 0x50, 0x9c, 0x4f, 0x93, 0x9a, 0xf2, 0xb0, 0x85, + 0x50, 0x51, 0x87, 0x81, 0x7f, 0x42, 0x90, 0xef, 0x9d, 0xdd, 0x58, 0x57, 0xf4, 0x54, 0xb8, 0x86, + 0xa2, 0x91, 0x3a, 0x5f, 0x82, 0xae, 0x0b, 0xd0, 0x57, 0xf0, 0x9a, 0x02, 0xb4, 0xfb, 0xa6, 0x33, + 0xe3, 0x6e, 0xfc, 0xd5, 0xff, 0xc8, 0x08, 0xad, 0x43, 0x70, 0x4b, 0xb2, 0x91, 0x31, 0xaf, 0x94, + 0x34, 0x69, 0x2b, 0x94, 0x92, 0xf6, 0x71, 0x0d, 0xda, 0x6b, 0x82, 0x74, 0x0d, 0xbf, 0x34, 0x00, + 0x69, 0x60, 0x2d, 0xf0, 0x2f, 0x08, 0xf2, 0xbd, 0x73, 0x55, 0x29, 0xb0, 0xc2, 0x78, 0x28, 0x05, + 0x56, 0xd9, 0x0a, 0xed, 0xba, 0xc0, 0xbe, 0x8a, 0xb7, 0x06, 0xc0, 0x4e, 0x0c, 0x7a, 0xfc, 0x03, + 0x82, 0xf1, 0x84, 0x37, 0xc0, 0x69, 0xa1, 0xd8, 0x49, 0xcf, 0x92, 0xd2, 0x76, 0x68, 0x57, 0xc4, + 0x36, 0x2e, 0xe3, 0x4b, 0x27, 0x6f, 0x23, 0x69, 0x4f, 0xf0, 0x8f, 0x08, 0x72, 0xb1, 0x39, 0xab, + 0x7c, 0xa0, 0xfa, 0x39, 0x0e, 0xe5, 0x03, 0xd5, 0xd7, 0x50, 0x68, 0xd7, 0x04, 0xea, 0x26, 0x5e, + 0x57, 0xa3, 0xd6, 0xed, 0x13, 0x15, 0x17, 0x72, 0x7f, 0x87, 0x60, 0x2c, 0xee, 0x0f, 0x70, 0x2a, + 0x96, 0xae, 0xd0, 0x4b, 0x29, 0xb3, 0x25, 0xfa, 0x9a, 0x40, 0x5f, 0xc5, 0xcb, 0x8f, 0xa3, 0x72, + 0x28, 0xf1, 0x87, 0x30, 0x12, 0xda, 0x00, 0x7c, 0x41, 0xd1, 0x33, 0xe6, 0x36, 0x8a, 0x33, 0x27, + 0x64, 0x49, 0xa2, 0x19, 0x41, 0x54, 0xc6, 0x53, 0xca, 0x87, 0x4c, 0x58, 0x8f, 0x6b, 0x0f, 0x0e, + 0x4b, 0xe8, 0xe1, 0x61, 0x09, 0xfd, 0x79, 0x58, 0x42, 0x9f, 0x1f, 0x95, 0x32, 0x0f, 0x8f, 0x4a, + 0x99, 0xdf, 0x8e, 0x4a, 0x99, 0x1b, 0x46, 0x8a, 0x9f, 0x4a, 0xb2, 0xa6, 0xf8, 0x69, 0x5c, 0x1b, + 0x11, 0x7f, 0x86, 0x58, 0xfd, 0x37, 0x00, 0x00, 0xff, 0xff, 0xb7, 0xc9, 0xcd, 0x87, 0x2b, 0x11, + 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1076,6 +1143,8 @@ type QueryClient interface { ExchangeRate(ctx context.Context, in *QueryExchangeRateRequest, opts ...grpc.CallOption) (*QueryExchangeRateResponse, error) // ExchangeRateTwap returns twap exchange rate of a pair ExchangeRateTwap(ctx context.Context, in *QueryExchangeRateRequest, opts ...grpc.CallOption) (*QueryExchangeRateResponse, error) + // DatedExchangeRate returns latest price of a pair + DatedExchangeRate(ctx context.Context, in *QueryExchangeRateRequest, opts ...grpc.CallOption) (*QueryDatedExchangeRateResponse, error) // ExchangeRates returns exchange rates of all pairs ExchangeRates(ctx context.Context, in *QueryExchangeRatesRequest, opts ...grpc.CallOption) (*QueryExchangeRatesResponse, error) // Actives returns all active pairs @@ -1124,6 +1193,15 @@ func (c *queryClient) ExchangeRateTwap(ctx context.Context, in *QueryExchangeRat return out, nil } +func (c *queryClient) DatedExchangeRate(ctx context.Context, in *QueryExchangeRateRequest, opts ...grpc.CallOption) (*QueryDatedExchangeRateResponse, error) { + out := new(QueryDatedExchangeRateResponse) + err := c.cc.Invoke(ctx, "/nibiru.oracle.v1.Query/DatedExchangeRate", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *queryClient) ExchangeRates(ctx context.Context, in *QueryExchangeRatesRequest, opts ...grpc.CallOption) (*QueryExchangeRatesResponse, error) { out := new(QueryExchangeRatesResponse) err := c.cc.Invoke(ctx, "/nibiru.oracle.v1.Query/ExchangeRates", in, out, opts...) @@ -1220,6 +1298,8 @@ type QueryServer interface { ExchangeRate(context.Context, *QueryExchangeRateRequest) (*QueryExchangeRateResponse, error) // ExchangeRateTwap returns twap exchange rate of a pair ExchangeRateTwap(context.Context, *QueryExchangeRateRequest) (*QueryExchangeRateResponse, error) + // DatedExchangeRate returns latest price of a pair + DatedExchangeRate(context.Context, *QueryExchangeRateRequest) (*QueryDatedExchangeRateResponse, error) // ExchangeRates returns exchange rates of all pairs ExchangeRates(context.Context, *QueryExchangeRatesRequest) (*QueryExchangeRatesResponse, error) // Actives returns all active pairs @@ -1252,6 +1332,9 @@ func (*UnimplementedQueryServer) ExchangeRate(ctx context.Context, req *QueryExc func (*UnimplementedQueryServer) ExchangeRateTwap(ctx context.Context, req *QueryExchangeRateRequest) (*QueryExchangeRateResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ExchangeRateTwap not implemented") } +func (*UnimplementedQueryServer) DatedExchangeRate(ctx context.Context, req *QueryExchangeRateRequest) (*QueryDatedExchangeRateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DatedExchangeRate not implemented") +} func (*UnimplementedQueryServer) ExchangeRates(ctx context.Context, req *QueryExchangeRatesRequest) (*QueryExchangeRatesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ExchangeRates not implemented") } @@ -1323,6 +1406,24 @@ func _Query_ExchangeRateTwap_Handler(srv interface{}, ctx context.Context, dec f return interceptor(ctx, in, info, handler) } +func _Query_DatedExchangeRate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryExchangeRateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).DatedExchangeRate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/nibiru.oracle.v1.Query/DatedExchangeRate", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).DatedExchangeRate(ctx, req.(*QueryExchangeRateRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _Query_ExchangeRates_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryExchangeRatesRequest) if err := dec(in); err != nil { @@ -1515,6 +1616,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "ExchangeRateTwap", Handler: _Query_ExchangeRateTwap_Handler, }, + { + MethodName: "DatedExchangeRate", + Handler: _Query_DatedExchangeRate_Handler, + }, { MethodName: "ExchangeRates", Handler: _Query_ExchangeRates_Handler, @@ -1649,6 +1754,49 @@ func (m *QueryExchangeRatesRequest) MarshalToSizedBuffer(dAtA []byte) (int, erro return len(dAtA) - i, nil } +func (m *QueryDatedExchangeRateResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryDatedExchangeRateResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDatedExchangeRateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.BlockHeight != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.BlockHeight)) + i-- + dAtA[i] = 0x18 + } + if m.BlockTimestampMs != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.BlockTimestampMs)) + i-- + dAtA[i] = 0x10 + } + { + size := m.Price.Size() + i -= size + if _, err := m.Price.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + func (m *QueryExchangeRatesResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -2268,6 +2416,23 @@ func (m *QueryExchangeRatesRequest) Size() (n int) { return n } +func (m *QueryDatedExchangeRateResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Price.Size() + n += 1 + l + sovQuery(uint64(l)) + if m.BlockTimestampMs != 0 { + n += 1 + sovQuery(uint64(m.BlockTimestampMs)) + } + if m.BlockHeight != 0 { + n += 1 + sovQuery(uint64(m.BlockHeight)) + } + return n +} + func (m *QueryExchangeRatesResponse) Size() (n int) { if m == nil { return 0 @@ -2722,6 +2887,128 @@ func (m *QueryExchangeRatesRequest) Unmarshal(dAtA []byte) error { } return nil } +func (m *QueryDatedExchangeRateResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryDatedExchangeRateResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDatedExchangeRateResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Price", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Price.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockTimestampMs", wireType) + } + m.BlockTimestampMs = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BlockTimestampMs |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockHeight", wireType) + } + m.BlockHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BlockHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *QueryExchangeRatesResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/x/oracle/types/query.pb.gw.go b/x/oracle/types/query.pb.gw.go index f8af1a4f8..c9436368d 100644 --- a/x/oracle/types/query.pb.gw.go +++ b/x/oracle/types/query.pb.gw.go @@ -105,6 +105,42 @@ func local_request_Query_ExchangeRateTwap_0(ctx context.Context, marshaler runti } +var ( + filter_Query_DatedExchangeRate_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_DatedExchangeRate_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryExchangeRateRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_DatedExchangeRate_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.DatedExchangeRate(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_DatedExchangeRate_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryExchangeRateRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_DatedExchangeRate_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.DatedExchangeRate(ctx, &protoReq) + return msg, metadata, err + +} + func request_Query_ExchangeRates_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryExchangeRatesRequest var metadata runtime.ServerMetadata @@ -481,6 +517,29 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_DatedExchangeRate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_DatedExchangeRate_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_DatedExchangeRate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_Query_ExchangeRates_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -792,6 +851,26 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_DatedExchangeRate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_DatedExchangeRate_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_DatedExchangeRate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_Query_ExchangeRates_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -1000,6 +1079,8 @@ var ( pattern_Query_ExchangeRateTwap_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"nibiru", "oracle", "v1beta1", "exchange_rate_twap"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_DatedExchangeRate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"nibiru", "oracle", "v1beta1", "dated_exchange_rate"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_ExchangeRates_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"nibiru", "oracle", "v1beta1", "pairs", "exchange_rates"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_Actives_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"nibiru", "oracle", "v1beta1", "pairs", "actives"}, "", runtime.AssumeColonVerbOpt(false))) @@ -1026,6 +1107,8 @@ var ( forward_Query_ExchangeRateTwap_0 = runtime.ForwardResponseMessage + forward_Query_DatedExchangeRate_0 = runtime.ForwardResponseMessage + forward_Query_ExchangeRates_0 = runtime.ForwardResponseMessage forward_Query_Actives_0 = runtime.ForwardResponseMessage