把 NEAR 的 “链抽象与 AI 智能体” 搬进以太坊:用 Solidity 0.8.28 打造元交易意图结算核心底座

引言

兄弟们,如果你还在为切不完的跨链桥、交不起的多链 Gas 费、以及反人类的"各链储备原生代币"搞得焦头烂额,那说明你还没看清下一轮牛市的密码------链抽象(Chain Abstraction)智能体网络(Agentic Web)

在 NEAR Protocol 的生态里,用户早就过上了"只需动动嘴(自然语言/链下意图),后台全自动跨链结算"神仙日子。凭什么 EVM 用户就得当苦力?

今天咱们直接上硬菜!抛弃泛泛而谈的宏大叙事,直接用生产级代码说话:带你用 Solidity 0.8.28 + OpenZeppelin V5 ,在 EVM 链上硬核复刻一套类 NEAR 的免 Gas 意图(Intent)清算引擎与 AI 智能体结算枢纽

一、 为什么 EVM 需要"类 NEAR"的意图架构?

传统的 EVM 交互逻辑是"命令式"的:用户必须自己计算 Gas、自己调用合约、自己承担滑点。而 NEAR 开辟的 Intents 范式 彻底颠覆了这一点:

  1. 人类可读账户: 结合 ERC-4337 与 ENS,告别冗长冷酷的 0x,拥抱如 alice.eth 的丝滑体验。
  2. 免 Gas 与极简跨链: 用户只管在链下签个"意图(Intent)",底层的 Solver(求解器) 或 AI 节点抢着代付 Gas 并塞回资产。
  3. 隐私 AI 自治执行: 核心合约化身公正的"裁判长",结合 TEE(可信执行环境) ,让链下 AI 智能体在绝对安全的环境下完成复杂的多链路由。

话不多说,直接上核心合约!


二、 核心合约实战:NearStyleIntentExecutor

基于最新的 Solidity 0.8.28OpenZeppelin V5 模块化标准,我们编写了这份兼顾极致安全与高性能的清算合约。

js 复制代码
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

// 引入 OpenZeppelin V5 标准库
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/Nonces.sol";

/**
 * @title NearStyleIntentExecutor
 * @notice 模拟 NEAR 链抽象与 AI 意图的核心结算合约
 * @dev 基于 Solidity 0.8.28 与 OpenZeppelin V5 编写
 */
contract NearStyleIntentExecutor is EIP712, AccessControl, Nonces {
    using SafeERC20 for IERC20;

    // 定义角色:Solver(求解器)或 AI 智能体,有权在链上提交并执行用户的意图
    bytes32 public constant SOLVER_ROLE = keccak256("SOLVER_ROLE");

    // EIP-712 结构化哈希:定义用户"意图"的数据结构
    bytes32 public constant INTENT_TYPEHASH = keccak256(
        "Intent(address user,address fromToken,uint256 fromAmount,address toToken,uint256 minToAmount,uint256 fee,uint256 deadline,uint256 nonce)"
    );

    // 结构体:用户链下意图数据
    struct Intent {
        address user;         // 发出意图的用户
        address fromToken;     // 用户支付的资产
        uint256 fromAmount;    // 用户支付的资产数量
        address toToken;       // 用户期望获得的资产
        uint256 minToAmount;   // 用户要求的最低获得数量(防滑点)
        uint256 fee;           // 支付给 Solver/AI 的小费(从 fromToken 中扣除)
        uint256 deadline;      // 意图截止时间戳
    }

    // 事件:意图执行成功
    event IntentExecuted(
        bytes32 indexed intentHash,
        address indexed user,
        address indexed solver,
        address fromToken,
        address toToken,
        uint256 fromAmount,
        uint256 toAmount
    );

    /**
     * @dev 构造函数,初始化 EIP-712 域和权限控制
     */
    constructor(address admin) EIP712("NearStylePlatform", "1.0.0") {
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(SOLVER_ROLE, admin); // 默认管理员兼任第一个 Solver
    }

    /**
     * @notice 由经授权的 Solver 或 AI 智能体调用,在链上结算用户的离线意图
     * @param intent 用户签名的意图结构体
     * @param signature 用户的 EIP-712 密码学签名
     * @param actualToAmount Solver 实际塞回给用户的目标代币数量
     */
    function executeIntent(
        Intent calldata intent,
        bytes calldata signature,
        uint256 actualToAmount
    ) external onlyRole(SOLVER_ROLE) {
        // 1. 检查截止时间
        if (block.timestamp > intent.deadline) revert("Intent expired");
        
        // 2. 检查滑点防御
        if (actualToAmount < intent.minToAmount) revert("High slippage or insufficient output");

        // 3. 校验 Nonce 并递增(防止重放攻击)- 使用 OZ V5 的 _useNonce
        uint256 currentNonce = _useNonce(intent.user);

        // 4. 计算 EIP-712 哈希
        bytes32 structHash = keccak256(
            abi.encode(
                INTENT_TYPEHASH,
                intent.user,
                intent.fromToken,
                intent.fromAmount,
                intent.toToken,
                intent.minToAmount,
                intent.fee,
                intent.deadline,
                currentNonce
            )
        );
        bytes32 hash = _hashTypedDataV4(structHash);

        // 5. 验证签名者是否为发出意图的用户本身
        address signer = ECDSA.recover(hash, signature);
        if (signer != intent.user) revert("Invalid intent signature");

        // 6. 价值流转结算 (Asset Flow)
        // a. 从用户处扣除原始资产(扣除额包含给 Solver 的小费)
        IERC20(intent.fromToken).safeTransferFrom(intent.user, _msgSender(), intent.fromAmount);

        // b. Solver / AI 智能体将目标资产注入给用户(完成了跨链/DEX路由后的结果)
        IERC20(intent.toToken).safeTransferFrom(_msgSender(), intent.user, actualToAmount);

        // 释放执行事件
        emit IntentExecuted(hash, intent.user, _msgSender(), intent.fromToken, intent.toToken, intent.fromAmount, actualToAmount);
    }
}

