Address Precompile
Learn how the Paxeer Address precompile lets EVM contracts associate and query Cosmos and EVM addresses for seamless cross-chain identity management.
Address: 0x0000000000000000000000000000000000001004
The Paxeer Address precompile allows EVM applications to interact directly with Paxeer's address association system through standard smart contract calls. This enables querying and creating mappings between EVM addresses and their corresponding Cosmos addresses, enabling seamless cross-chain address management without needing separate Cosmos SDK integration.
How Does the Address Precompile Work?
The Address precompile at address 0x0000000000000000000000000000000000001004 exposes functions like associate(), associatePubKey(), getSeiAddr(), and getEvmAddr().
- Direct Integration: EVM contracts and dApps can call address association functions like any other smart contract method.
- Native Execution: Operations are executed at the Cosmos SDK level for maximum efficiency and security.
- Seamless Bridge: No need for separate wallet integrations or complex cross-chain interactions.
Use Cases
- Cross-Chain Identity: Link EVM and Cosmos addresses for unified user experiences across both execution environments.
- Address Resolution: Build applications that can seamlessly work with both EVM and Cosmos address formats.
- Wallet Integration: Enable users to associate their addresses once and use both EVM and native Paxeer functionality.
- DeFi Applications: Create protocols that can interact with users regardless of their preferred address format.
What You'll Learn in This Guide
By the end of this guide, you'll be able to:
- Integrate Address Operations - Call association and lookup functions directly from your EVM contracts and dApps
- Handle Signature Verification - Master the signature format requirements for secure address association
- Manage Address Mappings - Query and validate address relationships between EVM and Cosmos formats
- Build Cross-Chain Tools - Implement unified address management for applications spanning both execution environments
- Navigate Association Requirements - Understand when and how address association occurs automatically vs. manually
Functions
The Address precompile exposes the following functions:
Transaction Functions
/// Associates an account given it's signature of any custom message.
/// @param v The v component of the signature.
/// @param r The r component of the signature.
/// @param s The s component of the signature.
/// @param customMessage A custom message that was signed by the address to be associated.
/// @return seiAddr The associated Paxeer address.
/// @return evmAddr The associated EVM address.
function associate(
string memory v,
string memory r,
string memory s,
string memory customMessage
) external returns (string memory seiAddr, address evmAddr);
/// Associates an account given it's compressed pubkey in hex format (excluding the '0x')
/// @param pubKeyHex The Hex-encoded compressed pubkey of the account to be associated, excluding the '0x'
/// @return seiAddr The associated Paxeer address.
/// @return evmAddr The associated EVM address.
function associatePubKey(
string memory pubKeyHex
) external returns (string memory seiAddr, address evmAddr);
Query Functions
/// Queries the corresponding Paxeer Address for some EVM address.
/// @param addr The EVM Address for which we want the corresponding Paxeer address.
/// @return response The corresponding Paxeer address.
function getSeiAddr(
address addr
) external view returns (string memory response);
/// Queries the corresponding EVM Address for some Paxeer address.
/// @param addr The Paxeer Address for which we want the corresponding EVM address.
/// @return response The corresponding EVM address.
function getEvmAddr(
string memory addr
) external view returns (address response);
Using the Precompile
Setup
Prerequisites
Before getting started, ensure you have:
- Node.js (v18 or higher)
- npm or yarn package manager
- EVM-compatible wallet
- HPX tokens for gas
- Hardhat development environment set up
Install Dependencies
Install the required packages for interacting with Paxeer precompiles:
# Initiate a Hardhat 3 project (choose "Hardhat 3" and the Mocha + Ethers.js TypeScript setup)
npx hardhat --init
# Install ethers.js for smart contract interactions
npm install ethers
# Install Paxeer EVM bindings for precompile addresses and ABIs
npm install @sei-js/precompiles@2.1.2
Setup Hardhat Environment
Create a hardhat.config.ts file with the following content:
import { defineConfig, configVariable } from 'hardhat/config';
import hardhatToolboxMochaEthers from '@nomicfoundation/hardhat-toolbox-mocha-ethers';
export default defineConfig({
solidity: '0.8.28',
networks: {
hpx: {
type: 'http',
chainId: 1329,
url: 'https://evm-rpc.sei-apis.com',
accounts: [configVariable('PRIVATE_KEY')]
}
},
plugins: [hardhatToolboxMochaEthers]
});
Store your private key in Hardhat's encrypted keystore (no plaintext .env file needed):
npx hardhat keystore set PRIVATE_KEY
Import Precompile Components
// Import Address precompile address and ABI
// View the entire ABI here: https://github.com/paxeer-network/paxeer-chain/tree/main/precompiles/addr
import { ADDRESS_PRECOMPILE_ABI, ADDRESS_PRECOMPILE_ADDRESS } from '@sei-js/precompiles';
import { ethers } from 'ethers';0x0000000000000000000000000000000000001004 Contract Initialization
Set up your provider, signer, and contract instance:
// Using EVM-compatible wallet as the signer and provider
const provider = new ethers.BrowserProvider(window.ethereum);
await provider.send('eth_requestAccounts', []);
const signer = await provider.getSigner();
// Create a contract instance for the addr precompile
const addr = new ethers.Contract(ADDRESS_PRECOMPILE_ADDRESS, ADDRESS_PRECOMPILE_ABI, signer);When Does Association Happen?
Address association between EVM and Cosmos addresses occurs automatically in most cases:
| Method | Trigger | Requirements |
|---|---|---|
| Automatic | First transaction broadcast | Wallet signs any transaction |
| Manual (Signature) | associate() function | Custom message signature |
| Manual (PubKey) | associatePubKey() function | Compressed public key |
How Association Works
Automatic Association:
- Occurs when a wallet first signs and broadcasts any transaction on Paxeer
- No additional steps required for most users
- Association is permanent once created
Manual Association:
- Use
associate()for signature-based association - Use
associatePubKey()for public key-based association - Useful for advanced applications or when automatic association hasn't occurred
Address Format Requirements
EVM Addresses:
- Standard Ethereum format:
0x1234...(20 bytes, 40 hex characters) - Case-insensitive but typically lowercase
Paxeer Addresses:
- Cosmos format with
pax1...prefix for regular addresses - Validator addresses use
paxvaloper1...prefix - Bech32 encoding format
Step-by-Step Guide: Using the Address Precompile
Associate Address with Signature
// Associate addresses using a custom message signature
const message = `Associate my addresses - ${Date.now()}`;
try {
// Get signature from user's wallet
const signature = await signer.signMessage(message);
const sig = ethers.Signature.from(signature);
// Reconstruct the EIP-191 prefixed message that signMessage() actually signed:
// "\x19Ethereum Signed Message:\n" + message length + message
const customMessage = `\x19Ethereum Signed Message:\n${message.length}${message}`;
// Convert signature components to strings
const v = (sig.v - 27).toString();
const r = sig.r;
const s = sig.s;
// Call associate function
const tx = await addr.associate(v, r, s, customMessage);
const receipt = await tx.wait();
console.log('Association successful:', receipt);
} catch (error) {
console.error('Association failed:', error);
}Associate Address with Public Key
// Associate addresses using a public key
try {
// Create or get a wallet's public key
const wallet = ethers.Wallet.createRandom();
const pubKeyWithout0x = wallet.signingKey.compressedPublicKey.slice(2);
// Call associatePubKey function
const tx = await addr.associatePubKey(pubKeyWithout0x);
const receipt = await tx.wait();
console.log('Public key association successful:', receipt);
} catch (error) {
console.error('Public key association failed:', error);
}Query Paxeer Address from EVM Address
// Query the Paxeer address for a given EVM address
const evmAddress = '0x1234567890123456789012345678901234567890';
try {
const seiAddress = await addr.getSeiAddr(evmAddress);
console.log('Corresponding Paxeer address:', seiAddress);
} catch (error) {
if (error.message.includes('not found') || error.message.includes('no association')) {
console.log('No association found for this EVM address');
} else {
console.error('Error querying Paxeer address:', error);
}
}Query EVM Address from Paxeer Address
// Query the EVM address for a given Paxeer address
const seiAddress = 'pax1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6s8dpw';
try {
const evmAddress = await addr.getEvmAddr(seiAddress);
console.log('Corresponding EVM address:', evmAddress);
} catch (error) {
if (error.message.includes('not found') || error.message.includes('no association')) {
console.log('No association found for this Paxeer address');
} else {
console.error('Error querying EVM address:', error);
}
}Complete Integration Example
import { ADDRESS_PRECOMPILE_ABI, ADDRESS_PRECOMPILE_ADDRESS } from '@sei-js/precompiles';
import { ethers } from 'ethers';
async function addrExample() {
// Setup
const provider = new ethers.BrowserProvider(window.ethereum);
await provider.send('eth_requestAccounts', []);
const signer = await provider.getSigner();
const addr = new ethers.Contract(ADDRESS_PRECOMPILE_ADDRESS, ADDRESS_PRECOMPILE_ABI, signer);
const evmAddress = await signer.getAddress();
try {
// 1. Check if current EVM address has an associated Paxeer address
console.log('=== Checking Current Association ===');
try {
const associatedPax = await addr.getSeiAddr(evmAddress);
console.log('EVM address:', evmAddress);
console.log('Associated Paxeer address:', associatedPax);
// Verify reverse lookup
const reverseEvm = await addr.getEvmAddr(associatedPax);
console.log('Reverse lookup EVM address:', reverseEvm);
} catch (error) {
if (error.message.includes('not found') || error.message.includes('no association')) {
console.log('No association found. You may need to make a transaction first or associate manually.');
// 2. Demonstrate manual association with public key
console.log('=== Creating Manual Association ===');
const wallet = ethers.Wallet.createRandom();
const pubKeyWithout0x = wallet.signingKey.compressedPublicKey.slice(2);
const associateTx = await addr.associatePubKey(pubKeyWithout0x);
const receipt = await associateTx.wait();
console.log('Manual association completed:', receipt.transactionHash);
} else {
throw error;
}
}
// 3. Demonstrate address resolution for different formats
console.log('=== Address Resolution Examples ===');
// Test with known Paxeer address format
const testSeiAddr = 'pax1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6s8dpw';
try {
const correspondingEvm = await addr.getEvmAddr(testSeiAddr);
console.log('Test Paxeer address:', testSeiAddr);
console.log('Corresponding EVM address:', correspondingEvm);
} catch (error) {
console.log('Test address not associated:', testSeiAddr);
}
} catch (error) {
console.error('Operation failed:', error);
}
}Security Considerations & Risks
Association Permanence
- Permanent Mapping: Once addresses are associated, the mapping cannot be changed or removed
- Single Association: Each address can only be associated with one counterpart
- Verification: Always verify associations before depending on them in critical operations
Signature Security
- Custom Messages: Use unique, timestamped messages to prevent replay attacks
- Message Format: Follow exact Ethereum Signed Message format for compatibility
Troubleshooting
Common Issues and Solutions
Gas-Related Issues
// Set appropriate gas limits for different operations
const associateTx = await addr.associate(v, r, s, customMessage, {
gasLimit: 200000 // Association operations
});
const pubKeyTx = await addr.associatePubKey(pubKeyHex, {
gasLimit: 150000 // Public key association
});
Error Code Reference
| Error | Cause | Solution |
|---|---|---|
invalid signature | Malformed v, r, s components | Verify signature parsing and format |
already associated | Address already has association | Check existing association instead |
not found | No association exists | Create association first |
invalid public key | Malformed public key format | Use compressed format without 0x prefix |
invalid address format | Wrong address format | Use proper EVM (0x...) or Paxeer (pax1...) format |
Important Notes
Address Formats
- EVM Addresses: Use standard Ethereum format with
0xprefix - Paxeer Addresses: Use Cosmos format with
pax1prefix for regular addresses - Case Sensitivity: EVM addresses are case-insensitive, Paxeer addresses are case-sensitive
Association Timing
- Automatic: Most users get associated automatically on their first transaction
- Manual: Use precompile functions for programmatic association or when automatic association hasn't occurred
- Verification: Always verify associations exist before depending on them
Signature Requirements
- Message Format: Must include Ethereum Signed Message prefix for
associate()function - Component Format: v, r, s must be properly formatted hex strings
- Replay Protection: Use unique messages to prevent signature replay attacks
Gas Considerations
- Association operations: ~200,000 gas limit recommended
- Query operations: Standard view function gas usage
- Batch operations: Scale gas limits based on array size