Skip to content

Commit 9a85ba1

Browse files
update buyers guide
* update delegations guide * update the recurring payments guide * disable the advanced permissions guide
1 parent 397cc8e commit 9a85ba1

3 files changed

Lines changed: 167 additions & 308 deletions

File tree

gator-sidebar.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,9 @@ const sidebar = {
127127
collapsed: true,
128128
items: [
129129
'guides/x402/buyer/delegations',
130-
'guides/x402/buyer/advanced-permissions',
130+
// Disable advanced permissions guide until MetaMask Extension
131+
// is released with the new rules and permission type.
132+
// 'guides/x402/buyer/advanced-permissions',
131133
'guides/x402/buyer/recurring-payments',
132134
],
133135
},
Lines changed: 82 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
description: Pay for an x402-protected API data using delegation.
2+
description: Pay for an x402-protected API access using delegation.
33
sidebar_label: Delegations
44
keywords: [x402, ERC-7710, delegation, smart account, facilitator, buyer, API]
55
---
@@ -10,23 +10,34 @@ import GlossaryTerm from '@theme/GlossaryTerm';
1010

1111
# Pay for an x402 API with delegation
1212

13-
In this guide, you use a buyer <GlossaryTerm term="MetaMask smart account">smart account</GlossaryTerm>
14-
to access API data from an x402 server.
13+
In this guide, you use a buyer account to access API data from an x402 server by creating
14+
an <GlossaryTerm term="Open root delegation">open delegation</GlossaryTerm> that authorizes
15+
token transfers on your behalf.
1516

16-
You create an open delegation, restrict redemption to the facilitator, encode the delegation chain,
17-
and send it as the payment payload when calling a protected API route.
17+
You set up an `x402Erc7710Client` with a delegation provider, register it with the x402 client,
18+
and use `wrapFetchWithPayment` to automatically handle payment when calling a protected API route.
1819

1920
## Prerequisites
2021

2122
- [Install and set up the Smart Accounts Kit.](../../../get-started/install.md)
2223

2324
## Steps
2425

25-
### 1. Create a buyer account
26+
### 1. Install the dependencies
2627

27-
Create an account to represent the buyer, the <GlossaryTerm term="Delegator account">delegator</GlossaryTerm> who will create a delegation.
28+
```bash npm2yarn
29+
npm install @x402/core @x402/fetch @metamask/x402
30+
```
31+
32+
### 2. Create a buyer account
33+
34+
Create an account to represent the buyer, the
35+
<GlossaryTerm term="Delegator account">delegator</GlossaryTerm> who creates a delegation.
2836

