@goplausible/openclaw-algorand-plugin 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +112 -0
- package/index.ts +361 -0
- package/lib/mcp-servers.ts +14 -0
- package/lib/x402-fetch.ts +213 -0
- package/memory/algorand-plugin.md +82 -0
- package/openclaw.plugin.json +30 -0
- package/package.json +41 -0
- package/setup.ts +80 -0
- package/skills/algorand-development/SKILL.md +90 -0
- package/skills/algorand-development/references/build-smart-contracts-reference.md +79 -0
- package/skills/algorand-development/references/build-smart-contracts.md +52 -0
- package/skills/algorand-development/references/create-project-reference.md +86 -0
- package/skills/algorand-development/references/create-project.md +89 -0
- package/skills/algorand-development/references/implement-arc-standards-arc32-arc56.md +396 -0
- package/skills/algorand-development/references/implement-arc-standards-arc4.md +265 -0
- package/skills/algorand-development/references/implement-arc-standards.md +92 -0
- package/skills/algorand-development/references/search-algorand-examples-reference.md +119 -0
- package/skills/algorand-development/references/search-algorand-examples.md +89 -0
- package/skills/algorand-development/references/troubleshoot-errors-contract.md +373 -0
- package/skills/algorand-development/references/troubleshoot-errors-transaction.md +599 -0
- package/skills/algorand-development/references/troubleshoot-errors.md +105 -0
- package/skills/algorand-development/references/use-algokit-cli-reference.md +228 -0
- package/skills/algorand-development/references/use-algokit-cli.md +64 -0
- package/skills/algorand-interaction/SKILL.md +223 -0
- package/skills/algorand-interaction/references/algorand-mcp.md +743 -0
- package/skills/algorand-interaction/references/examples-algorand-mcp.md +647 -0
- package/skills/algorand-python/SKILL.md +95 -0
- package/skills/algorand-python/references/build-smart-contracts-decorators.md +413 -0
- package/skills/algorand-python/references/build-smart-contracts-reference.md +55 -0
- package/skills/algorand-python/references/build-smart-contracts-storage.md +452 -0
- package/skills/algorand-python/references/build-smart-contracts-transactions.md +445 -0
- package/skills/algorand-python/references/build-smart-contracts-types.md +438 -0
- package/skills/algorand-python/references/build-smart-contracts.md +82 -0
- package/skills/algorand-python/references/create-project-reference.md +55 -0
- package/skills/algorand-python/references/create-project.md +75 -0
- package/skills/algorand-python/references/implement-arc-standards-arc32-arc56.md +101 -0
- package/skills/algorand-python/references/implement-arc-standards-arc4.md +154 -0
- package/skills/algorand-python/references/implement-arc-standards.md +39 -0
- package/skills/algorand-python/references/troubleshoot-errors-contract.md +355 -0
- package/skills/algorand-python/references/troubleshoot-errors-transaction.md +430 -0
- package/skills/algorand-python/references/troubleshoot-errors.md +46 -0
- package/skills/algorand-python/references/use-algokit-utils-reference.md +350 -0
- package/skills/algorand-python/references/use-algokit-utils.md +76 -0
- package/skills/algorand-typescript/SKILL.md +131 -0
- package/skills/algorand-typescript/references/algorand-ts-migration-from-beta.md +448 -0
- package/skills/algorand-typescript/references/algorand-ts-migration-from-tealscript.md +487 -0
- package/skills/algorand-typescript/references/algorand-ts-migration.md +102 -0
- package/skills/algorand-typescript/references/algorand-typescript-syntax-methods-and-abi.md +134 -0
- package/skills/algorand-typescript/references/algorand-typescript-syntax-reference.md +58 -0
- package/skills/algorand-typescript/references/algorand-typescript-syntax-storage.md +154 -0
- package/skills/algorand-typescript/references/algorand-typescript-syntax-transactions.md +187 -0
- package/skills/algorand-typescript/references/algorand-typescript-syntax-types-and-values.md +150 -0
- package/skills/algorand-typescript/references/algorand-typescript-syntax.md +84 -0
- package/skills/algorand-typescript/references/build-smart-contracts-reference.md +52 -0
- package/skills/algorand-typescript/references/build-smart-contracts.md +74 -0
- package/skills/algorand-typescript/references/call-smart-contracts-reference.md +237 -0
- package/skills/algorand-typescript/references/call-smart-contracts.md +183 -0
- package/skills/algorand-typescript/references/create-project-reference.md +53 -0
- package/skills/algorand-typescript/references/create-project.md +86 -0
- package/skills/algorand-typescript/references/deploy-react-frontend-examples.md +527 -0
- package/skills/algorand-typescript/references/deploy-react-frontend-reference.md +412 -0
- package/skills/algorand-typescript/references/deploy-react-frontend.md +239 -0
- package/skills/algorand-typescript/references/implement-arc-standards-arc32-arc56.md +73 -0
- package/skills/algorand-typescript/references/implement-arc-standards-arc4.md +126 -0
- package/skills/algorand-typescript/references/implement-arc-standards.md +44 -0
- package/skills/algorand-typescript/references/test-smart-contracts-examples.md +245 -0
- package/skills/algorand-typescript/references/test-smart-contracts-unit-tests.md +147 -0
- package/skills/algorand-typescript/references/test-smart-contracts.md +127 -0
- package/skills/algorand-typescript/references/troubleshoot-errors-contract.md +296 -0
- package/skills/algorand-typescript/references/troubleshoot-errors-transaction.md +438 -0
- package/skills/algorand-typescript/references/troubleshoot-errors.md +56 -0
- package/skills/algorand-typescript/references/use-algokit-utils-reference.md +342 -0
- package/skills/algorand-typescript/references/use-algokit-utils.md +74 -0
- package/skills/algorand-x402-python/SKILL.md +113 -0
- package/skills/algorand-x402-python/references/create-python-x402-client-examples.md +469 -0
- package/skills/algorand-x402-python/references/create-python-x402-client-reference.md +313 -0
- package/skills/algorand-x402-python/references/create-python-x402-client.md +207 -0
- package/skills/algorand-x402-python/references/create-python-x402-facilitator-examples.md +924 -0
- package/skills/algorand-x402-python/references/create-python-x402-facilitator-reference.md +629 -0
- package/skills/algorand-x402-python/references/create-python-x402-facilitator.md +408 -0
- package/skills/algorand-x402-python/references/create-python-x402-server-examples.md +703 -0
- package/skills/algorand-x402-python/references/create-python-x402-server-reference.md +303 -0
- package/skills/algorand-x402-python/references/create-python-x402-server.md +221 -0
- package/skills/algorand-x402-python/references/explain-algorand-x402-python-examples.md +605 -0
- package/skills/algorand-x402-python/references/explain-algorand-x402-python-reference.md +315 -0
- package/skills/algorand-x402-python/references/explain-algorand-x402-python.md +167 -0
- package/skills/algorand-x402-python/references/use-python-x402-core-avm-examples.md +554 -0
- package/skills/algorand-x402-python/references/use-python-x402-core-avm-reference.md +278 -0
- package/skills/algorand-x402-python/references/use-python-x402-core-avm.md +166 -0
- package/skills/algorand-x402-typescript/SKILL.md +129 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-client-examples.md +879 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-client-reference.md +371 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-client.md +236 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-facilitator-examples.md +875 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-facilitator-reference.md +461 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-facilitator.md +270 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-nextjs-examples.md +1181 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-nextjs-reference.md +360 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-nextjs.md +251 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-paywall-examples.md +870 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-paywall-reference.md +323 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-paywall.md +281 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-server-examples.md +1135 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-server-reference.md +382 -0
- package/skills/algorand-x402-typescript/references/create-typescript-x402-server.md +216 -0
- package/skills/algorand-x402-typescript/references/explain-algorand-x402-typescript-examples.md +616 -0
- package/skills/algorand-x402-typescript/references/explain-algorand-x402-typescript-reference.md +323 -0
- package/skills/algorand-x402-typescript/references/explain-algorand-x402-typescript.md +232 -0
- package/skills/algorand-x402-typescript/references/use-typescript-x402-core-avm-examples.md +1417 -0
- package/skills/algorand-x402-typescript/references/use-typescript-x402-core-avm-reference.md +504 -0
- package/skills/algorand-x402-typescript/references/use-typescript-x402-core-avm.md +158 -0
|
@@ -0,0 +1,879 @@
|
|
|
1
|
+
# x402 HTTP Client Examples
|
|
2
|
+
|
|
3
|
+
## Quick Start (Fetch)
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
import { wrapFetchWithPayment, x402Client } from "@x402-avm/fetch";
|
|
7
|
+
import { registerExactAvmScheme } from "@x402-avm/avm/exact/client";
|
|
8
|
+
import algosdk from "algosdk";
|
|
9
|
+
|
|
10
|
+
const secretKey = Buffer.from(process.env.AVM_PRIVATE_KEY!, "base64");
|
|
11
|
+
const address = algosdk.encodeAddress(secretKey.slice(32));
|
|
12
|
+
const signer = {
|
|
13
|
+
address,
|
|
14
|
+
signTransactions: async (txns: Uint8Array[], indexesToSign?: number[]) => {
|
|
15
|
+
return txns.map((txn, i) => {
|
|
16
|
+
if (indexesToSign && !indexesToSign.includes(i)) return null;
|
|
17
|
+
const decoded = algosdk.decodeUnsignedTransaction(txn);
|
|
18
|
+
const signed = algosdk.signTransaction(decoded, secretKey);
|
|
19
|
+
return signed.blob;
|
|
20
|
+
});
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const client = new x402Client();
|
|
25
|
+
registerExactAvmScheme(client, { signer });
|
|
26
|
+
|
|
27
|
+
const fetchWithPay = wrapFetchWithPayment(fetch, client);
|
|
28
|
+
const response = await fetchWithPay("https://api.example.com/premium-data");
|
|
29
|
+
const data = await response.json();
|
|
30
|
+
console.log(data);
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Quick Start (Axios)
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import axios from "axios";
|
|
39
|
+
import { wrapAxiosWithPayment, x402Client } from "@x402-avm/axios";
|
|
40
|
+
import { registerExactAvmScheme } from "@x402-avm/avm/exact/client";
|
|
41
|
+
import algosdk from "algosdk";
|
|
42
|
+
|
|
43
|
+
const secretKey = Buffer.from(process.env.AVM_PRIVATE_KEY!, "base64");
|
|
44
|
+
const address = algosdk.encodeAddress(secretKey.slice(32));
|
|
45
|
+
const signer = {
|
|
46
|
+
address,
|
|
47
|
+
signTransactions: async (txns: Uint8Array[], indexesToSign?: number[]) => {
|
|
48
|
+
return txns.map((txn, i) => {
|
|
49
|
+
if (indexesToSign && !indexesToSign.includes(i)) return null;
|
|
50
|
+
const decoded = algosdk.decodeUnsignedTransaction(txn);
|
|
51
|
+
const signed = algosdk.signTransaction(decoded, secretKey);
|
|
52
|
+
return signed.blob;
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const client = new x402Client();
|
|
58
|
+
registerExactAvmScheme(client, { signer });
|
|
59
|
+
|
|
60
|
+
const api = wrapAxiosWithPayment(axios.create(), client);
|
|
61
|
+
const response = await api.get("https://api.example.com/premium-data");
|
|
62
|
+
console.log(response.data);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Basic Fetch Usage
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { wrapFetchWithPayment, x402Client } from "@x402-avm/fetch";
|
|
71
|
+
import { registerExactAvmScheme } from "@x402-avm/avm/exact/client";
|
|
72
|
+
|
|
73
|
+
const client = new x402Client();
|
|
74
|
+
registerExactAvmScheme(client, { signer });
|
|
75
|
+
|
|
76
|
+
const fetchWithPay = wrapFetchWithPayment(fetch, client);
|
|
77
|
+
|
|
78
|
+
// GET request
|
|
79
|
+
const getResponse = await fetchWithPay("https://api.example.com/paid-content");
|
|
80
|
+
console.log(await getResponse.json());
|
|
81
|
+
|
|
82
|
+
// POST request with body
|
|
83
|
+
const postResponse = await fetchWithPay("https://api.example.com/paid-action", {
|
|
84
|
+
method: "POST",
|
|
85
|
+
headers: { "Content-Type": "application/json" },
|
|
86
|
+
body: JSON.stringify({ query: "premium data" }),
|
|
87
|
+
});
|
|
88
|
+
console.log(await postResponse.json());
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Basic Axios Usage
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
import axios from "axios";
|
|
97
|
+
import { wrapAxiosWithPayment, x402Client } from "@x402-avm/axios";
|
|
98
|
+
import { registerExactAvmScheme } from "@x402-avm/avm/exact/client";
|
|
99
|
+
|
|
100
|
+
const client = new x402Client();
|
|
101
|
+
registerExactAvmScheme(client, { signer });
|
|
102
|
+
|
|
103
|
+
const api = wrapAxiosWithPayment(axios.create({
|
|
104
|
+
baseURL: "https://api.example.com",
|
|
105
|
+
timeout: 30000,
|
|
106
|
+
headers: { Accept: "application/json" },
|
|
107
|
+
}), client);
|
|
108
|
+
|
|
109
|
+
// GET request
|
|
110
|
+
const getResult = await api.get("/paid-content");
|
|
111
|
+
console.log(getResult.data);
|
|
112
|
+
|
|
113
|
+
// POST request
|
|
114
|
+
const postResult = await api.post("/paid-action", {
|
|
115
|
+
query: "premium search",
|
|
116
|
+
});
|
|
117
|
+
console.log(postResult.data);
|
|
118
|
+
|
|
119
|
+
// PUT request with custom headers
|
|
120
|
+
const putResult = await api.put("/paid-resource/123", {
|
|
121
|
+
name: "Updated Name",
|
|
122
|
+
}, {
|
|
123
|
+
headers: { "X-Custom-Header": "value" },
|
|
124
|
+
});
|
|
125
|
+
console.log(putResult.data);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Wrapping the Default Axios Instance
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
import axios from "axios";
|
|
134
|
+
import { wrapAxiosWithPayment, x402Client } from "@x402-avm/axios";
|
|
135
|
+
import { registerExactAvmScheme } from "@x402-avm/avm/exact/client";
|
|
136
|
+
|
|
137
|
+
const client = new x402Client();
|
|
138
|
+
registerExactAvmScheme(client, { signer });
|
|
139
|
+
|
|
140
|
+
wrapAxiosWithPayment(axios, client);
|
|
141
|
+
|
|
142
|
+
const response = await axios.get("https://api.example.com/paid");
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Config-Based Fetch Setup
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import {
|
|
151
|
+
wrapFetchWithPaymentFromConfig,
|
|
152
|
+
type x402ClientConfig,
|
|
153
|
+
} from "@x402-avm/fetch";
|
|
154
|
+
import { ExactAvmScheme } from "@x402-avm/avm";
|
|
155
|
+
|
|
156
|
+
const config: x402ClientConfig = {
|
|
157
|
+
schemes: [
|
|
158
|
+
{
|
|
159
|
+
network: "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
160
|
+
client: new ExactAvmScheme(signer),
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
policies: [
|
|
164
|
+
(version, reqs) => reqs.filter((r) => BigInt(r.amount ?? "0") < BigInt("10000000")),
|
|
165
|
+
],
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const fetchWithPay = wrapFetchWithPaymentFromConfig(fetch, config);
|
|
169
|
+
const response = await fetchWithPay("https://api.example.com/paid-endpoint");
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Config-Based Axios Setup
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
import axios from "axios";
|
|
178
|
+
import {
|
|
179
|
+
wrapAxiosWithPaymentFromConfig,
|
|
180
|
+
type x402ClientConfig,
|
|
181
|
+
} from "@x402-avm/axios";
|
|
182
|
+
import { ExactAvmScheme } from "@x402-avm/avm";
|
|
183
|
+
|
|
184
|
+
const config: x402ClientConfig = {
|
|
185
|
+
schemes: [
|
|
186
|
+
{
|
|
187
|
+
network: "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
188
|
+
client: new ExactAvmScheme(signer),
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
policies: [
|
|
192
|
+
(version, reqs) => reqs.filter((r) => BigInt(r.amount ?? "0") < BigInt("5000000")),
|
|
193
|
+
],
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const api = wrapAxiosWithPaymentFromConfig(axios.create(), config);
|
|
197
|
+
const response = await api.get("https://api.example.com/paid-endpoint");
|
|
198
|
+
console.log(response.data);
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Wildcard Network Registration
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
const config: x402ClientConfig = {
|
|
207
|
+
schemes: [
|
|
208
|
+
{
|
|
209
|
+
network: "algorand:*",
|
|
210
|
+
client: new ExactAvmScheme(signer),
|
|
211
|
+
},
|
|
212
|
+
],
|
|
213
|
+
};
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## ClientAvmSigner: Node.js Implementation
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
import algosdk from "algosdk";
|
|
222
|
+
import type { ClientAvmSigner } from "@x402-avm/avm";
|
|
223
|
+
|
|
224
|
+
function createNodeSigner(privateKeyBase64: string): ClientAvmSigner {
|
|
225
|
+
const secretKey = Buffer.from(privateKeyBase64, "base64");
|
|
226
|
+
const address = algosdk.encodeAddress(secretKey.slice(32));
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
address,
|
|
230
|
+
signTransactions: async (
|
|
231
|
+
txns: Uint8Array[],
|
|
232
|
+
indexesToSign?: number[],
|
|
233
|
+
): Promise<(Uint8Array | null)[]> => {
|
|
234
|
+
return txns.map((txnBytes, i) => {
|
|
235
|
+
if (indexesToSign && !indexesToSign.includes(i)) {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
const decoded = algosdk.decodeUnsignedTransaction(txnBytes);
|
|
239
|
+
const signed = algosdk.signTransaction(decoded, secretKey);
|
|
240
|
+
return signed.blob;
|
|
241
|
+
});
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const signer = createNodeSigner(process.env.AVM_PRIVATE_KEY!);
|
|
247
|
+
console.log("Signer address:", signer.address);
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## ClientAvmSigner: Browser with @txnlab/use-wallet (React Hook)
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
import { useWallet } from "@txnlab/use-wallet-react";
|
|
256
|
+
import type { ClientAvmSigner } from "@x402-avm/avm";
|
|
257
|
+
|
|
258
|
+
function useAvmSigner(): ClientAvmSigner | null {
|
|
259
|
+
const { activeAccount, signTransactions } = useWallet();
|
|
260
|
+
|
|
261
|
+
if (!activeAccount) return null;
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
address: activeAccount.address,
|
|
265
|
+
signTransactions: async (
|
|
266
|
+
txns: Uint8Array[],
|
|
267
|
+
indexesToSign?: number[],
|
|
268
|
+
): Promise<(Uint8Array | null)[]> => {
|
|
269
|
+
const signed = await signTransactions(txns, indexesToSign);
|
|
270
|
+
return signed;
|
|
271
|
+
},
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## ClientAvmSigner: Browser with Pera Wallet (Vanilla JS)
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
import { PeraWalletConnect } from "@perawallet/connect";
|
|
282
|
+
import { wrapFetchWithPayment, x402Client } from "@x402-avm/fetch";
|
|
283
|
+
import { registerExactAvmScheme } from "@x402-avm/avm/exact/client";
|
|
284
|
+
import type { ClientAvmSigner } from "@x402-avm/avm";
|
|
285
|
+
|
|
286
|
+
const peraWallet = new PeraWalletConnect();
|
|
287
|
+
|
|
288
|
+
async function setupPaymentFetch(): Promise<typeof fetch> {
|
|
289
|
+
const accounts = await peraWallet.connect();
|
|
290
|
+
const address = accounts[0];
|
|
291
|
+
|
|
292
|
+
const signer: ClientAvmSigner = {
|
|
293
|
+
address,
|
|
294
|
+
signTransactions: async (txns: Uint8Array[], indexesToSign?: number[]) => {
|
|
295
|
+
const txnGroup = txns.map((txn, i) => ({
|
|
296
|
+
txn,
|
|
297
|
+
signers: indexesToSign && !indexesToSign.includes(i) ? [] : [address],
|
|
298
|
+
}));
|
|
299
|
+
|
|
300
|
+
const signedTxns = await peraWallet.signTransaction([txnGroup]);
|
|
301
|
+
return txns.map((_, i) => {
|
|
302
|
+
if (indexesToSign && !indexesToSign.includes(i)) return null;
|
|
303
|
+
return signedTxns.shift() ?? null;
|
|
304
|
+
});
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
const client = new x402Client();
|
|
309
|
+
registerExactAvmScheme(client, { signer });
|
|
310
|
+
return wrapFetchWithPayment(fetch, client);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const paidFetch = await setupPaymentFetch();
|
|
314
|
+
const response = await paidFetch("https://api.example.com/paid-api");
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## Payment Policies: Prefer Algorand
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
import { wrapFetchWithPayment, x402Client, type PaymentPolicy } from "@x402-avm/fetch";
|
|
323
|
+
import { registerExactAvmScheme } from "@x402-avm/avm/exact/client";
|
|
324
|
+
|
|
325
|
+
const preferAlgorand: PaymentPolicy = (version, reqs) => {
|
|
326
|
+
const algorandReqs = reqs.filter((r) => r.network.startsWith("algorand:"));
|
|
327
|
+
return algorandReqs.length > 0 ? algorandReqs : reqs;
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
const client = new x402Client();
|
|
331
|
+
registerExactAvmScheme(client, { signer });
|
|
332
|
+
client.registerPolicy(preferAlgorand);
|
|
333
|
+
|
|
334
|
+
const fetchWithPay = wrapFetchWithPayment(fetch, client);
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## Payment Policies: Maximum Amount Limit
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
const maxAmount: PaymentPolicy = (version, reqs) => {
|
|
343
|
+
return reqs.filter((r) => {
|
|
344
|
+
const amount = BigInt(r.amount ?? r.maxAmountRequired ?? "0");
|
|
345
|
+
return amount <= BigInt("1000000"); // 1 USDC
|
|
346
|
+
});
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
client.registerPolicy(maxAmount);
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## Payment Policies: Prefer Testnet
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
import { ALGORAND_TESTNET_CAIP2 } from "@x402-avm/avm";
|
|
358
|
+
|
|
359
|
+
const preferTestnet: PaymentPolicy = (version, reqs) => {
|
|
360
|
+
const testnetReqs = reqs.filter((r) => r.network === ALGORAND_TESTNET_CAIP2);
|
|
361
|
+
return testnetReqs.length > 0 ? testnetReqs : reqs;
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
client.registerPolicy(preferTestnet);
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## Combining Multiple Policies
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
const client = new x402Client();
|
|
373
|
+
registerExactAvmScheme(client, { signer });
|
|
374
|
+
|
|
375
|
+
client
|
|
376
|
+
.registerPolicy(preferAlgorand)
|
|
377
|
+
.registerPolicy(preferTestnet)
|
|
378
|
+
.registerPolicy(maxAmount);
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
## Custom Algod Endpoint
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
const client = new x402Client();
|
|
387
|
+
registerExactAvmScheme(client, {
|
|
388
|
+
signer,
|
|
389
|
+
algodConfig: {
|
|
390
|
+
algodUrl: "https://your-private-node.example.com",
|
|
391
|
+
algodToken: "your-api-token",
|
|
392
|
+
},
|
|
393
|
+
});
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
## Pre-Configured Algod Client
|
|
399
|
+
|
|
400
|
+
```typescript
|
|
401
|
+
import algosdk from "algosdk";
|
|
402
|
+
import { registerExactAvmScheme } from "@x402-avm/avm/exact/client";
|
|
403
|
+
|
|
404
|
+
const algodClient = new algosdk.Algodv2(
|
|
405
|
+
"your-token",
|
|
406
|
+
"https://your-node.example.com",
|
|
407
|
+
443,
|
|
408
|
+
);
|
|
409
|
+
|
|
410
|
+
const client = new x402Client();
|
|
411
|
+
registerExactAvmScheme(client, {
|
|
412
|
+
signer,
|
|
413
|
+
algodConfig: {
|
|
414
|
+
algodClient,
|
|
415
|
+
},
|
|
416
|
+
});
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## Specific Network Registration
|
|
422
|
+
|
|
423
|
+
```typescript
|
|
424
|
+
import { ALGORAND_TESTNET_CAIP2 } from "@x402-avm/avm";
|
|
425
|
+
import { registerExactAvmScheme } from "@x402-avm/avm/exact/client";
|
|
426
|
+
|
|
427
|
+
const client = new x402Client();
|
|
428
|
+
registerExactAvmScheme(client, {
|
|
429
|
+
signer,
|
|
430
|
+
networks: [ALGORAND_TESTNET_CAIP2],
|
|
431
|
+
});
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
## Error Handling (Fetch)
|
|
437
|
+
|
|
438
|
+
```typescript
|
|
439
|
+
import { wrapFetchWithPayment, x402Client } from "@x402-avm/fetch";
|
|
440
|
+
import { registerExactAvmScheme } from "@x402-avm/avm/exact/client";
|
|
441
|
+
|
|
442
|
+
const client = new x402Client();
|
|
443
|
+
registerExactAvmScheme(client, { signer });
|
|
444
|
+
const fetchWithPay = wrapFetchWithPayment(fetch, client);
|
|
445
|
+
|
|
446
|
+
try {
|
|
447
|
+
const response = await fetchWithPay("https://api.example.com/paid-endpoint");
|
|
448
|
+
|
|
449
|
+
if (response.ok) {
|
|
450
|
+
const data = await response.json();
|
|
451
|
+
console.log("Success:", data);
|
|
452
|
+
} else {
|
|
453
|
+
console.error(`Server error: ${response.status} ${response.statusText}`);
|
|
454
|
+
}
|
|
455
|
+
} catch (error) {
|
|
456
|
+
if (error instanceof Error) {
|
|
457
|
+
if (error.message.includes("Failed to parse payment requirements")) {
|
|
458
|
+
console.error("Invalid payment requirements from server");
|
|
459
|
+
} else if (error.message.includes("Failed to create payment payload")) {
|
|
460
|
+
console.error("Could not create payment:", error.message);
|
|
461
|
+
} else if (error.message.includes("Payment already attempted")) {
|
|
462
|
+
console.error("Payment was rejected by the server");
|
|
463
|
+
} else if (error.message.includes("No network/scheme registered")) {
|
|
464
|
+
console.error("Unsupported payment network requested");
|
|
465
|
+
} else if (error.message.includes("Payment creation aborted")) {
|
|
466
|
+
console.error("Payment was blocked by policy");
|
|
467
|
+
} else {
|
|
468
|
+
console.error("Unexpected error:", error.message);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
## Error Handling (Axios)
|
|
477
|
+
|
|
478
|
+
```typescript
|
|
479
|
+
import axios, { AxiosError } from "axios";
|
|
480
|
+
import { wrapAxiosWithPayment, x402Client } from "@x402-avm/axios";
|
|
481
|
+
import { registerExactAvmScheme } from "@x402-avm/avm/exact/client";
|
|
482
|
+
|
|
483
|
+
const client = new x402Client();
|
|
484
|
+
registerExactAvmScheme(client, { signer });
|
|
485
|
+
const api = wrapAxiosWithPayment(axios.create(), client);
|
|
486
|
+
|
|
487
|
+
try {
|
|
488
|
+
const response = await api.get("https://api.example.com/paid-content");
|
|
489
|
+
console.log("Success:", response.data);
|
|
490
|
+
} catch (error) {
|
|
491
|
+
if (error instanceof AxiosError) {
|
|
492
|
+
if (error.response) {
|
|
493
|
+
console.error(`Server returned ${error.response.status}:`, error.response.data);
|
|
494
|
+
} else if (error.request) {
|
|
495
|
+
console.error("No response received:", error.message);
|
|
496
|
+
} else {
|
|
497
|
+
console.error("Request setup error:", error.message);
|
|
498
|
+
}
|
|
499
|
+
} else if (error instanceof Error) {
|
|
500
|
+
if (error.message.includes("Failed to parse payment requirements")) {
|
|
501
|
+
console.error("Server sent invalid 402 response");
|
|
502
|
+
} else if (error.message.includes("Failed to create payment payload")) {
|
|
503
|
+
console.error("Could not sign payment:", error.message);
|
|
504
|
+
} else if (error.message.includes("No network/scheme registered")) {
|
|
505
|
+
console.error("Server requires a payment network we don't support");
|
|
506
|
+
} else if (error.message.includes("All payment requirements were filtered out")) {
|
|
507
|
+
console.error("All payment options were rejected by our policies");
|
|
508
|
+
} else {
|
|
509
|
+
console.error("Unexpected error:", error.message);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
---
|
|
516
|
+
|
|
517
|
+
## Lifecycle Hooks
|
|
518
|
+
|
|
519
|
+
```typescript
|
|
520
|
+
const client = new x402Client();
|
|
521
|
+
registerExactAvmScheme(client, { signer });
|
|
522
|
+
|
|
523
|
+
client.onBeforePaymentCreation(async (context) => {
|
|
524
|
+
const { selectedRequirements } = context;
|
|
525
|
+
console.log(
|
|
526
|
+
`About to pay ${selectedRequirements.amount} on ${selectedRequirements.network}`,
|
|
527
|
+
);
|
|
528
|
+
|
|
529
|
+
const amountUSDC = Number(selectedRequirements.amount) / 1_000_000;
|
|
530
|
+
if (amountUSDC > 10) {
|
|
531
|
+
return { abort: true, reason: "Amount exceeds $10 USDC limit" };
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
client.onAfterPaymentCreation(async (context) => {
|
|
536
|
+
console.log("Payment created successfully for:", context.paymentRequired.resource?.url);
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
client.onPaymentCreationFailure(async (context) => {
|
|
540
|
+
console.error("Payment failed:", context.error.message);
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
const fetchWithPay = wrapFetchWithPayment(fetch, client);
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
---
|
|
547
|
+
|
|
548
|
+
## Reading Payment Response Headers
|
|
549
|
+
|
|
550
|
+
```typescript
|
|
551
|
+
import { decodePaymentResponseHeader } from "@x402-avm/fetch";
|
|
552
|
+
|
|
553
|
+
const response = await fetchWithPay("https://api.example.com/paid-endpoint");
|
|
554
|
+
|
|
555
|
+
const paymentResponseHeader =
|
|
556
|
+
response.headers.get("PAYMENT-RESPONSE");
|
|
557
|
+
|
|
558
|
+
if (paymentResponseHeader) {
|
|
559
|
+
const receipt = decodePaymentResponseHeader(paymentResponseHeader);
|
|
560
|
+
console.log("Transaction settled:", receipt);
|
|
561
|
+
}
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
---
|
|
565
|
+
|
|
566
|
+
## Custom Payment Requirements Selector
|
|
567
|
+
|
|
568
|
+
```typescript
|
|
569
|
+
const cheapestFirst = (version: number, reqs: PaymentRequirements[]) => {
|
|
570
|
+
return reqs.sort((a, b) => {
|
|
571
|
+
const amountA = BigInt(a.amount ?? a.maxAmountRequired ?? "0");
|
|
572
|
+
const amountB = BigInt(b.amount ?? b.maxAmountRequired ?? "0");
|
|
573
|
+
return amountA < amountB ? -1 : amountA > amountB ? 1 : 0;
|
|
574
|
+
})[0];
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
const client = new x402Client(cheapestFirst);
|
|
578
|
+
registerExactAvmScheme(client, { signer });
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
---
|
|
582
|
+
|
|
583
|
+
## Complete Browser Example (React + Pera Wallet)
|
|
584
|
+
|
|
585
|
+
```tsx
|
|
586
|
+
import React, { useState, useMemo, useCallback } from "react";
|
|
587
|
+
import { WalletProvider, useWallet } from "@txnlab/use-wallet-react";
|
|
588
|
+
import { WalletId } from "@txnlab/use-wallet";
|
|
589
|
+
import { wrapFetchWithPayment, x402Client } from "@x402-avm/fetch";
|
|
590
|
+
import { registerExactAvmScheme } from "@x402-avm/avm/exact/client";
|
|
591
|
+
import type { ClientAvmSigner } from "@x402-avm/avm";
|
|
592
|
+
|
|
593
|
+
const WALLET_PROVIDERS = [
|
|
594
|
+
{ id: WalletId.PERA },
|
|
595
|
+
{ id: WalletId.DEFLY },
|
|
596
|
+
{ id: WalletId.LUTE },
|
|
597
|
+
];
|
|
598
|
+
|
|
599
|
+
function PaidApiDemo() {
|
|
600
|
+
const { activeAccount, signTransactions, providers } = useWallet();
|
|
601
|
+
const [result, setResult] = useState<string>("");
|
|
602
|
+
const [loading, setLoading] = useState(false);
|
|
603
|
+
|
|
604
|
+
const signer: ClientAvmSigner | null = useMemo(() => {
|
|
605
|
+
if (!activeAccount) return null;
|
|
606
|
+
return {
|
|
607
|
+
address: activeAccount.address,
|
|
608
|
+
signTransactions: async (txns: Uint8Array[], indexesToSign?: number[]) => {
|
|
609
|
+
return signTransactions(txns, indexesToSign);
|
|
610
|
+
},
|
|
611
|
+
};
|
|
612
|
+
}, [activeAccount, signTransactions]);
|
|
613
|
+
|
|
614
|
+
const fetchWithPay = useMemo(() => {
|
|
615
|
+
if (!signer) return null;
|
|
616
|
+
const client = new x402Client();
|
|
617
|
+
registerExactAvmScheme(client, { signer });
|
|
618
|
+
return wrapFetchWithPayment(fetch, client);
|
|
619
|
+
}, [signer]);
|
|
620
|
+
|
|
621
|
+
const handleConnect = useCallback(async () => {
|
|
622
|
+
const pera = providers?.find((p) => p.metadata.id === WalletId.PERA);
|
|
623
|
+
if (pera) await pera.connect();
|
|
624
|
+
}, [providers]);
|
|
625
|
+
|
|
626
|
+
const handleFetch = useCallback(async () => {
|
|
627
|
+
if (!fetchWithPay) return;
|
|
628
|
+
setLoading(true);
|
|
629
|
+
try {
|
|
630
|
+
const response = await fetchWithPay("https://api.example.com/premium-content");
|
|
631
|
+
const data = await response.json();
|
|
632
|
+
setResult(JSON.stringify(data, null, 2));
|
|
633
|
+
} catch (err) {
|
|
634
|
+
setResult(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
635
|
+
} finally {
|
|
636
|
+
setLoading(false);
|
|
637
|
+
}
|
|
638
|
+
}, [fetchWithPay]);
|
|
639
|
+
|
|
640
|
+
return (
|
|
641
|
+
<div style={{ padding: "20px", fontFamily: "monospace" }}>
|
|
642
|
+
<h1>x402-avm Fetch Demo</h1>
|
|
643
|
+
{!activeAccount ? (
|
|
644
|
+
<button onClick={handleConnect}>Connect Pera Wallet</button>
|
|
645
|
+
) : (
|
|
646
|
+
<div>
|
|
647
|
+
<p>Connected: {activeAccount.address.slice(0, 8)}...</p>
|
|
648
|
+
<button onClick={handleFetch} disabled={loading}>
|
|
649
|
+
{loading ? "Paying & Fetching..." : "Fetch Premium Content ($0.01 USDC)"}
|
|
650
|
+
</button>
|
|
651
|
+
</div>
|
|
652
|
+
)}
|
|
653
|
+
{result && (
|
|
654
|
+
<pre style={{ background: "#f5f5f5", padding: "10px", marginTop: "10px" }}>
|
|
655
|
+
{result}
|
|
656
|
+
</pre>
|
|
657
|
+
)}
|
|
658
|
+
</div>
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
export default function App() {
|
|
663
|
+
return (
|
|
664
|
+
<WalletProvider wallets={WALLET_PROVIDERS}>
|
|
665
|
+
<PaidApiDemo />
|
|
666
|
+
</WalletProvider>
|
|
667
|
+
);
|
|
668
|
+
}
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
---
|
|
672
|
+
|
|
673
|
+
## Complete Node.js CLI Example
|
|
674
|
+
|
|
675
|
+
```typescript
|
|
676
|
+
import { wrapFetchWithPayment, x402Client } from "@x402-avm/fetch";
|
|
677
|
+
import { registerExactAvmScheme } from "@x402-avm/avm/exact/client";
|
|
678
|
+
import { ALGORAND_TESTNET_CAIP2 } from "@x402-avm/avm";
|
|
679
|
+
import algosdk from "algosdk";
|
|
680
|
+
|
|
681
|
+
async function main() {
|
|
682
|
+
const privateKey = process.env.AVM_PRIVATE_KEY;
|
|
683
|
+
if (!privateKey) {
|
|
684
|
+
console.error("Error: AVM_PRIVATE_KEY environment variable is required");
|
|
685
|
+
console.error("Format: Base64-encoded 64-byte key (32-byte seed + 32-byte pubkey)");
|
|
686
|
+
process.exit(1);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
const secretKey = Buffer.from(privateKey, "base64");
|
|
690
|
+
const address = algosdk.encodeAddress(secretKey.slice(32));
|
|
691
|
+
console.log(`Using address: ${address}`);
|
|
692
|
+
|
|
693
|
+
const signer = {
|
|
694
|
+
address,
|
|
695
|
+
signTransactions: async (txns: Uint8Array[], indexesToSign?: number[]) => {
|
|
696
|
+
return txns.map((txn, i) => {
|
|
697
|
+
if (indexesToSign && !indexesToSign.includes(i)) return null;
|
|
698
|
+
const decoded = algosdk.decodeUnsignedTransaction(txn);
|
|
699
|
+
const signed = algosdk.signTransaction(decoded, secretKey);
|
|
700
|
+
return signed.blob;
|
|
701
|
+
});
|
|
702
|
+
},
|
|
703
|
+
};
|
|
704
|
+
|
|
705
|
+
const client = new x402Client();
|
|
706
|
+
registerExactAvmScheme(client, {
|
|
707
|
+
signer,
|
|
708
|
+
algodConfig: {
|
|
709
|
+
algodUrl: process.env.ALGOD_TESTNET_URL || "https://testnet-api.algonode.cloud",
|
|
710
|
+
},
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
client.registerPolicy((version, reqs) => {
|
|
714
|
+
return reqs.filter((r) => BigInt(r.amount ?? "0") <= BigInt("5000000"));
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
client.onBeforePaymentCreation(async ({ selectedRequirements }) => {
|
|
718
|
+
const amountUSDC = Number(selectedRequirements.amount) / 1_000_000;
|
|
719
|
+
console.log(`[x402] Paying $${amountUSDC.toFixed(6)} USDC on ${selectedRequirements.network}`);
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
client.onAfterPaymentCreation(async () => {
|
|
723
|
+
console.log("[x402] Payment transaction signed successfully");
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
const fetchWithPay = wrapFetchWithPayment(fetch, client);
|
|
727
|
+
|
|
728
|
+
const url = process.argv[2] || "https://api.example.com/paid-endpoint";
|
|
729
|
+
console.log(`\nFetching: ${url}`);
|
|
730
|
+
|
|
731
|
+
try {
|
|
732
|
+
const response = await fetchWithPay(url);
|
|
733
|
+
console.log(`Status: ${response.status} ${response.statusText}`);
|
|
734
|
+
|
|
735
|
+
const paymentResponse = response.headers.get("PAYMENT-RESPONSE");
|
|
736
|
+
if (paymentResponse) {
|
|
737
|
+
console.log(`Payment Response: ${paymentResponse}`);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
const contentType = response.headers.get("content-type") || "";
|
|
741
|
+
if (contentType.includes("application/json")) {
|
|
742
|
+
const data = await response.json();
|
|
743
|
+
console.log("\nResponse:", JSON.stringify(data, null, 2));
|
|
744
|
+
} else {
|
|
745
|
+
const text = await response.text();
|
|
746
|
+
console.log("\nResponse:", text);
|
|
747
|
+
}
|
|
748
|
+
} catch (error) {
|
|
749
|
+
console.error("\nFailed:", error instanceof Error ? error.message : error);
|
|
750
|
+
process.exit(1);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
main();
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
---
|
|
758
|
+
|
|
759
|
+
## Complete Axios Node.js API Client
|
|
760
|
+
|
|
761
|
+
```typescript
|
|
762
|
+
import axios, { AxiosError } from "axios";
|
|
763
|
+
import { wrapAxiosWithPayment, x402Client, type PaymentPolicy } from "@x402-avm/axios";
|
|
764
|
+
import { registerExactAvmScheme } from "@x402-avm/avm/exact/client";
|
|
765
|
+
import { ALGORAND_TESTNET_CAIP2 } from "@x402-avm/avm";
|
|
766
|
+
import algosdk from "algosdk";
|
|
767
|
+
|
|
768
|
+
const secretKey = Buffer.from(process.env.AVM_PRIVATE_KEY!, "base64");
|
|
769
|
+
const address = algosdk.encodeAddress(secretKey.slice(32));
|
|
770
|
+
|
|
771
|
+
const signer = {
|
|
772
|
+
address,
|
|
773
|
+
signTransactions: async (txns: Uint8Array[], indexesToSign?: number[]) => {
|
|
774
|
+
return txns.map((txn, i) => {
|
|
775
|
+
if (indexesToSign && !indexesToSign.includes(i)) return null;
|
|
776
|
+
const decoded = algosdk.decodeUnsignedTransaction(txn);
|
|
777
|
+
const signed = algosdk.signTransaction(decoded, secretKey);
|
|
778
|
+
return signed.blob;
|
|
779
|
+
});
|
|
780
|
+
},
|
|
781
|
+
};
|
|
782
|
+
|
|
783
|
+
const client = new x402Client();
|
|
784
|
+
registerExactAvmScheme(client, {
|
|
785
|
+
signer,
|
|
786
|
+
algodConfig: {
|
|
787
|
+
algodUrl: process.env.ALGOD_TESTNET_URL || "https://testnet-api.algonode.cloud",
|
|
788
|
+
},
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
const maxPaymentPolicy: PaymentPolicy = (version, reqs) => {
|
|
792
|
+
return reqs.filter((r) => BigInt(r.amount ?? "0") <= BigInt("5000000"));
|
|
793
|
+
};
|
|
794
|
+
|
|
795
|
+
client.registerPolicy(maxPaymentPolicy);
|
|
796
|
+
|
|
797
|
+
client.onBeforePaymentCreation(async ({ selectedRequirements }) => {
|
|
798
|
+
const usdcAmount = Number(selectedRequirements.amount) / 1_000_000;
|
|
799
|
+
console.log(`[x402] Paying $${usdcAmount.toFixed(6)} to ${selectedRequirements.payTo}`);
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
client.onAfterPaymentCreation(async () => {
|
|
803
|
+
console.log("[x402] Transaction signed and submitted");
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
const api = wrapAxiosWithPayment(
|
|
807
|
+
axios.create({
|
|
808
|
+
baseURL: "https://api.example.com",
|
|
809
|
+
timeout: 30000,
|
|
810
|
+
headers: {
|
|
811
|
+
Accept: "application/json",
|
|
812
|
+
"User-Agent": "x402-avm-client/1.0",
|
|
813
|
+
},
|
|
814
|
+
}),
|
|
815
|
+
client,
|
|
816
|
+
);
|
|
817
|
+
|
|
818
|
+
async function fetchPremiumContent(contentId: string) {
|
|
819
|
+
try {
|
|
820
|
+
const response = await api.get(`/premium/content/${contentId}`);
|
|
821
|
+
console.log("Content:", response.data);
|
|
822
|
+
|
|
823
|
+
const paymentReceipt = response.headers["payment-response"];
|
|
824
|
+
if (paymentReceipt) {
|
|
825
|
+
console.log("Payment receipt:", paymentReceipt);
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
return response.data;
|
|
829
|
+
} catch (error) {
|
|
830
|
+
if (error instanceof AxiosError) {
|
|
831
|
+
if (error.response?.status === 402) {
|
|
832
|
+
console.error("Payment failed -- server rejected our payment");
|
|
833
|
+
} else if (error.response?.status === 404) {
|
|
834
|
+
console.error("Content not found");
|
|
835
|
+
} else {
|
|
836
|
+
console.error(`API error: ${error.response?.status}`, error.response?.data);
|
|
837
|
+
}
|
|
838
|
+
} else {
|
|
839
|
+
console.error("Unexpected error:", error);
|
|
840
|
+
}
|
|
841
|
+
throw error;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
async function main() {
|
|
846
|
+
console.log(`Wallet address: ${address}`);
|
|
847
|
+
console.log(`Network: ${ALGORAND_TESTNET_CAIP2}\n`);
|
|
848
|
+
|
|
849
|
+
const content = await fetchPremiumContent("article-123");
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
main().catch((err) => {
|
|
853
|
+
console.error("Fatal:", err.message);
|
|
854
|
+
process.exit(1);
|
|
855
|
+
});
|
|
856
|
+
```
|
|
857
|
+
|
|
858
|
+
---
|
|
859
|
+
|
|
860
|
+
## Axios Interceptor Order
|
|
861
|
+
|
|
862
|
+
```typescript
|
|
863
|
+
const api = axios.create();
|
|
864
|
+
|
|
865
|
+
api.interceptors.request.use((config) => {
|
|
866
|
+
config.headers.set("Authorization", `Bearer ${getToken()}`);
|
|
867
|
+
return config;
|
|
868
|
+
});
|
|
869
|
+
|
|
870
|
+
api.interceptors.response.use(
|
|
871
|
+
(response) => {
|
|
872
|
+
console.log(`${response.config.method} ${response.config.url}: ${response.status}`);
|
|
873
|
+
return response;
|
|
874
|
+
},
|
|
875
|
+
);
|
|
876
|
+
|
|
877
|
+
// Add payment interceptor last
|
|
878
|
+
wrapAxiosWithPayment(api, client);
|
|
879
|
+
```
|