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,629 @@
|
|
|
1
|
+
# Python x402 Facilitator and Bazaar Discovery Reference
|
|
2
|
+
|
|
3
|
+
Detailed API reference for building x402 facilitator services in Python with Algorand (AVM) support, including Bazaar discovery extension.
|
|
4
|
+
|
|
5
|
+
## FacilitatorAvmSigner Protocol
|
|
6
|
+
|
|
7
|
+
The `FacilitatorAvmSigner` is a Python Protocol class (structural typing -- no inheritance required). Any class implementing all six methods satisfies the protocol.
|
|
8
|
+
|
|
9
|
+
**Import:**
|
|
10
|
+
```python
|
|
11
|
+
from x402.mechanisms.avm.signer import FacilitatorAvmSigner
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
### Method: `get_addresses()`
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
def get_addresses(self) -> list[str]:
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Returns all managed fee payer addresses. The facilitator uses these addresses to identify which transactions it is responsible for signing in an atomic group.
|
|
21
|
+
|
|
22
|
+
- **Returns:** List of 58-character Algorand addresses
|
|
23
|
+
- **Typical implementation:** Returns a single-element list with the facilitator's address
|
|
24
|
+
|
|
25
|
+
### Method: `sign_transaction()`
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
def sign_transaction(
|
|
29
|
+
self, txn_bytes: bytes, fee_payer: str, network: str,
|
|
30
|
+
) -> bytes:
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Signs a single transaction with the fee payer's private key.
|
|
34
|
+
|
|
35
|
+
- **Parameters:**
|
|
36
|
+
- `txn_bytes` -- Raw msgpack-encoded unsigned transaction bytes
|
|
37
|
+
- `fee_payer` -- 58-character Algorand address of the fee payer
|
|
38
|
+
- `network` -- CAIP-2 network identifier (e.g., `"algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="`)
|
|
39
|
+
- **Returns:** Raw msgpack-encoded signed transaction bytes
|
|
40
|
+
- **Encoding note:** Must convert `txn_bytes` to base64 before calling `msgpack_decode()`, then convert `msgpack_encode()` result back from base64
|
|
41
|
+
|
|
42
|
+
### Method: `sign_group()`
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
def sign_group(
|
|
46
|
+
self,
|
|
47
|
+
group_bytes: list[bytes],
|
|
48
|
+
fee_payer: str,
|
|
49
|
+
indexes_to_sign: list[int],
|
|
50
|
+
network: str,
|
|
51
|
+
) -> list[bytes]:
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Signs specified transactions in an atomic group.
|
|
55
|
+
|
|
56
|
+
- **Parameters:**
|
|
57
|
+
- `group_bytes` -- List of raw msgpack-encoded transaction bytes (mix of signed and unsigned)
|
|
58
|
+
- `fee_payer` -- 58-character Algorand address
|
|
59
|
+
- `indexes_to_sign` -- Indexes of transactions this facilitator should sign
|
|
60
|
+
- `network` -- CAIP-2 network identifier
|
|
61
|
+
- **Returns:** Updated list with signed bytes at specified indexes, unchanged bytes elsewhere
|
|
62
|
+
- **Typical pattern:** Delegates to `sign_transaction()` for each index
|
|
63
|
+
|
|
64
|
+
### Method: `simulate_group()`
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
def simulate_group(
|
|
68
|
+
self, group_bytes: list[bytes], network: str,
|
|
69
|
+
) -> None:
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Simulates a transaction group to verify it would succeed on-chain. Raises an exception if simulation fails.
|
|
73
|
+
|
|
74
|
+
- **Parameters:**
|
|
75
|
+
- `group_bytes` -- List of raw msgpack-encoded transaction bytes
|
|
76
|
+
- `network` -- CAIP-2 network identifier
|
|
77
|
+
- **Returns:** None (raises on failure)
|
|
78
|
+
- **Key pattern:** Wrap unsigned `Transaction` objects with `SignedTransaction(txn, None)` and set `allow_empty_signatures=True`
|
|
79
|
+
- **Checks:** Iterates `txn-groups` in simulation result for `failure-message`
|
|
80
|
+
|
|
81
|
+
### Method: `send_group()`
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
def send_group(
|
|
85
|
+
self, group_bytes: list[bytes], network: str,
|
|
86
|
+
) -> str:
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Sends a fully signed transaction group to the network.
|
|
90
|
+
|
|
91
|
+
- **Parameters:**
|
|
92
|
+
- `group_bytes` -- List of raw msgpack-encoded signed transaction bytes
|
|
93
|
+
- `network` -- CAIP-2 network identifier
|
|
94
|
+
- **Returns:** Transaction ID string
|
|
95
|
+
- **Key pattern:** Use `send_raw_transaction(base64.b64encode(b"".join(group_bytes)))` to concatenate and send all transactions efficiently
|
|
96
|
+
- **Important:** Do NOT use `send_transactions()` which asserts inputs are NOT `Transaction` type
|
|
97
|
+
|
|
98
|
+
### Method: `confirm_transaction()`
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
def confirm_transaction(
|
|
102
|
+
self, txid: str, network: str, rounds: int = 4,
|
|
103
|
+
) -> None:
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Waits for transaction confirmation on the blockchain.
|
|
107
|
+
|
|
108
|
+
- **Parameters:**
|
|
109
|
+
- `txid` -- Transaction ID returned by `send_group()`
|
|
110
|
+
- `network` -- CAIP-2 network identifier
|
|
111
|
+
- `rounds` -- Maximum number of rounds to wait (default: 4)
|
|
112
|
+
- **Returns:** None (raises on timeout)
|
|
113
|
+
- **Note:** Algorand has instant finality; once confirmed, the transaction is permanent
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## register_exact_avm_facilitator
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
from x402.mechanisms.avm.exact import register_exact_avm_facilitator
|
|
121
|
+
|
|
122
|
+
register_exact_avm_facilitator(
|
|
123
|
+
facilitator: x402Facilitator,
|
|
124
|
+
signer: FacilitatorAvmSigner,
|
|
125
|
+
networks: list[str] = None,
|
|
126
|
+
)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Registers the AVM exact payment scheme on a facilitator instance.
|
|
130
|
+
|
|
131
|
+
- **Parameters:**
|
|
132
|
+
- `facilitator` -- An `x402Facilitator` instance
|
|
133
|
+
- `signer` -- Any object implementing the `FacilitatorAvmSigner` protocol
|
|
134
|
+
- `networks` -- List of CAIP-2 network identifiers to register for. Defaults to `[ALGORAND_TESTNET_CAIP2, ALGORAND_MAINNET_CAIP2]`
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## ExactAvmFacilitatorScheme
|
|
139
|
+
|
|
140
|
+
The scheme class registered by `register_exact_avm_facilitator`. It handles:
|
|
141
|
+
|
|
142
|
+
- **Verification:** Decodes the payment group, validates transaction structure, simulates on-chain
|
|
143
|
+
- **Settlement:** Signs the fee payer transaction, sends the atomic group, confirms on-chain
|
|
144
|
+
- **Security validation:** Checks for rekey attacks, close-to exploits, blocked transaction types
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Multi-Network Facilitator (EVM + SVM + AVM)
|
|
149
|
+
|
|
150
|
+
A facilitator can support multiple blockchain networks simultaneously:
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
from x402 import x402Facilitator
|
|
154
|
+
|
|
155
|
+
# EVM registration
|
|
156
|
+
from x402.mechanisms.evm.exact import register_exact_evm_facilitator
|
|
157
|
+
# SVM registration
|
|
158
|
+
from x402.mechanisms.svm.exact import register_exact_svm_facilitator
|
|
159
|
+
# AVM registration
|
|
160
|
+
from x402.mechanisms.avm.exact import register_exact_avm_facilitator
|
|
161
|
+
|
|
162
|
+
facilitator = x402Facilitator()
|
|
163
|
+
|
|
164
|
+
# Register all networks
|
|
165
|
+
register_exact_evm_facilitator(facilitator, evm_signer)
|
|
166
|
+
register_exact_svm_facilitator(facilitator, svm_signer)
|
|
167
|
+
register_exact_avm_facilitator(facilitator, avm_signer, networks=[ALGORAND_TESTNET_CAIP2])
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**Signer types by network:**
|
|
171
|
+
|
|
172
|
+
| Network | Signer Type | Import |
|
|
173
|
+
|---------|------------|--------|
|
|
174
|
+
| EVM | `FacilitatorWeb3Signer` | `from x402.mechanisms.evm.signer import FacilitatorWeb3Signer` |
|
|
175
|
+
| SVM | `FacilitatorKeypairSigner` | `from x402.mechanisms.svm.signer import FacilitatorKeypairSigner` |
|
|
176
|
+
| AVM | `FacilitatorAvmSigner` | `from x402.mechanisms.avm.signer import FacilitatorAvmSigner` |
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Facilitator Lifecycle Hooks
|
|
181
|
+
|
|
182
|
+
| Hook | Signature | Description |
|
|
183
|
+
|------|-----------|-------------|
|
|
184
|
+
| `on_before_verify` | `(payload, requirements) -> None` | Called before payment verification |
|
|
185
|
+
| `on_after_verify` | `(payload, requirements, result) -> None` | Called after verification completes |
|
|
186
|
+
| `on_before_settle` | `(payload, requirements) -> None` | Called before settlement submission |
|
|
187
|
+
| `on_after_settle` | `(payload, requirements, result) -> None` | Called after settlement completes |
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Environment Variables
|
|
192
|
+
|
|
193
|
+
| Variable | Required | Description | Default |
|
|
194
|
+
|----------|----------|-------------|---------|
|
|
195
|
+
| `AVM_PRIVATE_KEY` | Yes | Base64-encoded 64-byte key (32-byte seed + 32-byte pubkey) | -- |
|
|
196
|
+
| `ALGOD_SERVER` | No | Custom Algod node URL | AlgoNode public endpoint |
|
|
197
|
+
| `ALGOD_TOKEN` | No | Algod API token | `""` |
|
|
198
|
+
| `ALGOD_MAINNET_URL` | No | Mainnet Algod URL | `https://mainnet-api.algonode.cloud` |
|
|
199
|
+
| `ALGOD_TESTNET_URL` | No | Testnet Algod URL | `https://testnet-api.algonode.cloud` |
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## algosdk Encoding Reference (v2.11.1)
|
|
204
|
+
|
|
205
|
+
The Python `algosdk` has different encoding conventions than the TypeScript version. This is the most common source of bugs:
|
|
206
|
+
|
|
207
|
+
| Operation | Python algosdk | TypeScript algosdk |
|
|
208
|
+
|-----------|---------------|-------------------|
|
|
209
|
+
| `msgpack_decode(s)` | Expects **base64 string** | N/A (uses `decodeUnsignedTransaction(Uint8Array)`) |
|
|
210
|
+
| `msgpack_encode(obj)` | Returns **base64 string** | N/A (uses `txn.toByte()` returning `Uint8Array`) |
|
|
211
|
+
| `Transaction.sign(key)` | Expects **base64 string** key | `signTransaction(txn, Uint8Array)` |
|
|
212
|
+
| SDK protocol | Passes **raw msgpack bytes** | Passes **raw `Uint8Array`** |
|
|
213
|
+
|
|
214
|
+
**Boundary conversion patterns:**
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
import base64
|
|
218
|
+
from algosdk import encoding
|
|
219
|
+
|
|
220
|
+
# Raw bytes -> algosdk object (DECODE)
|
|
221
|
+
raw_bytes: bytes = ...
|
|
222
|
+
b64_string = base64.b64encode(raw_bytes).decode("utf-8")
|
|
223
|
+
txn_obj = encoding.msgpack_decode(b64_string)
|
|
224
|
+
|
|
225
|
+
# algosdk object -> raw bytes (ENCODE)
|
|
226
|
+
b64_string = encoding.msgpack_encode(txn_obj)
|
|
227
|
+
raw_bytes = base64.b64decode(b64_string)
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## API Endpoints
|
|
233
|
+
|
|
234
|
+
A standard x402 facilitator exposes three endpoints:
|
|
235
|
+
|
|
236
|
+
| Endpoint | Method | Description | Request Body |
|
|
237
|
+
|----------|--------|-------------|-------------|
|
|
238
|
+
| `/supported` | GET | List supported networks | -- |
|
|
239
|
+
| `/verify` | POST | Verify a payment payload | `{paymentPayload, paymentRequirements}` |
|
|
240
|
+
| `/settle` | POST | Settle a verified payment | `{paymentPayload, paymentRequirements}` |
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## Installation Commands
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
# Minimal facilitator
|
|
248
|
+
pip install "x402-avm[avm,fastapi]"
|
|
249
|
+
|
|
250
|
+
# With uvicorn for production
|
|
251
|
+
pip install "x402-avm[avm,fastapi]" uvicorn
|
|
252
|
+
|
|
253
|
+
# Multi-chain facilitator (EVM + SVM + AVM)
|
|
254
|
+
pip install "x402-avm[all,fastapi]"
|
|
255
|
+
|
|
256
|
+
# Everything
|
|
257
|
+
pip install "x402-avm[all]"
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## Testing
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
# Start the facilitator
|
|
266
|
+
AVM_PRIVATE_KEY="your-key" uvicorn facilitator_service:app --port 4000
|
|
267
|
+
|
|
268
|
+
# Check supported networks
|
|
269
|
+
curl http://localhost:4000/supported
|
|
270
|
+
|
|
271
|
+
# Verify a payment (with actual payload)
|
|
272
|
+
curl -X POST http://localhost:4000/verify \
|
|
273
|
+
-H "Content-Type: application/json" \
|
|
274
|
+
-d '{"paymentPayload": {...}, "paymentRequirements": {...}}'
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Bazaar Extension API
|
|
282
|
+
|
|
283
|
+
### declare_discovery_extension()
|
|
284
|
+
|
|
285
|
+
```python
|
|
286
|
+
from x402.extensions.bazaar import declare_discovery_extension
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Creates a discovery extension dict for route configuration.
|
|
290
|
+
|
|
291
|
+
**Signature:**
|
|
292
|
+
```python
|
|
293
|
+
def declare_discovery_extension(
|
|
294
|
+
input: dict,
|
|
295
|
+
input_schema: dict,
|
|
296
|
+
body_type: str | None = None,
|
|
297
|
+
output: OutputConfig | None = None,
|
|
298
|
+
) -> dict:
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
**Parameters:**
|
|
302
|
+
|
|
303
|
+
| Parameter | Type | Required | Description |
|
|
304
|
+
|-----------|------|----------|-------------|
|
|
305
|
+
| `input` | `dict` | Yes | Example values for query parameters or request body |
|
|
306
|
+
| `input_schema` | `dict` | Yes | JSON Schema describing the expected input |
|
|
307
|
+
| `body_type` | `str` or `None` | No | `None` for query (GET), `"json"` / `"form-data"` / `"text"` for body (POST) |
|
|
308
|
+
| `output` | `OutputConfig` or `None` | No | Output configuration with example and optional schema |
|
|
309
|
+
|
|
310
|
+
**Returns:** `{"bazaar": {"info": {...}, "schema": {...}}}`
|
|
311
|
+
|
|
312
|
+
**body_type values:**
|
|
313
|
+
|
|
314
|
+
| Value | HTTP Methods | Description |
|
|
315
|
+
|-------|-------------|-------------|
|
|
316
|
+
| `None` (default) | GET, HEAD, DELETE | Query parameter extension |
|
|
317
|
+
| `"json"` | POST, PUT, PATCH | JSON body extension |
|
|
318
|
+
| `"form-data"` | POST, PUT, PATCH | Form-data body extension |
|
|
319
|
+
| `"text"` | POST, PUT, PATCH | Plain text body extension |
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
### extract_discovery_info()
|
|
324
|
+
|
|
325
|
+
```python
|
|
326
|
+
from x402.extensions.bazaar import extract_discovery_info
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Extracts discovery info from a payment request. Handles both v2 (extensions in PaymentPayload) and v1 (output_schema in PaymentRequirements) formats.
|
|
330
|
+
|
|
331
|
+
**Signature:**
|
|
332
|
+
```python
|
|
333
|
+
def extract_discovery_info(
|
|
334
|
+
payment_payload: PaymentPayload | dict,
|
|
335
|
+
payment_requirements: PaymentRequirements | dict,
|
|
336
|
+
validate: bool = True,
|
|
337
|
+
) -> DiscoveredResource | None:
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
**Parameters:**
|
|
341
|
+
|
|
342
|
+
| Parameter | Type | Required | Description |
|
|
343
|
+
|-----------|------|----------|-------------|
|
|
344
|
+
| `payment_payload` | `PaymentPayload` or `dict` | Yes | The payment payload from the client |
|
|
345
|
+
| `payment_requirements` | `PaymentRequirements` or `dict` | Yes | The payment requirements from the server |
|
|
346
|
+
| `validate` | `bool` | No | Validate v2 extensions before extracting (default: `True`) |
|
|
347
|
+
|
|
348
|
+
**Returns:** `DiscoveredResource` or `None` if no Bazaar extension found
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
### extract_discovery_info_from_extension()
|
|
353
|
+
|
|
354
|
+
```python
|
|
355
|
+
from x402.extensions.bazaar import extract_discovery_info_from_extension
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Extracts discovery info from an extension object directly.
|
|
359
|
+
|
|
360
|
+
**Signature:**
|
|
361
|
+
```python
|
|
362
|
+
def extract_discovery_info_from_extension(
|
|
363
|
+
extension: dict | DiscoveryExtension,
|
|
364
|
+
validate: bool = True,
|
|
365
|
+
) -> DiscoveryInfo:
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
**Parameters:**
|
|
369
|
+
|
|
370
|
+
| Parameter | Type | Required | Description |
|
|
371
|
+
|-----------|------|----------|-------------|
|
|
372
|
+
| `extension` | `dict` or `DiscoveryExtension` | Yes | Extension with `"info"` and `"schema"` keys |
|
|
373
|
+
| `validate` | `bool` | No | Raises `ValueError` if validation fails (default: `True`) |
|
|
374
|
+
|
|
375
|
+
**Returns:** `QueryDiscoveryInfo` or `BodyDiscoveryInfo`
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
### validate_discovery_extension()
|
|
380
|
+
|
|
381
|
+
```python
|
|
382
|
+
from x402.extensions.bazaar import validate_discovery_extension
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Validates that an extension's `info` data conforms to its `schema`.
|
|
386
|
+
|
|
387
|
+
**Signature:**
|
|
388
|
+
```python
|
|
389
|
+
def validate_discovery_extension(
|
|
390
|
+
extension: dict | DiscoveryExtension,
|
|
391
|
+
) -> ValidationResult:
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
**Parameters:**
|
|
395
|
+
|
|
396
|
+
| Parameter | Type | Required | Description |
|
|
397
|
+
|-----------|------|----------|-------------|
|
|
398
|
+
| `extension` | `dict` or `DiscoveryExtension` | Yes | Extension to validate |
|
|
399
|
+
|
|
400
|
+
**Returns:** `ValidationResult` with `valid: bool` and `errors: list[str]`
|
|
401
|
+
|
|
402
|
+
---
|
|
403
|
+
|
|
404
|
+
### validate_and_extract()
|
|
405
|
+
|
|
406
|
+
```python
|
|
407
|
+
from x402.extensions.bazaar import validate_and_extract
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
Validates and extracts discovery info in one step.
|
|
411
|
+
|
|
412
|
+
**Signature:**
|
|
413
|
+
```python
|
|
414
|
+
def validate_and_extract(
|
|
415
|
+
extension: dict | DiscoveryExtension,
|
|
416
|
+
) -> ValidationExtractResult:
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
**Returns:** Object with `valid: bool`, `errors: list[str]`, and `info: DiscoveryInfo | None`
|
|
420
|
+
|
|
421
|
+
---
|
|
422
|
+
|
|
423
|
+
### bazaar_resource_server_extension
|
|
424
|
+
|
|
425
|
+
```python
|
|
426
|
+
from x402.extensions.bazaar import bazaar_resource_server_extension
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
A server-side extension that enriches discovery declarations with HTTP method information at runtime. Must be registered on an `x402ResourceServer` or `x402ResourceServerSync` instance.
|
|
430
|
+
|
|
431
|
+
**Usage:**
|
|
432
|
+
```python
|
|
433
|
+
server.register_extension(bazaar_resource_server_extension)
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
**What it does at runtime:**
|
|
437
|
+
1. Intercepts the declaration enrichment step when an HTTP request arrives for a payment-protected route with a `"bazaar"` extension
|
|
438
|
+
2. Reads the HTTP method from the transport context (e.g., `request.method`)
|
|
439
|
+
3. Injects the method into `info.input.method` and updates the schema
|
|
440
|
+
4. The enriched extension is included in the 402 Payment Required response
|
|
441
|
+
|
|
442
|
+
---
|
|
443
|
+
|
|
444
|
+
### with_bazaar()
|
|
445
|
+
|
|
446
|
+
```python
|
|
447
|
+
from x402.extensions.bazaar import with_bazaar
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
Extends a facilitator client with discovery query capabilities.
|
|
451
|
+
|
|
452
|
+
**Signature:**
|
|
453
|
+
```python
|
|
454
|
+
def with_bazaar(
|
|
455
|
+
client: HTTPFacilitatorClient,
|
|
456
|
+
) -> BazaarExtendedClient:
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
**Returns:** Extended client with `client.extensions.discovery.list_resources()` method. All original facilitator client methods are delegated transparently.
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
### OutputConfig
|
|
464
|
+
|
|
465
|
+
```python
|
|
466
|
+
from x402.extensions.bazaar import OutputConfig
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
Configuration for the output portion of a discovery extension.
|
|
470
|
+
|
|
471
|
+
**Fields:**
|
|
472
|
+
|
|
473
|
+
| Field | Type | Required | Description |
|
|
474
|
+
|-------|------|----------|-------------|
|
|
475
|
+
| `example` | `dict` | Yes | Example response value |
|
|
476
|
+
| `schema` | `dict` or `None` | No | JSON Schema for the response |
|
|
477
|
+
|
|
478
|
+
---
|
|
479
|
+
|
|
480
|
+
## Bazaar Data Types
|
|
481
|
+
|
|
482
|
+
### DiscoveredResource
|
|
483
|
+
|
|
484
|
+
```python
|
|
485
|
+
@dataclass
|
|
486
|
+
class DiscoveredResource:
|
|
487
|
+
resource_url: str # Normalized URL (no query/hash)
|
|
488
|
+
method: str # HTTP method (GET, POST, etc.)
|
|
489
|
+
x402_version: int # 1 or 2
|
|
490
|
+
discovery_info: DiscoveryInfo # QueryDiscoveryInfo or BodyDiscoveryInfo
|
|
491
|
+
description: str | None # From resource info or requirements
|
|
492
|
+
mime_type: str | None # From resource info or requirements
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
### DiscoveryInfo (Union Type)
|
|
496
|
+
|
|
497
|
+
```python
|
|
498
|
+
DiscoveryInfo = QueryDiscoveryInfo | BodyDiscoveryInfo
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
### ValidationResult
|
|
502
|
+
|
|
503
|
+
```python
|
|
504
|
+
@dataclass
|
|
505
|
+
class ValidationResult:
|
|
506
|
+
valid: bool
|
|
507
|
+
errors: list[str] # Empty when valid
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
---
|
|
511
|
+
|
|
512
|
+
## Bazaar Pydantic Models (from `x402.extensions.bazaar.types`)
|
|
513
|
+
|
|
514
|
+
| Type | Fields | Description |
|
|
515
|
+
|------|--------|-------------|
|
|
516
|
+
| `QueryInput` | `type`, `method`, `query_params`, `headers` | Input spec for GET/HEAD/DELETE |
|
|
517
|
+
| `BodyInput` | `type`, `method`, `body_type`, `body`, `query_params`, `headers` | Input spec for POST/PUT/PATCH |
|
|
518
|
+
| `OutputInfo` | `type`, `format`, `example` | Output specification |
|
|
519
|
+
| `QueryDiscoveryInfo` | `input: QueryInput`, `output: OutputInfo` | Discovery info for query methods |
|
|
520
|
+
| `BodyDiscoveryInfo` | `input: BodyInput`, `output: OutputInfo` | Discovery info for body methods |
|
|
521
|
+
| `QueryDiscoveryExtension` | `info: QueryDiscoveryInfo`, `schema_: dict` | Full extension with schema (query) |
|
|
522
|
+
| `BodyDiscoveryExtension` | `info: BodyDiscoveryInfo`, `schema_: dict` | Full extension with schema (body) |
|
|
523
|
+
|
|
524
|
+
### Bazaar Union Types
|
|
525
|
+
|
|
526
|
+
| Name | Members | Description |
|
|
527
|
+
|------|---------|-------------|
|
|
528
|
+
| `DiscoveryInfo` | `QueryDiscoveryInfo \| BodyDiscoveryInfo` | Any discovery info |
|
|
529
|
+
| `DiscoveryExtension` | `QueryDiscoveryExtension \| BodyDiscoveryExtension` | Any extension |
|
|
530
|
+
|
|
531
|
+
---
|
|
532
|
+
|
|
533
|
+
## Bazaar Type Constants and Literals
|
|
534
|
+
|
|
535
|
+
| Name | Value | Description |
|
|
536
|
+
|------|-------|-------------|
|
|
537
|
+
| `BAZAAR` | `"bazaar"` | Extension key identifier in route configs |
|
|
538
|
+
|
|
539
|
+
| Literal Type | Values | Description |
|
|
540
|
+
|-------------|--------|-------------|
|
|
541
|
+
| `QueryParamMethods` | `"GET"`, `"HEAD"`, `"DELETE"` | HTTP methods using query parameters |
|
|
542
|
+
| `BodyMethods` | `"POST"`, `"PUT"`, `"PATCH"` | HTTP methods using request bodies |
|
|
543
|
+
| `BodyType` | `"json"`, `"form-data"`, `"text"` | Content types for body requests |
|
|
544
|
+
|
|
545
|
+
---
|
|
546
|
+
|
|
547
|
+
## Bazaar Config Dataclasses
|
|
548
|
+
|
|
549
|
+
| Type | Fields | Description |
|
|
550
|
+
|------|--------|-------------|
|
|
551
|
+
| `OutputConfig` | `example`, `schema` | Configure output for `declare_discovery_extension` |
|
|
552
|
+
| `DeclareQueryDiscoveryConfig` | `input`, `input_schema`, `output` | Config for query discovery |
|
|
553
|
+
| `DeclareBodyDiscoveryConfig` | `input`, `input_schema`, `body_type`, `output` | Config for body discovery |
|
|
554
|
+
|
|
555
|
+
---
|
|
556
|
+
|
|
557
|
+
## Bazaar Client Types (from `x402.extensions.bazaar.facilitator_client`)
|
|
558
|
+
|
|
559
|
+
| Type | Description |
|
|
560
|
+
|------|-------------|
|
|
561
|
+
| `ListDiscoveryResourcesParams` | Params for `list_resources()`: `type`, `limit`, `offset` |
|
|
562
|
+
| `DiscoveryResource` | A discovered resource: `url`, `type`, `metadata` |
|
|
563
|
+
| `DiscoveryResourcesResponse` | Response: `resources`, `total`, `limit`, `offset` |
|
|
564
|
+
| `BazaarDiscoveryExtension` | Discovery query class with `list_resources()` method |
|
|
565
|
+
| `BazaarClientExtension` | Container with `.discovery` attribute |
|
|
566
|
+
| `BazaarExtendedClient` | Extended client with `.extensions` attribute |
|
|
567
|
+
|
|
568
|
+
---
|
|
569
|
+
|
|
570
|
+
## Bazaar Helper Functions (from `x402.extensions.bazaar.types`)
|
|
571
|
+
|
|
572
|
+
| Function | Import | Description |
|
|
573
|
+
|----------|--------|-------------|
|
|
574
|
+
| `parse_discovery_extension(data)` | `from x402.extensions.bazaar.types import parse_discovery_extension` | Parse raw dict into typed extension |
|
|
575
|
+
| `parse_discovery_info(data)` | `from x402.extensions.bazaar.types import parse_discovery_info` | Parse raw dict into typed info |
|
|
576
|
+
| `is_query_method(method)` | `from x402.extensions.bazaar.types import is_query_method` | Check if method is GET/HEAD/DELETE |
|
|
577
|
+
| `is_body_method(method)` | `from x402.extensions.bazaar.types import is_body_method` | Check if method is POST/PUT/PATCH |
|
|
578
|
+
|
|
579
|
+
---
|
|
580
|
+
|
|
581
|
+
## Bazaar Functions Summary Table
|
|
582
|
+
|
|
583
|
+
| Function | Import | Description |
|
|
584
|
+
|----------|--------|-------------|
|
|
585
|
+
| `declare_discovery_extension` | `from x402.extensions.bazaar import ...` | Create discovery extension dict for route config |
|
|
586
|
+
| `validate_discovery_extension` | `from x402.extensions.bazaar import ...` | Validate extension info against its schema |
|
|
587
|
+
| `extract_discovery_info` | `from x402.extensions.bazaar import ...` | Extract discovery info from payment request (v1 + v2) |
|
|
588
|
+
| `extract_discovery_info_from_extension` | `from x402.extensions.bazaar import ...` | Extract info from extension object directly |
|
|
589
|
+
| `validate_and_extract` | `from x402.extensions.bazaar import ...` | Validate and extract in one step |
|
|
590
|
+
| `with_bazaar` | `from x402.extensions.bazaar import ...` | Extend facilitator client with discovery queries |
|
|
591
|
+
| `parse_discovery_extension` | `from x402.extensions.bazaar.types import ...` | Parse raw dict into typed extension |
|
|
592
|
+
| `parse_discovery_info` | `from x402.extensions.bazaar.types import ...` | Parse raw dict into typed info |
|
|
593
|
+
| `is_query_method` | `from x402.extensions.bazaar.types import ...` | Check if method is GET/HEAD/DELETE |
|
|
594
|
+
| `is_body_method` | `from x402.extensions.bazaar.types import ...` | Check if method is POST/PUT/PATCH |
|
|
595
|
+
|
|
596
|
+
---
|
|
597
|
+
|
|
598
|
+
## Bazaar Installation Commands
|
|
599
|
+
|
|
600
|
+
```bash
|
|
601
|
+
# Extensions only
|
|
602
|
+
pip install "x402-avm[extensions]"
|
|
603
|
+
|
|
604
|
+
# Extensions + Algorand support
|
|
605
|
+
pip install "x402-avm[extensions,avm]"
|
|
606
|
+
|
|
607
|
+
# Extensions + Algorand + FastAPI
|
|
608
|
+
pip install "x402-avm[extensions,avm,fastapi]"
|
|
609
|
+
|
|
610
|
+
# Extensions + Algorand + Flask
|
|
611
|
+
pip install "x402-avm[extensions,avm,flask]"
|
|
612
|
+
|
|
613
|
+
# Everything
|
|
614
|
+
pip install "x402-avm[all]"
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
The `[extensions]` extra adds one dependency: `jsonschema>=4.0.0`.
|
|
618
|
+
|
|
619
|
+
---
|
|
620
|
+
|
|
621
|
+
## External Resources
|
|
622
|
+
|
|
623
|
+
- [Facilitator Example Source](https://github.com/GoPlausible/x402-avm/tree/branch-v2-algorand-publish/examples/python/facilitator)
|
|
624
|
+
- [Extensions Examples](https://github.com/GoPlausible/x402-avm/tree/branch-v2-algorand-publish/examples/)
|
|
625
|
+
- [x402-avm AVM Documentation](https://github.com/GoPlausible/.github/blob/main/profile/algorand-x402-documentation/)
|
|
626
|
+
- [algosdk Python Reference](https://py-algorand-sdk.readthedocs.io/)
|
|
627
|
+
- [Algorand Developer Portal](https://developer.algorand.org/)
|
|
628
|
+
- [JSON Schema Specification](https://json-schema.org/)
|
|
629
|
+
- [CAIP-2 Chain ID Specification](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md)
|