Quickstart
Use a deployed ERC-8004 Identity Adapter from TypeScript with viem.
Prerequisites#
- Node.js 18+
- viem:
npm install viem - An RPC URL for the target chain (this guide uses Sepolia)
- A wallet with a small amount of ETH on the target chain for gas
- An external token you control (ERC-721, ERC-1155, or ERC-6909)
Adapter addresses#
| Chain | Chain ID | Adapter Proxy |
|---|---|---|
| Ethereum | 1 | 0xde152AfB7db5373F34876E1499fbD893A82dD336 |
| Base | 8453 | 0x270d25D2c59A8bcA1B0f40ad95fF7806c0025c27 |
| Sepolia | 11155111 | 0x7621630cB63a73a194f45A3E6801B8C6A7eC2f92 |
Set up viem#
import { createPublicClient, createWalletClient, http, parseEventLogs, toHex } from 'viem';
import { sepolia } from 'viem/chains';
import { privateKeyToAccount } from 'viem/accounts';
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const publicClient = createPublicClient({ chain: sepolia, transport: http(process.env.RPC_URL) });
const walletClient = createWalletClient({ chain: sepolia, transport: http(process.env.RPC_URL), account });
const ADAPTER = '0x7621630cB63a73a194f45A3E6801B8C6A7eC2f92' as const;
Adapter ABI#
Minimal fragment for this quickstart:
const ADAPTER_ABI = [
{
type: 'function', name: 'register', stateMutability: 'nonpayable',
inputs: [
{ name: 'standard', type: 'uint8' },
{ name: 'tokenContract', type: 'address' },
{ name: 'tokenId', type: 'uint256' },
{ name: 'agentURI', type: 'string' },
],
outputs: [{ name: 'agentId', type: 'uint256' }],
},
{
type: 'function', name: 'setAgentURI', stateMutability: 'nonpayable',
inputs: [
{ name: 'agentId', type: 'uint256' },
{ name: 'newURI', type: 'string' },
],
outputs: [],
},
{
type: 'function', name: 'setMetadata', stateMutability: 'nonpayable',
inputs: [
{ name: 'agentId', type: 'uint256' },
{ name: 'metadataKey', type: 'string' },
{ name: 'metadataValue', type: 'bytes' },
],
outputs: [],
},
{
type: 'function', name: 'getAgentWallet', stateMutability: 'view',
inputs: [{ name: 'agentId', type: 'uint256' }],
outputs: [{ type: 'address' }],
},
{
type: 'event', name: 'AgentBound',
inputs: [
{ name: 'agentId', type: 'uint256', indexed: true },
{ name: 'standard', type: 'uint8', indexed: true },
{ name: 'tokenContract', type: 'address', indexed: true },
{ name: 'tokenId', type: 'uint256', indexed: false },
{ name: 'registeredBy', type: 'address', indexed: false },
],
},
] as const;
Register an agent#
Bind an external ERC-721 token to a new ERC-8004 identity using the
no-metadata convenience overload. The caller must currently own tokenId
on tokenContract.
const ERC721 = 0; // 0 = ERC721, 1 = ERC1155, 2 = ERC6909
const NFT = '0xYourNftContractAddress' as const;
const TOKEN_ID = 1n;
const registerHash = await walletClient.writeContract({
address: ADAPTER,
abi: ADAPTER_ABI,
functionName: 'register',
args: [ERC721, NFT, TOKEN_ID, 'ipfs://your-agent-metadata.json'],
});
const receipt = await publicClient.waitForTransactionReceipt({ hash: registerHash });
const [event] = parseEventLogs({
abi: ADAPTER_ABI,
eventName: 'AgentBound',
logs: receipt.logs,
});
const agentId = event.args.agentId;
Add metadata#
After registering, write additional metadata keys. The bound token's holder is the only address that can do this.
await walletClient.writeContract({
address: ADAPTER,
abi: ADAPTER_ABI,
functionName: 'setMetadata',
args: [agentId, 'description', toHex('My agent')],
});
Update the agent URI#
await walletClient.writeContract({
address: ADAPTER,
abi: ADAPTER_ABI,
functionName: 'setAgentURI',
args: [agentId, 'ipfs://new-metadata.json'],
});
The adapter checks the bound token contract on every write. If you no longer control the token, the call reverts.
Read the bound wallet#
const wallet = await publicClient.readContract({
address: ADAPTER,
abi: ADAPTER_ABI,
functionName: 'getAgentWallet',
args: [agentId],
});
Next steps#
- Concepts, the binding model, shared control, and wallet proofs.
- Contract Reference, the full function list including the metadata-array
registeroverload.