Methods for Associating a Wallet on Sei
Wallet association on the Sei network ensures the public key becomes known to the chain. Without this step, the chain cannot determine the Bech32 (sei...
) address or the EVM-compatible (0x...
) address from one another.
Below are 4 distinct methods for associating a wallet along with relevant defitnitions. Each method differs in terms of security considerations and required actions.
Global Constants
These constants for the addr
precompile can also be found in the repo Sei-Chain/precompiles (opens in a new tab):
// Constants for precompile address and ABI
export const ADDRESS_PRECOMPILE_ADDRESS = '0x0000000000000000000000000000000000001004';
export const ADDRESS_PRECOMPILE_ABI = [
{
inputs: [
{ internalType: 'string', name: 'v', type: 'string' },
{ internalType: 'string', name: 'r', type: 'string' },
{ internalType: 'string', name: 's', type: 'string' },
{ internalType: 'string', name: 'customMessage', type: 'string' }
],
name: 'associate',
outputs: [
{ internalType: 'string', name: 'seiAddr', type: 'string' },
{ internalType: 'address', name: 'evmAddr', type: 'address' }
],
stateMutability: 'nonpayable',
type: 'function'
},
{
inputs: [{ internalType: 'string', name: 'pubKeyHex', type: 'string' }],
name: 'associatePubKey',
outputs: [
{ internalType: 'string', name: 'seiAddr', type: 'string' },
{ internalType: 'address', name: 'evmAddr', type: 'address' }
],
stateMutability: 'nonpayable',
type: 'function'
},
{
inputs: [{ internalType: 'address', name: 'addr', type: 'address' }],
name: 'getSeiAddr',
outputs: [{ internalType: 'string', name: 'response', type: 'string' }],
stateMutability: 'view',
type: 'function'
},
{
inputs: [{ internalType: 'string', name: 'addr', type: 'string' }],
name: 'getEvmAddr',
outputs: [{ internalType: 'address', name: 'response', type: 'address' }],
stateMutability: 'view',
type: 'function'
}
];
Method 1: Direct Private Key Association
Security Risk: High – Requires the private key to be directly available. Exposing the private key can compromise the wallet.
This method directly uses the private key to interact with the network.
import { createPublicClient, createWalletClient, http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { seiTestnet } from 'viem/chains';
import { ADDRESS_PRECOMPILE_ABI, ADDRESS_PRECOMPILE_ADDRESS } from '@sei-js/evm';
const PRIVATE_KEY = '<replace_with_private_key>';
const publicClient = createPublicClient({ chain: seiTestnet, transport: http() });
const client = createWalletClient({ chain: seiTestnet, transport: http() });
const account = privateKeyToAccount(PRIVATE_KEY);
const response = await client.writeContract({
account,
address: ADDRESS_PRECOMPILE_ADDRESS,
abi: ADDRESS_PRECOMPILE_ABI,
functionName: 'associate',
args: ['0', '0', '0', 'example_message'],
gasPrice: BigInt(100_000_000_000)
});
console.log(response);
Method 2: Associate via Signed Message
Security Risk: Medium – Requires signing a specific message using the private key.
This method involves signing a predefined message to prove ownership of the account.
import { privateKeyToAccount, generatePrivateKey } from 'viem/accounts';
import { parseSignature, toHex } from 'viem';
const associate = async () => {
const account = privateKeyToAccount('<replace_with_private_key>');
const newPk = generatePrivateKey();
const newAccount = privateKeyToAccount(newPk);
const message = 'associate';
const signature = await newAccount.signMessage({ message });
const parsedSignature = parseSignature(signature);
const response = await client.writeContract({
account,
address: ADDRESS_PRECOMPILE_ADDRESS,
abi: ADDRESS_PRECOMPILE_ABI,
functionName: 'associate',
args: [toHex(Number(parsedSignature.v) - 27), parsedSignature.r, parsedSignature.s, message],
gasPrice: BigInt(100_000_000_000)
});
console.log(response);
};
associate();
Method 3: Associate via Public Key
Security Risk: Lower – Involves using the public key, which is less sensitive than the private key.
This method compresses the public key and sends it for association.
import secp256k1 from 'secp256k1';
import { privateKeyToAccount, generatePrivateKey } from 'viem/accounts';
const associateViaPubkey = async () => {
const account = privateKeyToAccount('<replace_with_private_key>');
const newPk = generatePrivateKey();
const newAccount = privateKeyToAccount(newPk);
const publicKeyBuffer = Buffer.from(newAccount.publicKey.slice(2), 'hex');
const compressedPubKey = secp256k1.publicKeyConvert(publicKeyBuffer, true);
const response = await client.writeContract({
account,
address: ADDRESS_PRECOMPILE_ADDRESS,
abi: ADDRESS_PRECOMPILE_ABI,
functionName: 'associatePubKey',
args: [Buffer.from(compressedPubKey).toString('hex')],
gasPrice: BigInt(100_000_000_000)
});
console.log(response);
};
associateViaPubkey();
Method 4: Gasless Association via Signed Message
Security Risk: Low – Requires signing a message but does not expose the private key. No gas is consumed if the account already has funds.
This method signs a message and uses the sei_associate
RPC call to finalize the association.
import { parseSignature, numberToHex } from 'viem';
interface AssociateRequest {
r: string;
s: string;
v: string;
custom_message: string;
}
interface AssociateRequestSchema {
Method: 'sei_associate';
Parameters: [request: AssociateRequest];
ReturnType: null;
}
const associateGasless = async (signature: `0x${string}`, message: string) => {
const parsedSignature = parseSignature(signature);
const messageLength = Buffer.from(message, 'utf8').length;
const messageToSign = `\x19Ethereum Signed Message:\n${messageLength}${message}`;
const request: AssociateRequest = {
r: parsedSignature.r,
s: parsedSignature.s,
v: numberToHex(Number(parsedSignature.v) - 27),
custom_message: messageToSign
};
const response = await client.request<AssociateRequestSchema>({
method: 'sei_associate',
params: [request]
});
console.log(response);
};
// Example Usage
associateGasless('<replace_with_signature>', 'example_message');
Summary of Methods
Method | Security Risk | User Action Required |
---|---|---|
1. Direct Private Key | High | Provide private key directly. |
2. Signed Message | Medium | Sign a predefined message to prove ownership. |
3. Public Key | Lower | Provide a compressed public key for association. |
4. Gasless Signed Message | Low | Sign a message without requiring gas (if funded). |
Using any of these methods will ensure the public key is known to the chain, enabling automatic association between the EVM-compatible and Bech32 addresses.