Skip to content

Commit

Permalink
fix(evm): ensure stateDB is nil after tx execution (#2173)
Browse files Browse the repository at this point in the history
* fix(evm): ensure stateDB is nil after tx execution

* Update CHANGELOG.md

* test: add smart contract test (precompile sendToBank then ERC20 transfer)
  • Loading branch information
k-yang authored Jan 21, 2025
1 parent 8a4b968 commit 0e3ab43
Show file tree
Hide file tree
Showing 11 changed files with 214 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ needed to include double quotes around the hexadecimal string.
- [#2169](https://github.com/NibiruChain/nibiru/pull/2169) - fix(evm): Better handling erc20 metadata
- [#2170](https://github.com/NibiruChain/nibiru/pull/2170) - chore: Remove redundant allowUnprotectedTxs
- [#2172](https://github.com/NibiruChain/nibiru/pull/2172) - chore: close iterator in IterateEpochInfo
- [#2173](https://github.com/NibiruChain/nibiru/pull/2173) - fix(evm): clear `StateDB` between calls

#### Nibiru EVM | Before Audit 2 - 2024-12-06

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"_format": "hh-sol-artifact-1",
"contractName": "TestPrecompileSendToBankThenERC20Transfer",
"sourceName": "contracts/TestPrecompileSendToBankThenERC20Transfer.sol",
"abi": [
{
"inputs": [
{
"internalType": "address",
"name": "_erc20",
"type": "address"
},
{
"internalType": "string",
"name": "_recipient",
"type": "string"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "attack",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "erc20",
"outputs": [
{
"internalType": "contract IERC20",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "recipient",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
}
],
"bytecode": "0x60806040523480156200001157600080fd5b5060405162000d8c38038062000d8c833981810160405281019062000037919062000289565b816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600190816200008891906200053a565b50505062000621565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620000d282620000a5565b9050919050565b620000e481620000c5565b8114620000f057600080fd5b50565b6000815190506200010481620000d9565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6200015f8262000114565b810181811067ffffffffffffffff8211171562000181576200018062000125565b5b80604052505050565b60006200019662000091565b9050620001a4828262000154565b919050565b600067ffffffffffffffff821115620001c757620001c662000125565b5b620001d28262000114565b9050602081019050919050565b60005b83811015620001ff578082015181840152602081019050620001e2565b60008484015250505050565b6000620002226200021c84620001a9565b6200018a565b9050828152602081018484840111156200024157620002406200010f565b5b6200024e848285620001df565b509392505050565b600082601f8301126200026e576200026d6200010a565b5b8151620002808482602086016200020b565b91505092915050565b60008060408385031215620002a357620002a26200009b565b5b6000620002b385828601620000f3565b925050602083015167ffffffffffffffff811115620002d757620002d6620000a0565b5b620002e58582860162000256565b9150509250929050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200034257607f821691505b602082108103620003585762000357620002fa565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620003c27fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000383565b620003ce868362000383565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006200041b620004156200040f84620003e6565b620003f0565b620003e6565b9050919050565b6000819050919050565b6200043783620003fa565b6200044f620004468262000422565b84845462000390565b825550505050565b600090565b6200046662000457565b620004738184846200042c565b505050565b5b818110156200049b576200048f6000826200045c565b60018101905062000479565b5050565b601f821115620004ea57620004b4816200035e565b620004bf8462000373565b81016020851015620004cf578190505b620004e7620004de8562000373565b83018262000478565b50505b505050565b600082821c905092915050565b60006200050f60001984600802620004ef565b1980831691505092915050565b60006200052a8383620004fc565b9150826002028217905092915050565b6200054582620002ef565b67ffffffffffffffff81111562000561576200056062000125565b5b6200056d825462000329565b6200057a8282856200049f565b600060209050601f831160018114620005b257600084156200059d578287015190505b620005a985826200051c565b86555062000619565b601f198416620005c2866200035e565b60005b82811015620005ec57848901518255600182019150602085019450602081019050620005c5565b868310156200060c578489015162000608601f891682620004fc565b8355505b6001600288020188555050505b505050505050565b61075b80620006316000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806366d003ac14610046578063785e9e86146100645780639e5faafc14610082575b600080fd5b61004e61008c565b60405161005b91906103b6565b60405180910390f35b61006c61011a565b6040516100799190610457565b60405180910390f35b61008a61013e565b005b60018054610099906104a1565b80601f01602080910402602001604051908101604052809291908181526020018280546100c5906104a1565b80156101125780601f106100e757610100808354040283529160200191610112565b820191906000526020600020905b8154815290600101906020018083116100f557829003601f168201915b505050505081565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161019a91906104f3565b602060405180830381865afa1580156101b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101db9190610549565b905061080073ffffffffffffffffffffffffffffffffffffffff1663e77a47bf60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff168360016040518463ffffffff1660e01b815260040161023d9392919061061e565b6020604051808303816000875af115801561025c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102809190610549565b5060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a9059cbb61dead60016040518363ffffffff1660e01b81526004016102df929190610697565b6020604051808303816000875af11580156102fe573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061032291906106f8565b5050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610360578082015181840152602081019050610345565b60008484015250505050565b6000601f19601f8301169050919050565b600061038882610326565b6103928185610331565b93506103a2818560208601610342565b6103ab8161036c565b840191505092915050565b600060208201905081810360008301526103d0818461037d565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600061041d610418610413846103d8565b6103f8565b6103d8565b9050919050565b600061042f82610402565b9050919050565b600061044182610424565b9050919050565b61045181610436565b82525050565b600060208201905061046c6000830184610448565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806104b957607f821691505b6020821081036104cc576104cb610472565b5b50919050565b60006104dd826103d8565b9050919050565b6104ed816104d2565b82525050565b600060208201905061050860008301846104e4565b92915050565b600080fd5b6000819050919050565b61052681610513565b811461053157600080fd5b50565b6000815190506105438161051d565b92915050565b60006020828403121561055f5761055e61050e565b5b600061056d84828501610534565b91505092915050565b61057f81610513565b82525050565b60008190508160005260206000209050919050565b600081546105a7816104a1565b6105b18186610331565b945060018216600081146105cc57600181146105e257610615565b60ff198316865281151560200286019350610615565b6105eb85610585565b60005b8381101561060d578154818901526001820191506020810190506105ee565b808801955050505b50505092915050565b600060608201905061063360008301866104e4565b6106406020830185610576565b8181036040830152610652818461059a565b9050949350505050565b6000819050919050565b600061068161067c6106778461065c565b6103f8565b610513565b9050919050565b61069181610666565b82525050565b60006040820190506106ac60008301856104e4565b6106b96020830184610688565b9392505050565b60008115159050919050565b6106d5816106c0565b81146106e057600080fd5b50565b6000815190506106f2816106cc565b92915050565b60006020828403121561070e5761070d61050e565b5b600061071c848285016106e3565b9150509291505056fea26469706673582212208ef08251d818031ab4c74d22a618b6305b406be371abda8aeb6b83c46f6c195a64736f6c63430008180033",
"deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100415760003560e01c806366d003ac14610046578063785e9e86146100645780639e5faafc14610082575b600080fd5b61004e61008c565b60405161005b91906103b6565b60405180910390f35b61006c61011a565b6040516100799190610457565b60405180910390f35b61008a61013e565b005b60018054610099906104a1565b80601f01602080910402602001604051908101604052809291908181526020018280546100c5906104a1565b80156101125780601f106100e757610100808354040283529160200191610112565b820191906000526020600020905b8154815290600101906020018083116100f557829003601f168201915b505050505081565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161019a91906104f3565b602060405180830381865afa1580156101b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101db9190610549565b905061080073ffffffffffffffffffffffffffffffffffffffff1663e77a47bf60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff168360016040518463ffffffff1660e01b815260040161023d9392919061061e565b6020604051808303816000875af115801561025c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102809190610549565b5060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a9059cbb61dead60016040518363ffffffff1660e01b81526004016102df929190610697565b6020604051808303816000875af11580156102fe573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061032291906106f8565b5050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610360578082015181840152602081019050610345565b60008484015250505050565b6000601f19601f8301169050919050565b600061038882610326565b6103928185610331565b93506103a2818560208601610342565b6103ab8161036c565b840191505092915050565b600060208201905081810360008301526103d0818461037d565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600061041d610418610413846103d8565b6103f8565b6103d8565b9050919050565b600061042f82610402565b9050919050565b600061044182610424565b9050919050565b61045181610436565b82525050565b600060208201905061046c6000830184610448565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806104b957607f821691505b6020821081036104cc576104cb610472565b5b50919050565b60006104dd826103d8565b9050919050565b6104ed816104d2565b82525050565b600060208201905061050860008301846104e4565b92915050565b600080fd5b6000819050919050565b61052681610513565b811461053157600080fd5b50565b6000815190506105438161051d565b92915050565b60006020828403121561055f5761055e61050e565b5b600061056d84828501610534565b91505092915050565b61057f81610513565b82525050565b60008190508160005260206000209050919050565b600081546105a7816104a1565b6105b18186610331565b945060018216600081146105cc57600181146105e257610615565b60ff198316865281151560200286019350610615565b6105eb85610585565b60005b8381101561060d578154818901526001820191506020810190506105ee565b808801955050505b50505092915050565b600060608201905061063360008301866104e4565b6106406020830185610576565b8181036040830152610652818461059a565b9050949350505050565b6000819050919050565b600061068161067c6106778461065c565b6103f8565b610513565b9050919050565b61069181610666565b82525050565b60006040820190506106ac60008301856104e4565b6106b96020830184610688565b9392505050565b60008115159050919050565b6106d5816106c0565b81146106e057600080fd5b50565b6000815190506106f2816106cc565b92915050565b60006020828403121561070e5761070d61050e565b5b600061071c848285016106e3565b9150509291505056fea26469706673582212208ef08251d818031ab4c74d22a618b6305b406be371abda8aeb6b83c46f6c195a64736f6c63430008180033",
"linkReferences": {},
"deployedLinkReferences": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "./IFunToken.sol";

contract TestPrecompileSendToBankThenERC20Transfer {
IERC20 public erc20;
string public recipient;

constructor(address _erc20, string memory _recipient) {
erc20 = IERC20(_erc20);
recipient = _recipient;
}

function attack() public {
// transfer this contract's entire balance to the recipient
uint balance = erc20.balanceOf(address(this));
// sendToBank should reduce balance to zero
FUNTOKEN_PRECOMPILE.sendToBank(address(erc20), balance, recipient);

// this call should fail because of the balance is zero
erc20.transfer(0x000000000000000000000000000000000000dEaD, 1);
}
}
9 changes: 9 additions & 0 deletions x/evm/embeds/embeds.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ var (
testRandom []byte
//go:embed artifacts/contracts/MKR.sol/DSToken.json
testMetadataBytes32 []byte
//go:embed artifacts/contracts/TestPrecompileSendToBankThenERC20Transfer.sol/TestPrecompileSendToBankThenERC20Transfer.json
testPrecompileSendToBankThenERC20Transfer []byte
)

var (
Expand Down Expand Up @@ -148,6 +150,12 @@ var (
Name: "MKR.sol",
EmbedJSON: testMetadataBytes32,
}

// SmartContract_TestPrecompileSendToBankThenERC20Transfer is a test contract that sends to bank then calls ERC20 transfer
SmartContract_TestPrecompileSendToBankThenERC20Transfer = CompiledEvmContract{
Name: "TestPrecompileSendToBankThenERC20Transfer.sol",
EmbedJSON: testPrecompileSendToBankThenERC20Transfer,
}
)

func init() {
Expand All @@ -166,6 +174,7 @@ func init() {
SmartContract_TestERC20TransferWithFee.MustLoad()
SmartContract_TestRandom.MustLoad()
SmartContract_TestBytes32Metadata.MustLoad()
SmartContract_TestPrecompileSendToBankThenERC20Transfer.MustLoad()
}

type CompiledEvmContract struct {
Expand Down
4 changes: 2 additions & 2 deletions x/evm/embeds/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions x/evm/keeper/funtoken_from_coin.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ func (k *Keeper) deployERC20ForBankCoin(
if stateDB == nil {
stateDB = k.NewStateDB(ctx, txConfig)
}
defer func() {
k.Bank.StateDB = nil
}()
evmObj := k.NewEVM(ctx, evmMsg, evmCfg, nil /*tracer*/, stateDB)
evmResp, err := k.CallContractWithInput(
ctx, evmObj, evm.EVM_MODULE_ADDRESS, nil, true /*commit*/, input, Erc20GasLimitDeploy,
Expand All @@ -110,8 +113,6 @@ func (k *Keeper) deployERC20ForBankCoin(
if err != nil {
return gethcommon.Address{}, errors.Wrap(err, "failed to commit stateDB")
}
// Don't need the StateDB anymore because it's not usable after committing
k.Bank.StateDB = nil

ctx.GasMeter().ConsumeGas(evmResp.GasUsed, "deploy erc20 funtoken contract")

Expand Down
101 changes: 100 additions & 1 deletion x/evm/keeper/funtoken_from_coin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,6 @@ func (s *FunTokenFromCoinSuite) TestERC20TransferThenPrecompileSend() {
// - Module account: 10 NIBI escrowed (which Test contract holds as 10 WNIBI)
func (s *FunTokenFromCoinSuite) TestPrecompileSelfCallRevert() {
deps := evmtest.NewTestDeps()
evmObj, _ := deps.NewEVM()

// Initial setup
funToken := s.fundAndCreateFunToken(deps, 10e6)
Expand Down Expand Up @@ -606,6 +605,7 @@ func (s *FunTokenFromCoinSuite) TestPrecompileSelfCallRevert() {
sdk.NewCoins(sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(10e6))),
))

evmObj, _ := deps.NewEVM()
evmtest.FunTokenBalanceAssert{
FunToken: funToken,
Account: testContractAddr,
Expand All @@ -620,6 +620,7 @@ func (s *FunTokenFromCoinSuite) TestPrecompileSelfCallRevert() {
charles := evmtest.NewEthPrivAcc()

s.T().Log("call test contract")
evmObj, _ = deps.NewEVM()
contractInput, err := embeds.SmartContract_TestPrecompileSelfCallRevert.ABI.Pack(
"selfCallTransferFunds",
alice.EthAddr,
Expand Down Expand Up @@ -672,6 +673,104 @@ func (s *FunTokenFromCoinSuite) TestPrecompileSelfCallRevert() {
}.Assert(s.T(), deps, evmObj)
}

// TestPrecompileSelfCallRevert
// 1. Creates a funtoken from coin.
// 2. Calls the test contract
// a. sendToBank
// b. erc20 transfer
//
// INITIAL STATE:
// - Test contract funds: 10 WNIBI
// CONTRACT CALL:
// - Sends 10 WNIBI to Alice, and try to send 1 NIBI to Bob
// EXPECTED:
// - all changes reverted because of not enough balance
// - Test contract funds: 10 WNIBI
// - Alice: 10 WNIBI
// - Bob: 0 NIBI
// - Module account: 10 NIBI escrowed (which Test contract holds as 10 WNIBI)
func (s *FunTokenFromCoinSuite) TestPrecompileSendToBankThenErc20Transfer() {
deps := evmtest.NewTestDeps()

// Initial setup
funToken := s.fundAndCreateFunToken(deps, 10e6)

s.T().Log("Deploy Test Contract")
deployResp, err := evmtest.DeployContract(
&deps,
embeds.SmartContract_TestPrecompileSendToBankThenERC20Transfer,
funToken.Erc20Addr.Address,
deps.Sender.NibiruAddr.String(),
)
s.Require().NoError(err)
testContractAddr := deployResp.ContractAddr

s.T().Log("Convert bank coin to erc-20: give test contract 10 WNIBI (erc20)")
_, err = deps.EvmKeeper.ConvertCoinToEvm(
sdk.WrapSDKContext(deps.Ctx),
&evm.MsgConvertCoinToEvm{
Sender: deps.Sender.NibiruAddr.String(),
BankCoin: sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(10e6)),
ToEthAddr: eth.EIP55Addr{Address: testContractAddr},
},
)
s.Require().NoError(err)

// Create Alice and Bob. Contract will try to send Alice native coins and
// send Bob ERC20 tokens.
alice := evmtest.NewEthPrivAcc()
bob := evmtest.NewEthPrivAcc()

s.T().Log("call test contract")
contractInput, err := embeds.SmartContract_TestPrecompileSendToBankThenERC20Transfer.ABI.Pack(
"attack",
)
s.Require().NoError(err)
evmObj, _ := deps.NewEVM()
_, err = deps.EvmKeeper.CallContractWithInput(
deps.Ctx,
evmObj,
deps.Sender.EthAddr,
&testContractAddr,
true,
contractInput,
evmtest.FunTokenGasLimitSendToEvm,
)
s.Require().ErrorContains(err, "execution reverted")

evmtest.FunTokenBalanceAssert{
FunToken: funToken,
Account: alice.EthAddr,
BalanceBank: big.NewInt(0),
BalanceERC20: big.NewInt(0),
Description: "Alice has 0 NIBI / 0 WNIBI",
}.Assert(s.T(), deps, evmObj)

evmtest.FunTokenBalanceAssert{
FunToken: funToken,
Account: bob.EthAddr,
BalanceBank: big.NewInt(0),
BalanceERC20: big.NewInt(0),
Description: "Charles has 0 NIBI / 0 WNIBI",
}.Assert(s.T(), deps, evmObj)

evmtest.FunTokenBalanceAssert{
FunToken: funToken,
Account: testContractAddr,
BalanceBank: big.NewInt(0),
BalanceERC20: big.NewInt(10e6),
Description: "Test contract has 10 NIBI / 10 WNIBI",
}.Assert(s.T(), deps, evmObj)

evmtest.FunTokenBalanceAssert{
FunToken: funToken,
Account: evm.EVM_MODULE_ADDRESS,
BalanceBank: big.NewInt(10e6),
BalanceERC20: big.NewInt(0),
Description: "Module account has 10 NIBI escrowed",
}.Assert(s.T(), deps, evmObj)
}

// fundAndCreateFunToken creates initial setup for tests
func (s *FunTokenFromCoinSuite) fundAndCreateFunToken(deps evmtest.TestDeps, unibiAmount int64) evm.FunToken {
bankDenom := evm.EVMBankDenom
Expand Down
Loading

0 comments on commit 0e3ab43

Please sign in to comment.