Skip to main content

Overview

The Address precompile enables bidirectional address mapping between Sei’s native Cosmos addresses (bech32 format) and EVM addresses (hex format). This is essential for cross-chain functionality and allows users to associate their Cosmos and EVM identities. Contract Address: 0x0000000000000000000000000000000000001004

Key Features

  • Address Translation: Convert between Sei and EVM address formats
  • Address Association: Link Cosmos and EVM addresses for the same account
  • Cross-Chain Identity: Enable seamless interaction between EVM and Cosmos ecosystems
  • Public Key Association: Associate addresses using public key cryptography

Available Functions

View Functions

Get the associated Sei (Cosmos) address for an EVM address.Parameters:
  • addr (address): The EVM address to lookup
Returns: Associated Sei address in bech32 format
const seiAddress = await addressContract.getSeiAddr(
  "0x742d35Cc6634C0532925a3b8D6Ac0c2fb15d8d2A"
);
// Returns: "sei1wsltdx4m9k8dafl92nkwmywd6k7jdlpz5h3j3q"
Get the associated EVM address for a Sei (Cosmos) address.Parameters:
  • addr (string): The Sei address to lookup (bech32 format)
Returns: Associated EVM address in hex format
const evmAddress = await addressContract.getEvmAddr(
  "sei1wsltdx4m9k8dafl92nkwmywd6k7jdlpz5h3j3q"
);
// Returns: "0x742d35Cc6634C0532925a3b8D6Ac0c2fb15d8d2A"

State-Changing Functions

Associate addresses using signature verification.Parameters:
  • v (string): Recovery ID from signature
  • r (string): R component of signature
  • s (string): S component of signature
  • customMessage (string): Custom message that was signed
Returns: Tuple of (seiAddr, evmAddr)
const [seiAddr, evmAddr] = await addressContract.associate(
  "27", // v
  "0x1234...", // r
  "0x5678...", // s
  "Associate my addresses" // customMessage
);
Associate addresses using a public key.Parameters:
  • pubKeyHex (string): Public key in hexadecimal format
Returns: Tuple of (seiAddr, evmAddr)
const [seiAddr, evmAddr] = await addressContract.associatePubKey(
  "0x04a1b2c3d4e5f6..." // Uncompressed public key
);

Usage Examples