核心安全机制解密(OpenZeppelin V5 杀手级特性):

  • 原子化 Nonce 追踪 (_useNonce): 告别手写计数器的蛮荒时代。V5 内置模块在每次意图完成时自动加锁自增,同一张离线签名在链上永久失效,防重放直接拉满。
  • 可信隔离屏障 (AccessControl + TEE): 为什么不能像 UniswapX 那样公开给全网撮合?因为我们要复现 NEAR 的 AI 智能体网络!只有跑在硬件隔离区(TEE)里的白名单 Solver 节点才能调用,从根源上掐断了恶意抢跑(Front-running)和夹子攻击。
  • 现代代币防御 (SafeERC20): 完美适配各种奇葩实现的非标准 ERC-20,防范转账不返回 bool 导致的恶意吞钱。

三、 生产级集成测试:用 Viem 验证闭环

光说不练假把式,我们直接上现代化的 TypeScript 测试脚本(基于 ViemNode.js 原生测试框架),模拟真实用户签名、Solver 代付执行的全流程:

  • 测试用例:NearStyleIntentExecutor Integration Test
    • 场景 1:链抽象核心流转 --- 用户链下签名意图,Solver 成功在链上代付 Gas 执行
    • 场景 2:权限拦截 --- 未被授权 SOLVER_ROLE 的恶意节点无法提交并结算用户意图
    • 场景 3:防重放攻击 --- 同一个用户签名意图无法被执行第二次
    • 场景 4:滑点防御 --- 当实际塞给用户的代币少于用户意图的最低要求时回滚
js 复制代码
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { parseEther, getAddress, maxUint256 } from "viem";
import { network } from "hardhat";

