Standard ERC20 token functionality for native Cosmos tokens
totalSupply
import { ethers } from "ethers";
// The address of the ERC20 precompile is dynamic, assigned when a token pair is registered.
const erc20PrecompileAddress = "<ERC20_PRECOMPILE_ADDRESS>";
const provider = new ethers.JsonRpcProvider("<RPC_URL>");
// A generic ERC20 ABI is sufficient for read-only calls
const erc20Abi = ["function totalSupply() view returns (uint256)"];
const contract = new ethers.Contract(erc20PrecompileAddress, erc20Abi, 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
import { ethers } from "ethers";
const erc20PrecompileAddress = "<ERC20_PRECOMPILE_ADDRESS>";
const accountAddress = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"; // Placeholder
const provider = new ethers.JsonRpcProvider("<RPC_URL>");
const erc20Abi = ["function balanceOf(address account) view returns (uint256)"];
const contract = new ethers.Contract(erc20PrecompileAddress, erc20Abi, 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
transferFrom
approve
allowance
import { ethers } from "ethers";
const erc20PrecompileAddress = "<ERC20_PRECOMPILE_ADDRESS>";
const ownerAddress = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"; // Placeholder
const spenderAddress = "0x27f320b7280911c7987a421a8138997a48d4b315"; // Placeholder
const provider = new ethers.JsonRpcProvider("<RPC_URL>");
const erc20Abi = ["function allowance(address owner, address spender) view returns (uint256)"];
const contract = new ethers.Contract(erc20PrecompileAddress, erc20Abi, 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
import { ethers } from "ethers";
const erc20PrecompileAddress = "<ERC20_PRECOMPILE_ADDRESS>";
const provider = new ethers.JsonRpcProvider("<RPC_URL>");
const erc20Abi = ["function name() view returns (string)"];
const contract = new ethers.Contract(erc20PrecompileAddress, erc20Abi, 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
import { ethers } from "ethers";
const erc20PrecompileAddress = "<ERC20_PRECOMPILE_ADDRESS>";
const provider = new ethers.JsonRpcProvider("<RPC_URL>");
const erc20Abi = ["function symbol() view returns (string)"];
const contract = new ethers.Contract(erc20PrecompileAddress, erc20Abi, 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
import { ethers } from "ethers";
const erc20PrecompileAddress = "<ERC20_PRECOMPILE_ADDRESS>";
const provider = new ethers.JsonRpcProvider("<RPC_URL>");
const erc20Abi = ["function decimals() view returns (uint8)"];
const contract = new ethers.Contract(erc20PrecompileAddress, erc20Abi, 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();
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
{
"_format": "hh-sol-artifact-1",
"contractName": "IERC20MetadataAllowance",
"sourceName": "solidity/precompiles/erc20/IERC20MetadataAllowance.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": "from", "type": "address" }, { "indexed": true, "internalType": "address", "name": "to", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "value", "type": "uint256" } ], "name": "Transfer", "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": "decimals", "outputs": [ { "internalType": "uint8", "name": "", "type": "uint8" } ], "stateMutability": "view", "type": "function" },
{ "inputs": [ { "internalType": "address", "name": "spender", "type": "address" }, { "internalType": "uint256", "name": "subtractedValue", "type": "uint256" } ], "name": "decreaseAllowance", "outputs": [ { "internalType": "bool", "name": "approved", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" },
{ "inputs": [ { "internalType": "address", "name": "spender", "type": "address" }, { "internalType": "uint256", "name": "addedValue", "type": "uint256" } ], "name": "increaseAllowance", "outputs": [ { "internalType": "bool", "name": "approved", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" },
{ "inputs": [], "name": "name", "outputs": [ { "internalType": "string", "name": "", "type": "string" } ], "stateMutability": "view", "type": "function" },
{ "inputs": [], "name": "symbol", "outputs": [ { "internalType": "string", "name": "", "type": "string" } ], "stateMutability": "view", "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" }
]
}
Was this page helpful?