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 +38 -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,371 @@
|
|
|
1
|
+
# x402 HTTP Client Reference
|
|
2
|
+
|
|
3
|
+
Detailed API reference for `@x402-avm/fetch`, `@x402-avm/axios`, and `@x402-avm/avm` client packages.
|
|
4
|
+
|
|
5
|
+
## Package: @x402-avm/fetch
|
|
6
|
+
|
|
7
|
+
### Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @x402-avm/fetch @x402-avm/avm algosdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Exports
|
|
14
|
+
|
|
15
|
+
| Export | Type | Description |
|
|
16
|
+
|--------|------|-------------|
|
|
17
|
+
| `wrapFetchWithPayment` | Function | Wraps fetch with automatic 402 payment handling |
|
|
18
|
+
| `wrapFetchWithPaymentFromConfig` | Function | Config-based variant of the above |
|
|
19
|
+
| `x402Client` | Class | Core client for managing payment schemes |
|
|
20
|
+
| `x402HTTPClient` | Class | HTTP-level payment client |
|
|
21
|
+
| `decodePaymentResponseHeader` | Function | Decodes the PAYMENT-RESPONSE header |
|
|
22
|
+
| `PaymentPolicy` | Type | Policy function type for filtering requirements |
|
|
23
|
+
| `SchemeRegistration` | Type | Scheme registration configuration |
|
|
24
|
+
| `x402ClientConfig` | Type | Configuration object type |
|
|
25
|
+
| `PaymentRequired` | Type | 402 response structure |
|
|
26
|
+
| `PaymentRequirements` | Type | Individual payment requirement |
|
|
27
|
+
| `PaymentPayload` | Type | Signed payment payload |
|
|
28
|
+
| `Network` | Type | Network identifier string type |
|
|
29
|
+
| `SchemeNetworkClient` | Type | Client-side scheme interface |
|
|
30
|
+
|
|
31
|
+
### wrapFetchWithPayment
|
|
32
|
+
|
|
33
|
+
Wraps a standard `fetch` function with automatic 402 payment handling.
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
function wrapFetchWithPayment(
|
|
37
|
+
fetch: typeof globalThis.fetch,
|
|
38
|
+
client: x402Client | x402HTTPClient,
|
|
39
|
+
): (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Flow:**
|
|
43
|
+
1. Makes the initial HTTP request normally
|
|
44
|
+
2. If the server responds with 402, parses payment requirements from the response
|
|
45
|
+
3. Selects a suitable payment method based on registered schemes
|
|
46
|
+
4. Creates a payment payload by signing a transaction group
|
|
47
|
+
5. Retries the request with the `PAYMENT-SIGNATURE` header
|
|
48
|
+
6. If the response is anything other than 402, returns it as-is
|
|
49
|
+
|
|
50
|
+
### wrapFetchWithPaymentFromConfig
|
|
51
|
+
|
|
52
|
+
Config-based variant that creates the `x402Client` internally.
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
function wrapFetchWithPaymentFromConfig(
|
|
56
|
+
fetch: typeof globalThis.fetch,
|
|
57
|
+
config: x402ClientConfig,
|
|
58
|
+
): (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### x402ClientConfig
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
interface x402ClientConfig {
|
|
65
|
+
schemes: Array<{
|
|
66
|
+
network: string; // CAIP-2 identifier or wildcard ("algorand:*")
|
|
67
|
+
client: SchemeNetworkClient;
|
|
68
|
+
}>;
|
|
69
|
+
policies?: PaymentPolicy[];
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### PaymentPolicy
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
type PaymentPolicy = (
|
|
77
|
+
version: number,
|
|
78
|
+
requirements: PaymentRequirements[],
|
|
79
|
+
) => PaymentRequirements[];
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Policies are applied in registration order. Each policy receives the remaining requirements and returns a filtered/transformed list.
|
|
83
|
+
|
|
84
|
+
### decodePaymentResponseHeader
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
function decodePaymentResponseHeader(header: string): any;
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Decodes the `PAYMENT-RESPONSE` header returned by the server after settlement.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Package: @x402-avm/axios
|
|
95
|
+
|
|
96
|
+
### Installation
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
npm install @x402-avm/axios @x402-avm/avm algosdk axios
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Exports
|
|
103
|
+
|
|
104
|
+
| Export | Type | Description |
|
|
105
|
+
|--------|------|-------------|
|
|
106
|
+
| `wrapAxiosWithPayment` | Function | Wraps Axios instance with 402 payment interceptor |
|
|
107
|
+
| `wrapAxiosWithPaymentFromConfig` | Function | Config-based variant of the above |
|
|
108
|
+
| `x402Client` | Class | Core client for managing payment schemes |
|
|
109
|
+
| `x402HTTPClient` | Class | HTTP-level payment client |
|
|
110
|
+
| `decodePaymentResponseHeader` | Function | Decodes the PAYMENT-RESPONSE header |
|
|
111
|
+
| `PaymentPolicy` | Type | Policy function type for filtering requirements |
|
|
112
|
+
| `SchemeRegistration` | Type | Scheme registration configuration |
|
|
113
|
+
| `x402ClientConfig` | Type | Configuration object type |
|
|
114
|
+
| `PaymentRequired` | Type | 402 response structure |
|
|
115
|
+
| `PaymentRequirements` | Type | Individual payment requirement |
|
|
116
|
+
| `PaymentPayload` | Type | Signed payment payload |
|
|
117
|
+
| `Network` | Type | Network identifier string type |
|
|
118
|
+
| `SchemeNetworkClient` | Type | Client-side scheme interface |
|
|
119
|
+
|
|
120
|
+
### wrapAxiosWithPayment
|
|
121
|
+
|
|
122
|
+
Adds a response interceptor that handles 402 responses by signing and submitting payment transactions.
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
function wrapAxiosWithPayment(
|
|
126
|
+
axiosInstance: AxiosInstance,
|
|
127
|
+
client: x402Client | x402HTTPClient,
|
|
128
|
+
): AxiosInstance;
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Returns the same Axios instance (mutated with the interceptor). The interceptor:
|
|
132
|
+
- Catches 402 responses
|
|
133
|
+
- Parses payment requirements (headers for V2, body for V1)
|
|
134
|
+
- Creates payment payload via `x402Client`
|
|
135
|
+
- Marks the request with `__is402Retry = true` to prevent infinite loops
|
|
136
|
+
- Retries with `PAYMENT-SIGNATURE` header
|
|
137
|
+
|
|
138
|
+
### wrapAxiosWithPaymentFromConfig
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
function wrapAxiosWithPaymentFromConfig(
|
|
142
|
+
axiosInstance: AxiosInstance,
|
|
143
|
+
config: x402ClientConfig,
|
|
144
|
+
): AxiosInstance;
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Interceptor Behavior
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
Client Request --> Axios sends request --> Server Response
|
|
151
|
+
|
|
|
152
|
+
Status != 402 -------> Return response normally
|
|
153
|
+
Status == 402 -------> Already retried? --> Reject
|
|
154
|
+
|
|
|
155
|
+
Parse PaymentRequired
|
|
156
|
+
Create payment payload
|
|
157
|
+
Retry with PAYMENT-SIGNATURE
|
|
158
|
+
Return retried response
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Key details:
|
|
162
|
+
- **Single retry**: Only retries once per 402
|
|
163
|
+
- **Request mutation**: Modifies original config and retries via `axiosInstance.request()`
|
|
164
|
+
- **Concurrent requests**: Each 402 is handled independently
|
|
165
|
+
- **Interceptor order**: Payment interceptor should be added last
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Package: @x402-avm/avm
|
|
170
|
+
|
|
171
|
+
### Installation
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
npm install @x402-avm/avm algosdk
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Exports
|
|
178
|
+
|
|
179
|
+
| Export | Type | Description |
|
|
180
|
+
|--------|------|-------------|
|
|
181
|
+
| `ExactAvmScheme` | Class | Algorand exact payment scheme (client) |
|
|
182
|
+
| `ClientAvmSigner` | Interface | Signer interface for client wallets |
|
|
183
|
+
| `ClientAvmConfig` | Interface | Algod client configuration |
|
|
184
|
+
| `ALGORAND_TESTNET_CAIP2` | Constant | `"algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="` |
|
|
185
|
+
| `ALGORAND_MAINNET_CAIP2` | Constant | `"algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8="` |
|
|
186
|
+
| `isAvmSignerWallet` | Function | Type guard for ClientAvmSigner |
|
|
187
|
+
|
|
188
|
+
### ClientAvmSigner Interface
|
|
189
|
+
|
|
190
|
+
The interface that bridges wallets (browser or server) to the x402 payment system.
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
interface ClientAvmSigner {
|
|
194
|
+
/** The Algorand address of the payer */
|
|
195
|
+
address: string;
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Sign one or more transactions in a group.
|
|
199
|
+
* @param txns - Array of unsigned transaction bytes (msgpack)
|
|
200
|
+
* @param indexesToSign - Optional indices of transactions to sign.
|
|
201
|
+
* If omitted, sign all transactions.
|
|
202
|
+
* @returns Array where signed transactions contain the signed blob,
|
|
203
|
+
* and skipped transactions are null
|
|
204
|
+
*/
|
|
205
|
+
signTransactions(
|
|
206
|
+
txns: Uint8Array[],
|
|
207
|
+
indexesToSign?: number[],
|
|
208
|
+
): Promise<(Uint8Array | null)[]>;
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Subpath: @x402-avm/avm/exact/client
|
|
213
|
+
|
|
214
|
+
| Export | Type | Description |
|
|
215
|
+
|--------|------|-------------|
|
|
216
|
+
| `registerExactAvmScheme` | Function | Registers AVM schemes (V1 + V2) to an x402Client |
|
|
217
|
+
| `AvmClientConfig` | Interface | Configuration for AVM client registration |
|
|
218
|
+
|
|
219
|
+
### registerExactAvmScheme
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
function registerExactAvmScheme(
|
|
223
|
+
client: x402Client,
|
|
224
|
+
config: AvmClientConfig,
|
|
225
|
+
): void;
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### AvmClientConfig
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
interface AvmClientConfig {
|
|
232
|
+
/** The client signer implementation */
|
|
233
|
+
signer: ClientAvmSigner;
|
|
234
|
+
|
|
235
|
+
/** Optional Algod configuration */
|
|
236
|
+
algodConfig?: {
|
|
237
|
+
/** Algod URL (defaults to AlgoNode testnet/mainnet) */
|
|
238
|
+
algodUrl?: string;
|
|
239
|
+
/** Algod API token */
|
|
240
|
+
algodToken?: string;
|
|
241
|
+
/** Pre-configured Algodv2 client */
|
|
242
|
+
algodClient?: algosdk.Algodv2;
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
/** Optional: restrict to specific networks */
|
|
246
|
+
networks?: string[];
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## x402Client Class
|
|
253
|
+
|
|
254
|
+
### Constructor
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
const client = new x402Client(selector?: PaymentRequirementsSelector);
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Optional `selector` overrides default selection logic for choosing among payment requirements.
|
|
261
|
+
|
|
262
|
+
### Methods
|
|
263
|
+
|
|
264
|
+
| Method | Signature | Description |
|
|
265
|
+
|--------|-----------|-------------|
|
|
266
|
+
| `registerPolicy` | `(policy: PaymentPolicy) => x402Client` | Register a payment policy (chainable) |
|
|
267
|
+
| `onBeforePaymentCreation` | `(hook: BeforePaymentHook) => void` | Register pre-payment hook |
|
|
268
|
+
| `onAfterPaymentCreation` | `(hook: AfterPaymentHook) => void` | Register post-payment hook |
|
|
269
|
+
| `onPaymentCreationFailure` | `(hook: PaymentFailureHook) => void` | Register failure hook |
|
|
270
|
+
|
|
271
|
+
### Lifecycle Hooks
|
|
272
|
+
|
|
273
|
+
**BeforePaymentCreation:**
|
|
274
|
+
```typescript
|
|
275
|
+
client.onBeforePaymentCreation(async (context) => {
|
|
276
|
+
// context.selectedRequirements - the chosen payment requirements
|
|
277
|
+
// context.paymentRequired - full 402 response
|
|
278
|
+
// Return { abort: true, reason: "..." } to cancel payment
|
|
279
|
+
});
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
**AfterPaymentCreation:**
|
|
283
|
+
```typescript
|
|
284
|
+
client.onAfterPaymentCreation(async (context) => {
|
|
285
|
+
// context.paymentPayload - the signed payload
|
|
286
|
+
// context.paymentRequired - full 402 response
|
|
287
|
+
});
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
**PaymentCreationFailure:**
|
|
291
|
+
```typescript
|
|
292
|
+
client.onPaymentCreationFailure(async (context) => {
|
|
293
|
+
// context.error - the error that occurred
|
|
294
|
+
// Return { recovered: true, payload: ... } to provide fallback
|
|
295
|
+
});
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## Environment Variables
|
|
301
|
+
|
|
302
|
+
| Variable | Description | Format |
|
|
303
|
+
|----------|-------------|--------|
|
|
304
|
+
| `AVM_PRIVATE_KEY` | Algorand private key | Base64-encoded 64-byte key (32-byte seed + 32-byte pubkey) |
|
|
305
|
+
| `ALGOD_TESTNET_URL` | Custom Algod testnet URL | URL string (default: `https://testnet-api.algonode.cloud`) |
|
|
306
|
+
| `ALGOD_MAINNET_URL` | Custom Algod mainnet URL | URL string (default: `https://mainnet-api.algonode.cloud`) |
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## Network Constants
|
|
311
|
+
|
|
312
|
+
| Constant | Value |
|
|
313
|
+
|----------|-------|
|
|
314
|
+
| `ALGORAND_TESTNET_CAIP2` | `"algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="` |
|
|
315
|
+
| `ALGORAND_MAINNET_CAIP2` | `"algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8="` |
|
|
316
|
+
| `V1_ALGORAND_TESTNET` | `"algorand-testnet"` |
|
|
317
|
+
| `V1_ALGORAND_MAINNET` | `"algorand-mainnet"` |
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
## Testing
|
|
322
|
+
|
|
323
|
+
### Unit Testing a Client
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
import { x402Client } from "@x402-avm/fetch";
|
|
327
|
+
import { registerExactAvmScheme } from "@x402-avm/avm/exact/client";
|
|
328
|
+
|
|
329
|
+
// Create a mock signer for testing
|
|
330
|
+
const mockSigner = {
|
|
331
|
+
address: "TEST_ADDRESS_58_CHARS_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
|
332
|
+
signTransactions: async (txns: Uint8Array[], indexesToSign?: number[]) => {
|
|
333
|
+
return txns.map((_, i) => {
|
|
334
|
+
if (indexesToSign && !indexesToSign.includes(i)) return null;
|
|
335
|
+
return new Uint8Array([0x80]); // mock signed bytes
|
|
336
|
+
});
|
|
337
|
+
},
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
const client = new x402Client();
|
|
341
|
+
registerExactAvmScheme(client, { signer: mockSigner });
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### Integration Testing with Real Transactions
|
|
345
|
+
|
|
346
|
+
```bash
|
|
347
|
+
# Set up environment
|
|
348
|
+
export AVM_PRIVATE_KEY="your-base64-key-here"
|
|
349
|
+
|
|
350
|
+
# Run with tsx
|
|
351
|
+
npx tsx client-test.ts https://api.example.com/paid-endpoint
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## Important Notes
|
|
357
|
+
|
|
358
|
+
- `AVM_PRIVATE_KEY` is a Base64-encoded 64-byte key. The first 32 bytes are the seed, the last 32 bytes are the public key.
|
|
359
|
+
- Address derivation always uses `algosdk.encodeAddress(secretKey.slice(32))` -- the public key portion.
|
|
360
|
+
- The `algorand:*` wildcard in config-based setups matches any Algorand network (testnet or mainnet).
|
|
361
|
+
- Policies are composable and applied in order. An empty result from any policy means no payment options are available.
|
|
362
|
+
- The Axios interceptor modifies the instance in place and returns it. Do not create a new instance after wrapping.
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## External Resources
|
|
367
|
+
|
|
368
|
+
- [x402-avm Examples Repository](https://github.com/GoPlausible/x402-avm/tree/branch-v2-algorand-publish/examples/)
|
|
369
|
+
- [x402-avm Documentation](https://github.com/GoPlausible/.github/blob/main/profile/algorand-x402-documentation/)
|
|
370
|
+
- [@txnlab/use-wallet Documentation](https://txnlab.gitbook.io/use-wallet)
|
|
371
|
+
- [algosdk TypeScript Reference](https://algorand.github.io/js-algorand-sdk/)
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# Creating x402 HTTP Clients (Fetch and Axios)
|
|
2
|
+
|
|
3
|
+
Build HTTP clients that automatically detect `402 Payment Required` responses, sign Algorand transactions, and retry requests with payment proof -- all transparently.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
Before using this skill, ensure:
|
|
8
|
+
|
|
9
|
+
1. **Node.js or browser environment** with TypeScript support
|
|
10
|
+
2. **An Algorand wallet or private key** for signing payment transactions
|
|
11
|
+
3. **USDC balance** on the target network (testnet or mainnet) in the payer's account
|
|
12
|
+
|
|
13
|
+
## Core Workflow: The 402 Payment Flow
|
|
14
|
+
|
|
15
|
+
The key insight is that `wrapFetchWithPayment` and `wrapAxiosWithPayment` intercept 402 responses, sign a transaction group, and retry the original request with the payment header -- all automatically:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Client Request (GET /api/premium)
|
|
19
|
+
|
|
|
20
|
+
v
|
|
21
|
+
Server Response
|
|
22
|
+
|
|
|
23
|
+
+-- Status != 402 --> Return response as-is
|
|
24
|
+
|
|
|
25
|
+
+-- Status == 402 -->
|
|
26
|
+
|
|
|
27
|
+
+-- Parse PaymentRequired (headers V2 / body V1)
|
|
28
|
+
+-- Select scheme via registered x402Client
|
|
29
|
+
+-- Build atomic transaction group
|
|
30
|
+
+-- Sign with ClientAvmSigner (wallet or private key)
|
|
31
|
+
+-- Encode PAYMENT-SIGNATURE header
|
|
32
|
+
+-- Retry original request with payment header
|
|
33
|
+
|
|
|
34
|
+
v
|
|
35
|
+
Return retried response
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## How to Proceed
|
|
39
|
+
|
|
40
|
+
### Step 1: Install Dependencies
|
|
41
|
+
|
|
42
|
+
For Fetch-based clients:
|
|
43
|
+
```bash
|
|
44
|
+
npm install @x402-avm/fetch @x402-avm/avm algosdk
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
For Axios-based clients:
|
|
48
|
+
```bash
|
|
49
|
+
npm install @x402-avm/axios @x402-avm/avm algosdk axios
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Step 2: Implement a ClientAvmSigner
|
|
53
|
+
|
|
54
|
+
The `ClientAvmSigner` interface is what bridges your wallet or private key to the x402 payment system.
|
|
55
|
+
|
|
56
|
+
**Interface:**
|
|
57
|
+
```typescript
|
|
58
|
+
interface ClientAvmSigner {
|
|
59
|
+
address: string;
|
|
60
|
+
signTransactions(
|
|
61
|
+
txns: Uint8Array[],
|
|
62
|
+
indexesToSign?: number[],
|
|
63
|
+
): Promise<(Uint8Array | null)[]>;
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**For Node.js (private key):**
|
|
68
|
+
```typescript
|
|
69
|
+
import algosdk from "algosdk";
|
|
70
|
+
|
|
71
|
+
const secretKey = Buffer.from(process.env.AVM_PRIVATE_KEY!, "base64");
|
|
72
|
+
const address = algosdk.encodeAddress(secretKey.slice(32));
|
|
73
|
+
|
|
74
|
+
const signer = {
|
|
75
|
+
address,
|
|
76
|
+
signTransactions: async (txns: Uint8Array[], indexesToSign?: number[]) => {
|
|
77
|
+
return txns.map((txn, i) => {
|
|
78
|
+
if (indexesToSign && !indexesToSign.includes(i)) return null;
|
|
79
|
+
const decoded = algosdk.decodeUnsignedTransaction(txn);
|
|
80
|
+
const signed = algosdk.signTransaction(decoded, secretKey);
|
|
81
|
+
return signed.blob;
|
|
82
|
+
});
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**For Browser (@txnlab/use-wallet):**
|
|
88
|
+
```typescript
|
|
89
|
+
import { useWallet } from "@txnlab/use-wallet-react";
|
|
90
|
+
import type { ClientAvmSigner } from "@x402-avm/avm";
|
|
91
|
+
|
|
92
|
+
function useAvmSigner(): ClientAvmSigner | null {
|
|
93
|
+
const { activeAccount, signTransactions } = useWallet();
|
|
94
|
+
if (!activeAccount) return null;
|
|
95
|
+
return {
|
|
96
|
+
address: activeAccount.address,
|
|
97
|
+
signTransactions: async (txns: Uint8Array[], indexesToSign?: number[]) => {
|
|
98
|
+
return signTransactions(txns, indexesToSign);
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Step 3: Create and Configure the x402Client
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import { x402Client } from "@x402-avm/fetch"; // or "@x402-avm/axios"
|
|
108
|
+
import { registerExactAvmScheme } from "@x402-avm/avm/exact/client";
|
|
109
|
+
|
|
110
|
+
const client = new x402Client();
|
|
111
|
+
registerExactAvmScheme(client, { signer });
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Step 4: Wrap Fetch or Axios
|
|
115
|
+
|
|
116
|
+
**Fetch:**
|
|
117
|
+
```typescript
|
|
118
|
+
import { wrapFetchWithPayment } from "@x402-avm/fetch";
|
|
119
|
+
|
|
120
|
+
const fetchWithPay = wrapFetchWithPayment(fetch, client);
|
|
121
|
+
const response = await fetchWithPay("https://api.example.com/premium-data");
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Axios:**
|
|
125
|
+
```typescript
|
|
126
|
+
import axios from "axios";
|
|
127
|
+
import { wrapAxiosWithPayment } from "@x402-avm/axios";
|
|
128
|
+
|
|
129
|
+
const api = wrapAxiosWithPayment(axios.create(), client);
|
|
130
|
+
const response = await api.get("https://api.example.com/premium-data");
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Step 5: Add Payment Policies (Optional)
|
|
134
|
+
|
|
135
|
+
Policies filter payment requirements before selection. They are applied in order:
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
import type { PaymentPolicy } from "@x402-avm/fetch";
|
|
139
|
+
|
|
140
|
+
const maxAmount: PaymentPolicy = (version, reqs) => {
|
|
141
|
+
return reqs.filter((r) => BigInt(r.amount ?? "0") <= BigInt("5000000"));
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const preferAlgorand: PaymentPolicy = (version, reqs) => {
|
|
145
|
+
const algoReqs = reqs.filter((r) => r.network.startsWith("algorand:"));
|
|
146
|
+
return algoReqs.length > 0 ? algoReqs : reqs;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
client.registerPolicy(preferAlgorand).registerPolicy(maxAmount);
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Step 6: Add Lifecycle Hooks (Optional)
|
|
153
|
+
|
|
154
|
+
Monitor and control the payment lifecycle:
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
client.onBeforePaymentCreation(async ({ selectedRequirements }) => {
|
|
158
|
+
const amountUSDC = Number(selectedRequirements.amount) / 1_000_000;
|
|
159
|
+
console.log(`Paying $${amountUSDC.toFixed(6)} USDC`);
|
|
160
|
+
if (amountUSDC > 10) {
|
|
161
|
+
return { abort: true, reason: "Amount exceeds $10 limit" };
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
client.onAfterPaymentCreation(async () => {
|
|
166
|
+
console.log("Payment signed successfully");
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
client.onPaymentCreationFailure(async ({ error }) => {
|
|
170
|
+
console.error("Payment failed:", error.message);
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Important Rules / Guidelines
|
|
175
|
+
|
|
176
|
+
1. **Always register a scheme before wrapping** -- `registerExactAvmScheme(client, { signer })` must be called before `wrapFetchWithPayment` or `wrapAxiosWithPayment`
|
|
177
|
+
2. **AVM_PRIVATE_KEY format** -- Base64-encoded 64-byte key (32-byte seed + 32-byte public key)
|
|
178
|
+
3. **Address derivation** -- Always use `algosdk.encodeAddress(secretKey.slice(32))`, never the first 32 bytes
|
|
179
|
+
4. **Single retry** -- The wrapper retries exactly once after 402. If the retry also returns 402, the error is propagated
|
|
180
|
+
5. **Interceptor order for Axios** -- Add your own interceptors first, then call `wrapAxiosWithPayment` last
|
|
181
|
+
6. **Config-based alternative** -- Use `wrapFetchWithPaymentFromConfig` / `wrapAxiosWithPaymentFromConfig` for declarative setup without manual `x402Client` construction
|
|
182
|
+
7. **Wildcard networks** -- Use `"algorand:*"` in config-based setups to match any Algorand network
|
|
183
|
+
|
|
184
|
+
## Config-Based Setup (Alternative)
|
|
185
|
+
|
|
186
|
+
Instead of creating an `x402Client` manually, use the config-based approach:
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
import { wrapFetchWithPaymentFromConfig, type x402ClientConfig } from "@x402-avm/fetch";
|
|
190
|
+
import { ExactAvmScheme } from "@x402-avm/avm";
|
|
191
|
+
|
|
192
|
+
const config: x402ClientConfig = {
|
|
193
|
+
schemes: [
|
|
194
|
+
{
|
|
195
|
+
network: "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
|
|
196
|
+
client: new ExactAvmScheme(signer),
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
policies: [maxAmount],
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const fetchWithPay = wrapFetchWithPaymentFromConfig(fetch, config);
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Common Errors / Troubleshooting
|
|
206
|
+
|
|
207
|
+
| Error | Cause | Solution |
|
|
208
|
+
|-------|-------|----------|
|
|
209
|
+
| `Failed to parse payment requirements` | Server returned 402 with invalid body | Check server is running x402-compatible middleware |
|
|
210
|
+
| `Failed to create payment payload` | Insufficient balance or wrong network | Ensure USDC balance on the correct network |
|
|
211
|
+
| `Payment already attempted` | Server returned 402 after payment was sent | Payment was rejected; check facilitator logs |
|
|
212
|
+
| `No network/scheme registered` | Server requires an unregistered network | Register the needed scheme with `registerExactAvmScheme` |
|
|
213
|
+
| `Payment creation aborted` | A `beforePaymentCreation` hook returned abort | Review hook logic; check amount limits |
|
|
214
|
+
| `All payment requirements were filtered out` | Policies removed all options | Relax policies or register additional schemes |
|
|
215
|
+
| `No client registered for x402 version: 2` | No schemes registered at all | Call `registerExactAvmScheme(client, { signer })` |
|
|
216
|
+
|
|
217
|
+
## Reading Payment Receipts
|
|
218
|
+
|
|
219
|
+
After a successful paid request, check the response header:
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
import { decodePaymentResponseHeader } from "@x402-avm/fetch";
|
|
223
|
+
|
|
224
|
+
const paymentResponseHeader = response.headers.get("PAYMENT-RESPONSE");
|
|
225
|
+
if (paymentResponseHeader) {
|
|
226
|
+
const receipt = decodePaymentResponseHeader(paymentResponseHeader);
|
|
227
|
+
console.log("Transaction settled:", receipt);
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## References / Further Reading
|
|
232
|
+
|
|
233
|
+
- [create-typescript-x402-client-reference.md](./create-typescript-x402-client-reference.md) - Detailed API reference
|
|
234
|
+
- [create-typescript-x402-client-examples.md](./create-typescript-x402-client-examples.md) - Complete code examples
|
|
235
|
+
- [x402-avm Fetch Examples](https://github.com/GoPlausible/x402-avm/tree/branch-v2-algorand-publish/examples/)
|
|
236
|
+
- [x402-avm Documentation](https://github.com/GoPlausible/.github/blob/main/profile/algorand-x402-documentation/)
|