29-
The delegator must be a <GlossaryTerm term="MetaMask smart account" />; use the toolkit's [`toMetaMaskSmartAccount`](../../../reference/smart-account.md#tometamasksmartaccount) method to create the buyer account.
37+
The delegator must be a <GlossaryTerm term="MetaMask smart account" />.
38+
Use the toolkit's
39+
[`toMetaMaskSmartAccount`](../../../reference/smart-account.md#tometamasksmartaccount) method to
40+
create the buyer account.
3041

3142
:::note Important
3243
Fund the smart account with USDC for the requested payment.
@@ -67,192 +78,90 @@ export const buyerAccount = privateKeyToAccount('0x<BUYER_PRIVATE_KEY>')
6778
</TabItem>
6879
</Tabs>
6980

70-
### 2. Get payment requirements
71-
72-
Call the protected API route once without the `PAYMENT-SIGNATURE` header.
73-
74-
The server returns `402` with the payment terms (`PAYMENT-REQUIRED`) in the response, which you use
75-
to build the payment payload.
76-
77-
<Tabs>
78-
<TabItem value="example.ts">
79-
80-
```ts
81-
import { PaymentRequirements } from './types'
82-
83-
// Update the URL
84-
const challengeResponse = await fetch('https://api.example.com/paid-endpoint')
85-
if (challengeResponse.status !== 402) {
86-
console.error('Expected 402 challenge from protected route')
87-
// Handle error
88-
}
89-
90-
const paymentRequiredHeader = challengeResponse.headers.get('PAYMENT-REQUIRED')
91-
if (!paymentRequiredHeader) {
92-
console.error('PAYMENT-REQUIRED header is missing')
93-
// Handle error
94-
}
95-
96-
const decodedPaymentRequired = Buffer.from(paymentRequiredHeader, 'base64').toString('utf-8')
97-
const paymentRequired = JSON.parse(decodedPaymentRequired) as {
98-
accepts: PaymentRequirements[]
99-
}
100-
101-
const accepted = paymentRequired.accepts[0]
102-
if (!accepted) {
103-
console.error('Server did not provide accepted payment requirements')
104-
// Handle error
105-
}
106-
107-
if (accepted.extra.assetTransferMethod !== 'erc7710') {
108-
console.error('Server does not support ERC-7710 delegation payments')
109-
// Handle error
110-
}
111-
```
112-
113-
</TabItem>
114-
<TabItem value="types.ts">
115-
116-
```ts
117-
export type PaymentRequirements = {
118-
scheme: string
119-
network: string
120-
amount: string
121-
asset: `0x${string}`
122-
payTo: `0x${string}`
123-
maxTimeoutSeconds: number
124-
extra: {
125-
assetTransferMethod: string
126-
facilitators?: `0x${string}`[]
127-
}
128-
}
129-
```
130-
131-
</TabItem>
132-
</Tabs>
133-
134-
### 3. Create a delegation
81+
### 3. Create an x402 ERC-7710 client
13582

136-
Create an [open root delegation](../../../concepts/delegation/overview.md#open-root-delegation)
137-
from the buyer smart account. With an open root delegation, the buyer delegates authority without
138-
setting a specific delegate. Use [`createOpenDelegation`](../../../reference/delegation/index.md#createopendelegation) to create the open root delegation.
83+
Create an `x402Erc7710Client` with a `delegationProvider` callback.
84+
The x402 client calls this function automatically when it needs to pay for a request, passing
85+
in the payment requirements from the server.
13986

87+
Inside the provider, create an [open delegation](../../../concepts/delegation/overview.md#open-root-delegation)
88+
using [`createOpenDelegation`](../../../reference/delegation/index.md#createopendelegation).
14089
This example uses the [`erc20TransferAmount`](../../../guides/delegation/use-delegation-scopes/spending-limit.md#erc-20-transfer-scope)
141-
scope to allow USDC transfers up to the amount requested in payment terms.
142-
It also uses the [`redeemer`](../../../reference/delegation/caveats.md#redeemer) caveat enforcer to restrict
143-
redemption to facilitator addresses provided by the server.
90+
scope to allow USDC transfers up to the requested amount, and the
91+
[`timestamp`](../../../reference/delegation/caveats.md#timestamp) caveat to set a short expiry.
14492

145-
:::warning
146-
Before creating the delegation, make sure your buyer smart account is deployed. If it is not
147-
deployed, delegation redemption will fail.
148-
:::
93+
For ERC-7710, x402 requires the payload fields `delegationManager`, `permissionContext`, and `delegator`.
94+
Use [`encodeDelegations`](../../../reference/delegation/index.md#encodedelegations) to encode the delegation chain.
14995

15096
```ts
15197
import { CaveatType, createOpenDelegation, ScopeType } from '@metamask/smart-accounts-kit'
152-
153-
const facilitators = accepted.extra.facilitators
154-
if (!facilitators || facilitators.length === 0) {
155-
console.log('No facilitators found in PAYMENT-REQUIRED')
156-
// Handle the error
157-
}
158-
159-
// Use the amount requested by the seller.
160-
const maxAmount = BigInt(accepted.amount)
161-
162-
const caveats = [
163-
{
164-
type: CaveatType.Redeemer,
165-
redeemers: facilitators,
166-
},
167-
]
168-
169-
const delegation = createOpenDelegation({
170-
from: buyerSmartAccount.address,
171-
environment: buyerSmartAccount.environment,
172-
scope: {
173-
type: ScopeType.Erc20TransferAmount,
174-
tokenAddress: accepted.asset,
175-
maxAmount,
98+
import { encodeDelegations } from '@metamask/smart-accounts-kit/utils'
99+
import { x402Erc7710Client } from '@metamask/x402'
100+
import { getAddress } from 'viem'
101+
102+
const erc7710Client = new x402Erc7710Client({
103+
delegationProvider: async requirements => {
104+
// Expires in 1 minute.
105+
const expiry = Math.floor(Date.now() / 1000) + 60
106+
107+
const delegation = createOpenDelegation({
108+
environment: buyerSmartAccount.environment,
109+
from: buyerSmartAccount.address,
110+
scope: {
111+
type: ScopeType.Erc20TransferAmount,
112+
tokenAddress: getAddress(requirements.asset),
113+
maxAmount: BigInt(requirements.amount),
114+
},
115+
caveats: [
116+
{
117+
type: CaveatType.Timestamp,
118+
afterThreshold: 0,
119+
beforeThreshold: expiry,
120+
},
121+
],
122+
})
123+
124+
const signature = await buyerSmartAccount.signDelegation({
125+
delegation,
126+
})
127+
128+
const signedDelegation = {
129+
...delegation,
130+
signature,
131+
}
132+
133+
return {
134+
delegationManager: buyerSmartAccount.environment.DelegationManager,
135+
permissionContext: encodeDelegations([signedDelegation]),
136+
delegator: buyerSmartAccount.address,
137+
}
176138
},
177-
caveats,
178-
})
179-
180-
const signature = await buyerSmartAccount.signDelegation({
181-
delegation,
182139
})
183-
184-
const signedDelegation = {
185-
...delegation,
186-
signature,
187-
}
188140
```
189141

190-
### 4. Create the payment payload
191-
192-
Create a payment payload using the signed delegation and accepted requirements.
193-
For ERC-7710 (Smart Contract Delegation), x402 requires the payload fields `delegationManager`, `permissionContext`, and `delegator`.
194-
The facilitator uses `permissionContext` to simulate during verification and then settle the payment.
142+
### 4. Register the client
195143

196-
Use `encodeDelegations` to encode the delegation chain.
197-
Then base64 encode the full x402 payment payload before sending it in the `PAYMENT-SIGNATURE` header.
198-
199-
<Tabs>
200-
<TabItem value="example.ts">
144+
Register the ERC-7710 client with the x402 core client for all EVM networks.
145+
Create an HTTP client and a payment-aware `fetch` function using `wrapFetchWithPayment`.
201146

202147
```ts
203-
import { encodeDelegations } from '@metamask/smart-accounts-kit/utils'
204-
import { PaymentPayload } from './types'
205-
206-
const permissionContext = encodeDelegations([signedDelegation])
148+
import { x402Client, x402HTTPClient } from '@x402/core/client'
149+
import { wrapFetchWithPayment } from '@x402/fetch'
207150

208-
const paymentPayload: PaymentPayload = {
209-
x402Version: 2,
210-
accepted,
211-
payload: {
212-
delegationManager: buyerSmartAccount.environment.DelegationManager,
213-
permissionContext,
214-
delegator: buyerSmartAccount.address,
215-
},
216-
}
151+
const coreClient = new x402Client().register('eip155:*', erc7710Client)
152+
const httpClient = new x402HTTPClient(coreClient)
217153

218-
const encodedPayment = Buffer.from(JSON.stringify(paymentPayload)).toString('base64')
154+
const fetchWithPayment = wrapFetchWithPayment(fetch, httpClient)
219155
```
220156

221-
</TabItem>
222-
<TabItem value="types.ts">
223-
224-
```ts
225-
export type PaymentPayload = {
226-
x402Version: 2
227-
accepted: PaymentRequirements
228-
payload: {
229-
delegationManager: `0x${string}`
230-
permissionContext: `0x${string}`
231-
delegator: `0x${string}`
232-
}
233-
}
234-
```
235-
236-
</TabItem>
237-
</Tabs>
238-
239157
### 5. Make the paid request
240158

241-
Send the encoded x402 payment payload in the `PAYMENT-SIGNATURE` header.
242-
If verification succeeds, the server returns the protected data.
159+
Call the protected endpoint using `fetchWithPayment`.
160+
It handles the x402 payment flow, calling your `delegationProvider`
161+
to create an open redelegation when the server returns a `402` response.
243162

244163
```ts
245-
const apiResponse = await fetch('https://api.example.com/paid-endpoint', {
246-
headers: {
247-
'PAYMENT-SIGNATURE': encodedPayment,
248-
},
164+
const paidResponse = await fetchWithPayment('https://api.example.com/paid-endpoint', {
165+
method: 'GET',
249166
})
250-
251-
if (!apiResponse.ok) {
252-
const errorBody = await apiResponse.json()
253-
throw new Error(errorBody.error ?? 'API request failed')
254-
}
255-
256-
const data = await apiResponse.json()
257-
console.log('Protected API response:', data)
258167
```

0 commit comments

Comments
 (0)