AgentSkillsCN

web3-development

Web3 与区块链开发技能,包括智能合约开发、DApp 前端集成、钱包连接、DeFi 协议对接等。适用于开发 Ethereum/EVM 兼容链上的去中心化应用,特别是任务撮合、理财集成(Aave)、DAO 治理等场景。

SKILL.md
--- frontmatter
name: web3-development
description: Web3 与区块链开发技能,包括智能合约开发、DApp 前端集成、钱包连接、DeFi 协议对接等。适用于开发 Ethereum/EVM 兼容链上的去中心化应用,特别是任务撮合、理财集成(Aave)、DAO 治理等场景。

Web3 Development Skill

本技能专为 Web3 智能体任务平台开发而设计,涵盖区块链全栈开发最佳实践。

适用场景

  • 智能合约开发(Solidity)
  • DApp 前端集成(wagmi、viem、RainbowKit)
  • 钱包连接与交易签名
  • DeFi 协议集成(Aave、Uniswap 等)
  • DAO 治理与投票系统
  • 多链部署与兼容

智能合约开发规范

目录结构

code
contracts/
├── core/              # 核心业务合约
│   ├── TaskEscrow.sol       # 任务托管
│   ├── AgentRegistry.sol    # Agent 注册
│   └── DAOGovernance.sol    # DAO 治理
├── integrations/      # 第三方协议集成
│   └── AaveIntegration.sol  # Aave 理财对接
├── interfaces/        # 接口定义
├── libraries/         # 工具库
└── test/             # 测试文件

安全最佳实践

  1. 访问控制

    • 使用 OpenZeppelin 的 OwnableAccessControl
    • 关键函数添加 onlyOwneronlyRole 修饰符
    • 多签钱包用于敏感操作
  2. 重入攻击防护

    • 使用 ReentrancyGuard
    • 遵循 Checks-Effects-Interactions 模式
    solidity
    // ✅ 正确模式
    function withdraw(uint amount) external nonReentrant {
        require(balances[msg.sender] >= amount);  // Checks
        balances[msg.sender] -= amount;           // Effects
        payable(msg.sender).transfer(amount);     // Interactions
    }
    
  3. 整数溢出

    • Solidity 0.8+ 自带溢出检查
    • 复杂计算使用 OpenZeppelin Math
  4. 权限分离

    • 合约升级权限独立
    • 资金管理权限独立
    • 避免单点故障

代码规范

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

/**
 * @title TaskEscrow
 * @notice 任务资金托管合约,支持 ETH 和 ERC20 代币
 * @dev 实现任务创建、承接、交付、结算的完整生命周期
 */
contract TaskEscrow is ReentrancyGuard, Ownable {
    // 使用 uint256 而非 uint
    // 常量使用 UPPER_SNAKE_CASE
    uint256 public constant MIN_COMMISSION_RATE = 500;  // 5%
    uint256 public constant MAX_COMMISSION_RATE = 1000; // 10%

    // 事件命名使用过去式
    event TaskCreated(uint256 indexed taskId, address indexed employer);
    event TaskCompleted(uint256 indexed taskId, address indexed agent);

    // 错误使用 custom errors(省 gas)
    error InsufficientFunds();
    error TaskNotFound();
    error Unauthorized();
}

前端 Web3 集成

技术栈

  • wagmi v2 - React Hooks for Ethereum
  • viem - 底层以太坊交互库
  • RainbowKit - 钱包连接 UI
  • TanStack Query - 数据缓存与状态管理

钱包连接配置

typescript
// config/wagmi.ts
import { getDefaultConfig } from "@rainbow-me/rainbowkit";
import { mainnet, sepolia, polygon, arbitrum } from "wagmi/chains";

export const config = getDefaultConfig({
  appName: "Web3 Agent Marketplace",
  projectId: process.env.NEXT_PUBLIC_WALLET_CONNECT_ID!,
  chains: [mainnet, sepolia, polygon, arbitrum],
  ssr: false,
});

合约交互 Hook

typescript
// hooks/useTaskContract.ts
import {
  useReadContract,
  useWriteContract,
  useWaitForTransactionReceipt,
} from "wagmi";
import { parseEther } from "viem";

export function useCreateTask() {
  const { writeContract, data: hash, isPending, error } = useWriteContract();

  const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({
    hash,
  });

  const createTask = async (title: string, budget: string) => {
    writeContract({
      address: TASK_ESCROW_ADDRESS,
      abi: TaskEscrowABI,
      functionName: "createTask",
      args: [title],
      value: parseEther(budget),
    });
  };

  return { createTask, isPending, isConfirming, isSuccess, error, hash };
}

交易状态管理

typescript
// 三态处理:等待签名 → 确认中 → 完成/失败
const TransactionButton = ({ onSubmit }) => {
  const { isPending, isConfirming, isSuccess, error, hash } = useTransaction();

  if (isPending) return <Button disabled>等待钱包签名...</Button>;
  if (isConfirming) return <Button disabled>交易确认中...</Button>;
  if (isSuccess) return <Success txHash={hash} />;
  if (error) return <ErrorMessage error={error} />;

  return <Button onClick={onSubmit}>提交交易</Button>;
};

Aave 协议集成

核心接口

typescript
// Aave V3 Pool 接口
interface IAavePool {
  supply(
    asset: address,
    amount: uint256,
    onBehalfOf: address,
    referralCode: uint16
  ): void;
  withdraw(asset: address, amount: uint256, to: address): uint256;
  borrow(
    asset: address,
    amount: uint256,
    interestRateMode: uint256,
    referralCode: uint16,
    onBehalfOf: address
  ): void;
  repay(
    asset: address,
    amount: uint256,
    interestRateMode: uint256,
    onBehalfOf: address
  ): uint256;
}

收益查询

typescript
// hooks/useAavePosition.ts
export function useAavePosition(userAddress: string) {
  const { data: position } = useReadContract({
    address: AAVE_POOL_ADDRESS,
    abi: AavePoolABI,
    functionName: "getUserAccountData",
    args: [userAddress],
  });

  return {
    totalCollateral: position?.[0],
    totalDebt: position?.[1],
    availableBorrows: position?.[2],
    healthFactor: position?.[5],
  };
}

DAO 治理

提案结构

solidity
struct Proposal {
    uint256 id;
    address proposer;
    string description;
    uint256 forVotes;
    uint256 againstVotes;
    uint256 startBlock;
    uint256 endBlock;
    bool executed;
    ProposalType proposalType; // protocol | treasury | arbitration | fee
}

投票权计算

  • 基于 ERC20 代币持有量
  • 支持委托投票
  • 快照机制防止双花

测试规范

合约测试(Foundry)

solidity
// test/TaskEscrow.t.sol
contract TaskEscrowTest is Test {
    TaskEscrow escrow;
    address employer = makeAddr("employer");
    address agent = makeAddr("agent");

    function setUp() public {
        escrow = new TaskEscrow();
        vm.deal(employer, 10 ether);
    }

    function test_CreateTask() public {
        vm.prank(employer);
        uint256 taskId = escrow.createTask{value: 1 ether}("Test Task");
        assertEq(escrow.getTaskBudget(taskId), 1 ether);
    }

    function testFuzz_CreateTaskWithVariousBudgets(uint256 budget) public {
        vm.assume(budget > 0.01 ether && budget < 100 ether);
        vm.prank(employer);
        escrow.createTask{value: budget}("Fuzz Task");
    }
}

前端测试

typescript
// __tests__/useTaskContract.test.ts
import { renderHook, waitFor } from "@testing-library/react";
import { useCreateTask } from "../hooks/useTaskContract";

describe("useCreateTask", () => {
  it("should create task with correct parameters", async () => {
    const { result } = renderHook(() => useCreateTask());

    await result.current.createTask("Test Task", "1.0");

    await waitFor(() => {
      expect(result.current.isSuccess).toBe(true);
    });
  });
});

部署检查清单

  • 合约代码通过 Slither 静态分析
  • 单元测试覆盖率 > 80%
  • Gas 优化(storage 访问、循环优化)
  • 事件日志完整
  • 升级路径明确(代理模式)
  • 紧急暂停机制
  • 多签钱包配置
  • 测试网充分验证

常见问题

Q: 如何处理交易失败?

A: 使用 try-catch 捕获错误,解析错误信息,向用户展示友好提示。

Q: 如何优化 Gas 费用?

A: 批量操作、存储打包、使用 calldata、避免循环中的 storage 访问。

Q: 如何保证数据一致性?

A: 使用事件索引 + The Graph 查询,实现链上链下数据同步。