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,408 @@
|
|
|
1
|
+
# Creating Python x402 Facilitator Services and Bazaar Discovery
|
|
2
|
+
|
|
3
|
+
Build FastAPI-based facilitator services that verify and settle x402 payments on-chain for Algorand (AVM), with optional multi-network support for EVM and SVM. Includes Bazaar discovery extension for automatic cataloging and indexing of payment-gated APIs.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
Before using this skill, ensure:
|
|
8
|
+
|
|
9
|
+
1. **Python 3.10+** is installed
|
|
10
|
+
2. **x402-avm package** is available: `pip install "x402-avm[avm,fastapi]"`
|
|
11
|
+
3. **Algorand private key** is available as a Base64-encoded 64-byte key (32-byte seed + 32-byte pubkey) in `AVM_PRIVATE_KEY` environment variable
|
|
12
|
+
4. **Algod node access** via AlgoNode (default) or custom node via `ALGOD_SERVER` / `ALGOD_TOKEN`
|
|
13
|
+
|
|
14
|
+
## Core Workflow: Facilitator Payment Lifecycle
|
|
15
|
+
|
|
16
|
+
The facilitator sits between the resource server and the blockchain, handling payment verification and settlement:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
Resource Server Facilitator Blockchain
|
|
20
|
+
| | |
|
|
21
|
+
|--- /verify (payload) ---->| |
|
|
22
|
+
| |-- simulate_group() -------->|
|
|
23
|
+
| |<-- simulation result -------|
|
|
24
|
+
|<-- {isValid: true} -------| |
|
|
25
|
+
| | |
|
|
26
|
+
|--- /settle (payload) ---->| |
|
|
27
|
+
| |-- sign_group() ------------>|
|
|
28
|
+
| |-- send_group() ------------>|
|
|
29
|
+
| |<-- txid --------------------|
|
|
30
|
+
| |-- confirm_transaction() --->|
|
|
31
|
+
|<-- {success, txid} -------| |
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## How to Proceed
|
|
35
|
+
|
|
36
|
+
### Step 1: Install Dependencies
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install "x402-avm[avm,fastapi]"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
This installs `py-algorand-sdk` (algosdk), FastAPI, uvicorn, and the x402-avm SDK.
|
|
43
|
+
|
|
44
|
+
### Step 2: Implement FacilitatorAvmSigner
|
|
45
|
+
|
|
46
|
+
The `FacilitatorAvmSigner` protocol defines six methods. Implement all of them using `algosdk`:
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
import os
|
|
50
|
+
import base64
|
|
51
|
+
from algosdk import encoding, transaction
|
|
52
|
+
from algosdk.v2client import algod
|
|
53
|
+
from x402.mechanisms.avm.constants import NETWORK_CONFIGS
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class AlgorandFacilitatorSigner:
|
|
57
|
+
def __init__(self, private_key_b64: str, algod_url: str = "", algod_token: str = ""):
|
|
58
|
+
self._secret_key = base64.b64decode(private_key_b64)
|
|
59
|
+
self._address = encoding.encode_address(self._secret_key[32:])
|
|
60
|
+
self._signing_key = base64.b64encode(self._secret_key).decode()
|
|
61
|
+
self._clients: dict[str, algod.AlgodClient] = {}
|
|
62
|
+
if algod_url:
|
|
63
|
+
self._default_client = algod.AlgodClient(algod_token, algod_url)
|
|
64
|
+
else:
|
|
65
|
+
self._default_client = None
|
|
66
|
+
|
|
67
|
+
def _get_client(self, network: str) -> algod.AlgodClient:
|
|
68
|
+
if network not in self._clients:
|
|
69
|
+
if self._default_client:
|
|
70
|
+
self._clients[network] = self._default_client
|
|
71
|
+
else:
|
|
72
|
+
config = NETWORK_CONFIGS.get(network, {})
|
|
73
|
+
url = config.get("algod_url", "https://testnet-api.algonode.cloud")
|
|
74
|
+
self._clients[network] = algod.AlgodClient("", url)
|
|
75
|
+
return self._clients[network]
|
|
76
|
+
|
|
77
|
+
def get_addresses(self) -> list[str]:
|
|
78
|
+
return [self._address]
|
|
79
|
+
|
|
80
|
+
def sign_transaction(self, txn_bytes: bytes, fee_payer: str, network: str) -> bytes:
|
|
81
|
+
b64 = base64.b64encode(txn_bytes).decode("utf-8")
|
|
82
|
+
txn_obj = encoding.msgpack_decode(b64)
|
|
83
|
+
signed = txn_obj.sign(self._signing_key)
|
|
84
|
+
return base64.b64decode(encoding.msgpack_encode(signed))
|
|
85
|
+
|
|
86
|
+
def sign_group(self, group_bytes, fee_payer, indexes_to_sign, network):
|
|
87
|
+
result = list(group_bytes)
|
|
88
|
+
for i in indexes_to_sign:
|
|
89
|
+
result[i] = self.sign_transaction(group_bytes[i], fee_payer, network)
|
|
90
|
+
return result
|
|
91
|
+
|
|
92
|
+
def simulate_group(self, group_bytes, network):
|
|
93
|
+
client = self._get_client(network)
|
|
94
|
+
stxns = []
|
|
95
|
+
for txn_bytes in group_bytes:
|
|
96
|
+
b64 = base64.b64encode(txn_bytes).decode("utf-8")
|
|
97
|
+
obj = encoding.msgpack_decode(b64)
|
|
98
|
+
if isinstance(obj, transaction.SignedTransaction):
|
|
99
|
+
stxns.append(obj)
|
|
100
|
+
else:
|
|
101
|
+
stxns.append(transaction.SignedTransaction(obj, None))
|
|
102
|
+
req = transaction.SimulateRequest(
|
|
103
|
+
txn_groups=[transaction.SimulateRequestTransactionGroup(txns=stxns)],
|
|
104
|
+
allow_empty_signatures=True,
|
|
105
|
+
)
|
|
106
|
+
result = client.simulate_raw_transactions(req)
|
|
107
|
+
for group in result.get("txn-groups", []):
|
|
108
|
+
if group.get("failure-message"):
|
|
109
|
+
raise Exception(f"Simulation failed: {group['failure-message']}")
|
|
110
|
+
|
|
111
|
+
def send_group(self, group_bytes, network):
|
|
112
|
+
client = self._get_client(network)
|
|
113
|
+
return client.send_raw_transaction(base64.b64encode(b"".join(group_bytes)))
|
|
114
|
+
|
|
115
|
+
def confirm_transaction(self, txid, network, rounds=4):
|
|
116
|
+
client = self._get_client(network)
|
|
117
|
+
transaction.wait_for_confirmation(client, txid, rounds)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Step 3: Register the Signer with x402Facilitator
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
from x402 import x402Facilitator
|
|
124
|
+
from x402.mechanisms.avm.exact import register_exact_avm_facilitator
|
|
125
|
+
from x402.mechanisms.avm import ALGORAND_TESTNET_CAIP2, ALGORAND_MAINNET_CAIP2
|
|
126
|
+
|
|
127
|
+
signer = AlgorandFacilitatorSigner(
|
|
128
|
+
private_key_b64=os.environ["AVM_PRIVATE_KEY"],
|
|
129
|
+
algod_url=os.environ.get("ALGOD_SERVER", ""),
|
|
130
|
+
algod_token=os.environ.get("ALGOD_TOKEN", ""),
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
facilitator = x402Facilitator()
|
|
134
|
+
register_exact_avm_facilitator(
|
|
135
|
+
facilitator,
|
|
136
|
+
signer,
|
|
137
|
+
networks=[ALGORAND_TESTNET_CAIP2, ALGORAND_MAINNET_CAIP2],
|
|
138
|
+
)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Step 4: Create FastAPI Endpoints
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
from fastapi import FastAPI, Request
|
|
145
|
+
from fastapi.responses import JSONResponse
|
|
146
|
+
|
|
147
|
+
app = FastAPI(title="x402-avm Facilitator Service")
|
|
148
|
+
|
|
149
|
+
@app.get("/supported")
|
|
150
|
+
async def supported():
|
|
151
|
+
return facilitator.get_supported_networks()
|
|
152
|
+
|
|
153
|
+
@app.post("/verify")
|
|
154
|
+
async def verify(request: Request):
|
|
155
|
+
body = await request.json()
|
|
156
|
+
try:
|
|
157
|
+
result = await facilitator.verify(
|
|
158
|
+
body["paymentPayload"], body["paymentRequirements"]
|
|
159
|
+
)
|
|
160
|
+
return result
|
|
161
|
+
except Exception as e:
|
|
162
|
+
return JSONResponse(status_code=400, content={"error": str(e)})
|
|
163
|
+
|
|
164
|
+
@app.post("/settle")
|
|
165
|
+
async def settle(request: Request):
|
|
166
|
+
body = await request.json()
|
|
167
|
+
try:
|
|
168
|
+
result = await facilitator.settle(
|
|
169
|
+
body["paymentPayload"], body["paymentRequirements"]
|
|
170
|
+
)
|
|
171
|
+
return result
|
|
172
|
+
except Exception as e:
|
|
173
|
+
return JSONResponse(status_code=400, content={"error": str(e)})
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Step 5: Run the Service
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
AVM_PRIVATE_KEY="your-base64-key" uvicorn facilitator_service:app --port 4000
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Important Rules / Guidelines
|
|
183
|
+
|
|
184
|
+
1. **algosdk encoding boundaries** -- `msgpack_decode()` expects a base64 string, not raw bytes. Always convert: `base64.b64encode(raw_bytes).decode("utf-8")` before decoding. `msgpack_encode()` returns a base64 string; convert back with `base64.b64decode()`.
|
|
185
|
+
2. **Private key format** -- `Transaction.sign()` expects a base64-encoded string of the 64-byte key, not raw bytes.
|
|
186
|
+
3. **send_group pattern** -- Use `send_raw_transaction(base64.b64encode(b"".join(group_bytes)))` to avoid unnecessary decode/re-encode overhead.
|
|
187
|
+
4. **simulate_group pattern** -- Wrap unsigned transactions with `SignedTransaction(txn, None)` and use `allow_empty_signatures=True`.
|
|
188
|
+
5. **Address derivation** -- Use `encoding.encode_address(secret_key[32:])` to derive the address from the 64-byte key.
|
|
189
|
+
6. **First-class AVM** -- Register AVM unconditionally, never behind `if (env.AVM_*)` guards.
|
|
190
|
+
7. **Network constants** -- Import `ALGORAND_TESTNET_CAIP2` and `ALGORAND_MAINNET_CAIP2` from the SDK; do not hardcode genesis hashes in application code.
|
|
191
|
+
|
|
192
|
+
## Lifecycle Hooks
|
|
193
|
+
|
|
194
|
+
The `x402Facilitator` supports lifecycle hooks for custom logic:
|
|
195
|
+
|
|
196
|
+
| Hook | When Called | Use Case |
|
|
197
|
+
|------|-----------|----------|
|
|
198
|
+
| `on_before_verify` | Before payment verification | Logging, rate limiting |
|
|
199
|
+
| `on_after_verify` | After verification completes | Analytics, caching |
|
|
200
|
+
| `on_before_settle` | Before settlement submission | Final validation |
|
|
201
|
+
| `on_after_settle` | After settlement completes | Notification, receipts |
|
|
202
|
+
|
|
203
|
+
## Common Errors / Troubleshooting
|
|
204
|
+
|
|
205
|
+
| Error | Cause | Solution |
|
|
206
|
+
|-------|-------|----------|
|
|
207
|
+
| `msgpack_decode expects string` | Passing raw bytes to `msgpack_decode` | Wrap with `base64.b64encode(raw).decode()` |
|
|
208
|
+
| `Invalid key length` | Wrong private key format | Ensure `AVM_PRIVATE_KEY` is base64-encoded 64 bytes |
|
|
209
|
+
| `Simulation failed` | Transaction would fail on-chain | Check balances, asset opt-in, fee amounts |
|
|
210
|
+
| `send_raw_transaction expects bytes-like` | Wrong argument type | Use `base64.b64encode(b"".join(group_bytes))` |
|
|
211
|
+
| `Transaction not confirmed` | Node timeout or network issue | Increase `rounds` parameter or check node connectivity |
|
|
212
|
+
| `send_transactions asserts NOT Transaction` | Passing unsigned `Transaction` to `send_transactions` | Use `send_raw_transaction` instead |
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Bazaar Discovery Extension
|
|
217
|
+
|
|
218
|
+
Bazaar is a resource discovery protocol built into x402. It allows payment-gated APIs to advertise:
|
|
219
|
+
|
|
220
|
+
- **What they accept as input** -- query parameters for GET/HEAD/DELETE, or request body schemas for POST/PUT/PATCH
|
|
221
|
+
- **What they return as output** -- the shape, type, and examples of the response data
|
|
222
|
+
|
|
223
|
+
This metadata enables facilitators to automatically catalog and index x402-enabled resources, so clients can discover APIs by their capabilities rather than just by URL. It is a machine-readable menu for paid APIs.
|
|
224
|
+
|
|
225
|
+
```
|
|
226
|
+
Resource Server (declares) Facilitator (catalogs) Client (discovers)
|
|
227
|
+
+---------------------------+ +----------------------+ +-------------------+
|
|
228
|
+
| RouteConfig + extensions: | | extract_discovery_ | | with_bazaar() |
|
|
229
|
+
| **declare_discovery_ | ---> | info() | ---> | .list_resources()|
|
|
230
|
+
| extension(...) | | validate_discovery_ | | |
|
|
231
|
+
+---------------------------+ | extension() | +-------------------+
|
|
232
|
+
+----------------------+
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Bazaar Prerequisites
|
|
236
|
+
|
|
237
|
+
1. **x402-avm with extensions** is available: `pip install "x402-avm[extensions,avm]"`
|
|
238
|
+
2. **Web framework** installed: `pip install "x402-avm[extensions,avm,fastapi]"` or `[...,flask]`
|
|
239
|
+
|
|
240
|
+
The `extensions` extra installs `jsonschema>=4.0.0` for schema validation.
|
|
241
|
+
|
|
242
|
+
### Step 6: Declare Discovery Extensions on Routes
|
|
243
|
+
|
|
244
|
+
Use `declare_discovery_extension()` on your route configurations to describe what each endpoint accepts and returns.
|
|
245
|
+
|
|
246
|
+
**For GET endpoints (query parameters):**
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
from x402.extensions.bazaar import declare_discovery_extension, OutputConfig
|
|
250
|
+
|
|
251
|
+
discovery = declare_discovery_extension(
|
|
252
|
+
input={"city": "San Francisco"},
|
|
253
|
+
input_schema={
|
|
254
|
+
"properties": {
|
|
255
|
+
"city": {"type": "string", "description": "City name"},
|
|
256
|
+
},
|
|
257
|
+
"required": ["city"],
|
|
258
|
+
},
|
|
259
|
+
output=OutputConfig(
|
|
260
|
+
example={"weather": "sunny", "temperature": 70},
|
|
261
|
+
schema={
|
|
262
|
+
"properties": {
|
|
263
|
+
"weather": {"type": "string"},
|
|
264
|
+
"temperature": {"type": "number"},
|
|
265
|
+
},
|
|
266
|
+
"required": ["weather", "temperature"],
|
|
267
|
+
},
|
|
268
|
+
),
|
|
269
|
+
)
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**For POST endpoints (JSON body):**
|
|
273
|
+
|
|
274
|
+
```python
|
|
275
|
+
discovery = declare_discovery_extension(
|
|
276
|
+
input={"prompt": "Tell me about Algorand", "max_tokens": 100},
|
|
277
|
+
input_schema={
|
|
278
|
+
"properties": {
|
|
279
|
+
"prompt": {"type": "string"},
|
|
280
|
+
"max_tokens": {"type": "integer"},
|
|
281
|
+
},
|
|
282
|
+
"required": ["prompt"],
|
|
283
|
+
},
|
|
284
|
+
body_type="json",
|
|
285
|
+
output=OutputConfig(
|
|
286
|
+
example={"text": "Algorand is a...", "tokens_used": 42},
|
|
287
|
+
),
|
|
288
|
+
)
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Step 7: Add Extensions to Route Configuration
|
|
292
|
+
|
|
293
|
+
Spread the discovery dict into your route's `extensions` field:
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
from x402.http import PaymentOption
|
|
297
|
+
from x402.http.types import RouteConfig
|
|
298
|
+
from x402.schemas import Network
|
|
299
|
+
|
|
300
|
+
AVM_NETWORK: Network = "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="
|
|
301
|
+
|
|
302
|
+
routes = {
|
|
303
|
+
"GET /weather": RouteConfig(
|
|
304
|
+
accepts=[
|
|
305
|
+
PaymentOption(
|
|
306
|
+
scheme="exact",
|
|
307
|
+
pay_to=AVM_ADDRESS,
|
|
308
|
+
price="$0.001",
|
|
309
|
+
network=AVM_NETWORK,
|
|
310
|
+
),
|
|
311
|
+
],
|
|
312
|
+
description="Weather report",
|
|
313
|
+
mime_type="application/json",
|
|
314
|
+
extensions={
|
|
315
|
+
**declare_discovery_extension(
|
|
316
|
+
input={"city": "San Francisco"},
|
|
317
|
+
input_schema={
|
|
318
|
+
"properties": {"city": {"type": "string"}},
|
|
319
|
+
"required": ["city"],
|
|
320
|
+
},
|
|
321
|
+
output=OutputConfig(
|
|
322
|
+
example={"weather": "sunny", "temperature": 70},
|
|
323
|
+
),
|
|
324
|
+
)
|
|
325
|
+
},
|
|
326
|
+
),
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Step 8: Register Bazaar Extension on Server
|
|
331
|
+
|
|
332
|
+
```python
|
|
333
|
+
from x402.server import x402ResourceServer
|
|
334
|
+
from x402.http import FacilitatorConfig, HTTPFacilitatorClient
|
|
335
|
+
from x402.extensions.bazaar import bazaar_resource_server_extension
|
|
336
|
+
from x402.mechanisms.avm.exact import ExactAvmServerScheme
|
|
337
|
+
|
|
338
|
+
facilitator = HTTPFacilitatorClient(FacilitatorConfig(url="https://x402.org/facilitator"))
|
|
339
|
+
server = x402ResourceServer(facilitator)
|
|
340
|
+
server.register(AVM_NETWORK, ExactAvmServerScheme())
|
|
341
|
+
server.register_extension(bazaar_resource_server_extension)
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### Step 9: Extract and Validate on Facilitator Side
|
|
345
|
+
|
|
346
|
+
```python
|
|
347
|
+
from x402.extensions.bazaar import extract_discovery_info, validate_discovery_extension
|
|
348
|
+
|
|
349
|
+
# Extract discovery info from a payment request
|
|
350
|
+
discovered = extract_discovery_info(
|
|
351
|
+
payment_payload=payment_payload,
|
|
352
|
+
payment_requirements=payment_requirements,
|
|
353
|
+
validate=True,
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
if discovered:
|
|
357
|
+
print(f"URL: {discovered.resource_url}")
|
|
358
|
+
print(f"Method: {discovered.method}")
|
|
359
|
+
print(f"Description: {discovered.description}")
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### Step 10: Apply Payment Middleware with Bazaar
|
|
363
|
+
|
|
364
|
+
**FastAPI:**
|
|
365
|
+
|
|
366
|
+
```python
|
|
367
|
+
from fastapi import FastAPI
|
|
368
|
+
from x402.http.middleware.fastapi import PaymentMiddlewareASGI
|
|
369
|
+
|
|
370
|
+
app = FastAPI()
|
|
371
|
+
app.add_middleware(PaymentMiddlewareASGI, routes=routes, server=server)
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
**Flask:**
|
|
375
|
+
|
|
376
|
+
```python
|
|
377
|
+
from flask import Flask
|
|
378
|
+
from x402.http.middleware.flask import payment_middleware
|
|
379
|
+
|
|
380
|
+
app = Flask(__name__)
|
|
381
|
+
payment_middleware(app, routes=routes, server=server)
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### Bazaar Rules / Guidelines
|
|
385
|
+
|
|
386
|
+
1. **Discovery is chain-agnostic** -- The same Bazaar metadata applies regardless of which payment network (AVM, EVM, SVM) the client uses.
|
|
387
|
+
2. **Extensions spread into routes** -- Use `**declare_discovery_extension(...)` to merge the `{"bazaar": {...}}` dict into the route's `extensions` dict.
|
|
388
|
+
3. **body_type determines input type** -- `None` (default) creates a query extension (GET/HEAD/DELETE). `"json"` creates a body extension (POST/PUT/PATCH).
|
|
389
|
+
4. **Server extension is required** -- Register `bazaar_resource_server_extension` on the server to enrich declarations with HTTP method at runtime.
|
|
390
|
+
5. **Validation uses jsonschema** -- The `[extensions]` extra installs jsonschema. Validation checks that `info` data conforms to its `schema`.
|
|
391
|
+
|
|
392
|
+
### Bazaar Common Errors
|
|
393
|
+
|
|
394
|
+
| Error | Cause | Solution |
|
|
395
|
+
|-------|-------|----------|
|
|
396
|
+
| `ImportError: jsonschema` | Missing `[extensions]` extra | Install with `pip install "x402-avm[extensions]"` |
|
|
397
|
+
| `KeyError: 'bazaar'` | Extension not declared on route | Add `**declare_discovery_extension(...)` to route config `extensions` |
|
|
398
|
+
| `ValidationResult.valid is False` | Extension info does not match schema | Check that example data matches the declared input/output schema |
|
|
399
|
+
| `discovered is None` | No Bazaar extension in the payment request | Ensure both server-side declaration and extension registration are in place |
|
|
400
|
+
| `body_type not set for POST` | POST route treated as query extension | Add `body_type="json"` to `declare_discovery_extension()` |
|
|
401
|
+
|
|
402
|
+
## References / Further Reading
|
|
403
|
+
|
|
404
|
+
- [create-python-x402-facilitator-reference.md](./create-python-x402-facilitator-reference.md) -- Detailed API reference for FacilitatorAvmSigner protocol and Bazaar extension
|
|
405
|
+
- [create-python-x402-facilitator-examples.md](./create-python-x402-facilitator-examples.md) -- Complete code examples (including Bazaar discovery)
|
|
406
|
+
- [Facilitator Example](https://github.com/GoPlausible/x402-avm/tree/branch-v2-algorand-publish/examples/python/facilitator)
|
|
407
|
+
- [Extensions Examples](https://github.com/GoPlausible/x402-avm/tree/branch-v2-algorand-publish/examples/)
|
|
408
|
+
- [x402-avm AVM Documentation](https://github.com/GoPlausible/.github/blob/main/profile/algorand-x402-documentation/)
|