Skip to content
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
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,33 @@
# CHANGELOG

## UNRELEASED

### DEPENDENCIES

### API-BREAKING

### IMPROVEMENTS

### FEATURES

### BUG FIXES

## v0.6.2

### IMPROVEMENTS
- [\#1243](https://github.com/cosmos/evm/pull/1243) Deploy contracts from an EOA rather than a module account in the test helpers. It is also now required: contract creation bumps the sender's nonce, `SetAccount` persists nonce and balance together, and the EVM commit path may not write a module account's balance.

### BUG FIXES
- [\#1222](https://github.com/cosmos/evm/pull/1222) Propagate ERC20 conversion ack in IBC v2 `OnRecvPacket`.

## v0.6.1

### IMPROVEMENTS
- [\#1238](https://github.com/cosmos/evm/pull/1238) Validate ICS-20 acknowledgement encoding in the erc20 IBC v2 middleware.

### BUG FIXES
- Align precompile gas calculation with expected EVM gas semantics.

## v0.6.0

Follow the [migration document](docs/migrations/v0.5.x_to_v0.6.0.md) for upgrade instructions.
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ src="repo_header.svg"
alt="Cosmos EVM - A plug-and-play solution that adds EVM compatibility and customizability to your chain"
/>

## What is Cosmos EVM?

Cosmos EVM is a plug-and-play solution that adds EVM compatibility and customizability to your Cosmos SDK chain. Cosmos EVM is used by Ondo, Mezo, Mantra, XRP sidechain, Telegram Application Chain (TAC), Stable, and others. Cosmos EVM equips Cosmos chains with complete Ethereum capabilities: Solidity smart contracts, Ethereum JSON-RPC, native support for the EVM wallet/token/user experience, and access to the entire Ethereum developer ecosystem. Its precompiles and extensions allow developers to leverage modules like [IBC](https://github.com/cosmos/ibc-go) with EVM and get native ERC-20 support for tokens on Cosmos.

Cosmos EVM is customizable for your business use case, chain architecture, and performance needs.

**Please note**: This repo is undergoing changes while the code is being audited and tested. For the time being we will
be making v0.x releases. Some breaking changes might occur. Cosmos Labs will only mark the Cosmos EVM repository as stable with a v1
release after the audit, key stability features and benchmarking are completed.
Expand Down
10 changes: 10 additions & 0 deletions contracts/solidity/precompiles/bank/testdata/BankCaller.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,14 @@ contract BankCaller {
function callSupplyOf(address erc20Address) external view returns (uint256) {
return IBANK_CONTRACT.supplyOf(erc20Address);
}

// Calls totalSupply with explicit gas forwarding and measures the gas consumed
// by the inner call. Returns whether the call succeeded and the actual gas used.
function callTotalSupplyWithGas(uint256 gasForward) external view returns (bool success, uint256 innerGasUsed) {
uint256 gasBefore = gasleft();
(success, ) = IBANK_PRECOMPILE_ADDRESS.staticcall{gas: gasForward}(
abi.encodeWithSelector(IBank.totalSupply.selector)
);
innerGasUsed = gasBefore - gasleft();
}
}
1 change: 1 addition & 0 deletions evmd/mempool.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ func (app *EVMD) configureEVMMempool(appOpts servertypes.AppOptions, logger log.
),
)
app.SetPrepareProposal(abciProposalHandler.PrepareProposalHandler())
app.SetProcessProposal(abciProposalHandler.ProcessProposalHandler())

return nil
}
Expand Down
34 changes: 24 additions & 10 deletions evmd/tests/ibc/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,22 @@ func SetupNativeErc20(t *testing.T, chain *evmibctesting.TestChain, senderAcc ev
evmCtx := chain.GetContext()
evmApp := chain.App.(evm.EvmApp)

// Deploy new ERC20 contract with default metadata
stateDB := statedb.New(chain.GetContext(), chain.App.(evm.EvmApp).GetEVMKeeper(), statedb.NewEmptyTxConfig())
// Deploy new ERC20 contract with default metadata.
// The deployer is a dedicated EOA.
deployer := common.BytesToAddress([]byte("erc20-test-deployer"))
deployerAccAddr := sdk.AccAddress(deployer.Bytes())
if evmApp.GetAccountKeeper().GetAccount(evmCtx, deployerAccAddr) == nil {
evmApp.GetAccountKeeper().SetAccount(evmCtx, evmApp.GetAccountKeeper().NewAccountWithAddress(evmCtx, deployerAccAddr))
}

stateDB := statedb.New(evmCtx, evmApp.GetEVMKeeper(), statedb.NewEmptyTxConfig())
contractAddr, err := DeployERC20Contract(evmCtx, stateDB, evmApp.GetAccountKeeper(), evmApp.GetEVMKeeper(), banktypes.Metadata{
DenomUnits: []*banktypes.DenomUnit{
{Denom: "example", Exponent: 18},
},
Name: "Example",
Symbol: "Ex",
})
}, deployer)
if err != nil {
t.Fatalf("ERC20 deployment failed: %v", err)
}
Expand All @@ -88,7 +95,7 @@ func SetupNativeErc20(t *testing.T, chain *evmibctesting.TestChain, senderAcc ev
evmCtx,
stateDB,
contractAbi,
erc20types.ModuleAddress,
deployer,
contractAddr,
true,
false,
Expand Down Expand Up @@ -116,7 +123,8 @@ func SetupNativeErc20(t *testing.T, chain *evmibctesting.TestChain, senderAcc ev
}
}

// SetupNativeErc20 deploys, registers, and mints a native ERC20 token on an EVM-based chain.
// DeployContract deploys an arbitrary contract on an EVM-based chain.
// Like DeployERC20Contract, the sender is an EOA rather than a module account.
func DeployContract(t *testing.T, chain *evmibctesting.TestChain, deploymentData testutiltypes.ContractDeploymentData) (common.Address, error) {
t.Helper()

Expand Down Expand Up @@ -145,14 +153,20 @@ func DeployContract(t *testing.T, chain *evmibctesting.TestChain, deploymentData
return crypto.CreateAddress(from, account.Nonce), nil
}

// DeployERC20Contract creates and deploys an ERC20 contract on the EVM with the
// erc20 module account as owner.
// DeployERC20Contract creates and deploys an ERC20 contract on the EVM with
// deployer as owner.
//
// deployer must be an EOA. Do not pass a module account.
// It is also required in practice. Contract creation bumps the sender's nonce,
// SetAccount persists nonce and balance together, and the EVM commit path is
// not allowed to write a module account's balance.
func DeployERC20Contract(
ctx sdk.Context,
stateDB *statedb.StateDB,
accountKeeper erc20types.AccountKeeper,
evmKeeper erc20types.EVMKeeper,
coinMetadata banktypes.Metadata,
deployer common.Address,
) (common.Address, error) {
decimals := uint8(0)
if len(coinMetadata.DenomUnits) > 0 {
Expand All @@ -173,13 +187,13 @@ func DeployERC20Contract(
copy(data[:len(contracts.ERC20MinterBurnerDecimalsContract.Bin)], contracts.ERC20MinterBurnerDecimalsContract.Bin)
copy(data[len(contracts.ERC20MinterBurnerDecimalsContract.Bin):], ctorArgs)

nonce, err := accountKeeper.GetSequence(ctx, erc20types.ModuleAddress.Bytes())
nonce, err := accountKeeper.GetSequence(ctx, deployer.Bytes())
if err != nil {
return common.Address{}, err
}

contractAddr := crypto.CreateAddress(erc20types.ModuleAddress, nonce)
_, err = evmKeeper.CallEVMWithData(ctx, stateDB, erc20types.ModuleAddress, nil, data, true, false, nil)
contractAddr := crypto.CreateAddress(deployer, nonce)
_, err = evmKeeper.CallEVMWithData(ctx, stateDB, deployer, nil, data, true, false, nil)
if err != nil {
return common.Address{}, errorsmod.Wrapf(err, "failed to deploy contract for %s", coinMetadata.Name)
}
Expand Down
28 changes: 26 additions & 2 deletions precompiles/bank/testdata/BankCaller.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,34 @@
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "gasForward",
"type": "uint256"
}
],
"name": "callTotalSupplyWithGas",
"outputs": [
{
"internalType": "bool",
"name": "success",
"type": "bool"
},
{
"internalType": "uint256",
"name": "innerGasUsed",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
}
],
"bytecode": "0x6080806040523461001657610378908161001c8239f35b600080fdfe60806040908082526004918236101561001757600080fd5b600091823560e01c90816389129c681461016257508063acab2f94146100e95763bba60ca01461004657600080fd5b346100e557602092836003193601126100e15780356001600160a01b038116908190036100dd578251631890039360e21b81529182015283816024816108045afa9283156100d257809361009d575b505051908152f35b909192508382813d83116100cb575b6100b68183610234565b810103126100c8575051903880610095565b80fd5b503d6100ac565b8251903d90823e3d90fd5b8380fd5b8280fd5b5080fd5b508290346100e157826003193601126100e15782815180936318160ddd60e01b8252816108045afa918215610158578361012f9493610133575b505051918291826101e1565b0390f35b6101509293503d8091833e6101488183610234565b81019061026c565b908380610123565b81513d85823e3d90fd5b92939050346100dd5760203660031901126100dd5780356001600160a01b03811691908290036101dd576327e235e360e01b845283015282826024816108045afa918215610158578361012f94936101c057505051918291826101e1565b6101d59293503d8091833e6101488183610234565b903880610123565b8480fd5b60208082019080835283518092528060408094019401926000905b83821061020b57505050505090565b845180516001600160a01b031687528301518684015294850194938201936001909101906101fc565b90601f8019910116810190811067ffffffffffffffff82111761025657604052565b634e487b7160e01b600052604160045260246000fd5b6020808284031261032857815167ffffffffffffffff9283821161032857019083601f83011215610328578151838111610256576040938451956102b5848460051b0188610234565b828752838088019360061b86010194818611610328578401925b8584106102e0575050505050505090565b8684830312610328578651908782018281108582111761032d5788528451906001600160a01b0382168203610328578287928a945282870151838201528152019301926102cf565b600080fd5b60246000634e487b7160e01b81526041600452fdfea2646970667358221220069405aa45fc21b29f725237543db2b8600a62c69a04e7a4c44dce45d314303e64736f6c63430008140033",
"deployedBytecode": "0x60806040908082526004918236101561001757600080fd5b600091823560e01c90816389129c681461016257508063acab2f94146100e95763bba60ca01461004657600080fd5b346100e557602092836003193601126100e15780356001600160a01b038116908190036100dd578251631890039360e21b81529182015283816024816108045afa9283156100d257809361009d575b505051908152f35b909192508382813d83116100cb575b6100b68183610234565b810103126100c8575051903880610095565b80fd5b503d6100ac565b8251903d90823e3d90fd5b8380fd5b8280fd5b5080fd5b508290346100e157826003193601126100e15782815180936318160ddd60e01b8252816108045afa918215610158578361012f9493610133575b505051918291826101e1565b0390f35b6101509293503d8091833e6101488183610234565b81019061026c565b908380610123565b81513d85823e3d90fd5b92939050346100dd5760203660031901126100dd5780356001600160a01b03811691908290036101dd576327e235e360e01b845283015282826024816108045afa918215610158578361012f94936101c057505051918291826101e1565b6101d59293503d8091833e6101488183610234565b903880610123565b8480fd5b60208082019080835283518092528060408094019401926000905b83821061020b57505050505090565b845180516001600160a01b031687528301518684015294850194938201936001909101906101fc565b90601f8019910116810190811067ffffffffffffffff82111761025657604052565b634e487b7160e01b600052604160045260246000fd5b6020808284031261032857815167ffffffffffffffff9283821161032857019083601f83011215610328578151838111610256576040938451956102b5848460051b0188610234565b828752838088019360061b86010194818611610328578401925b8584106102e0575050505050505090565b8684830312610328578651908782018281108582111761032d5788528451906001600160a01b0382168203610328578287928a945282870151838201528152019301926102cf565b600080fd5b60246000634e487b7160e01b81526041600452fdfea2646970667358221220069405aa45fc21b29f725237543db2b8600a62c69a04e7a4c44dce45d314303e64736f6c63430008140033",
"bytecode": "0x608080604052346100165761045b908161001c8239f35b600080fdfe60806040908082526004908136101561001757600080fd5b600090813560e01c90816389129c681461024557508063acab2f94146101cd578063bba60ca01461012b5763d47052031461005157600080fd5b34610128576020366003190112610128575a90835192602084016318160ddd60e01b81528185528585019467ffffffffffffffff95818110878211176101155787525183918291906108048535fa933d1561010f573d9081116100fc578551906100c5601f8201601f191660200183610317565b81528260203d92013e5b5a83039283116100e9575050825191151582526020820152f35b634e487b7160e01b825260119052602490fd5b634e487b7160e01b835260418252602483fd5b506100cf565b634e487b7160e01b855260418452602485fd5b80fd5b5082346101c957602092836003193601126101c55780356001600160a01b038116908190036101c1578251631890039360e21b81529182015283816024816108045afa9283156101b6578093610184575b505051908152f35b909192508382813d83116101af575b61019d8183610317565b8101031261012857505190838061017c565b503d610193565b8251903d90823e3d90fd5b8380fd5b8280fd5b5080fd5b5091346101c557826003193601126101c55782815180936318160ddd60e01b8252816108045afa91821561023b57836102129493610216575b505051918291826102c4565b0390f35b6102339293503d8091833e61022b8183610317565b81019061034f565b903880610206565b81513d85823e3d90fd5b90508383346101c15760203660031901126101c15780356001600160a01b03811691908290036102c0576327e235e360e01b845283015282826024816108045afa91821561023b578361021294936102a357505051918291826102c4565b6102b89293503d8091833e61022b8183610317565b908380610206565b8480fd5b60208082019080835283518092528060408094019401926000905b8382106102ee57505050505090565b845180516001600160a01b031687528301518684015294850194938201936001909101906102df565b90601f8019910116810190811067ffffffffffffffff82111761033957604052565b634e487b7160e01b600052604160045260246000fd5b6020808284031261040b57815167ffffffffffffffff9283821161040b57019083601f8301121561040b57815183811161033957604093845195610398848460051b0188610317565b828752838088019360061b8601019481861161040b578401925b8584106103c3575050505050505090565b868483031261040b57865190878201828110858211176104105788528451906001600160a01b038216820361040b578287928a945282870151838201528152019301926103b2565b600080fd5b60246000634e487b7160e01b81526041600452fdfea26469706673582212206291c412c52d95f56ebd6bcace337eb3149a918c5ed8398ed4845c859755136a64736f6c63430008140033",
"deployedBytecode": "0x60806040908082526004908136101561001757600080fd5b600090813560e01c90816389129c681461024557508063acab2f94146101cd578063bba60ca01461012b5763d47052031461005157600080fd5b34610128576020366003190112610128575a90835192602084016318160ddd60e01b81528185528585019467ffffffffffffffff95818110878211176101155787525183918291906108048535fa933d1561010f573d9081116100fc578551906100c5601f8201601f191660200183610317565b81528260203d92013e5b5a83039283116100e9575050825191151582526020820152f35b634e487b7160e01b825260119052602490fd5b634e487b7160e01b835260418252602483fd5b506100cf565b634e487b7160e01b855260418452602485fd5b80fd5b5082346101c957602092836003193601126101c55780356001600160a01b038116908190036101c1578251631890039360e21b81529182015283816024816108045afa9283156101b6578093610184575b505051908152f35b909192508382813d83116101af575b61019d8183610317565b8101031261012857505190838061017c565b503d610193565b8251903d90823e3d90fd5b8380fd5b8280fd5b5080fd5b5091346101c557826003193601126101c55782815180936318160ddd60e01b8252816108045afa91821561023b57836102129493610216575b505051918291826102c4565b0390f35b6102339293503d8091833e61022b8183610317565b81019061034f565b903880610206565b81513d85823e3d90fd5b90508383346101c15760203660031901126101c15780356001600160a01b03811691908290036102c0576327e235e360e01b845283015282826024816108045afa91821561023b578361021294936102a357505051918291826102c4565b6102b89293503d8091833e61022b8183610317565b908380610206565b8480fd5b60208082019080835283518092528060408094019401926000905b8382106102ee57505050505090565b845180516001600160a01b031687528301518684015294850194938201936001909101906102df565b90601f8019910116810190811067ffffffffffffffff82111761033957604052565b634e487b7160e01b600052604160045260246000fd5b6020808284031261040b57815167ffffffffffffffff9283821161040b57019083601f8301121561040b57815183811161033957604093845195610398848460051b0188610317565b828752838088019360061b8601019481861161040b578401925b8584106103c3575050505050505090565b868483031261040b57865190878201828110858211176104105788528451906001600160a01b038216820361040b578287928a945282870151838201528152019301926103b2565b600080fd5b60246000634e487b7160e01b81526041600452fdfea26469706673582212206291c412c52d95f56ebd6bcace337eb3149a918c5ed8398ed4845c859755136a64736f6c63430008140033",
"linkReferences": {},
"deployedLinkReferences": {}
}
10 changes: 10 additions & 0 deletions precompiles/bank/testdata/BankCaller.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,14 @@ contract BankCaller {
function callSupplyOf(address erc20Address) external view returns (uint256) {
return IBANK_CONTRACT.supplyOf(erc20Address);
}

// Calls totalSupply with explicit gas forwarding and measures the gas consumed
// by the inner call. Returns whether the call succeeded and the actual gas used.
function callTotalSupplyWithGas(uint256 gasForward) external view returns (bool success, uint256 innerGasUsed) {
uint256 gasBefore = gasleft();
(success, ) = IBANK_PRECOMPILE_ADDRESS.staticcall{gas: gasForward}(
abi.encodeWithSelector(IBank.totalSupply.selector)
);
innerGasUsed = gasBefore - gasleft();
}
}
55 changes: 50 additions & 5 deletions precompiles/common/balance_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,27 +98,70 @@ func TestParseAddress(t *testing.T) {
func TestParseAmount(t *testing.T) {
testCases := []struct {
name string
chainID testconstants.ChainID
maleate func() sdk.Event
expAmt *uint256.Int
expError bool
}{
{
name: "valid amount",
name: "valid amount",
chainID: testconstants.ExampleChainID,
maleate: func() sdk.Event {
coinStr := sdk.NewCoins(sdk.NewInt64Coin(evmtypes.GetEVMCoinDenom(), 5)).String()
return sdk.NewEvent("bank", sdk.NewAttribute(sdk.AttributeKeyAmount, coinStr))
},
expAmt: uint256.NewInt(5),
},
{
name: "missing amount",
name: "unrelated denom is ignored",
chainID: testconstants.ExampleChainID,
maleate: func() sdk.Event {
coinStr := sdk.NewCoins(sdk.NewInt64Coin("foobar", 7)).String()
return sdk.NewEvent("bank", sdk.NewAttribute(sdk.AttributeKeyAmount, coinStr))
},
expAmt: uint256.NewInt(0),
},
{
name: "base denom is scaled to 18 decimals",
chainID: testconstants.SixDecimalsChainID,
maleate: func() sdk.Event {
coinStr := sdk.NewCoins(sdk.NewInt64Coin(evmtypes.GetEVMCoinDenom(), 100)).String()
return sdk.NewEvent("bank", sdk.NewAttribute(sdk.AttributeKeyAmount, coinStr))
},
expAmt: uint256.NewInt(100_000_000_000_000),
},
{
name: "extended denom is taken as is",
chainID: testconstants.SixDecimalsChainID,
maleate: func() sdk.Event {
coinStr := sdk.NewCoins(sdk.NewInt64Coin(evmtypes.GetEVMCoinExtendedDenom(), 500)).String()
return sdk.NewEvent("bank", sdk.NewAttribute(sdk.AttributeKeyAmount, coinStr))
},
expAmt: uint256.NewInt(500),
},
{
name: "base and extended denoms are summed",
chainID: testconstants.SixDecimalsChainID,
maleate: func() sdk.Event {
coinStr := sdk.NewCoins(
sdk.NewInt64Coin(evmtypes.GetEVMCoinDenom(), 100),
sdk.NewInt64Coin(evmtypes.GetEVMCoinExtendedDenom(), 500),
).String()
return sdk.NewEvent("bank", sdk.NewAttribute(sdk.AttributeKeyAmount, coinStr))
},
expAmt: uint256.NewInt(100_000_000_000_500),
},
{
name: "missing amount",
chainID: testconstants.ExampleChainID,
maleate: func() sdk.Event {
return sdk.NewEvent("bank")
},
expError: true,
},
{
name: "invalid coins",
name: "invalid coins",
chainID: testconstants.ExampleChainID,
maleate: func() sdk.Event {
return sdk.NewEvent("bank", sdk.NewAttribute(sdk.AttributeKeyAmount, "invalid"))
},
Expand All @@ -128,7 +171,9 @@ func TestParseAmount(t *testing.T) {

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
setupBalanceHandlerTest(t)
configurator := evmtypes.NewEVMConfigurator()
configurator.ResetTestConfig()
require.NoError(t, configurator.WithEVMCoinInfo(testconstants.ExampleChainCoinInfo[tc.chainID]).Configure())

amt, err := cmn.ParseAmount(tc.maleate())
if tc.expError {
Expand All @@ -137,7 +182,7 @@ func TestParseAmount(t *testing.T) {
}

require.NoError(t, err)
require.True(t, amt.Eq(tc.expAmt))
require.Equal(t, tc.expAmt.String(), amt.String())
})
}
}
Expand Down
Loading
Loading