Skip to content

SDK: Permit2 interactions (stacked on #23) - #24

Merged
kylecrawshaw merged 3 commits into
mainfrom
claude/charming-carson-hr92hc-permit2
Sep 30, 2026
Merged

kylecrawshaw merged 3 commits into
mainfrom
claude/charming-carson-hr92hc-permit2

Conversation

@kylecrawshaw

Copy link
Copy Markdown
Member

Stacked on #23 (ERC-20 interactions), which is stacked on #22 (balances). Review only the last commit.

What

packages/sdk/src/permit2.ts: viem actions plus a permit2Actions() client extension for the canonical Permit2 contract (PERMIT2_ADDRESS, shared by every Radius network), covering both of its flows.

  • One-time approval: approvePermit2 (unlimited by default, the x402 model), getPermit2Approval
  • SignatureTransfer (what x402 uses): signPermit2Transfer signs a PermitTransferFrom, or PermitWitnessTransferFrom when a witness is given; permit2TransferFrom submits it from the spender (permitTransferFrom or permitWitnessTransferFrom with the witness hash and type string); isPermit2NonceUsed reads the nonce bitmap; nonces are random by default
  • AllowanceTransfer (Uniswap-style): signPermit2Allowance signs a PermitSingle with the nonce read from Permit2; permit2Permit records it; permit2AllowanceTransferFrom draws on it; getPermit2Allowance returns { amount, expiration, nonce }
  • Exported building blocks: permit2Domain, PERMIT_TRANSFER_FROM_TYPES, PERMIT_SINGLE_TYPES, permit2WitnessTypeString, permit2WitnessHash, PERMIT2_ABI
  • Deadlines default to 600 s, the same cap the x402 client applies

The witness type string is derived with EIP-712's ordering rule (referenced structs sorted by name). Tests pin it against the x402 Witness(address to,address facilitator,uint256 validAfter) layout radius-cli signs, and check it against viem's own type hash so the on-chain string verifies the signed digest.

Examples

  • examples/agent-buyer/permit2-pull.mjs (new): payer signs a transfer off-chain, a collector account pulls the SBC with it and shows the nonce flipping to used.
  • examples/agent-buyer/fresh-wallet.mjs: reads the Permit2 approval through getPermit2Approval.

Tests

18 new tests; every signature is recovered to the owner and every submitted call is decoded from the raw transaction. SDK total 110 passing. Not run against a live node from this environment; permit2-pull.mjs is the live check.

🤖 Generated with Claude Code

https://claude.ai/code/session_01D1P1hkDQJ1iTaqumjZWtg8


Generated by Claude Code

@kylecrawshaw
kylecrawshaw added this pull request to stack #32 September 23, 2026 14:34
@kylecrawshaw
kylecrawshaw force-pushed the claude/charming-carson-hr92hc-permit2 branch from 1f7b7b4 to 2cadb51 Compare September 23, 2026 14:45
@kylecrawshaw
kylecrawshaw force-pushed the claude/charming-carson-hr92hc-permit2 branch from 2cadb51 to ad7d0a4 Compare September 23, 2026 15:54
@kylecrawshaw
kylecrawshaw force-pushed the claude/charming-carson-hr92hc-permit2 branch 2 times, most recently from d31c44f to fa2daa4 Compare September 23, 2026 18:53
@github-actions

github-actions Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

🦋 Changeset detected

Latest commit: 8f7d178

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
radius-sdk Minor
radius-cli Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

azf20
azf20 previously approved these changes Sep 24, 2026
@kylecrawshaw
kylecrawshaw force-pushed the claude/charming-carson-hr92hc-permit2 branch from a42a4d4 to 4de5806 Compare September 25, 2026 01:54
Base automatically changed from claude/charming-carson-hr92hc-erc20 to main September 25, 2026 13:59
claude and others added 3 commits September 25, 2026 09:59
New src/permit2.ts with viem actions and a permit2Actions() client
extension for the canonical Permit2 contract, covering both flows:

- SignatureTransfer: signPermit2Transfer (PermitTransferFrom, or
  PermitWitnessTransferFrom with a witness), permit2TransferFrom for the
  spender, isPermit2NonceUsed against the nonce bitmap, random nonces.
- AllowanceTransfer: signPermit2Allowance (PermitSingle, nonce read from
  Permit2), permit2Permit, permit2AllowanceTransferFrom,
  getPermit2Allowance.
- approvePermit2 / getPermit2Approval for the one-time ERC-20 approval.

The EIP-712 domain, type sets, witness hash and witness type string are
exported; the type string is derived with EIP-712's ordering rule and
tested against the x402 layout and viem's type hash.

Tests use the in-memory node from the ERC-20 change and recover every
signature to the owner. Examples: fresh-wallet reads the Permit2 approval
through the new action; permit2-pull.mjs signs a transfer off-chain and
pulls it from a second account.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D1P1hkDQJ1iTaqumjZWtg8
The Permit2 actions and EIP-712 helpers import viem, which the root entry
point must not load at runtime. Their runtime exports move to
radius-sdk/client alongside the balance and ERC-20 actions; the root keeps
only the types. Add the changeset.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…etwork

Mirror the ERC-20 change: only the Radius presets have a default token, a
custom chain throws a `config` error naming the action until `token` is
given, and `permit2Actions({ network })` resolves the default from the
network's payment asset after checking it is the client's chain. Document
`wait: false` as `pending`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@kylecrawshaw
kylecrawshaw force-pushed the claude/charming-carson-hr92hc-permit2 branch from 4de5806 to 8f7d178 Compare September 25, 2026 13:59

@erikzrekz erikzrekz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few comments and ideas. Nothing major blocking from my POV.

Comment thread packages/sdk/README.md
veto, every allowance the payment client grants. The plain `erc20Actions().approve` on your own
wallet client has no hook: it is you signing, not the SDK.

## Permit2

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EIP-3009 will come next, yes?

* Uniswap Permit2 interactions as viem actions, for the canonical Permit2 contract every
* Radius network shares (`PERMIT2_ADDRESS`). Two Permit2 flows are covered:
*
* SignatureTransfer (what x402 uses): the owner signs a one-off `PermitTransferFrom`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to include a way to invalidate a nonce if the buyer never ends up using it. Maybe something like invalidatePermit2Nonce()? Agents will probably find this useful.

There's also revoking permit2 allowance entirely, which is a lockdown sort of feature.


const word = (v: bigint | number) => numberToHex(BigInt(v), { size: 32 });

/** Permit2 + SBC view answers. */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we get a real e2e witness bound permit produced by this code against the test facilitator? That would be a good demonstration that this works against another service.

@kylecrawshaw

Copy link
Copy Markdown
Member Author

I've created issues to track the above as follow up work

@kylecrawshaw
kylecrawshaw merged commit c504f65 into main Sep 30, 2026
4 checks passed
@kylecrawshaw
kylecrawshaw deleted the claude/charming-carson-hr92hc-permit2 branch September 30, 2026 20:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants