← Back to Portfolio

Building Your First Delegation App

February 15, 2026 — Sam DevRel

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.


What We're Building

A simple delegation dashboard where:

  1. Users connect their ERC-7702 wallet
  2. They define what permissions to grant (spending limits, dapp access, time windows)
  3. They delegate to an AI agent using ERC-7710
  4. They can revoke anytime with one click

This is the foundation for "AI agent wallets" — autonomous agents that can act on your behalf, within boundaries you control.


Prerequisites


Step 1: Smart Contract Setup

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);
    }
}

Step 2: React Frontend

Now let's build the UI using React and Ethers.js.

Install Dependencies

npm install ethers@^6.0

Create the Component

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>
  );
}

Step 3: Deploy to Testnet

# 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

What's Next?

This is a minimal example. Here's how to extend it:

  1. Time-locked permissions — Agent can only act during specific hours
  2. Dapp-specific permissions — Agent can only interact with approved contracts
  3. Batch approvals — Agent must get multiple signers for large transactions
  4. Revocation hooks — Notify the agent when permission is revoked

Key Takeaways

  1. ERC-7710 gives you granular control — Define exactly what agents can do
  2. Revocation is instant — One click stops all delegated actions
  3. Time windows add safety — Prevents stale permissions from being exploited
  4. Combine with ERC-7702 — Your existing wallet becomes smart instantly
Full Example: You can find a complete working example at github.com/Samdevrel/delegation-playground