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
6 changes: 5 additions & 1 deletion .github/workflows/test.yml
Comment thread
jhelison marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ jobs:
if: env.GIT_DIFF
run: |
go test -v -coverprofile=profile.txt -covermode=atomic -coverpkg=./... $(go list ./... | grep -v -e '/tests/e2e' | grep -v -e '/tests/interchain') | grep -v "store.go:" | grep -v "mutable_tree.go:"
- name: wasmbinding coverage (test build tag)
if: env.GIT_DIFF
run: |
go test -tags=test -coverprofile=profile-wasmbinding.txt -covermode=atomic ./wasmbinding/...
- uses: actions/upload-artifact@v4
if: env.GIT_DIFF
with:
Expand All @@ -56,7 +60,7 @@ jobs:
uses: codecov/codecov-action@v4
if: env.GIT_DIFF
with:
files: ./profile.txt
files: ./profile.txt,./profile-wasmbinding.txt
token: ${{ secrets.CODECOV_TOKEN }}

repo-analysis:
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
- Indexed admins to reduce query space on tokenfactory denom queries
- Fix native token supply inflation from the stateful precompiles by wrapping the account address codec (`evmAddressCodec`) to reject non-20-byte accounts (e.g. a 32-byte bech32 withdraw, module, or CosmWasm contract address) at decode time, preventing such addresses from being truncated and minted a duplicate balance when mirrored into the EVM StateDB
- Close governance vote minimum-stake bypass in `GovVoteDecorator` by enforcing the stake check on `MsgVoteWeighted` (`govv1` and `govv1beta1`) and recursing into nested `authz.MsgExec` messages so wrapped votes can no longer skip the requirement
- Bound the CosmWasm→EVM ERC20 query bindings (`wasmbinding/evm/queries.go`) by routing every internal sub-call (`decimals`, `name`, `symbol`, `totalSupply`, `balanceOf`, `allowance`) through a helper that passes the transaction's remaining SDK gas as the `gasCap` and charges the SDK gas meter for the actual pre-refund EVM work (`MaxUsedGas`), so a malicious ERC20 can no longer force repeatable, undercharged internal EVM execution

### Removed

Expand Down
29 changes: 23 additions & 6 deletions wasmbinding/evm/queries.go
Comment thread
mattkii marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"math/big"

wasmvmtypes "github.com/CosmWasm/wasmvm/v3/types"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/vm"
Expand Down Expand Up @@ -139,7 +140,7 @@ func (qp *QueryPlugin) HandleERC20Information(ctx sdk.Context, call *evmbindingt
stateDB := statedb.New(ctx, qp.evmKeeper, statedb.NewEmptyTxConfig())

// Query the decimals
callRes, err := qp.evmKeeper.CallEVM(ctx, stateDB, erc20ABI, erc20types.ModuleAddress, to, false, false, nil, "decimals")
callRes, err := qp.callERC20Query(ctx, stateDB, erc20ABI, to, "decimals")
if err != nil {
return nil, err
}
Expand All @@ -156,7 +157,7 @@ func (qp *QueryPlugin) HandleERC20Information(ctx sdk.Context, call *evmbindingt
res.Decimals = decimals

// Query the name
callRes, err = qp.evmKeeper.CallEVM(ctx, stateDB, erc20ABI, erc20types.ModuleAddress, to, false, false, nil, "name")
callRes, err = qp.callERC20Query(ctx, stateDB, erc20ABI, to, "name")
if err != nil {
return nil, err
}
Expand All @@ -173,7 +174,7 @@ func (qp *QueryPlugin) HandleERC20Information(ctx sdk.Context, call *evmbindingt
res.Name = name

// Query the symbol
callRes, err = qp.evmKeeper.CallEVM(ctx, stateDB, erc20ABI, erc20types.ModuleAddress, to, false, false, nil, "symbol")
callRes, err = qp.callERC20Query(ctx, stateDB, erc20ABI, to, "symbol")
if err != nil {
return nil, err
}
Expand All @@ -190,7 +191,7 @@ func (qp *QueryPlugin) HandleERC20Information(ctx sdk.Context, call *evmbindingt
res.Symbol = symbol

// Query the total supply
callRes, err = qp.evmKeeper.CallEVM(ctx, stateDB, erc20ABI, erc20types.ModuleAddress, to, false, false, nil, "totalSupply")
callRes, err = qp.callERC20Query(ctx, stateDB, erc20ABI, to, "totalSupply")
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -221,7 +222,7 @@ func (qp *QueryPlugin) HandleERC20Balance(ctx sdk.Context, call *evmbindingtypes
stateDB := statedb.New(ctx, qp.evmKeeper, statedb.NewEmptyTxConfig())

// Query the balance
callRes, err := qp.evmKeeper.CallEVM(ctx, stateDB, erc20ABI, erc20types.ModuleAddress, to, false, false, nil, "balanceOf", address)
callRes, err := qp.callERC20Query(ctx, stateDB, erc20ABI, to, "balanceOf", address)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -253,7 +254,7 @@ func (qp *QueryPlugin) HandleERC20Allowance(ctx sdk.Context, call *evmbindingtyp
stateDB := statedb.New(ctx, qp.evmKeeper, statedb.NewEmptyTxConfig())

// Query the allowance
callRes, err := qp.evmKeeper.CallEVM(ctx, stateDB, erc20ABI, erc20types.ModuleAddress, to, false, false, nil, "allowance", owner, spender)
callRes, err := qp.callERC20Query(ctx, stateDB, erc20ABI, to, "allowance", owner, spender)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -303,6 +304,22 @@ func handleRevertError(vmError string, ret []byte) error {
return nil
}

// callERC20Query runs an internal ERC20 EVM query and charges the SDK gas meter for the actual pre-refund work
func (qp *QueryPlugin) callERC20Query(ctx sdk.Context, stateDB *statedb.StateDB, erc20ABI abi.ABI, contract common.Address, method string, args ...interface{}) (*evmtypes.MsgEthereumTxResponse, error) {
gasCap := new(big.Int).SetUint64(ctx.GasMeter().GasRemaining())

res, err := qp.evmKeeper.CallEVM(ctx, stateDB, erc20ABI, erc20types.ModuleAddress, contract, false, false, gasCap, method, args...)
if err != nil {
return nil, err
}

if res.MaxUsedGas > res.GasUsed {
ctx.GasMeter().ConsumeGas(res.MaxUsedGas-res.GasUsed, "evm query pre-refund work")
}

return res, nil
}

// buildEthCallRequest builds the EVM query call
func buildEthCallRequest(ctx sdk.Context, to common.Address, data hexutil.Bytes, chainID *big.Int, proposer []byte) (*evmtypes.EthCallRequest, error) {
// Build the arguments
Expand Down
Loading