Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(evm): ensure stateDB is nil after tx execution #2173

Merged
merged 4 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading