基于 EIP-3009 与 Permit2 的 EVM 支付实现:深入解读 @x402/evm 包
2026/9/17 6:11:36 网站建设 项目流程

基于 EIP-3009 与 Permit2 的 EVM 支付实现:深入解读 @x402/evm 包

【免费下载链接】x402A payments protocol for the internet. Built on HTTP.项目地址: https://gitcode.com/GitHub_Trending/x4/x402

@x402/evm是 x402 支付协议在 EVM(以太坊虚拟机)链上的官方实现,采用Exact支付方案,核心基于 EIP-3009TransferWithAuthorization签名授权,并支持以 Permit2 作为通用兜底。本文以 typescript/packages/mechanisms/evm/README.md 为主体,结合仓库源码与测试,完整梳理该包的客户端(Client)、Facilitator、资源服务器(Server)三种角色、V1/V2 协议差异、注册与 RPC 配置方式、签名者抽象、资产转移方法与支持网络清单,帮助你直接把这套"免 gas 的 HTTP 支付"能力接入自己的 TypeScript 应用。

包定位与三大组件

@x402/evm的目标是:在 EVM 兼容链上,让买家(客户端)通过链下签名完成支付授权,让 Facilitator(支付处理方)负责链上验证与结算,让资源服务器(Service)构建支付需求(Payment Requirements),从而把传统 HTTP 资源访问改造成"先支付、后访问"的按次计费接口。

从源码结构看,该包提供三类角色对应的实现,三者共享同一套 EVM 签名与 EIP-712 基础设施:

  • Client(付款方):为需要发起支付的应用程序设计,持有钱包/签名者,负责根据服务端返回的支付需求生成 EIP-3009 或 Permit2 支付载荷。对应实现见 client/scheme.ts 中的ExactEvmScheme
  • Facilitator(支付处理方):为验证并执行链上交易的支付处理器设计,负责verify(离线验证签名与需求)与settle(链上结算)。对应实现见 facilitator/scheme.ts。
  • Service(资源服务器):为接受支付并构建支付需求的资源服务器设计,负责生成PaymentRequirements并校验支付结果。

命名说明:README 中习惯称ExactEvmClient/ExactEvmFacilitator/ExactEvmServer,而当前源码中三个角色的实际类名统一为ExactEvmScheme(分别实现SchemeNetworkClient/SchemeNetworkFacilitator接口),通过不同的注册入口暴露。

安装与包导出结构

在任意 TypeScript 项目中安装:

npm install @x402/evm

该包依赖@x402/core(核心协议类型与客户端)、viem(EVM 交互)与zod(载荷校验),依赖声明可查看 package.json。

包的导出被划分为多个子路径,便于按需引入:

导出路径内容
@x402/evmV2 协议(CAIP-2 网络标识):ExactEvmSchemetoClientEvmSignertoFacilitatorEvmSignerClientEvmSigner/FacilitatorEvmSigner类型、Permit2 辅助函数(createPermit2ApprovalTxgetPermit2AllowanceReadParams)、EIP-3009/Permit2 载荷类型与常量
@x402/evm/v1V1 协议(简单网络名):ExactEvmSchemeV1NETWORKS常量
@x402/evm/exact/client客户端专用导出:ExactEvmSchemeregisterExactEvmSchemeEvmClientConfig类型
@x402/evm/exact/facilitatorFacilitator 专用导出:ExactEvmSchemeregisterExactEvmSchemeEvmFacilitatorConfig类型
@x402/evm/exact/server服务器专用导出:ExactEvmSchemeregisterExactEvmSchemeEvmResourceServerConfig类型
@x402/evm/exact/v1/client@x402/evm/exact/v1/facilitatorV1 客户端与 Facilitator 单独入口
@x402/evm/upto/*Upto 方案(按需支付)的 client/server/facilitator 实现

主入口 src/index.ts 还会额外导出PERMIT2_ADDRESSx402ExactPermit2ProxyAddressx402UptoPermit2ProxyAddresspermit2WitnessTypeseip3009ABI等链上常量,以及isPermit2Payload/isEIP3009Payload等类型守卫。

V2 与 V1 协议差异

该包同时维护 V2(现代 x402 协议,CAIP-2 网络标识)与 V1(旧协议,简单网络名)两套实现,差异对照如下:

维度V2(主包)V1(@x402/evm/v1
网络格式CAIP-2,如eip155:8453简单名称,如base-sepolia
通配符支持支持,如eip155:*不支持,固定网络列表
载荷结构部分载荷(核心层包裹元数据)完整载荷
扩展支持完整支持有限支持
默认有效期1 小时10 分钟(含缓冲)

V1 支持网络由 src/v1/index.ts 中的EVM_NETWORK_CHAIN_ID_MAP定义(即NETWORKS常量的来源):

[ "ethereum", "sepolia", "abstract", "abstract-testnet", "base-sepolia", "base", "avalanche-fuji", "avalanche", "iotex", "sei", "sei-testnet", "polygon", "polygon-amoy", "peaq", "story", "educhain", "skale-base-sepolia", "megaeth", "monad", "stable", "stable-testnet" ]

注意:README 中给出的 V1 网络清单是核心列表;当前源码EVM_NETWORK_CHAIN_ID_MAP还额外包含了ethereumsepoliamegaethmonadstablestable-testnet,并以getEvmChainIdV1(network)提供"网络名 → 链 ID"的映射能力,遇到未知网络名会抛出Unsupported v1 network错误。

用法一:直接注册(全控制)

最直接的方式是显式注册 scheme 到x402Client实例。README 示例中同时注册了 V2 通配符与 V1 网络:

import { x402Client } from "@x402/core/client"; import { ExactEvmScheme } from "@x402/evm"; import { ExactEvmSchemeV1 } from "@x402/evm/v1"; const client = new x402Client() .register("eip155:*", new ExactEvmScheme(signer)) .registerSchemeV1("base-sepolia", new ExactEvmSchemeV1(signer)) .registerSchemeV1("base", new ExactEvmSchemeV1(signer));

对于希望"一步到位"的场景,可以使用registerExactEvmScheme(见 client/register.ts),它一次性完成:

  1. 注册 V2 scheme:未传networks时注册eip155:*通配符,否则逐网络注册;
  2. 注册全部 V1 网络:遍历NETWORKS常量,为每个网络注册ExactEvmSchemeV1
  3. 应用客户端策略(policies)。

其配置对象EvmClientConfig支持字段:

  • signer(必填):用于创建支付载荷的 EVM 签名者;
  • paymentRequirementsSelector:可选的支付需求选择函数,缺省使用默认选择器(取第一个可用选项);
  • policies:可选PaymentPolicy[]
  • schemeOptions:可选 RPC 配置,支持单配置{ rpcUrl }或按链 ID 键控的多配置{ 8453: { rpcUrl } }
  • networks:可选,指定要注册的网络,缺省注册eip155:*
import { registerExactEvmScheme } from "@x402/evm/exact/client/register"; import { x402Client } from "@x402/core/client"; import { privateKeyToAccount } from "viem/accounts"; const account = privateKeyToAccount("0x..."); const client = new x402Client(); registerExactEvmScheme(client, { signer: account });

Extension RPC 配置(可选)

ExactEvmScheme的基础流程只需要签名者支持address+signTypedData。当服务端声明了EIP-2612 / ERC-20 Approval 的 gas sponsoring 扩展时,客户端还需要链上读取与交易签名能力(readContractgetTransactionCountestimateFeesPerGassignTransaction)。这些能力可以显式通过 RPC URL 配置补齐——SDK不会应用任何"链默认 RPC"兜底:

// 按网络显式注册 const client = new x402Client() .register("eip155:137", new ExactEvmScheme(signer, { rpcUrl: polygonRpcUrl })) .register("eip155:8453", new ExactEvmScheme(signer, { rpcUrl: baseRpcUrl })); // 通配符注册 + 按链 ID 键控的配置映射 const wildcardClient = new x402Client().register( "eip155:*", new ExactEvmScheme(signer, { 137: { rpcUrl: polygonRpcUrl }, 8453: { rpcUrl: baseRpcUrl }, }), );

底层逻辑在 shared/rpc.ts 中:

  • EvmSchemeOptions是"扁平配置{ rpcUrl }"与"按链 ID 键控映射Record<number, { rpcUrl }>"的联合类型,isConfigByChainId通过"所有键均为数字"判断属于哪种形态;
  • resolveRpcUrl(network, options)从 CAIP-2 网络标识解析出链 ID(getEvmChainId),再查表得到对应 RPC URL;
  • resolveExtensionRpcCapabilities(network, signer, options)会在签名者缺少readContract/getTransactionCount/estimateFeesPerGas时,用createPublicClient创建的 viem 公共客户端按需回填这些能力(RPC 客户端按 URL 缓存复用)。

用法二:基于 Config(更灵活)

不逐个手写注册时,可以直接从配置对象构建客户端:

import { x402Client } from "@x402/core/client"; import { ExactEvmScheme } from "@x402/evm"; const client = x402Client.fromConfig({ schemes: [ { network: "eip155:*", client: new ExactEvmScheme(signer) }, { network: "base-sepolia", client: new ExactEvmSchemeV1(signer), x402Version: 1 } ], policies: [myCustomPolicy] });

签名者抽象:ClientEvmSigner 与 FacilitatorEvmSigner

客户端与 Facilitator 对签名者的能力要求不同,定义集中在 src/signer.ts:

ClientEvmSigner(付款方)核心能力:

  • address:签名者地址;
  • signTypedData(message):签署 EIP-712 结构化数据(基础流程唯一必需的能力);
  • 可选readContract:链上读取,仅扩展富化(EIP-2612 / ERC-20 Approval)需要;
  • 可选signTransaction/getTransactionCount/estimateFeesPerGas:ERC-20 Approval gas sponsoring 需要。

典型构造方式是用 viem 的WalletClient扩展publicActions,或通过toClientEvmSigner(account, publicClient)组合:

import { createWalletClient, http } from "viem"; import { privateKeyToAccount } from "viem/accounts"; import { baseSepolia } from "viem/chains"; import { publicActions } from "viem/actions"; const client = createWalletClient({ account: privateKeyToAccount('0x...'), chain: baseSepolia, transport: http(), }).extend(publicActions);

toClientEvmSigner(signer, publicClient)会把签名者自身的可选能力与 publicClient 的能力合并:优先取签名者自带实现,缺失时回退到 publicClient 的readContract/getTransactionCount/estimateFeesPerGas

FacilitatorEvmSigner(结算方)要求更完整:getAddresses()(支持多地址轮换、密钥轮换与高可用)、readContractverifyTypedDatawriteContractsendTransactionwaitForTransactionReceiptgetCodetoFacilitatorEvmSigner(wallet)会将单地址钱包包装为getAddresses() => [address]以兼容多地址接口。

客户端如何路由:EIP-3009 还是 Permit2

ExactEvmScheme.createPaymentPayload(见 client/scheme.ts)依据支付需求中的requirements.extra.assetTransferMethod决定走哪条路径,缺省回退到"eip3009"(兼容旧版 Facilitator):

  • eip3009:直接生成 EIP-3009 载荷(createEIP3009Payload)。
  • permit2:先生成 Permit2 载荷,再按优先级尝试附加扩展:
    1. 若服务端声明eip2612GasSponsoring且签名者支持readContract,当 Permit2 授权额度不足时,自动签署 EIP-2612 permit(trySignEip2612PermitExtension),实现"免 gas 的首次授权";
    2. 否则尝试 ERC-20 Approval 扩展(trySignErc20ApprovalExtension),为不支持 EIP-2612 的代币签署approve交易;
    3. 都不适用时直接返回 Permit2 载荷。

Facilitator 侧则通过isPermit2Payload类型守卫区分两种载荷,分别路由到verifyPermit2/settlePermit2verifyEIP3009/settleEIP3009(见 facilitator/scheme.ts)。

Facilitator 构造时还支持两个开关(均默认false):

  • deployERC4337WithEIP6492:遇到未部署合约账户(如 ERC-4337 智能钱包)的签名时,通过 EIP-6492 部署并验证;
  • simulateInSettle:结算的二次校验阶段是否执行链上模拟。

链上结算:Permit2 代理合约

Permit2 路径的链上结算由 x402 自部署的代理合约完成,相关常量见 src/constants.ts:

  • PERMIT2_ADDRESS = 0x000000000022D473030F116dDEE9F6B43aC78BA3:Uniswap Permit2 的规范地址,所有 EVM 链通过 CREATE2 部署为同一地址;
  • x402ExactPermit2ProxyAddress = 0x402085c248EeA27D92E8b30b2C58ed07f9E20001:Exact 方案代理,vanity 地址0x4020...0001,基于 Arachnid 确定性部署器与 vanity-miner 挖掘的盐(前缀0x4020、后缀0001)确定性部署;
  • x402UptoPermit2ProxyAddress = 0x4020A4f3b7b90ccA423B9fabCc0CE57C6C240002:Upto 方案代理(0x4020...0002)。

EIP-712 结构化类型同样定义在 constants 中。Exact 方案的 Permit2 witness 为Witness(address to, uint256 validAfter),且类型必须按字母序排列(TokenPermissions < Witness)以匹配 Permit2 合约的 typehash 计算;Upto 方案的 witness 额外包含facilitator字段,确保只有被授权的 Facilitator 才能结算。

EIP-3009 侧,eip3009ABI提供了transferWithAuthorization(含v,r,s与 bytes 签名两种重载)、balanceOfversionnameauthorizationState等接口定义,authorizationTypes定义了TransferWithAuthorization的 EIP-712 字段(fromtovaluevalidAftervalidBeforenonce)。

资产转移方式与默认资产

该包支持两种资产转移方法(详见 docs/core-concepts/network-and-token-support.mdx):

  • EIP-3009:适用于原生支持transferWithAuthorization()的代币(如 USDC、EURC)。最简单、真正免 gas——一次链下签名即可完成授权,无需前置授权步骤,是首选路径。
  • Permit2:适用于任意 ERC-20 代币。作为通用兜底,需要一次性链上授权(可通过上文提到的 EIP-2612 / ERC-20 Approval gas sponsoring 扩展把这次授权也做成免 gas)。

两种方式都由 Facilitator 赞助 gas、以签名授权、保证支付安全。

当服务器使用美元字符串定价(如"$0.01")时,需要知道使用哪个稳定币。默认资产表在 shared/defaultAssets.ts 的DEFAULT_STABLECOINS中按 CAIP-2 网络标识索引,例如:

  • eip155:8453(Base 主网):USDC0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913,EIP-712 name"USD Coin"、version"2"、6 位小数;
  • eip155:84532(Base Sepolia):USDC("USDC"/"2"/ 6 位小数);
  • eip155:4326(MegaETH):MegaUSD("MegaUSD"/"1"/ 18 位小数)。

ExactDefaultAssetInfo还包含两个客户端行为提示字段:assetTransferMethod(对不支持 EIP-3009 的代币标记为"permit2")与supportsEip2612(标记代币是否实现 EIP-2612permit(),决定是否在extra中附带 name/version 供客户端签署免 gas permit)。当前默认资产链的完整清单与新增链的接入方式,参见 网络与代币支持文档。对于未配置默认资产的链,应使用registerMoneyParser()或直接以TokenAmountamountInAtomicUnits+ 资产地址)指定价格。

支持的网络

V2 网络(CAIP-2 标识,eip155:<chainId>覆盖任意 EVM 链):

  • eip155:1— Ethereum 主网
  • eip155:8453— Base 主网
  • eip155:84532— Base Sepolia
  • eip155:*— 通配符,匹配所有 EVM 链
  • 任意eip155:<chainId>网络

V1 网络(简单名称):见上文NETWORKS常量清单。

开发与测试命令

仓库内对该包提供了完整的开发脚本(见 package.json):

# 构建(tsup 打包 CJS + ESM + 类型声明) npm run build # 单元测试(vitest) npm run test # 集成测试(独立配置 vitest.integration.config.ts) npm run test:integration # Lint 与格式检查 npm run lint npm run format

测试覆盖集中在 test 目录:unit/exact/下有针对客户端 RPC 回填、载荷创建与 Facilitator 验证/结算的测试,unit/v1/覆盖 V1 客户端与 Facilitator,integrations/exact-evm.test.ts则提供 Exact 方案的端到端集成验证。

相关包与进一步阅读

  • @x402/core:核心协议类型与客户端(x402Client、策略、支付需求选择器);
  • @x402/fetch:带自动支付处理的 HTTP 封装;
  • @x402/svm:Solana/SVM 实现;
  • @x402/stellar:Stellar 实现;
  • 钱包端与服务器端完整接入流程,可参考 docs/getting-started/quickstart-for-buyers.mdx 与 docs/getting-started/quickstart-for-sellers.mdx;
  • 链上结算合约本体位于 contracts/evm/src/x402ExactPermit2Proxy.sol,其测试见 contracts/evm/test/x402ExactPermit2Proxy.t.sol,可作为理解settle/settleWithPermit语义的补充材料。

【免费下载链接】x402A payments protocol for the internet. Built on HTTP.项目地址: https://gitcode.com/GitHub_Trending/x4/x402

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询