erc20

package
v0.4.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 14, 2025 License: Apache-2.0 Imports: 26 Imported by: 0

README

ERC20 Precompile

The ERC20 precompile enables native Cosmos SDK coins to be accessed and managed through the standard ERC20 token interface within the EVM. This allows smart contracts to interact with native tokens using familiar ERC20 methods.

Interface

The precompile implements the standard ERC20 interface with additional metadata support:

IERC20 Methods
// Query Methods
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);

// Transaction Methods
function transfer(address to, uint256 amount) external returns (bool);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
IERC20Metadata Methods
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);

Gas Costs

The following gas costs are charged for each method:

Method Gas Cost
transfer 9,000
transferFrom 30,500
approve 8,100
name 3,421
symbol 3,464
decimals 427
totalSupply 2,480
balanceOf 2,870
allowance 3,225

Implementation Details

Token Pair Mapping

Each ERC20 precompile instance is associated with a TokenPair that links:

  • A Cosmos SDK denomination (e.g., uatom)
  • An ERC20 contract address

The precompile address is determined by the token pair configuration.

Transfer Mechanism
  • Direct transfers (transfer): Execute a bank send message from the caller to the recipient
  • Delegated transfers (transferFrom):
    • Check and update the spender's allowance
    • Execute a bank send message from the token owner to the recipient
    • Emit both Transfer and Approval events
Metadata Handling

Token metadata is resolved in the following priority:

  1. Bank module metadata (if registered)
  2. IBC voucher base denomination (for IBC tokens)
  3. Inferred from denomination (e.g., uatom → name: "Atom", symbol: "ATOM", decimals: 6)
Balance Integration

The precompile integrates with the Cosmos SDK bank module:

  • Balances are read directly from the bank keeper
  • Transfers use bank send messages for state changes
  • Special handling for the EVM native token (18 decimal conversion)
Error Handling
  • Prevents receiving funds directly to the precompile address
  • Validates transfer amounts and allowances
  • Converts bank module errors to ERC20-compatible errors

Events

The precompile emits standard ERC20 events:

event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);

Security Considerations

  1. No Direct Funding: The precompile cannot receive funds through msg.value to prevent loss of funds
  2. Allowance Management: Follows the standard ERC20 allowance pattern with proper checks
  3. Balance Consistency: All balance changes go through the bank module ensuring consistency

Usage Example

// Assuming the precompile is deployed at a specific address for a native token
IERC20 token = IERC20(0x...); // Precompile address for the native token

// Check balance
uint256 balance = token.balanceOf(msg.sender);

// Transfer tokens
token.transfer(recipient, 1000000); // Transfer 1 token (assuming 6 decimals)

// Approve and transferFrom
token.approve(spender, 5000000);
// Spender can now call:
token.transferFrom(owner, recipient, 3000000);

Integration Notes

  • The precompile is automatically available for registered token pairs
  • Smart contracts can interact with native tokens without wrapping
  • Full compatibility with existing ERC20 tooling and libraries

Documentation

Index

Constants

View Source
const (
	GasTransfer     = 9_000
	GasTransferFrom = 30_500
	GasApprove      = 8_100
	GasName         = 3_421
	GasSymbol       = 3_464
	GasDecimals     = 427
	GasTotalSupply  = 2_480
	GasBalanceOf    = 2_870
	GasAllowance    = 3_225
)
View Source
const (
	ErrIntegerOverflow           = "amount %s causes integer overflow"
	ErrInvalidOwner              = "invalid from address: %s"
	ErrInvalidReceiver           = "invalid to address: %s"
	ErrNoAllowanceForToken       = "allowance for token %s does not exist"
	ErrSubtractMoreThanAllowance = "subtracted value cannot be greater than existing allowance for denom %s: %s > %s"
	ErrCannotReceiveFunds        = "cannot receive funds, received: %s"
)

Errors that have formatted information are defined here as a string.

View Source
const (
	// EventTypeTransfer defines the event type for the ERC-20 Transfer and TransferFrom transactions.
	EventTypeTransfer = "Transfer"

	// EventTypeApproval defines the event type for the ERC-20 Approval event.
	EventTypeApproval = "Approval"
)
View Source
const (
	// NameMethod defines the ABI method name for the ERC-20 Name
	// query.
	NameMethod = "name"
	// SymbolMethod defines the ABI method name for the ERC-20 Symbol
	// query.
	SymbolMethod = "symbol"
	// DecimalsMethod defines the ABI method name for the ERC-20 Decimals
	// query.
	DecimalsMethod = "decimals"
	// TotalSupplyMethod defines the ABI method name for the ERC-20 TotalSupply
	// query.
	TotalSupplyMethod = "totalSupply"
	// BalanceOfMethod defines the ABI method name for the ERC-20 BalanceOf
	// query.
	BalanceOfMethod = "balanceOf"
	// AllowanceMethod defines the ABI method name for the Allowance
	// query.
	AllowanceMethod = "allowance"
)
View Source
const (
	// TransferMethod defines the ABI method name for the ERC-20 transfer
	// transaction.
	TransferMethod = "transfer"
	// TransferFromMethod defines the ABI method name for the ERC-20 transferFrom
	// transaction.
	TransferFromMethod = "transferFrom"
	// ApproveMethod defines the ABI method name for ERC-20 Approve
	// transaction.
	ApproveMethod = "approve"
	// DecreaseAllowanceMethod defines the ABI method name for the DecreaseAllowance
	// transaction.
	DecreaseAllowanceMethod = "decreaseAllowance"
	// IncreaseAllowanceMethod defines the ABI method name for the IncreaseAllowance
	// transaction.
	IncreaseAllowanceMethod = "increaseAllowance"
)

Variables

View Source
var (
	// Precompile errors
	ErrDecreaseNonPositiveValue = errors.New("cannot decrease allowance with non-positive values")
	ErrIncreaseNonPositiveValue = errors.New("cannot increase allowance with non-positive values")
	ErrNegativeAmount           = errors.New("cannot approve negative values")
	ErrSpenderIsOwner           = errors.New("spender cannot be the owner")

	// ERC20 errors
	ErrDecreasedAllowanceBelowZero  = errors.New("ERC20: decreased allowance below zero")
	ErrInsufficientAllowance        = errors.New("ERC20: insufficient allowance")
	ErrTransferAmountExceedsBalance = errors.New("ERC20: transfer amount exceeds balance")
)

Functions

func ConvertErrToERC20Error

func ConvertErrToERC20Error(err error) error

ConvertErrToERC20Error is a helper function which maps errors raised by the Cosmos SDK stack to the corresponding errors which are raised by an ERC20 contract.

TODO: Create the full RevertError types instead of just the standard error type.

TODO: Return ERC-6093 compliant errors.

func ParseAllowanceArgs

func ParseAllowanceArgs(args []interface{}) (
	owner, spender common.Address, err error,
)

ParseAllowanceArgs parses the allowance arguments and returns the owner and the spender addresses.

func ParseApproveArgs

func ParseApproveArgs(args []interface{}) (
	spender common.Address, amount *big.Int, err error,
)

ParseApproveArgs parses the approval arguments and returns the spender address and amount.

func ParseBalanceOfArgs

func ParseBalanceOfArgs(args []interface{}) (common.Address, error)

ParseBalanceOfArgs parses the balanceOf arguments and returns the account address.

func ParseTransferArgs

func ParseTransferArgs(args []interface{}) (
	to common.Address, amount *big.Int, err error,
)

ParseTransferArgs parses the arguments from the transfer method and returns the destination address (to) and amount.

func ParseTransferFromArgs

func ParseTransferFromArgs(args []interface{}) (
	from, to common.Address, amount *big.Int, err error,
)

ParseTransferFromArgs parses the arguments from the transferFrom method and returns the sender address (from), destination address (to) and amount.

Types

type Erc20Keeper added in v0.2.0

type Erc20Keeper interface {
	GetAllowance(ctx sdk.Context, erc20 common.Address, owner common.Address, spender common.Address) (*big.Int, error)
	SetAllowance(ctx sdk.Context, erc20 common.Address, owner common.Address, spender common.Address, value *big.Int) error
	DeleteAllowance(ctx sdk.Context, erc20 common.Address, owner common.Address, spender common.Address) error
}

revive:disable-next-line exported

type EventApproval

type EventApproval struct {
	Owner   common.Address
	Spender common.Address
	Value   *big.Int
}

EventApproval defines the event data for the ERC20 Approval events.

type EventTransfer

type EventTransfer struct {
	From  common.Address
	To    common.Address
	Value *big.Int
}

EventTransfer defines the event data for the ERC20 Transfer events.

type MsgServer added in v0.3.0

type MsgServer struct {
	cmn.BankKeeper
}

func NewMsgServerImpl added in v0.3.0

func NewMsgServerImpl(keeper cmn.BankKeeper) *MsgServer

NewMsgServerImpl returns an implementation of the bank MsgServer interface for the provided Keeper.

func (MsgServer) Send added in v0.3.0

func (m MsgServer) Send(goCtx context.Context, msg *banktypes.MsgSend) error

type Precompile

type Precompile struct {
	cmn.Precompile

	// BankKeeper is a public field so that the werc20 precompile can use it.
	BankKeeper cmn.BankKeeper
	// contains filtered or unexported fields
}

Precompile defines the precompiled contract for ERC-20.

func NewPrecompile

func NewPrecompile(
	tokenPair erc20types.TokenPair,
	bankKeeper cmn.BankKeeper,
	erc20Keeper Erc20Keeper,
	transferKeeper transferkeeper.Keeper,
) (*Precompile, error)

NewPrecompile creates a new ERC-20 Precompile instance as a PrecompiledContract interface.

func (Precompile) Allowance