describe("NearStyleIntentExecutor Integration Test", function () {
  /**
   * 部署及初始化脚手架
   */
  async function deployFixture() {
    const { viem } = await (network as any).connect();
    const [admin, user, solver, randomUser] = await viem.getWalletClients();
    const publicClient = await viem.getPublicClient();

    // 1. 部署 NearStyleIntentExecutor 结算合约,并指定 admin
    const executor = await viem.deployContract("NearStyleIntentExecutor", [admin.account.address]);

    // 2. 为测试准备两个模拟 ERC20 代币:用户支付的 (fromToken) 和 期望获得的 (toToken)
    // 假设使用参考脚本中的 BoykaYuriToken,或任意符合标准的 MockERC20
    const fromToken = await viem.deployContract("BoykaYuriToken", [admin.account.address, admin.account.address]);
    const toToken = await viem.deployContract("BoykaYuriToken", [admin.account.address, admin.account.address]);

    // 3. 给 Solver 角色赋权(让 solver 钱包拥有 SOLVER_ROLE)
    const SOLVER_ROLE = await executor.read.SOLVER_ROLE();
    await executor.write.grantRole([SOLVER_ROLE, solver.account.address], { account: admin.account });

    // 4. 为用户和 Solver 分配初始代币并授权
    const initialAmount = parseEther("1000");
    await fromToken.write.transfer([user.account.address, initialAmount], { account: admin.account });
    await toToken.write.transfer([solver.account.address, initialAmount], { account: admin.account });

    // 用户授权给 Executor 扣款,Solver 授权给 Executor 扣款结算
    await fromToken.write.approve([executor.address, maxUint256], { account: user.account });
    await toToken.write.approve([executor.address, maxUint256], { account: solver.account });

    const chainId = BigInt(await publicClient.getChainId());

    return {
      executor,
      fromToken,
      toToken,
      admin,
      user,
      solver,
      randomUser,
      publicClient,
      chainId,
    };
  }

  /**
   * 辅助函数:在链下为用户生成符合 EIP-712 标准的意图签名
   */
  async function signUserIntent(params: {
    client: any;
    verifyingContract: `0x${string}`;
    chainId: bigint;
    intent: any;
    nonce: bigint;
  }) {
    const domain = {
      name: "NearStylePlatform",
      version: "1.0.0",
      chainId: Number(params.chainId),
      verifyingContract: params.verifyingContract,
    } as const;

    const types = {
      Intent: [
        { name: "user", type: "address" },
        { name: "fromToken", type: "address" },
        { name: "fromAmount", type: "uint256" },
        { name: "toToken", type: "address" },
        { name: "minToAmount", type: "uint256" },
        { name: "fee", type: "uint256" },
        { name: "deadline", type: "uint256" },
        { name: "nonce", type: "uint256" },
      ],
    } as const;

    const message = {
      user: params.intent.user,
      fromToken: params.intent.fromToken,
      fromAmount: params.intent.fromAmount,
      toToken: params.intent.toToken,
      minToAmount: params.intent.minToAmount,
      fee: params.intent.fee,
      deadline: params.intent.deadline,
      nonce: params.nonce,
    } as const;

    return await params.client.signTypedData({
      account: params.client.account,
      domain,
      types,
      primaryType: "Intent",
      message,
    });
  }

  it("场景 1:链抽象核心流转 --- 用户链下签名意图,Solver 成功在链上代付 Gas 执行", async function () {
    const { executor, fromToken, toToken, user, solver, publicClient, chainId } = await deployFixture();

    const fromAmount = parseEther("100");
    const minToAmount = parseEther("95");
    const actualToAmount = parseEther("97"); // 满足并优于最低滑点要求
    const fee = parseEther("2");            // 激励给 Solver 的小费
    const deadline = BigInt(Math.floor(Date.now() / 1000) + 3600); // 1小时后过期

    const intent = {
      user: user.account.address,
      fromToken: fromToken.address,
      fromAmount,
      toToken: toToken.address,
      minToAmount,
      fee,
      deadline,
    };

    // 获取当前 Nonce
    const nonce = await executor.read.nonces([user.account.address]);

    // 用户在链下进行 EIP-712 签名
    const signature = await signUserIntent({
      client: user,
      verifyingContract: executor.address,
      chainId,
      intent,
      nonce,
    });

    // 记录执行前各方的资产余额
    const userFromBefore = await fromToken.read.balanceOf([user.account.address]);
    const userToBefore = await toToken.read.balanceOf([user.account.address]);
    const solverFromBefore = await fromToken.read.balanceOf([solver.account.address]);
    const solverToBefore = await toToken.read.balanceOf([solver.account.address]);

    // Solver 拿着用户的签名在链上触发执行(Gas 费由 solver 的钱包地址扣除)
    await executor.write.executeIntent([intent, signature, actualToAmount], {
      account: solver.account,
    });

    // 验证资产清算逻辑是否准确
    assert.equal(
      await fromToken.read.balanceOf([user.account.address]),
      userFromBefore - fromAmount,
      "用户应被扣除指定的 input 资产"
    );
    assert.equal(
      await toToken.read.balanceOf([user.account.address]),
      userToBefore + actualToAmount,
      "用户应收到 Solver 注入的 output 资产"
    );
    assert.equal(
      await fromToken.read.balanceOf([solver.account.address]),
      solverFromBefore + fromAmount,
      "Solver 应全额接收用户的 input 资产(包含小费)"
    );
    assert.equal(
      await toToken.read.balanceOf([solver.account.address]),
      solverToBefore - actualToAmount,
      "Solver 的 output 资产应相应扣除"
    );

    // 验证 OpenZeppelin V5 的 Nonce 自增
    const newNonce = await executor.read.nonces([user.account.address]);
    assert.equal(newNonce, nonce + 1n, "执行成功后 Nonce 应自增 1");
  });

  it("场景 2:权限拦截 --- 未被授权 SOLVER_ROLE 的恶意节点无法提交并结算用户意图", async function () {
    const { executor, fromToken, toToken, user, randomUser, chainId } = await deployFixture();

    const intent = {
      user: user.account.address,
      fromToken: fromToken.address,
      fromAmount: parseEther("10"),
      toToken: toToken.address,
      minToAmount: parseEther("9"),
      fee: parseEther("1"),
      deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
    };

    const signature = await signUserIntent({
      client: user,
      verifyingContract: executor.address,
      chainId,
      intent,
      nonce: 0n,
    });

    // 恶意未经授权的外部用户调用应当被 AccessControl 拒绝
    await assert.rejects(
      async () => {
        await executor.write.executeIntent([intent, signature, parseEther("9.5")], {
          account: randomUser.account,
        });
      },
      /AccessControlUnauthorizedAccount/,
      "非允许的 Solver 不允许触发意图清算"
    );
  });

  it("场景 3:防重放攻击 --- 同一个用户签名意图无法被执行第二次", async function () {
    const { executor, fromToken, toToken, user, solver, chainId } = await deployFixture();

    const intent = {
      user: user.account.address,
      fromToken: fromToken.address,
      fromAmount: parseEther("10"),
      toToken: toToken.address,
      minToAmount: parseEther("9"),
      fee: parseEther("1"),
      deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
    };

    const signature = await signUserIntent({
      client: user,
      verifyingContract: executor.address,
      chainId,
      intent,
      nonce: 0n,
    });

    // 第一次提交:成功
    await executor.write.executeIntent([intent, signature, parseEther("9.5")], { account: solver.account });

    // 第二次恶意重放同一笔签名:应当失败(因为 Nonce 已经变成了 1,与签名中的 0 不符)
    await assert.rejects(
      async () => {
        await executor.write.executeIntent([intent, signature, parseEther("9.5")], { account: solver.account });
      },
      /Invalid intent signature/,
      "同一意图被重放时因签名校验不匹配应该被拦截"
    );
  });

  it("场景 4:滑点防御 --- 当实际塞给用户的代币少于用户意图的最低要求时回滚", async function () {
    const { executor, fromToken, toToken, user, solver, chainId } = await deployFixture();

    const intent = {
      user: user.account.address,
      fromToken: fromToken.address,
      fromAmount: parseEther("10"),
      toToken: toToken.address,
      minToAmount: parseEther("9.5"), // 用户卡死:最低接受 9.5
      fee: parseEther("1"),
      deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
    };

    const signature = await signUserIntent({
      client: user,
      verifyingContract: executor.address,
      chainId,
      intent,
      nonce: 0n,
    });

    // Solver 试图恶劣滑点恶意扣留,只塞回 9.0 个代币
    await assert.rejects(
      async () => {
        await executor.write.executeIntent([intent, signature, parseEther("9.0")], { account: solver.account });
      },
      /High slippage or insufficient output/,
      "滑点超出用户预期应予以安全回滚"
    );
  });
});

四、 总结与未来展望

通过这套方案,我们成功把 NEAR 链抽象最精髓的 "Intent-Centric(以意图为中心)" 架构在 EVM 链上跑通了。

在实战中,你可以把这套合约部署在 Arbitrum、Base 等高性能 L2 上,甚至结合 Fhenix、Inco 等全同态加密(FHE)隐私公链。当用户只需要在前端输入一句话:"帮我把账户里的 100 U 换成收益最高的主流资产",后台由 TEE 中的 AI 智能体与 Solver 网络无缝接管跨链、路由与清算------这,就是属于智能体经济(Agentic Economy)的绝对未来。

相关推荐
磐链科技20 小时前
区块链开发公司发展趋势分析:智能合约、DApp与行业数字化应用探索
区块链·智能合约
木西2 天前
链上再保险怎么落地?Re Protocol RWA 分层合约架构深度拆解
web3·智能合约·solidity
磐链科技3 天前
Web3 钱包安全与体验优化:MPC、TEE、交易模拟与 DApp 连接设计
区块链·智能合约
磐链科技4 天前
区块链DApp开发部署篇:多链上线、Gas优化与运营监控
区块链·智能合约
磐链科技4 天前
区块链DApp开发实战:Solidity智能合约设计与测试
区块链·智能合约
木西5 天前
CME 大宗商品链上实验:基于 Uniswap v4 Hook 的合约架构深度解析
web3·智能合约·solidity
阿菜ACai6 天前
20260913-Ampleforth 安全事件:黑客恶意提案掏空金库,路人善意取消保卫资产
智能合约
木西8 天前
深度拆解 Ether.fi 核心架构:当 Liquid Restaking 遇上 ERC-4337 与智能金库
web3·智能合约·solidity
明月_清风8 天前
Foundry Invariant Testing 实战:ERC-4626 + Handler + Ghost Variable
后端·web3·solidity