import { createPublicClient, createWalletClient, http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { 
  seiTestnet,
  ADDRESS_PRECOMPILE_ABI,
  ADDRESS_PRECOMPILE_ADDRESS 
} from '@sei-js/precompiles/viem';

const publicClient = createPublicClient({
  chain: seiTestnet,
  transport: http()
});

const account = privateKeyToAccount('0x...');
const walletClient = createWalletClient({
  account,
  chain: seiTestnet,
  transport: http()
});

// Get Sei address for EVM address
async function getSeiAddress(evmAddress: string) {
  try {
    const seiAddress = await publicClient.readContract({
      address: ADDRESS_PRECOMPILE_ADDRESS,
      abi: ADDRESS_PRECOMPILE_ABI,
      functionName: 'getSeiAddr',
      args: [evmAddress]
    });
    
    return seiAddress;
  } catch (error) {
    console.log('No association found for', evmAddress);
    return null;
  }
}

// Get EVM address for Sei address
async function getEvmAddress(seiAddress: string) {
  try {
    const evmAddress = await publicClient.readContract({
      address: ADDRESS_PRECOMPILE_ADDRESS,
      abi: ADDRESS_PRECOMPILE_ABI,
      functionName: 'getEvmAddr',
      args: [seiAddress]
    });
    
    return evmAddress;
  } catch (error) {
    console.log('No association found for', seiAddress);
    return null;
  }
}

// Associate addresses using public key
async function associateAddresses(publicKey: string) {
  const hash = await walletClient.writeContract({
    address: ADDRESS_PRECOMPILE_ADDRESS,
    abi: ADDRESS_PRECOMPILE_ABI,
    functionName: 'associatePubKey',
    args: [publicKey]
  });
  
  const receipt = await publicClient.waitForTransactionReceipt({ hash });
  
  // Parse the return values from logs or events
  return receipt;
}

// Check if addresses are associated
async function checkAssociation(evmAddress: string, seiAddress: string) {
  const [associatedSei, associatedEvm] = await Promise.all([
    getSeiAddress(evmAddress),
    getEvmAddress(seiAddress)
  ]);
  
  return {
    isAssociated: associatedSei === seiAddress && associatedEvm === evmAddress,
    evmToSei: associatedSei,
    seiToEvm: associatedEvm
  };
}

// Batch lookup multiple addresses
async function batchLookupAddresses(addresses: string[]) {
  const lookupPromises = addresses.map(async (addr) => {
    if (addr.startsWith('0x')) {
      // EVM address
      const seiAddr = await getSeiAddress(addr);
      return { evmAddr: addr, seiAddr, type: 'evm' };
    } else if (addr.startsWith('sei1')) {
      // Sei address
      const evmAddr = await getEvmAddress(addr);
      return { seiAddr: addr, evmAddr, type: 'sei' };
    }
    return { error: 'Invalid address format', address: addr };
  });
  
  return await Promise.all(lookupPromises);
}

Common Use Cases

Cross-Chain Applications

  • Unified Identity: Link user identities across EVM and Cosmos ecosystems
  • Asset Bridging: Map assets between different address formats
  • Multi-Chain Wallets: Support both EVM and Cosmos transactions

DeFi Integration

  • Liquidity Provision: Use associated addresses for cross-chain liquidity
  • Yield Farming: Participate in farming across both ecosystems
  • Governance: Vote with associated addresses in different governance systems

Wallet Applications

  • Address Book: Display both address formats for contacts
  • Transaction History: Track transactions across both address types
  • Portfolio Management: Aggregate holdings across associated addresses

Address Association Process

// 1. Get user's public key from wallet
const publicKey = await getPublicKeyFromWallet();

// 2. Associate addresses
const [seiAddr, evmAddr] = await addressContract.associatePubKey(publicKey);

// 3. Verify association
const isAssociated = await checkAssociation(evmAddr, seiAddr);

Using Signature Verification

// 1. Create custom message
const message = "Associate my Sei and EVM addresses";

// 2. Sign message with private key
const signature = await signMessage(message, privateKey);

// 3. Extract signature components
const { v, r, s } = parseSignature(signature);

// 4. Associate addresses
const [seiAddr, evmAddr] = await addressContract.associate(v, r, s, message);

Address Format Validation

// Validate EVM address format
function isValidEvmAddress(address: string): boolean {
  return /^0x[a-fA-F0-9]{40}$/.test(address);
}

// Validate Sei address format
function isValidSeiAddress(address: string): boolean {
  return /^sei1[a-z0-9]{38}$/.test(address);
}

// Address format converter utility
class AddressUtils {
  static isEvm(address: string): boolean {
    return address.startsWith('0x') && address.length === 42;
  }
  
  static isSei(address: string): boolean {
    return address.startsWith('sei1') && address.length === 43;
  }
  
  static getAddressType(address: string): 'evm' | 'sei' | 'unknown' {
    if (this.isEvm(address)) return 'evm';
    if (this.isSei(address)) return 'sei';
    return 'unknown';
  }
}

Error Handling

Common errors when using the Address precompile:
  • No association found: Addresses haven’t been linked yet
  • Invalid address format: Malformed EVM or Sei addresses
  • Association already exists: Trying to associate already linked addresses
  • Invalid signature: Signature verification failed during association
async function safeGetAssociation(address: string) {
  try {
    const addressType = AddressUtils.getAddressType(address);
    
    if (addressType === 'evm') {
      return await addressContract.getSeiAddr(address);
    } else if (addressType === 'sei') {
      return await addressContract.getEvmAddr(address);
    } else {
      throw new Error('Invalid address format');
    }
  } catch (error) {
    if (error.message.includes('no association')) {
      return null; // No association exists
    } else if (error.message.includes('invalid address')) {
      throw new Error('Invalid address format');
    } else {
      throw error; // Re-throw unexpected errors
    }
  }
}

Security Considerations

  • Public Key Security: Ensure public keys are handled securely
  • Signature Verification: Validate signatures before association
  • Address Ownership: Verify ownership before creating associations
  • Association Permanence: Address associations are typically permanent
// Verify address ownership before association
async function verifyAddressOwnership(address: string, signature: string) {
  const message = "I own this address";
  const recoveredAddress = recoverAddressFromSignature(message, signature);
  return recoveredAddress.toLowerCase() === address.toLowerCase();
}
  • Bank: Transfer tokens between associated addresses
  • IBC: Cross-chain transfers using address mapping
  • Staking: Delegate from associated addresses