Deploying an Agent Wallet
This guide walks through deploying a ClawWallet agent wallet on Abstract Chain, from setup to verification.
Prerequisites
- Node.js v18+
- An EOA wallet with ETH on Abstract Chain (≥ 0.001 ETH)
- An agent address (the AI agent that will operate the wallet)
Using ethers.js v6
1. Setup
npm install ethers
deploy-wallet.ts
import { ethers } from 'ethers';
// Contract addresses
const FACTORY = '0xf6B945dBf532D376A475E31be32F51972915B1cc';
// Connect to Abstract Chain
const provider = new ethers.JsonRpcProvider('https://api.mainnet.abs.xyz');
const owner = new ethers.Wallet('YOUR_PRIVATE_KEY', provider);
console.log('Owner:', owner.address);
console.log('Balance:', ethers.formatEther(await provider.getBalance(owner.address)), 'ETH');
2. Predict the Wallet Address
Before deploying, you can compute the deterministic wallet address:
const factoryABI = [
'function predictWalletAddress(address owner, address agent) external view returns (address)',
'function creationFee() external view returns (uint256)',
'function agentToWallet(address agent) external view returns (address)',
];
const factory = new ethers.Contract(FACTORY, factoryABI, provider);
const agentAddress = '0xYOUR_AGENT_EOA_ADDRESS';
// Check if this agent already has a wallet
const existingWallet = await factory.agentToWallet(agentAddress);
if (existingWallet !== ethers.ZeroAddress) {
console.log('Agent already has a wallet at:', existingWallet);
process.exit(0);
}
// Predict the address
const predicted = await factory.predictWalletAddress(owner.address, agentAddress);
console.log('Predicted wallet address:', predicted);
3. Deploy
const factoryWrite = new ethers.Contract(FACTORY, [
'function createWallet(address agent) external payable returns (address wallet)',
'function creationFee() external view returns (uint256)',
], owner);
// Get the creation fee
const creationFee = await factoryWrite.creationFee();
console.log('Creation fee:', ethers.formatEther(creationFee), 'ETH');
// Deploy
console.log('Deploying wallet...');
const tx = await factoryWrite.createWallet(agentAddress, {
value: creationFee,
});
console.log('TX hash:', tx.hash);
const receipt = await tx.wait();
console.log('✅ Wallet deployed in block', receipt.blockNumber);
4. Verify Deployment
const walletAddress = await factory.agentToWallet(agentAddress);
console.log('Wallet address:', walletAddress);
// Interact with the new wallet
const walletABI = [
'function owner() external view returns (address)',
'function agent() external view returns (address)',
'function isFrozen() external view returns (bool)',
'function getSpendingConfig() external view returns (tuple(uint256 perTxLimit, uint256 dailyLimit, uint256 dailySpent, uint256 lastResetDay))',
'function getInstalledSkills() external view returns (address[])',
];
const wallet = new ethers.Contract(walletAddress, walletABI, provider);
console.log('Wallet owner:', await wallet.owner());
console.log('Wallet agent:', await wallet.agent());
console.log('Frozen:', await wallet.isFrozen());
const spending = await wallet.getSpendingConfig();
console.log('Per-tx limit:', ethers.formatEther(spending.perTxLimit), 'ETH');
console.log('Daily limit:', ethers.formatEther(spending.dailyLimit), 'ETH');
Using viem
deploy-wallet-viem.ts
import { createWalletClient, createPublicClient, http, defineChain, parseEther, getContract } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
const abstractMainnet = defineChain({
id: 2741,
name: 'Abstract Mainnet',
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
rpcUrls: { default: { http: ['https://api.mainnet.abs.xyz'] } },
blockExplorers: { default: { name: 'AbsScan', url: 'https://abscan.org' } },
});
const account = privateKeyToAccount('0xYOUR_PRIVATE_KEY');
const publicClient = createPublicClient({
chain: abstractMainnet,
transport: http(),
});
const walletClient = createWalletClient({
account,
chain: abstractMainnet,
transport: http(),
});
const FACTORY = '0xf6B945dBf532D376A475E31be32F51972915B1cc' as const;
const agentAddress = '0xYOUR_AGENT_ADDRESS' as const;
// Read creation fee
const creationFee = await publicClient.readContract({
address: FACTORY,
abi: [{ name: 'creationFee', type: 'function', inputs: [], outputs: [{ type: 'uint256' }], stateMutability: 'view' }],
functionName: 'creationFee',
});
// Deploy wallet
const hash = await walletClient.writeContract({
address: FACTORY,
abi: [{ name: 'createWallet', type: 'function', inputs: [{ name: 'agent', type: 'address' }], outputs: [{ name: 'wallet', type: 'address' }], stateMutability: 'payable' }],
functionName: 'createWallet',
args: [agentAddress],
value: creationFee,
});
const receipt = await publicClient.waitForTransactionReceipt({ hash });
console.log('✅ Deployed in block', receipt.blockNumber);
Post-Deployment Setup
After deploying, configure the wallet:
Configure Spending Limits
const wallet = new ethers.Contract(walletAddress, [
'function setSpendingLimits(uint256 perTxLimit, uint256 dailyLimit) external',
], owner);
await wallet.setSpendingLimits(
ethers.parseEther('0.5'), // Max 0.5 ETH per tx
ethers.parseEther('2.0'), // Max 2 ETH per day
);
Install Skills
const wallet = new ethers.Contract(walletAddress, [
'function installSkill(address skill) external',
], owner);
await wallet.installSkill('0xSKILL_CONTRACT_ADDRESS');
Add a Session Key
const wallet = new ethers.Contract(walletAddress, [
'function addSessionKey(address key, uint256 validUntil, uint256 spendingLimit, address[] allowedTargets) external',
], owner);
await wallet.addSessionKey(
sessionKeyAddress,
Math.floor(Date.now() / 1000) + 86400 * 7, // 7 days
ethers.parseEther('1.0'),
[skillAddress1, skillAddress2],
);
Set Co-Signer for Emergency Freeze
const wallet = new ethers.Contract(walletAddress, [
'function setCoSigner(address newCoSigner) external',
], owner);
await wallet.setCoSigner('0xTRUSTED_CO_SIGNER_ADDRESS');
Register ERC-8004 Identity
const wallet = new ethers.Contract(walletAddress, [
'function setERC8004Registries(address identity, address reputation, address validation) external',
'function erc8004Register(string calldata agentURI) external returns (uint256)',
], owner);
// Set registry addresses
await wallet.setERC8004Registries(
'0x01949e45FabCD684bcD4747966145140aB4778E5',
'0x2AAab9989a127F9Cad871311673fd8c727738F5F',
'0x0703367EDa108aB5b7181c4d5feB6eC1C29476dc',
);
// Register identity (0.001 ETH fee forwarded to registry)
await wallet.erc8004Register('https://myagent.xyz/metadata.json', {
value: ethers.parseEther('0.001'),
});
Complete Script
full-deploy.ts
import { ethers } from 'ethers';
async function main() {
const provider = new ethers.JsonRpcProvider('https://api.mainnet.abs.xyz');
const owner = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
const FACTORY = '0xf6B945dBf532D376A475E31be32F51972915B1cc';
const agentAddress = process.env.AGENT_ADDRESS!;
const factory = new ethers.Contract(FACTORY, [
'function createWallet(address agent) external payable returns (address wallet)',
'function creationFee() external view returns (uint256)',
'function agentToWallet(address agent) external view returns (address)',
], owner);
// Check existing
const existing = await factory.agentToWallet(agentAddress);
if (existing !== ethers.ZeroAddress) {
console.log('Wallet already exists at:', existing);
return;
}
// Deploy
const fee = await factory.creationFee();
const tx = await factory.createWallet(agentAddress, { value: fee });
const receipt = await tx.wait();
const walletAddress = await factory.agentToWallet(agentAddress);
console.log('✅ Agent wallet deployed at:', walletAddress);
console.log(' TX:', receipt.hash);
console.log(' Block:', receipt.blockNumber);
console.log(' Explorer:', `https://abscan.org/address/${walletAddress}`);
}
main().catch(console.error);
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
InsufficientFee | Not enough ETH sent | Send at least creationFee() |
WalletAlreadyExists | Agent already has a wallet | Check agentToWallet() first |
ZeroAddress | Agent address is 0x0 | Provide a valid agent address |
Wallet deployment failed | zkSync deployer error | Ensure correct chain and sufficient gas |