feat(core,x402): GTokenAuthorization EIP-3009 ABI + signing helpers#23
Conversation
GTokenAuthorization v2.2.0 (SuperPaymaster main, 2026-05-21): - Add GTokenAuthorization.json ABI to @aastar/core - Export GTokenAuthorizationABI / GTokenAuthorizationArtifact - Add gTokenAuthorization viem action factory (gTokenAuthorizationActions) EIP-3009 signing (x402/eip3009): - Add ReceiveWithAuthorization and CancelAuthorization typed-data schemas - Add signReceiveWithAuthorization(): msg.sender must equal `to` on-chain - Add signCancelAuthorization(): signed by original authorizer - Add GTOKEN_EIP712_DOMAIN constant (name='GToken', version='1') Constraints: - MAX_AUTH_VALIDITY = 5 min (validBefore - validAfter < 300s) - receiveWithAuthorization: relay cannot submit, only recipient can - xPNTsToken is NOT included in signature (relay-supplied hint for RC-2)
🤖 Round 1 — Claude Code Review (Local Model)
🔴 CriticalNone. 🟠 HighH1 — On-chain
Required: Add a comment at the type definition level that explicitly states the trust boundary: "the signer does not commit to If 🟡 MediumM1 — No client-side The contract enforces // Suggested guard at top of signReceiveWithAuthorization:
if (params.validBefore - params.validAfter > 300n) {
throw new Error(`Authorization window ${params.validBefore - params.validAfter}s exceeds MAX_AUTH_VALIDITY (300s)`);
}
if (params.validBefore <= params.validAfter) {
throw new Error('validBefore must be greater than validAfter');
}Note: M2 — export const GTOKEN_EIP712_DOMAIN = { name: 'GToken', version: '1' } as const;If a caller passes Suggest renaming to M3 — const writeContract = (...) => (client as any).writeContract({ ... });The cast to 🔵 LowL1 — The test: it('ReceiveWithAuthorization has the same 6 fields as TransferWithAuthorization', () => {
expect(receive).toHaveLength(transfer.length);
expect(fieldNames(receive)).toEqual(fieldNames(transfer));
});This passes only because the schemas happen to be identical. It doesn't verify the actual expect(RECEIVE_TYPEHASH).not.toEqual(TRANSFER_TYPEHASH);L2 — Timestamp in ABI export comment will rot // GTokenAuthorization v2.2.0 — EIP-3009 gasless transfers (SuperPaymaster main, 2026-05-21)Deployment date in source comment will be misleading after maintenance deploys or version bumps. L3 — ✅ VerdictApprove with requests. Clean implementation overall — good test coverage for the new actions and signing helpers. H1 ( Round 2 (Codex) will follow. |
🤖 Round 2 — Codex Review
🔴 Critical无可验证发现(代码不可访问)。 🟠 HighH1 — 现有 通过读取可访问的 新 PR 的 GToken 专用 helper(
通用 x402/USDC helper 不需要此约束(合约不同)。 🟡 MediumM1 — 即使不考虑 MAX_AUTH_VALIDITY, 🔵 Low / N/A以下条目因代码不可访问无法验证:
|
⚔️ PK 对比 — Claude vs Codex (PR #23)
🏆 PK 结论Claude 覆盖更完整(Codex 受分支访问限制)。Codex 贡献了一个有价值的补充:M1 时间窗口验证只应加在 GToken 专用 helper 上,不应加在通用 📋 必须修复后 merge
立即开始 PR #20 review。 |
…trust model - signReceiveWithAuthorization now validates validBefore > validAfter and window <= 300s before signing, mirroring GTokenAuthorization RC-1 on-chain check - GTokenAuthorizationActions: expand xPNTsToken docs to explain it is NOT part of the EIP-712 signature (relay-supplied) and the RC-2 trust implications; add JSDoc on each arg for discoverability
🔄 Re-Review Round 2 — PR #23 (feat/gtokenauth-eip3009)Claude Round 2Fix commit [Claude H1] signReceiveWithAuthorization 校验 ✅ FIXED if (params.validBefore <= params.validAfter) {
throw new Error('validBefore must be greater than validAfter');
}
if (params.validBefore - params.validAfter > 300n) {
throw new Error(`Authorization window ${...}s exceeds MAX_AUTH_VALIDITY (300s)`);
}✓ 无效窗口在链上提交前即被拦截。 [Claude M1] xPNTsToken 信任模型文档 ✅ FIXED Codex Round 2[NEW H1]
建议修复方案(二选一):
[Low] 校验逻辑的负向测试缺失 PK Summary
原始两个 issue 均已修复。但 新 H1 需要处理后才建议合并:若 GTokenAuthorization 对 transfer 同样执行 MAX_AUTH_VALIDITY,请在对应签名路径加上防护。 |
jhfnetboy
left a comment
There was a problem hiding this comment.
原始两个 blocking issue 均已修复。新发现的 H1(signTransferWithAuthorization 缺少 MAX_AUTH_VALIDITY 校验守卫)需要处理后合并。
…IDITY guard GTokenAuthorization enforces 300s on-chain for both transfer and receive. Add a GToken-specific signGTokenTransferWithAuthorization wrapper that validates the authorization window before signing, while keeping the generic signTransferWithAuthorization unchanged for non-GToken callers. Also add negative test cases for both GToken signing helpers covering validBefore <= validAfter and window > 300s rejection paths.
🥊 对抗式双重 Review:Claude(Round 1)+ Codex(Round 2)
Round 1:Claude 审查🟡 Medium
🔵 Low
✅ 正面评价
Round 2:Codex 审查🟡 Mediumsigner/authorizer 地址不做客户端校验——可导致
🔵 Low
⚔️ PK 对比表
🏁 联合结论结论:Request Changes(需修改后合并) 必须修复(阻塞合并):
建议修复(不阻塞但强烈推荐):
可合并后跟进(下次迭代):
两位 reviewer 在核心问题上方向一致:account guard 不对称是真实缺陷,需要修复。Codex 提出的 signer/from 校验是一个额外的防御层,推荐采纳。整体实现质量高,修复上述 2 个必修项即可合并。
|
jhfnetboy
left a comment
There was a problem hiding this comment.
Request changes per combined Claude + Codex review — see detailed comment above.
…oken signers signGTokenTransferWithAuthorization was missing the walletClient.account guard that signReceiveWithAuthorization and signCancelAuthorization both have, creating an asymmetric API. Also add signer-address-must-match-from/authorizer checks to all three GToken signing functions: wrong account produces a clear client-side error instead of consuming gas on a guaranteed-to-fail tx. Tests updated to match: happy paths use the correct signing account, new negative cases cover no-account and signer-mismatch paths.
jhfnetboy
left a comment
There was a problem hiding this comment.
LGTM - all required and suggested fixes addressed. account guard + signer/from consistency checks added to all three signing functions, tests updated across the board. Ready to merge.
Summary
@aastar/core: 新增GTokenAuthorization.jsonABI(EIP-3009 无 gas 转账合约,v2.2.0);导出GTokenAuthorizationABI/GTokenAuthorizationArtifact;新增gTokenAuthorizationActions()viem action factory@aastar/x402: 新增ReceiveWithAuthorization、CancelAuthorizationEIP-712 typed-data schema;新增signReceiveWithAuthorization()、signCancelAuthorization()签名 helper;新增GTOKEN_EIP712_DOMAIN常量关键约束
MAX_AUTH_VALIDITYreceiveWithAuthorizationmsg.sender必须等于to(CallerMustBeRecipient);relay 无法代提交xPNTsToken字段Files Changed
packages/core/src/abis/GTokenAuthorization.json— 新增 ABIpackages/core/src/abis/abi.config.json— ABI 完整性 hash 注册packages/core/src/abis/index.ts— 导出GTokenAuthorizationABIpackages/core/src/actions/gTokenAuthorization.ts— viem actionspackages/core/src/actions/index.ts— 重导出packages/x402/src/eip3009.ts— 新增类型定义和签名函数packages/x402/src/index.ts— 新增导出Test Plan
pnpm --filter @aastar/core test— 单测通过(含 gTokenAuthorization.test.ts)pnpm --filter @aastar/x402 test— EIP-3009 authorization 测试通过pnpm -r build— 构建通过,无类型错误receiveWithAuthorization,确认签名有效