Available Precompiles:
| Precompile | Address | Description |
|---|---|---|
| Bank | 0x1001 | Query native denom balances (usei, factory tokens) |
| JSON | 0x1003 | Parse JSON data within contracts |
| Staking | 0x1005 | Delegation and staking operations |
| Governance | 0x1006 | Proposal voting |
| Distribution | 0x1007 | Claim staking rewards |
| P256 | 0x1011 | Verify P-256 elliptic curve signatures |
Setup
npm install viem ethers @sei-js/precompiles
import { createPublicClient, createWalletClient, http, custom } from 'viem';
import { sei } from 'viem/chains';
// Read-only
const client = createPublicClient({ chain: sei, transport: http() });
// Browser wallet (write)
const walletClient = createWalletClient({ chain: sei, transport: custom(window.ethereum) });
const [account] = await walletClient.getAddresses();
import { ethers } from 'ethers';
// Browser environment
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
// Node.js / scripts
// const provider = new ethers.JsonRpcProvider('https://evm-rpc.sei-apis.com');
// const signer = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
Bank Precompile
eth_getBalance only returns the EVM-side SEI balance. The Bank precompile lets you query any native denom — including factory tokens — from any address:
import { BANK_PRECOMPILE_ABI, BANK_PRECOMPILE_ADDRESS } from '@sei-js/precompiles';
// Query a specific denom balance
const balance = await client.readContract({
address: BANK_PRECOMPILE_ADDRESS,
abi: BANK_PRECOMPILE_ABI,
functionName: 'balance',
args: ['0xYourAddress', 'usei'],
});
// Query all native balances for an address
const allBalances = await client.readContract({
address: BANK_PRECOMPILE_ADDRESS,
abi: BANK_PRECOMPILE_ABI,
functionName: 'all_balances',
args: ['0xYourAddress'],
});
// Returns: [{ denom: 'usei', amount: '1000000' }, ...]
import { BANK_PRECOMPILE_ABI, BANK_PRECOMPILE_ADDRESS } from '@sei-js/precompiles';
const bank = new ethers.Contract(BANK_PRECOMPILE_ADDRESS, BANK_PRECOMPILE_ABI, provider);
const balance = await bank.balance('0xYourAddress', 'usei');
const allBalances = await bank.all_balances('0xYourAddress');
Staking Precompile
Delegate, undelegate, and query staking state:import { parseEther } from 'viem';
import { STAKING_PRECOMPILE_ABI, STAKING_PRECOMPILE_ADDRESS } from '@sei-js/precompiles';
// Delegate SEI to a validator
const delegateHash = await walletClient.writeContract({
address: STAKING_PRECOMPILE_ADDRESS,
abi: STAKING_PRECOMPILE_ABI,
functionName: 'delegate',
args: ['seivaloper1...'],
value: parseEther('10'),
});
// Query delegation
const delegation = await client.readContract({
address: STAKING_PRECOMPILE_ADDRESS,
abi: STAKING_PRECOMPILE_ABI,
functionName: 'delegation',
args: [account, 'seivaloper1...'],
});
// Undelegate 5 SEI — amount is in 6-decimal usei (1 SEI = 1_000_000 usei), not wei
const undelegateHash = await walletClient.writeContract({
address: STAKING_PRECOMPILE_ADDRESS,
abi: STAKING_PRECOMPILE_ABI,
functionName: 'undelegate',
args: ['seivaloper1...', 5_000_000n],
});
import { STAKING_PRECOMPILE_ABI, STAKING_PRECOMPILE_ADDRESS } from '@sei-js/precompiles';
const staking = new ethers.Contract(STAKING_PRECOMPILE_ADDRESS, STAKING_PRECOMPILE_ABI, signer);
const readStaking = new ethers.Contract(STAKING_PRECOMPILE_ADDRESS, STAKING_PRECOMPILE_ABI, provider);
// Delegate 10 SEI
const tx = await staking.delegate('seivaloper1...', { value: ethers.parseEther('10') });
await tx.wait();
// Query delegation
const delegation = await readStaking.delegation(await signer.getAddress(), 'seivaloper1...');
console.log('Delegated:', delegation.balance.amount.toString());
// Undelegate 5 SEI — amount is in 6-decimal usei (1 SEI = 1_000_000 usei), not wei
const undelegateTx = await staking.undelegate('seivaloper1...', ethers.parseUnits('5', 6));
await undelegateTx.wait();
Governance Precompile
Vote on an active governance proposal:import { GOVERNANCE_PRECOMPILE_ABI, GOVERNANCE_PRECOMPILE_ADDRESS } from '@sei-js/precompiles';
// Vote options: 1 = Yes, 2 = Abstain, 3 = No, 4 = NoWithVeto
const hash = await walletClient.writeContract({
address: GOVERNANCE_PRECOMPILE_ADDRESS,
abi: GOVERNANCE_PRECOMPILE_ABI,
functionName: 'vote',
args: [99n, 1],
});
import { GOVERNANCE_PRECOMPILE_ABI, GOVERNANCE_PRECOMPILE_ADDRESS } from '@sei-js/precompiles';
const governance = new ethers.Contract(GOVERNANCE_PRECOMPILE_ADDRESS, GOVERNANCE_PRECOMPILE_ABI, signer);
// 1 = Yes, 2 = Abstain, 3 = No, 4 = NoWithVeto
const tx = await governance.vote(99n, 1);
await tx.wait();
Distribution Precompile
Claim staking rewards from a validator:import { DISTRIBUTION_PRECOMPILE_ABI, DISTRIBUTION_PRECOMPILE_ADDRESS } from '@sei-js/precompiles';
const hash = await walletClient.writeContract({
address: DISTRIBUTION_PRECOMPILE_ADDRESS,
abi: DISTRIBUTION_PRECOMPILE_ABI,
functionName: 'withdrawDelegationRewards',
args: ['seivaloper1...'],
});
import { DISTRIBUTION_PRECOMPILE_ABI, DISTRIBUTION_PRECOMPILE_ADDRESS } from '@sei-js/precompiles';
const distribution = new ethers.Contract(
DISTRIBUTION_PRECOMPILE_ADDRESS,
DISTRIBUTION_PRECOMPILE_ABI,
signer,
);
const tx = await distribution.withdrawDelegationRewards('seivaloper1...');
await tx.wait();
JSON Precompile
Parse JSON data within EVM contracts or dApps — useful for processing oracle responses, NFT metadata, and structured payloads:import { toHex, toBytes } from 'viem';
import { JSON_PRECOMPILE_ABI, JSON_PRECOMPILE_ADDRESS } from '@sei-js/precompiles';
const data = { price: 100, symbol: 'SEI' };
const inputBytes = toBytes(JSON.stringify(data));
// Extract a string value
const symbolBytes = await client.readContract({
address: JSON_PRECOMPILE_ADDRESS,
abi: JSON_PRECOMPILE_ABI,
functionName: 'extractAsBytes',
args: [inputBytes, 'symbol'],
});
// Extract a numeric value
const price = await client.readContract({
address: JSON_PRECOMPILE_ADDRESS,
abi: JSON_PRECOMPILE_ABI,
functionName: 'extractAsUint256',
args: [inputBytes, 'price'],
});
import { JSON_PRECOMPILE_ABI, JSON_PRECOMPILE_ADDRESS } from '@sei-js/precompiles';
const jsonPrecompile = new ethers.Contract(JSON_PRECOMPILE_ADDRESS, JSON_PRECOMPILE_ABI, provider);
const data = { price: 100, symbol: 'SEI' };
const inputData = ethers.toUtf8Bytes(JSON.stringify(data));
// Extract a string value
const symbolBytes = await jsonPrecompile.extractAsBytes(inputData, 'symbol');
const symbol = ethers.toUtf8String(symbolBytes);
// Extract a numeric value
const price = await jsonPrecompile.extractAsUint256(inputData, 'price');
console.log(`${symbol}: ${price}`);
Common Patterns
Error Handling
Wrap precompile calls in try/catch and check for specific error codes:try {
const tx = await staking.delegate('seivaloper1...', { value: ethers.parseEther('10') });
const receipt = await tx.wait();
console.log('Success:', receipt.hash);
} catch (err: any) {
if (err.code === 'INSUFFICIENT_FUNDS') {
console.error('Not enough SEI');
} else if (err.code === 4001 || err.code === 'ACTION_REJECTED') {
console.log('User rejected');
} else {
console.error('Transaction failed:', err.message);
}
}
Batch Read Operations
Fetch multiple precompile values in parallel:const [delegation1, delegation2, rewards] = await Promise.all([
readStaking.delegation(address, validator1),
readStaking.delegation(address, validator2),
distribution.delegationTotalRewards(address),
]);
Gas Limits
Precompile calls that write state need enough gas. UseestimateGas rather than hard-coding:
viem
const gas = await client.estimateGas({
account,
address: STAKING_PRECOMPILE_ADDRESS,
abi: STAKING_PRECOMPILE_ABI,
functionName: 'delegate',
args: ['seivaloper1...'],
value: parseEther('10'),
});
Full Working Example
A Node.js script that delegates SEI and then casts a governance vote:import { ethers } from 'ethers';
import {
STAKING_PRECOMPILE_ABI, STAKING_PRECOMPILE_ADDRESS,
GOVERNANCE_PRECOMPILE_ABI, GOVERNANCE_PRECOMPILE_ADDRESS,
} from '@sei-js/precompiles';
async function main() {
const provider = new ethers.JsonRpcProvider('https://evm-rpc.sei-apis.com');
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
const staking = new ethers.Contract(STAKING_PRECOMPILE_ADDRESS, STAKING_PRECOMPILE_ABI, wallet);
const governance = new ethers.Contract(GOVERNANCE_PRECOMPILE_ADDRESS, GOVERNANCE_PRECOMPILE_ABI, wallet);
// Check current delegation
const readStaking = staking.connect(provider) as typeof staking;
const delegation = await readStaking.delegation(wallet.address, 'seivaloper1...');
console.log('Current delegation:', delegation.balance.amount.toString());
// Delegate 1 SEI
const delegateTx = await staking.delegate('seivaloper1...', { value: ethers.parseEther('1') });
await delegateTx.wait();
console.log('Delegated 1 SEI');
// Vote Yes on proposal 99
const voteTx = await governance.vote(99n, 1);
await voteTx.wait();
console.log('Vote cast');
}
main().catch(console.error);
PRIVATE_KEY=your_key npx tsx script.ts