Skip to main content
Sei precompiles are special smart contracts deployed at fixed addresses that expose native Sei functionality to EVM applications. They can be called like any standard smart contract using viem, ethers, or wagmi.
Available Precompiles:
PrecompileAddressDescription
Bank0x1001Query native denom balances (usei, factory tokens)
JSON0x1003Parse JSON data within contracts
Staking0x1005Delegation and staking operations
Governance0x1006Proposal voting
Distribution0x1007Claim staking rewards
P2560x1011Verify 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. Use estimateGas 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);
Run with:
PRIVATE_KEY=your_key npx tsx script.ts

Next Steps

For full ABI reference and all available functions on each precompile: