Ready to build with ERC-7702 and ERC-7710? Let's walk through creating a delegation app that lets users grant permissions to AI agents.
A simple delegation dashboard where:
This is the foundation for "AI agent wallets" — autonomous agents that can act on your behalf, within boundaries you control.
First, we need a delegation contract that implements ERC-7710.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
/**
* @title AgentDelegation
* @dev Simple delegation contract for AI agents
* Uses ERC-7710 patterns for permission grants
*/
contract AgentDelegation {
using SafeERC20 for IERC20;
struct Permission {
address agent;
address token;
uint256 spendLimit;
uint256 spent;
uint256 expiry;
bool active;
}
mapping(bytes32 => Permission) public permissions;
mapping(address => bytes32[]) public userDelegations;
event PermissionGranted(
bytes32 indexed id,
address indexed user,
address indexed agent,
address token,
uint256 limit,
uint256 expiry
);
event PermissionRevoked(bytes32 indexed id);
event PermissionUsed(bytes32 indexed id, uint256 amount);
function grantPermission(
address agent,
address token,
uint256 spendLimit,
uint256 expiry
) external returns (bytes32) {
require(agent != address(0), "Invalid agent");
require(spendLimit > 0, "Zero limit");
require(expiry > block.timestamp, "Invalid expiry");
bytes32 permissionId = keccak256(
abi.encodePacked(msg.sender, agent, token, block.timestamp)
);
permissions[permissionId] = Permission({
agent: agent,
token: token,
spendLimit: spendLimit,
spent: 0,
expiry: expiry,
active: true
});
userDelegations[msg.sender].push(permissionId);
emit PermissionGranted(permissionId, msg.sender, agent, token, spendLimit, expiry);
return permissionId;
}
function revokePermission(bytes32 permissionId) external {
require(permissions[permissionId].active, "Permission not active");
permissions[permissionId].active = false;
emit PermissionRevoked(permissionId);
}
function executeDelegation(
bytes32 permissionId,
uint256 amount,
address recipient
) external {
Permission storage perm = permissions[permissionId];
require(perm.active, "Permission inactive");
require(perm.agent == msg.sender, "Not authorized");
require(block.timestamp < perm.expiry, "Permission expired");
require(perm.spent + amount <= perm.spendLimit, "Exceeds limit");
perm.spent += amount;
// Transfer logic here...
emit PermissionUsed(permissionId, amount);
}
}
Now let's build the UI using React and Ethers.js.
npm install ethers@^6.0
import { useState, useEffect } from 'react';
import { ethers } from 'ethers';
const CONTRACT_ABI = [
"function grantPermission(address agent, address token, uint256 spendLimit, uint256 expiry) returns (bytes32)",
"function revokePermission(bytes32 permissionId)",
"function getUserDelegations(address user) view returns (bytes32[])",
"event PermissionGranted(bytes32 indexed id, address indexed user, address indexed agent, address token, uint256 limit, uint256 expiry)"
];
export default function DelegationDashboard() {
const [account, setAccount] = useState(null);
const [provider, setProvider] = useState(null);
const [contract, setContract] = useState(null);
const [delegations, setDelegations] = useState([]);
// Form state
const [agentAddress, setAgentAddress] = useState('');
const [spendLimit, setSpendLimit] = useState('');
const [expiryHours, setExpiryHours] = useState('24');
const connectWallet = async () => {
if (window.ethereum) {
const provider = new ethers.BrowserProvider(window.ethereum);
await provider.send("eth_requestAccounts", []);
const signer = await provider.getSigner();
const address = await signer.getAddress();
setProvider(provider);
setAccount(address);
const contractAddress = "0xYOUR_CONTRACT_ADDRESS";
const contract = new ethers.Contract(contractAddress, CONTRACT_ABI, signer);
setContract(contract);
}
};
const grantPermission = async () => {
if (!contract) return;
const expiry = Math.floor(Date.now() / 1000) + (parseInt(expiryHours) * 3600);
const tx = await contract.grantPermission(
agentAddress,
ethers.ZeroAddress, // ETH
ethers.parseEther(spendLimit),
expiry
);
await tx.wait();
alert("Permission granted!");
};
return (
<div className="p-8">
<h1 className="text-3xl font-bold mb-8">AI Agent Delegation</h1>
{!account ? (
<button onClick={connectWallet}>Connect Wallet</button>
) : (
<div>
<input placeholder="Agent Address" value={agentAddress} onChange={e => setAgentAddress(e.target.value)} />
<input placeholder="Spend Limit (ETH)" value={spendLimit} onChange={e => setSpendLimit(e.target.value)} />
<button onClick={grantPermission}>Grant Permission</button>
</div>
)}
</div>
);
}
# Install Hardhat
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox
# Initialize project
npx hardhat init
# Deploy contract
npx hardhat run scripts/deploy.js --network sepolia
This is a minimal example. Here's how to extend it: