Agent Wallets
ClawWallet agent wallets are ERC-4337 compatible smart contract accounts deployed on Abstract Chain (zkSync ERA). Unlike traditional EOA wallets, agent wallets provide programmable access control, spending limits, and composable skill integration — all purpose-built for autonomous AI agents.
How It Works
Account Abstraction on Abstract
Abstract Chain is built on zkSync ERA, where every account is a smart contract wallet by default. The ClawWalletFactory leverages this architecture by deploying wallets via create2Account, which flags each wallet as AA-compatible (AccountAbstractionVersion.Version1).
This means ClawWallet wallets natively support:
- Custom signature validation (ERC-1271)
- Paymaster-sponsored transactions (gasless)
- Programmatic transaction validation
Wallet Architecture
Each ClawWallet has three levels of access:
┌──────────────────────────────────┐
│ ClawWallet │
│ │
│ 👤 Owner (full control) │
│ ├── Set spending limits │
│ ├── Manage session keys │
│ ├── Install/remove skills │
│ ├── Set co-signer │
│ ├── Freeze / unfreeze │
│ ├── Configure ERC-8004 │
│ └── Approve ERC-20 tokens │
│ │
│ 🤖 Agent (operational control) │
│ ├── Execute transactions │
│ │ (within spending limits) │
│ ├── Call installed skills │
│ ├── Install/remove skills │
│ ├── Manage session keys │
│ └── Update agent URI │
│ │
│ 🔑 Session Keys (scoped access) │
│ ├── Time-limited │
│ ├── Spending-capped │
│ └── Target-restricted │
└──────────────────────────────────┘
Deploying a Wallet
Wallets are deployed through the ClawWalletFactory:
function createWallet(address agent) external payable returns (address wallet)
The factory:
- Charges a 0.0005 ETH creation fee (owner is exempt)
- Derives a deterministic salt:
keccak256(abi.encodePacked(msg.sender, agent)) - Deploys via
DEPLOYER_SYSTEM_CONTRACT.create2Account()with theClawWalletbytecode hash - Calls
initialize(owner, agent, skillRegistry)on the new wallet - Registers the
agent → walletmapping
Deterministic Addresses
You can predict a wallet's address before deployment:
const predictedAddress = await factory.predictWalletAddress(ownerAddress, agentAddress);
// Wallet will deploy at this exact address
Or use a custom salt for full control:
const customSalt = ethers.keccak256(ethers.toUtf8Bytes('my-custom-salt'));
const wallet = await factory.createWalletDeterministic(agentAddress, customSalt, {
value: creationFee,
});
Spending Limits
Every wallet has two configurable spending limits:
| Limit | Default | Set By |
|---|---|---|
| Per-Transaction | 1 ETH | Owner only |
| Daily | 10 ETH | Owner only |
// Owner sets limits
function setSpendingLimits(uint256 perTxLimit, uint256 dailyLimit) external onlyOwner;
Daily limits reset automatically at midnight UTC (tracked via block.timestamp / 1 days).
When an agent executes a transaction, the wallet enforces:
value <= perTxLimit— single transaction capdailySpent + value <= dailyLimit— cumulative daily cap
Session Keys
Session keys provide temporary, scoped access to the wallet. They're ideal for:
- Time-limited DeFi operations
- Delegated bot access
- Frontend dApp interactions
function addSessionKey(
address key, // The session key address
uint256 validUntil, // Expiration timestamp
uint256 spendingLimit, // Max total spending for this key
address[] calldata allowedTargets // Contracts this key can call
) external onlyOwnerOrAgent;
Session Key Properties
| Property | Description |
|---|---|
key | The address authorized to sign transactions |
validUntil | Unix timestamp when the key expires |
spendingLimit | Maximum cumulative ETH spending |
spent | Running total of ETH spent via this key |
allowedTargets | Whitelist of contract addresses (empty = unrestricted) |
active | Can be revoked at any time |
Example: Create a 24-hour session key
const sessionKeyAddress = '0xSESSION_KEY_ADDRESS';
const validUntil = Math.floor(Date.now() / 1000) + 86400; // 24 hours
const spendingLimit = ethers.parseEther('0.5');
const allowedTargets = ['0xSKILL_ADDRESS_1', '0xSKILL_ADDRESS_2'];
await wallet.addSessionKey(sessionKeyAddress, validUntil, spendingLimit, allowedTargets);
Skill Management
Agents can only call contracts that have been installed as skills. This whitelist prevents unauthorized contract interactions.
// Install a skill (owner or agent)
function installSkill(address skill) external onlyOwnerOrAgent;
// Uninstall a skill
function uninstallSkill(address skill) external onlyOwnerOrAgent;
// Check if a skill is installed
function isSkillInstalled(address skill) external view returns (bool);
// List all installed skills
function getInstalledSkills() external view returns (address[] memory);
When the agent calls execute() on a target that is an installed skill, the wallet automatically records the call via ClawSkillRegistry.recordSkillCall() for usage tracking.
When the agent (not the owner) calls execute(), the target must be either:
- An installed skill (
_installedSkills[target] == true) - The wallet itself (
target == address(this))
The owner has no target restrictions.
Emergency Controls
Freeze
The owner or co-signer can instantly freeze the wallet:
function freeze() external; // Owner or co-signer
function unfreeze() external onlyOwner; // Owner only
When frozen:
- All
execute()andexecuteBatch()calls revert - Token transfers via
transferERC20()revert - ERC-8004 feedback and validation requests revert
- Skill installations and other admin functions still work
Co-Signer
An optional co-signer provides a second freeze authority for security:
function setCoSigner(address newCoSigner) external onlyOwner;
The co-signer can only freeze — they cannot unfreeze or perform any other operation.
Batch Execution
Execute multiple transactions atomically:
const targets = ['0xTARGET_1', '0xTARGET_2'];
const values = [ethers.parseEther('0.01'), 0n];
const datas = ['0x', skillCalldata];
const tx = await wallet.executeBatch(targets, values, datas);
All transactions in a batch share the same spending limit tracking. If any transaction fails, the entire batch reverts.
ERC-1271 Signature Validation
ClawWallet implements ERC-1271 for smart contract signature verification:
function isValidSignature(bytes32 hash, bytes calldata signature)
external view returns (bytes4 magicValue);
A signature is valid if it was produced by the wallet owner. This is required for:
- ERC-8004
setAgentWallet()verification - dApp login (Sign-In with Ethereum)
- Off-chain message signing
ERC-8004 Integration
ClawWallet has deep integration with all three ERC-8004 registries:
Identity
function erc8004Register(string calldata agentURI) external onlyOwner returns (uint256 agentId);
function erc8004SetAgentURI(string calldata newURI) external onlyOwnerOrAgent;
function erc8004SetMetadata(string calldata key, bytes calldata value) external onlyOwner;
Reputation
function erc8004GiveFeedback(...) external onlyOwnerOrAgent notFrozen;
function erc8004RevokeFeedback(uint256 agentId, uint64 feedbackIndex) external onlyOwnerOrAgent;
function erc8004GetReputation(...) external view returns (uint64 count, int128 summaryValue, uint8 summaryValueDecimals);
Validation
function erc8004RequestValidation(address validatorAddress, string calldata requestURI, bytes32 requestHash) external onlyOwnerOrAgent notFrozen;
function erc8004GetValidationStatus(bytes32 requestHash) external view;
See ERC-8004 Identity for full documentation.
Factory Contract Reference
| Function | Description |
|---|---|
createWallet(agent) | Deploy a wallet (0.0005 ETH fee) |
createWalletDeterministic(agent, salt) | Deploy with custom salt |
predictWalletAddress(owner, agent) | Predict address before deployment |
agentToWallet(agent) | Look up wallet by agent address |
walletToAgent(wallet) | Look up agent by wallet address |
isClawWallet(wallet) | Check if an address is a ClawWallet |
getWalletCount() | Total wallets deployed |
allWallets(index) | Get wallet address by index |