func (p Precompile) Allowance(
	ctx sdk.Context,
	_ *vm.Contract,
	_ vm.StateDB,
	method *abi.Method,
	args []interface{},
) ([]byte, error)

Allowance returns the remaining allowance of a spender for a given owner.

func (Precompile) Approve

func (p Precompile) Approve(
	ctx sdk.Context,
	contract *vm.Contract,
	stateDB vm.StateDB,
	method *abi.Method,
	args []interface{},
) ([]byte, error)

Approve sets the given amount as the allowance of the spender address over the caller’s tokens. It returns a boolean value indicating whether the operation succeeded and emits the Approval event on success.

The Approve method handles 4 cases:

  1. no allowance, amount negative -> return error
  2. no allowance, amount positive -> create a new allowance
  3. allowance exists, amount 0 or negative -> delete allowance
  4. allowance exists, amount positive -> update allowance
  5. no allowance, amount 0 -> no-op but still emit Approval event

func (Precompile) BalanceOf

func (p Precompile) BalanceOf(
	ctx sdk.Context,
	_ *vm.Contract,
	_ vm.StateDB,
	method *abi.Method,
	args []interface{},
) ([]byte, error)

BalanceOf returns the amount of tokens owned by account. It fetches the balance of the coin from the bank keeper and returns zero if not found.

func (Precompile) Decimals

func (p Precompile) Decimals(
	ctx sdk.Context,
	_ *vm.Contract,
	_ vm.StateDB,
	method *abi.Method,
	_ []interface{},
) ([]byte, error)

Decimals returns the decimals places of the token. If the token metadata is registered in the bank module, it returns the display denomination exponent. Otherwise, it infers the decimal value from the first character of the base denomination (e.g. uatom -> 6).

func (Precompile) EmitApprovalEvent

func (p Precompile) EmitApprovalEvent(ctx sdk.Context, stateDB vm.StateDB, owner, spender common.Address, value *big.Int) error

EmitApprovalEvent creates a new approval event emitted on Approve transactions.

func (Precompile) EmitTransferEvent

func (p Precompile) EmitTransferEvent(ctx sdk.Context, stateDB vm.StateDB, from, to common.Address, value *big.Int) error

EmitTransferEvent creates a new Transfer event emitted on transfer and transferFrom transactions.

func (*Precompile) HandleMethod

func (p *Precompile) HandleMethod(
	ctx sdk.Context,
	contract *vm.Contract,
	stateDB vm.StateDB,
	method *abi.Method,
	args []interface{},
) (bz []byte, err error)

HandleMethod handles the execution of each of the ERC-20 methods.

func (Precompile) IsTransaction

func (Precompile) IsTransaction(method *abi.Method) bool

IsTransaction checks if the given method name corresponds to a transaction or query.

func (Precompile) Name

func (p Precompile) Name(
	ctx sdk.Context,
	_ *vm.Contract,
	_ vm.StateDB,
	method *abi.Method,
	_ []interface{},
) ([]byte, error)

Name returns the name of the token. If the token metadata is registered in the bank module, it returns its name. Otherwise, it returns the base denomination of the token capitalized (e.g. uatom -> Atom).

func (Precompile) RequiredGas

func (p Precompile) RequiredGas(input []byte) uint64

RequiredGas calculates the contract gas used for the

func (Precompile) Run

func (p Precompile) Run(evm *vm.EVM, contract *vm.Contract, readOnly bool) (bz []byte, err error)

Run executes the precompiled contract ERC-20 methods defined in the ABI.

func (Precompile) Symbol

func (p Precompile) Symbol(
	ctx sdk.Context,
	_ *vm.Contract,
	_ vm.StateDB,
	method *abi.Method,
	_ []interface{},
) ([]byte, error)

Symbol returns the symbol of the token. If the token metadata is registered in the bank module, it returns its symbol. Otherwise, it returns the base denomination of the token in uppercase (e.g. uatom -> ATOM).

func (Precompile) TotalSupply

func (p Precompile) TotalSupply(
	ctx sdk.Context,
	_ *vm.Contract,
	_ vm.StateDB,
	method *abi.Method,
	_ []interface{},
) ([]byte, error)

TotalSupply returns the amount of tokens in existence. It fetches the supply of the coin from the bank keeper and returns zero if not found.

func (*Precompile) Transfer

func (p *Precompile) Transfer(
	ctx sdk.Context,
	contract *vm.Contract,
	stateDB vm.StateDB,
	method *abi.Method,
	args []interface{},
) ([]byte, error)

Transfer executes a direct transfer from the caller address to the destination address.

func (*Precompile) TransferFrom

func (p *Precompile) TransferFrom(
	ctx sdk.Context,
	contract *vm.Contract,
	stateDB vm.StateDB,
	method *abi.Method,
	args []interface{},
) ([]byte, error)

TransferFrom executes a transfer on behalf of the specified from address in the call data to the destination address.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL