werc20

package
v0.5.0-rc.1 Latest Latest
Warning

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

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

README

WERC20 Precompile

The WERC20 (Wrapped ERC20) precompile provides a wrapped ERC20 interface for the native EVM token, similar to WETH on Ethereum. This allows the native token to be used in smart contracts that expect ERC20 tokens, maintaining compatibility with DeFi protocols and other ERC20-based applications.

Interface

The WERC20 precompile extends the standard ERC20 interface with additional deposit and withdraw functionality:

Inherited ERC20 Methods

All standard ERC20 and ERC20Metadata methods are available:

// ERC20 Standard Methods
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);

// ERC20 Metadata
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
WERC20 Specific Methods
// Deposit native tokens to receive wrapped tokens
function deposit() external payable;

// Withdraw wrapped tokens to receive native tokens (no-op for compatibility)
function withdraw(uint256 wad) external;

// Fallback function - calls deposit()
fallback() external payable;

// Receive function - calls deposit()
receive() external payable;

Gas Costs

Method Gas Cost
deposit 23,878
withdraw 9,207
ERC20 methods Same as ERC20 precompile

Implementation Details

Deposit Mechanism

The deposit function has a unique implementation:

  1. Accepts native tokens via msg.value
  2. Sends the native tokens back to the caller using the bank module
  3. Adjusts EVM state balances to reflect the "wrapping"
  4. Emits a Deposit event

This approach ensures that the native tokens remain in the user's account while providing the wrapped token interface.

Withdraw Mechanism

The withdraw function is implemented as a no-op that:

  1. Validates the user has sufficient balance
  2. Emits a Withdrawal event
  3. Does not actually perform any token transfers

This maintains interface compatibility with WETH-style contracts while preserving the native token functionality.

Balance Representation
  • Native token balances are automatically reflected as wrapped token balances
  • No actual wrapping occurs - the precompile provides a view over native balances
  • All ERC20 operations work on the underlying native token
Extended Precision

The precompile handles the extended precision of the native token (18 decimals in EVM vs 6 decimals in Cosmos SDK) through internal conversion.

Events

// Emitted when native tokens are "deposited"
event Deposit(address indexed dst, uint256 wad);

// Emitted when tokens are "withdrawn"
event Withdrawal(address indexed src, uint256 wad);

// 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 Lock-up: Native tokens are never locked in the precompile
  2. Direct Integration: Operations directly interact with the bank module
  3. Balance Consistency: EVM and Cosmos SDK balances remain synchronized
  4. Fallback Protection: Sending native tokens to the contract automatically triggers deposit

Usage Examples

Basic Deposit and Transfer
// Assume WERC20 is deployed at a specific address
IWERC20 wrappedToken = IWERC20(0x...); // WERC20 precompile address

// Deposit native tokens (sent value is returned to sender as wrapped tokens)
wrappedToken.deposit{value: 1 ether}();

// Now you can use it as an ERC20 token
wrappedToken.transfer(recipient, 0.5 ether);

// Check balance
uint256 balance = wrappedToken.balanceOf(msg.sender);
Integration with DeFi Protocols
contract DeFiProtocol {
    IWERC20 public wrappedNative;
    
    constructor(address _wrappedNative) {
        wrappedNative = IWERC20(_wrappedNative);
    }
    
    function depositCollateral() external payable {
        // Automatically wraps native tokens
        wrappedNative.deposit{value: msg.value}();
        
        // Now treat it as ERC20 collateral
        // The protocol can use standard ERC20 operations
    }
    
    function swapTokens(IERC20 tokenIn, uint256 amountIn) external {
        // Works seamlessly with wrapped native tokens
        tokenIn.transferFrom(msg.sender, address(this), amountIn);
        
        // Perform swap logic...
    }
}
Fallback Handling
// Sending native tokens directly to the WERC20 address triggers deposit
address payable wrappedAddress = payable(0x...); // WERC20 address
wrappedAddress.transfer(1 ether); // Automatically deposits

Differences from Traditional WETH

  1. No Actual Wrapping: Tokens remain as native tokens, only the interface changes
  2. No Lock-up: Native tokens are not locked in the contract
  3. Automatic Reflection: Native balance changes are immediately reflected
  4. Withdraw No-op: Withdraw function exists for compatibility but doesn't transfer

Integration Notes

  • The WERC20 precompile address is determined by the token pair configuration
  • Compatible with any protocol expecting ERC20 tokens
  • Maintains full native token functionality
  • Gas costs are comparable to standard ERC20 operations
  • Ideal for DeFi protocols, DEXs, and other ERC20-based applications

Documentation

Index

Constants

View Source
const (
	// EventTypeDeposit is the key of the event type for the Deposit transaction.
	EventTypeDeposit = "Deposit"
	// EventTypeWithdrawal is the key of the event type for the Withdraw transaction.
	EventTypeWithdrawal = "Withdrawal"
)
View Source
const (
	// DepositMethod defines the ABI method name for the IWERC20 deposit
	// transaction.
	DepositMethod = "deposit"
	// WithdrawMethod defines the ABI method name for the IWERC20 withdraw
	// transaction.
	WithdrawMethod = "withdraw"
)
View Source
const (
	// DepositRequiredGas defines the gas required for the Deposit transaction.
	DepositRequiredGas uint64 = 23_878
	// WithdrawRequiredGas defines the gas required for the Withdraw transaction.
	WithdrawRequiredGas uint64 = 9207
)

Variables

View Source
var (
	ABI abi.ABI
)

Functions

This section is empty.

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
}

type Precompile

type Precompile struct {
	*erc20.Precompile
}

Precompile defines the precompiled contract for WERC20.

func NewPrecompile

func NewPrecompile(
	tokenPair erc20types.TokenPair,
	bankKeeper cmn.BankKeeper,
	erc20Keeper Erc20Keeper,
	transferKeeper ibcutils.TransferKeeper,
) *Precompile

NewPrecompile creates a new WERC20 Precompile instance implementing the PrecompiledContract interface. This type wraps around the ERC20 Precompile instance to provide additional methods.

func (Precompile) Deposit

func (p Precompile) Deposit(
	ctx sdk.Context,
	contract *vm.Contract,
	stateDB vm.StateDB,
) ([]byte, error)

Deposit handles the payable deposit function. It retrieves the deposited amount and sends it back to the sender using the bank keeper.

func (Precompile) EmitDepositEvent

func (p Precompile) EmitDepositEvent(
	ctx sdk.Context,
	stateDB vm.StateDB,
	caller common.Address,
	amount *big.Int,
) error

EmitDepositEvent creates a new Deposit event emitted after a Deposit transaction.

func (Precompile) EmitWithdrawalEvent

func (p Precompile) EmitWithdrawalEvent(
	ctx sdk.Context,
	stateDB vm.StateDB,
	src common.Address,
	amount *big.Int,
) error

EmitWithdrawalEvent creates a new Withdrawal event emitted after a Withdraw transaction.

func (Precompile) Execute added in v0.5.0

func (p Precompile) Execute(ctx sdk.Context, stateDB vm.StateDB, contract *vm.Contract, readOnly bool) ([]byte, error)

func (Precompile) IsTransaction

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

IsTransaction returns true if the given method name correspond to a transaction. Returns false otherwise.

func (Precompile) RequiredGas

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

RequiredGas calculates the contract gas use.

func (Precompile) Run

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

func (Precompile) Withdraw

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

Withdraw is a no-op and mock function that provides the same interface as the WETH contract to support equality between the native coin and its wrapped ERC-20 (e.g. ATOM and WEVMOS).

Jump to

Keyboard shortcuts

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