Skip to main content

Overview

The WERC20 precompile provides a standardized way to wrap native EVM tokens, allowing them to be used as ERC20-compatible tokens within smart contracts. This precompile ensures a 1:1 backing between the native token and its wrapped counterpart, enabling seamless interaction with DeFi protocols and other dApps that rely on the ERC20 standard. Precompile Address: Dynamic (assigned per wrapped token) Related Module: Token wrapping utilities

Methods

deposit

Deposits native tokens in exchange for wrapped ERC20 tokens.

withdraw

Withdraws native tokens from wrapped ERC20 tokens.

totalSupply

Returns the total supply of the wrapped token.
import { ethers } from "ethers";

// Connect to the network
const provider = new ethers.JsonRpcProvider("<RPC_URL>");

// The WERC20 precompile address is dynamically assigned
const werc20PrecompileAddress = "<WERC20_PRECOMPILE_ADDRESS>";
const werc20Abi = ["function totalSupply() view returns (uint256)"];

// Create a contract instance
const contract = new ethers.Contract(werc20PrecompileAddress, werc20Abi, provider);

async function getTotalSupply() {
  try {
    const totalSupply = await contract.totalSupply();
    console.log("Total Supply:", totalSupply.toString());
  } catch (error) {
    console.error("Error fetching total supply:", error);
  }
}

getTotalSupply();

balanceOf

Returns the wrapped token balance for a specific account.
import { ethers } from "ethers";

// Connect to the network
const provider = new ethers.JsonRpcProvider("<RPC_URL>");

// The WERC20 precompile address is dynamically assigned
const werc20PrecompileAddress = "<WERC20_PRECOMPILE_ADDRESS>";
const accountAddress = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"; // Placeholder
const werc20Abi = ["function balanceOf(address account) view returns (uint256)"];

// Create a contract instance
const contract = new ethers.Contract(werc20PrecompileAddress, werc20Abi, provider);

async function getBalance() {
  try {
    const balance = await contract.balanceOf(accountAddress);
    console.log(`Balance of ${accountAddress}:`, balance.toString());
  } catch (error) {
    console.error("Error fetching balance:", error);
  }
}

getBalance();

transfer

Transfers wrapped tokens to a specified address.

approve

Approves a spender to transfer wrapped tokens on behalf of the caller.

transferFrom

Transfers wrapped tokens from one address to another, using an allowance.

allowance

Returns the remaining amount of tokens a spender is allowed to transfer on behalf of an owner.
import { ethers } from "ethers";

// Connect to the network
const provider = new ethers.JsonRpcProvider("<RPC_URL>");

// The WERC20 precompile address is dynamically assigned
const werc20PrecompileAddress = "<WERC20_PRECOMPILE_ADDRESS>";
const ownerAddress = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"; // Placeholder
const spenderAddress = "0x27f320b7280911c7987a421a8138997a48d4b315"; // Placeholder
const werc20Abi = ["function allowance(address owner, address spender) view returns (uint256)"];

// Create a contract instance
const contract = new ethers.Contract(werc20PrecompileAddress, werc20Abi, provider);

async function getAllowance() {
  try {
    const allowance = await contract.allowance(ownerAddress, spenderAddress);
    console.log("Allowance:", allowance.toString());
  } catch (error) {
    console.error("Error fetching allowance:", error);
  }
}

getAllowance();

name

Returns the name of the wrapped token.
import { ethers } from "ethers";

// Connect to the network
const provider = new ethers.JsonRpcProvider("<RPC_URL>");

// The WERC20 precompile address is dynamically assigned
const werc20PrecompileAddress = "<WERC20_PRECOMPILE_ADDRESS>";
const werc20Abi = ["function name() view returns (string)"];

// Create a contract instance
const contract = new ethers.Contract(werc20PrecompileAddress, werc20Abi, provider);

async function getName() {
  try {
    const name = await contract.name();
    console.log("Token Name:", name);
  } catch (error) {
    console.error("Error fetching name:", error);
  }
}

getName();

symbol

Returns the symbol of the wrapped token.
import { ethers } from "ethers";

// Connect to the network
const provider = new ethers.JsonRpcProvider("<RPC_URL>");

// The WERC20 precompile address is dynamically assigned
const werc20PrecompileAddress = "<WERC20_PRECOMPILE_ADDRESS>";
const werc20Abi = ["function symbol() view returns (string)"];

// Create a contract instance
const contract = new ethers.Contract(werc20PrecompileAddress, werc20Abi, provider);

async function getSymbol() {
  try {
    const symbol = await contract.symbol();
    console.log("Token Symbol:", symbol);
  } catch (error) {
    console.error("Error fetching symbol:", error);
  }
}

getSymbol();

decimals

Returns the number of decimals used by the wrapped token.
import { ethers } from "ethers";

// Connect to the network
const provider = new ethers.JsonRpcProvider("<RPC_URL>");

// The WERC20 precompile address is dynamically assigned
const werc20PrecompileAddress = "<WERC20_PRECOMPILE_ADDRESS>";
const werc20Abi = ["function decimals() view returns (uint8)"];

// Create a contract instance
const contract = new ethers.Contract(werc20PrecompileAddress, werc20Abi, provider);

async function getDecimals() {
  try {
    const decimals = await contract.decimals();
    console.log("Token Decimals:", decimals.toString());
  } catch (error) {
    console.error("Error fetching decimals:", error);
  }
}

getDecimals();

increaseAllowance

Increases the allowance granted to a spender.

decreaseAllowance

Decreases the allowance granted to a spender.

Full Solidity Interface & ABI

WERC20 Solidity Interface
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.8.18;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/// @author The Berachain Polaris Team
/// @title WERC20 Precompile Contract
/// @dev The interface through which solidity contracts will interact with the WERC20 precompile
interface IWERC20 is IERC20 {
    /// @dev Emitted when a user deposits tokens into the contract
    /// @param dst The address of the user who deposited tokens
    /// @param wad The amount of tokens deposited
    event Deposit(address indexed dst, uint256 wad);

    /// @dev Emitted when a user withdraws tokens from the contract
    /// @param src The address of the user who withdrew tokens
    /// @param wad The amount of tokens withdrawn
    event Withdrawal(address indexed src, uint256 wad);

    /// @dev Deposits tokens into the contract
    function deposit() external payable;

    /// @dev Withdraws tokens from the contract
    /// @param wad The amount of tokens to withdraw
    function withdraw(uint256 wad) external;
}
WERC20 ABI
{
  "_format": "hh-sol-artifact-1",
  "contractName": "IWERC20",
  "sourceName": "solidity/precompiles/werc20/IWERC20.sol",
  "abi": [
    { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "owner", "type": "address" }, { "indexed": true, "internalType": "address", "name": "spender", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "value", "type": "uint256" } ], "name": "Approval", "type": "event" },
    { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "dst", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "wad", "type": "uint256" } ], "name": "Deposit", "type": "event" },
    { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "from", "type": "address" }, { "indexed": true, "internalType": "address", "name": "to", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "value", "type": "uint256" } ], "name": "Transfer", "type": "event" },
    { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "src", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "wad", "type": "uint256" } ], "name": "Withdrawal", "type": "event" },
    { "inputs": [ { "internalType": "address", "name": "owner", "type": "address" }, { "internalType": "address", "name": "spender", "type": "address" } ], "name": "allowance", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" },
    { "inputs": [ { "internalType": "address", "name": "spender", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "approve", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" },
    { "inputs": [ { "internalType": "address", "name": "account", "type": "address" } ], "name": "balanceOf", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" },
    { "inputs": [], "name": "deposit", "outputs": [], "stateMutability": "payable", "type": "function" },
    { "inputs": [], "name": "totalSupply", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" },
    { "inputs": [ { "internalType": "address", "name": "to", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "transfer", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" },
    { "inputs": [ { "internalType": "address", "name": "from", "type": "address" }, { "internalType": "address", "name": "to", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "name": "transferFrom", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" },
    { "inputs": [ { "internalType": "uint256", "name": "wad", "type": "uint256" } ], "name": "withdraw", "outputs": [], "stateMutability": "nonpayable", "type": "function" }
  ]
}