Skip to main content

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:

  1. Charges a 0.0005 ETH creation fee (owner is exempt)
  2. Derives a deterministic salt: keccak256(abi.encodePacked(msg.sender, agent))
  3. Deploys via DEPLOYER_SYSTEM_CONTRACT.create2Account() with the ClawWallet bytecode hash
  4. Calls initialize(owner, agent, skillRegistry) on the new wallet
  5. Registers the agent → wallet mapping

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:

LimitDefaultSet By
Per-Transaction1 ETHOwner only
Daily10 ETHOwner 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:

  1. value <= perTxLimit — single transaction cap
  2. dailySpent + 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

PropertyDescription
keyThe address authorized to sign transactions
validUntilUnix timestamp when the key expires
spendingLimitMaximum cumulative ETH spending
spentRunning total of ETH spent via this key
allowedTargetsWhitelist of contract addresses (empty = unrestricted)
activeCan 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.

Agent Target Restriction

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() and executeBatch() 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

FunctionDescription
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