cosmos / cosmos/evm

Proposal: Support Solidity Custom Errors in Precompiles

Open
#954 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
164
Forks
213
Avg merge
3d 58m
Merged PRs (30d)
12

Description

## Background

Currently, the `precompiles/common` package in `cosmos/evm` always encodes revert data as `Error(string)` format when precompile execution fails (see `ReturnRevertError` function).

However, **Custom Errors** introduced in Solidity 0.8.4+ offer several advantages:
- Gas efficient (only selector is stored, no message string needed)
- Type safe (error argument types are checked)
- Distinguishable by error selector on the client side

For example
```sol
// Error.sol
error InvalidAddress(address addr);
error InvalidAmount(uint256 amount);
```
Using these custom errors allows transaction receipts to distinguish error types by selector, making debugging and error handling much easier.

## Problem
Currently, `RunNativeAction` always encodes errors as `Error(string)` format by calling `ReturnRevertError(evm, err)`:

## Proposal
Add support for custom revert data in the `precompiles/common` package:
1. Add RevertDataCarrier Interface
```go
// RevertDataCarrier is an error that carries custom ABI-encoded revert data
// (error selector + arguments) instead of a string message.
type RevertDataCarrier interface {
error
RevertData() []byte
}
```
2. Enhance ReturnRevertError
```go
func ReturnRevertError(evm *vm.EVM, err error) ([]byte, error) {
// Use custom revert data if available
if carrier, ok := err.(RevertDataCarrier); ok {
data := carrier.RevertData()
evm.Interpreter().SetReturnData(data)
return data, vm.ErrExecutionReverted
}

// Fallback to existing behavior: Error(string) format
revertReasonBz, encErr := evmtypes.RevertReasonBytes(err.Error())
if encErr != nil {
return nil, vm.ErrExecutionReverted
}
evm.Interpreter().SetReturnData(revertReasonBz)
return revertReasonBz, vm.ErrExecutionReverted
}
```

Usage Example
```go
recipientCosmosAddr, err := p.accountKeeper.AddressCodec().BytesToString(recipientEvmAddr.Bytes())
if err != nil {
// Get error definition from ABI
errorABI := abi.Errors["InvalidAddress"]

// Pack arguments
data, _ := errorABI.Inputs.Pack(recipientEvmAddr)

// selector (4 bytes) + data
revertData := append(errorABI.ID.Bytes()[:4], data...)

// Return custom revert data
return nil, &RevertWithData{Data: revertData}
}
```

## Benefits
Errors defined in the contract can be easily handled by the client or contract.
```js
const staking = await hre.ethers.getContractAt('StakingI', STAKING_PRECOMPILE_ADDRESS);

try {
await staking.connect(signer).delegate.staticCall(delegatorAddress, validatorAddress, amount, { gasLimit: GAS_LIMIT });
} catch (e) {
const revertData = e.data

// Decode revert data via contract interface (selector + args)
const decoded = staking.interface.parseError(revertData);
expect(decoded, 'revert data should decode to a known error').to.exist;
expect(decoded.name).to.equal('InvalidAddress', 'revert should be InvalidAddress');

// InvalidAddress(address addr)
const [address] = decoded.args;
expect(address.toLowerCase()).to.equal(signer.address.toLowerCase(), 'invalid address');
}
```

## Note
I've implemented this feature locally in our project by creating a `precompiles/common` package, but official support in `cosmos/evm` would be beneficial for the ecosystem. If this direction is good, I think I can easily contribute

Contributor guide

Open the contributing guide

Research direction

Start by reading the precompiles/common package, especially ReturnRevertError and the RunNativeAction call path, to understand the existing Error(string) encoding and interpreter return data handling. Add coverage for errors carrying custom ABI-encoded revert data while preserving the fallback behavior, and verify both paths return the expected data with vm.ErrExecutionReverted.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, solidity
Domain
backend-api-design, blockchain
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.