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,315 @@
|
|
|
1
|
+
# x402-avm Python Package Reference
|
|
2
|
+
|
|
3
|
+
Detailed reference for the `x402-avm` Python package covering package structure, extras, async/sync variants, signer protocols, registration functions, constants, utilities, and algosdk encoding.
|
|
4
|
+
|
|
5
|
+
## Package Structure
|
|
6
|
+
|
|
7
|
+
The package is published on PyPI as `x402-avm` but all imports use the `x402` namespace:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
x402/
|
|
11
|
+
__init__.py # x402Client, x402ClientSync, x402Facilitator, etc.
|
|
12
|
+
server.py # x402ResourceServer, x402ResourceServerSync
|
|
13
|
+
schemas.py # AssetAmount, Network, PaymentRequirements, etc.
|
|
14
|
+
http/
|
|
15
|
+
__init__.py # HTTPFacilitatorClient, HTTPFacilitatorClientSync, PaymentOption, etc.
|
|
16
|
+
types.py # RouteConfig, RoutesConfig, FacilitatorConfig
|
|
17
|
+
clients/
|
|
18
|
+
__init__.py # Lazy imports for httpx/requests
|
|
19
|
+
httpx.py # x402HttpxClient, wrapHttpxWithPayment, etc.
|
|
20
|
+
requests.py # x402_requests, wrapRequestsWithPayment, etc.
|
|
21
|
+
middleware/
|
|
22
|
+
__init__.py # Lazy imports for fastapi/flask
|
|
23
|
+
fastapi.py # PaymentMiddlewareASGI, payment_middleware, etc.
|
|
24
|
+
flask.py # PaymentMiddleware, payment_middleware, etc.
|
|
25
|
+
mechanisms/
|
|
26
|
+
avm/
|
|
27
|
+
__init__.py # Re-exports constants
|
|
28
|
+
constants.py # Network IDs, USDC config, algod URLs
|
|
29
|
+
signer.py # ClientAvmSigner, FacilitatorAvmSigner Protocols
|
|
30
|
+
utils.py # Address validation, encoding, network utils
|
|
31
|
+
exact/
|
|
32
|
+
__init__.py # ExactAvmClientScheme, ExactAvmServerScheme, etc.
|
|
33
|
+
register.py # register_exact_avm_client, register_exact_avm_server, etc.
|
|
34
|
+
evm/ # EVM mechanism (same structure)
|
|
35
|
+
svm/ # SVM mechanism (same structure)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Extras
|
|
39
|
+
|
|
40
|
+
| Extra | Installs | Use Case |
|
|
41
|
+
|-------|----------|----------|
|
|
42
|
+
| `[avm]` | `py-algorand-sdk>=2.0.0` | Algorand transaction signing |
|
|
43
|
+
| `[evm]` | `web3`, `eth-account` | Ethereum/EVM transaction signing |
|
|
44
|
+
| `[svm]` | `solders`, `solana-py` | Solana/SVM transaction signing |
|
|
45
|
+
| `[fastapi]` | `fastapi[standard]>=0.115.0`, `starlette>=0.27.0` | FastAPI async middleware |
|
|
46
|
+
| `[flask]` | `flask>=3.0.0` | Flask sync middleware |
|
|
47
|
+
| `[httpx]` | `httpx>=0.28.1` | Async HTTP client |
|
|
48
|
+
| `[requests]` | `requests>=2.31.0` | Sync HTTP client |
|
|
49
|
+
| `[mechanisms]` | All mechanism extras | All blockchain mechanisms |
|
|
50
|
+
| `[clients]` | `httpx`, `requests` | All HTTP clients |
|
|
51
|
+
| `[servers]` | `fastapi`, `flask` | All server frameworks |
|
|
52
|
+
| `[extensions]` | Extensions dependencies | Optional extensions |
|
|
53
|
+
| `[all]` | Everything | Full installation |
|
|
54
|
+
|
|
55
|
+
Installation examples:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pip install "x402-avm[avm]"
|
|
59
|
+
pip install "x402-avm[fastapi,avm]"
|
|
60
|
+
pip install "x402-avm[flask,avm]"
|
|
61
|
+
pip install "x402-avm[httpx,avm]"
|
|
62
|
+
pip install "x402-avm[requests,avm]"
|
|
63
|
+
pip install "x402-avm[all]"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Async vs Sync Component Table
|
|
67
|
+
|
|
68
|
+
| Component | Async (FastAPI/httpx) | Sync (Flask/requests) |
|
|
69
|
+
|-----------|----------------------|----------------------|
|
|
70
|
+
| x402 Client | `x402Client` | `x402ClientSync` |
|
|
71
|
+
| Resource Server | `x402ResourceServer` | `x402ResourceServerSync` |
|
|
72
|
+
| Facilitator Client | `HTTPFacilitatorClient` | `HTTPFacilitatorClientSync` |
|
|
73
|
+
| HTTP Resource Server | `x402HTTPResourceServer` | `x402HTTPResourceServerSync` |
|
|
74
|
+
| HTTP Client wrapper | `x402HTTPClient` | `x402HTTPClientSync` |
|
|
75
|
+
| Middleware (FastAPI) | `PaymentMiddlewareASGI` | N/A |
|
|
76
|
+
| Middleware (Flask) | N/A | `PaymentMiddleware` |
|
|
77
|
+
| HTTP Client (httpx) | `x402HttpxClient` | N/A |
|
|
78
|
+
| HTTP Client (requests) | N/A | `x402_requests` |
|
|
79
|
+
| Payment info storage | `request.state.payment_payload` | `flask.g.payment_payload` |
|
|
80
|
+
|
|
81
|
+
## ClientAvmSigner Protocol
|
|
82
|
+
|
|
83
|
+
Defined in `x402.mechanisms.avm.signer`. Structural typing -- no inheritance required.
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
class ClientAvmSigner(Protocol):
|
|
87
|
+
@property
|
|
88
|
+
def address(self) -> str:
|
|
89
|
+
"""58-character Algorand address."""
|
|
90
|
+
...
|
|
91
|
+
|
|
92
|
+
def sign_transactions(
|
|
93
|
+
self,
|
|
94
|
+
unsigned_txns: list[bytes],
|
|
95
|
+
indexes_to_sign: list[int],
|
|
96
|
+
) -> list[bytes | None]:
|
|
97
|
+
"""Sign specified transactions in a group.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
unsigned_txns: Raw msgpack-encoded unsigned transactions.
|
|
101
|
+
indexes_to_sign: Indexes this signer should sign.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Parallel list: signed bytes at signed indexes, None elsewhere.
|
|
105
|
+
"""
|
|
106
|
+
...
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Implementation Notes
|
|
110
|
+
|
|
111
|
+
- `unsigned_txns` contains raw msgpack bytes, not base64 strings
|
|
112
|
+
- Convert at boundary: `base64.b64encode(txn_bytes).decode()` before `msgpack_decode`
|
|
113
|
+
- `Transaction.sign(key)` expects base64-encoded private key string
|
|
114
|
+
- Convert back: `base64.b64decode(msgpack_encode(signed))` to return raw bytes
|
|
115
|
+
|
|
116
|
+
## FacilitatorAvmSigner Protocol
|
|
117
|
+
|
|
118
|
+
Defined in `x402.mechanisms.avm.signer`. Required for facilitator services.
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
class FacilitatorAvmSigner(Protocol):
|
|
122
|
+
def get_addresses(self) -> list[str]: ...
|
|
123
|
+
def sign_transaction(self, txn_bytes: bytes, fee_payer: str, network: str) -> bytes: ...
|
|
124
|
+
def sign_group(self, group_bytes: list[bytes], fee_payer: str, indexes_to_sign: list[int], network: str) -> list[bytes]: ...
|
|
125
|
+
def simulate_group(self, group_bytes: list[bytes], network: str) -> None: ...
|
|
126
|
+
def send_group(self, group_bytes: list[bytes], network: str) -> str: ...
|
|
127
|
+
def confirm_transaction(self, txid: str, network: str, rounds: int = 4) -> None: ...
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Implementation Patterns
|
|
131
|
+
|
|
132
|
+
| Method | Key Pattern |
|
|
133
|
+
|--------|-------------|
|
|
134
|
+
| `simulate_group` | Wrap unsigned `Transaction` with `SignedTransaction(txn, None)`, use `allow_empty_signatures=True` |
|
|
135
|
+
| `send_group` | Use `send_raw_transaction(base64.b64encode(b"".join(group_bytes)))` |
|
|
136
|
+
| `confirm_transaction` | Use `transaction.wait_for_confirmation(client, txid, rounds)` |
|
|
137
|
+
|
|
138
|
+
## Registration Functions
|
|
139
|
+
|
|
140
|
+
### `register_exact_avm_client(client, signer, networks=None, algod_url=None)`
|
|
141
|
+
|
|
142
|
+
Registers AVM payment scheme on an x402 client.
|
|
143
|
+
|
|
144
|
+
| Parameter | Type | Default | Description |
|
|
145
|
+
|-----------|------|---------|-------------|
|
|
146
|
+
| `client` | `x402Client \| x402ClientSync` | required | The x402 client instance |
|
|
147
|
+
| `signer` | `ClientAvmSigner` | required | Signer implementation |
|
|
148
|
+
| `networks` | `str \| list[str] \| None` | `None` | Specific networks; default registers `"algorand:*"` wildcard + V1 names |
|
|
149
|
+
| `algod_url` | `str \| None` | `None` | Custom algod endpoint |
|
|
150
|
+
|
|
151
|
+
### `register_exact_avm_server(server)`
|
|
152
|
+
|
|
153
|
+
Registers AVM scheme on a resource server.
|
|
154
|
+
|
|
155
|
+
| Parameter | Type | Description |
|
|
156
|
+
|-----------|------|-------------|
|
|
157
|
+
| `server` | `x402ResourceServer \| x402ResourceServerSync` | The resource server |
|
|
158
|
+
|
|
159
|
+
### `register_exact_avm_facilitator(facilitator, signer, networks)`
|
|
160
|
+
|
|
161
|
+
Registers AVM scheme on a facilitator.
|
|
162
|
+
|
|
163
|
+
| Parameter | Type | Description |
|
|
164
|
+
|-----------|------|-------------|
|
|
165
|
+
| `facilitator` | `x402Facilitator` | The facilitator instance |
|
|
166
|
+
| `signer` | `FacilitatorAvmSigner` | Facilitator signer implementation |
|
|
167
|
+
| `networks` | `list[str]` | CAIP-2 network identifiers to support |
|
|
168
|
+
|
|
169
|
+
## Constants
|
|
170
|
+
|
|
171
|
+
Defined in `x402.mechanisms.avm.constants`:
|
|
172
|
+
|
|
173
|
+
### Network Identifiers
|
|
174
|
+
|
|
175
|
+
| Constant | Value |
|
|
176
|
+
|----------|-------|
|
|
177
|
+
| `ALGORAND_MAINNET_CAIP2` | `"algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8="` |
|
|
178
|
+
| `ALGORAND_TESTNET_CAIP2` | `"algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="` |
|
|
179
|
+
| `SUPPORTED_NETWORKS` | `[MAINNET_CAIP2, TESTNET_CAIP2]` |
|
|
180
|
+
| `MAINNET_GENESIS_HASH` | `"wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8="` |
|
|
181
|
+
| `TESTNET_GENESIS_HASH` | `"SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="` |
|
|
182
|
+
| `V1_NETWORKS` | `["algorand-mainnet", "algorand-testnet"]` |
|
|
183
|
+
|
|
184
|
+
### USDC Configuration
|
|
185
|
+
|
|
186
|
+
| Constant | Value |
|
|
187
|
+
|----------|-------|
|
|
188
|
+
| `USDC_MAINNET_ASA_ID` | `31566704` |
|
|
189
|
+
| `USDC_TESTNET_ASA_ID` | `10458941` |
|
|
190
|
+
| `DEFAULT_DECIMALS` | `6` |
|
|
191
|
+
|
|
192
|
+
### Algod Endpoints
|
|
193
|
+
|
|
194
|
+
| Constant | Default Value |
|
|
195
|
+
|----------|---------------|
|
|
196
|
+
| `MAINNET_ALGOD_URL` | env `ALGOD_MAINNET_URL` or `"https://mainnet-api.algonode.cloud"` |
|
|
197
|
+
| `TESTNET_ALGOD_URL` | env `ALGOD_TESTNET_URL` or `"https://testnet-api.algonode.cloud"` |
|
|
198
|
+
| `FALLBACK_ALGOD_MAINNET` | `"https://mainnet-api.algonode.cloud"` |
|
|
199
|
+
| `FALLBACK_ALGOD_TESTNET` | `"https://testnet-api.algonode.cloud"` |
|
|
200
|
+
|
|
201
|
+
### Transaction Limits
|
|
202
|
+
|
|
203
|
+
| Constant | Value |
|
|
204
|
+
|----------|-------|
|
|
205
|
+
| `MAX_GROUP_SIZE` | `16` |
|
|
206
|
+
| `MIN_TXN_FEE` | `1000` (microAlgos) |
|
|
207
|
+
|
|
208
|
+
## Utility Functions
|
|
209
|
+
|
|
210
|
+
Defined in `x402.mechanisms.avm.utils`:
|
|
211
|
+
|
|
212
|
+
| Function | Signature | Returns |
|
|
213
|
+
|----------|-----------|---------|
|
|
214
|
+
| `is_valid_address(addr)` | `str -> bool` | Whether address is valid 58-char Algorand address |
|
|
215
|
+
| `to_atomic_amount(amount)` | `float -> int` | Decimal to atomic units (e.g., 1.50 -> 1500000) |
|
|
216
|
+
| `from_atomic_amount(amount)` | `int -> float` | Atomic units to decimal (e.g., 1500000 -> 1.5) |
|
|
217
|
+
| `decode_transaction_bytes(raw)` | `bytes -> DecodedTransactionInfo` | Decoded transaction details |
|
|
218
|
+
| `decode_base64_transaction(b64)` | `str -> DecodedTransactionInfo` | Decode from base64 string |
|
|
219
|
+
| `decode_payment_group(group, index)` | `list[str], int -> PaymentGroupInfo` | Full group analysis |
|
|
220
|
+
| `encode_transaction_group(txns)` | `list[bytes] -> list[str]` | Encode raw bytes to base64 strings |
|
|
221
|
+
| `normalize_network(network)` | `str -> str` | Normalize V1 name to CAIP-2 |
|
|
222
|
+
| `is_valid_network(network)` | `str -> bool` | Check if network is recognized |
|
|
223
|
+
| `get_network_config(network)` | `str -> dict` | Full config for network |
|
|
224
|
+
| `get_usdc_asa_id(network)` | `str -> int` | USDC ASA ID for network |
|
|
225
|
+
| `get_genesis_hash(network)` | `str -> str` | Genesis hash for network |
|
|
226
|
+
| `network_from_genesis_hash(hash)` | `str -> str` | CAIP-2 from genesis hash |
|
|
227
|
+
| `validate_no_security_risks(info)` | `DecodedTransactionInfo -> str \| None` | Error code or None |
|
|
228
|
+
| `validate_fee_payer_transaction(info, addr)` | `DecodedTransactionInfo, str -> str \| None` | Error code or None |
|
|
229
|
+
| `is_blocked_transaction_type(type)` | `str -> bool` | Whether type is blocked (e.g., keyreg) |
|
|
230
|
+
|
|
231
|
+
## algosdk Encoding Notes (v2.11.1)
|
|
232
|
+
|
|
233
|
+
The Python `algosdk` has different encoding conventions than TypeScript algosdk:
|
|
234
|
+
|
|
235
|
+
| Operation | Python algosdk | TypeScript algosdk |
|
|
236
|
+
|-----------|---------------|-------------------|
|
|
237
|
+
| `msgpack_decode(s)` | Expects **base64 string** | N/A (uses `decodeUnsignedTransaction(Uint8Array)`) |
|
|
238
|
+
| `msgpack_encode(obj)` | Returns **base64 string** | N/A (uses `txn.toByte()` returning `Uint8Array`) |
|
|
239
|
+
| `Transaction.sign(key)` | Expects **base64 string** key | `signTransaction(txn, Uint8Array)` |
|
|
240
|
+
| SDK protocol | Passes **raw msgpack bytes** | Passes **raw `Uint8Array`** |
|
|
241
|
+
|
|
242
|
+
Boundary conversions:
|
|
243
|
+
|
|
244
|
+
```python
|
|
245
|
+
# Raw bytes -> algosdk object
|
|
246
|
+
txn_obj = encoding.msgpack_decode(base64.b64encode(raw_bytes).decode("utf-8"))
|
|
247
|
+
|
|
248
|
+
# algosdk object -> raw bytes
|
|
249
|
+
raw_bytes = base64.b64decode(encoding.msgpack_encode(txn_obj))
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Environment Variables
|
|
253
|
+
|
|
254
|
+
| Variable | Description | Default |
|
|
255
|
+
|----------|-------------|---------|
|
|
256
|
+
| `AVM_PRIVATE_KEY` | Base64-encoded 64-byte key (32-byte seed + 32-byte pubkey) | Required |
|
|
257
|
+
| `ALGOD_MAINNET_URL` | Custom algod mainnet endpoint | `https://mainnet-api.algonode.cloud` |
|
|
258
|
+
| `ALGOD_TESTNET_URL` | Custom algod testnet endpoint | `https://testnet-api.algonode.cloud` |
|
|
259
|
+
| `INDEXER_MAINNET_URL` | Custom indexer mainnet endpoint | `https://mainnet-idx.algonode.cloud` |
|
|
260
|
+
| `INDEXER_TESTNET_URL` | Custom indexer testnet endpoint | `https://testnet-idx.algonode.cloud` |
|
|
261
|
+
| `ALGOD_SERVER` | Algod URL for facilitator examples | N/A |
|
|
262
|
+
| `ALGOD_TOKEN` | Algod token for facilitator examples | N/A |
|
|
263
|
+
|
|
264
|
+
### Private Key Format
|
|
265
|
+
|
|
266
|
+
The `AVM_PRIVATE_KEY` is a Base64-encoded 64-byte key:
|
|
267
|
+
- First 32 bytes: Ed25519 seed (private key)
|
|
268
|
+
- Last 32 bytes: Ed25519 public key
|
|
269
|
+
- Address derivation: `encoding.encode_address(secret_key[32:])`
|
|
270
|
+
|
|
271
|
+
## Payment Flow
|
|
272
|
+
|
|
273
|
+
```
|
|
274
|
+
Client -> Resource Server -> Facilitator -> Algorand Network
|
|
275
|
+
| | | |
|
|
276
|
+
| 1. GET | | |
|
|
277
|
+
|--------->| | |
|
|
278
|
+
| 2. 402 | | |
|
|
279
|
+
|<---------| | |
|
|
280
|
+
| 3. Build | | |
|
|
281
|
+
| payload| | |
|
|
282
|
+
| 4. GET + | | |
|
|
283
|
+
| PAYMENT- | | |
|
|
284
|
+
| SIGNATURE| | |
|
|
285
|
+
|--------->| 5. verify() | |
|
|
286
|
+
| |----------------->| 6. simulate |
|
|
287
|
+
| | |--------------->|
|
|
288
|
+
| | |<---------------|
|
|
289
|
+
| |<-----------------| |
|
|
290
|
+
| | 7. settle() | |
|
|
291
|
+
| |----------------->| 8. sign + send |
|
|
292
|
+
| | |--------------->|
|
|
293
|
+
| | |<---------------|
|
|
294
|
+
| |<-----------------| 9. txId |
|
|
295
|
+
| 10. 200 | | |
|
|
296
|
+
|<---------| | |
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## Testing Notes
|
|
300
|
+
|
|
301
|
+
- Use Algorand TestNet for development with free test ALGO from the [Algorand Faucet](https://bank.testnet.algorand.network/)
|
|
302
|
+
- USDC on TestNet uses ASA ID `10458941` -- ensure receiver has opted in
|
|
303
|
+
- Simulation (`simulate_group`) validates transactions without submitting
|
|
304
|
+
- Algorand has instant finality -- once confirmed, transactions are permanent
|
|
305
|
+
- AlgoNode public endpoints require no authentication token
|
|
306
|
+
|
|
307
|
+
## External Resources
|
|
308
|
+
|
|
309
|
+
- [x402-avm on PyPI](https://pypi.org/project/x402-avm/)
|
|
310
|
+
- [x402-avm GitHub Repository](https://github.com/GoPlausible/x402-avm/tree/branch-v2-algorand-publish/)
|
|
311
|
+
- [x402-avm Examples](https://github.com/GoPlausible/x402-avm/tree/branch-v2-algorand-publish/examples/)
|
|
312
|
+
- [x402 Algorand Documentation](https://github.com/GoPlausible/.github/blob/main/profile/algorand-x402-documentation/)
|
|
313
|
+
- [Algorand Developer Portal](https://dev.algorand.co/)
|
|
314
|
+
- [py-algorand-sdk Documentation](https://py-algorand-sdk.readthedocs.io/)
|
|
315
|
+
- [Coinbase x402 Specification](https://github.com/coinbase/x402)
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# x402-avm Python Package for Algorand
|
|
2
|
+
|
|
3
|
+
The `x402-avm` Python package provides Algorand (AVM) payment protocol support through a modular extras system, Protocol-based signer interfaces, and async/sync variants for every component.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
Before using this skill, ensure:
|
|
8
|
+
|
|
9
|
+
1. **Python 3.10+** is installed
|
|
10
|
+
2. **pip** is available for package installation
|
|
11
|
+
3. **Understanding of x402 protocol** -- client sends request, gets 402, creates payment, retries with payment header
|
|
12
|
+
|
|
13
|
+
## Core Concept: Extras-Based Installation
|
|
14
|
+
|
|
15
|
+
The `x402-avm` package uses pip extras to install only what you need. The package name on PyPI is `x402-avm` but all imports use `from x402...` (not `from x402_avm...`).
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Minimal AVM support
|
|
19
|
+
pip install "x402-avm[avm]"
|
|
20
|
+
|
|
21
|
+
# Server frameworks
|
|
22
|
+
pip install "x402-avm[fastapi,avm]"
|
|
23
|
+
pip install "x402-avm[flask,avm]"
|
|
24
|
+
|
|
25
|
+
# HTTP clients
|
|
26
|
+
pip install "x402-avm[httpx,avm]"
|
|
27
|
+
pip install "x402-avm[requests,avm]"
|
|
28
|
+
|
|
29
|
+
# Everything
|
|
30
|
+
pip install "x402-avm[all]"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Core Workflow: Signer Protocol Pattern
|
|
34
|
+
|
|
35
|
+
The key insight is that x402 defines **Protocol classes** (structural typing) for signers. Your implementation does not need to inherit -- it just needs matching methods.
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
Private Key (env var)
|
|
39
|
+
|
|
|
40
|
+
v
|
|
41
|
+
Signer Implementation (satisfies Protocol)
|
|
42
|
+
|
|
|
43
|
+
v
|
|
44
|
+
register_exact_avm_client(x402_client, signer)
|
|
45
|
+
|
|
|
46
|
+
v
|
|
47
|
+
HTTP Client (httpx or requests) handles 402 automatically
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## How to Proceed
|
|
51
|
+
|
|
52
|
+
### Step 1: Choose Your Extras
|
|
53
|
+
|
|
54
|
+
| Use Case | Install Command |
|
|
55
|
+
|----------|----------------|
|
|
56
|
+
| Client with httpx (async) | `pip install "x402-avm[httpx,avm]"` |
|
|
57
|
+
| Client with requests (sync) | `pip install "x402-avm[requests,avm]"` |
|
|
58
|
+
| Server with FastAPI (async) | `pip install "x402-avm[fastapi,avm]"` |
|
|
59
|
+
| Server with Flask (sync) | `pip install "x402-avm[flask,avm]"` |
|
|
60
|
+
| Facilitator service | `pip install "x402-avm[fastapi,avm]"` or `"x402-avm[flask,avm]"` |
|
|
61
|
+
| Full installation | `pip install "x402-avm[all]"` |
|
|
62
|
+
|
|
63
|
+
### Step 2: Understand Async vs Sync Variants
|
|
64
|
+
|
|
65
|
+
Every core class has both async and sync variants. Using the wrong variant causes runtime errors.
|
|
66
|
+
|
|
67
|
+
| Component | Async (FastAPI/httpx) | Sync (Flask/requests) |
|
|
68
|
+
|-----------|----------------------|----------------------|
|
|
69
|
+
| x402 Client | `x402Client` | `x402ClientSync` |
|
|
70
|
+
| Resource Server | `x402ResourceServer` | `x402ResourceServerSync` |
|
|
71
|
+
| Facilitator Client | `HTTPFacilitatorClient` | `HTTPFacilitatorClientSync` |
|
|
72
|
+
| HTTP Resource Server | `x402HTTPResourceServer` | `x402HTTPResourceServerSync` |
|
|
73
|
+
|
|
74
|
+
### Step 3: Implement a Signer
|
|
75
|
+
|
|
76
|
+
For clients, implement `ClientAvmSigner`:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from x402.mechanisms.avm.signer import ClientAvmSigner
|
|
80
|
+
|
|
81
|
+
# Protocol -- just match this shape:
|
|
82
|
+
class ClientAvmSigner(Protocol):
|
|
83
|
+
@property
|
|
84
|
+
def address(self) -> str: ...
|
|
85
|
+
def sign_transactions(self, unsigned_txns: list[bytes], indexes_to_sign: list[int]) -> list[bytes | None]: ...
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
For facilitators, implement `FacilitatorAvmSigner`:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from x402.mechanisms.avm.signer import FacilitatorAvmSigner
|
|
92
|
+
|
|
93
|
+
# Protocol -- match this shape:
|
|
94
|
+
class FacilitatorAvmSigner(Protocol):
|
|
95
|
+
def get_addresses(self) -> list[str]: ...
|
|
96
|
+
def sign_transaction(self, txn_bytes: bytes, fee_payer: str, network: str) -> bytes: ...
|
|
97
|
+
def sign_group(self, group_bytes: list[bytes], fee_payer: str, indexes_to_sign: list[int], network: str) -> list[bytes]: ...
|
|
98
|
+
def simulate_group(self, group_bytes: list[bytes], network: str) -> None: ...
|
|
99
|
+
def send_group(self, group_bytes: list[bytes], network: str) -> str: ...
|
|
100
|
+
def confirm_transaction(self, txid: str, network: str, rounds: int = 4) -> None: ...
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Step 4: Handle algosdk Encoding Boundaries
|
|
104
|
+
|
|
105
|
+
This is the most common source of bugs. The x402 SDK passes **raw msgpack bytes** between methods, but Python algosdk (v2.11.1) works with **base64 strings**:
|
|
106
|
+
|
|
107
|
+
| Operation | Python algosdk Expects/Returns |
|
|
108
|
+
|-----------|-------------------------------|
|
|
109
|
+
| `msgpack_decode(s)` | Expects **base64 string** |
|
|
110
|
+
| `msgpack_encode(obj)` | Returns **base64 string** |
|
|
111
|
+
| `Transaction.sign(key)` | Expects **base64 string** key |
|
|
112
|
+
|
|
113
|
+
Boundary conversion:
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
import base64
|
|
117
|
+
from algosdk import encoding
|
|
118
|
+
|
|
119
|
+
# Raw bytes -> algosdk object (DECODE)
|
|
120
|
+
b64_string = base64.b64encode(raw_bytes).decode("utf-8")
|
|
121
|
+
txn_obj = encoding.msgpack_decode(b64_string)
|
|
122
|
+
|
|
123
|
+
# algosdk object -> raw bytes (ENCODE)
|
|
124
|
+
b64_string = encoding.msgpack_encode(txn_obj)
|
|
125
|
+
raw_bytes = base64.b64decode(b64_string)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Step 5: Register and Use
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from x402 import x402Client
|
|
132
|
+
from x402.mechanisms.avm.exact import register_exact_avm_client
|
|
133
|
+
|
|
134
|
+
signer = MyClientSigner(os.environ["AVM_PRIVATE_KEY"])
|
|
135
|
+
client = x402Client()
|
|
136
|
+
register_exact_avm_client(client, signer)
|
|
137
|
+
# Now use with httpx or requests client wrappers
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Important Rules / Guidelines
|
|
141
|
+
|
|
142
|
+
1. **Always use base64 boundaries** -- `msgpack_decode` expects base64 string, not raw bytes
|
|
143
|
+
2. **Match async/sync variants** -- FastAPI uses `x402Client`, Flask uses `x402ClientSync`
|
|
144
|
+
3. **Private key format** -- `AVM_PRIVATE_KEY` is Base64-encoded 64 bytes (32-byte seed + 32-byte pubkey)
|
|
145
|
+
4. **Address derivation** -- `encode_address(secret_key[32:])` extracts address from public key portion
|
|
146
|
+
5. **Import as x402** -- install as `x402-avm` but import as `from x402...`
|
|
147
|
+
6. **Protocol typing** -- no inheritance needed, just match the method signatures
|
|
148
|
+
|
|
149
|
+
## Common Errors / Troubleshooting
|
|
150
|
+
|
|
151
|
+
| Error | Cause | Solution |
|
|
152
|
+
|-------|-------|----------|
|
|
153
|
+
| `TypeError: a bytes-like object is required` | Passed raw bytes to `msgpack_decode` | Convert to base64 string first: `base64.b64encode(raw).decode()` |
|
|
154
|
+
| `TypeError: expected str, got bytes` | `msgpack_encode` returns base64 string, not bytes | Use `base64.b64decode(result)` to get raw bytes |
|
|
155
|
+
| `Invalid key length: expected 64` | Wrong key format | Ensure `AVM_PRIVATE_KEY` is Base64-encoded 64-byte key |
|
|
156
|
+
| `TypeError: x402HTTPAdapter requires sync client` | Used `x402Client` with requests | Use `x402ClientSync` for requests (sync) |
|
|
157
|
+
| `ImportError: AVM mechanism requires...` | Missing `py-algorand-sdk` | `pip install "x402-avm[avm]"` |
|
|
158
|
+
| `ModuleNotFoundError: x402_avm` | Wrong import name | Import as `from x402...` not `from x402_avm...` |
|
|
159
|
+
| `Simulation failed: ...` | Transaction group invalid | Check group ID assignment, fee calculations, asset opt-in |
|
|
160
|
+
|
|
161
|
+
## References / Further Reading
|
|
162
|
+
|
|
163
|
+
- [explain-algorand-x402-python-reference.md](./explain-algorand-x402-python-reference.md) - Detailed API reference for all Python components
|
|
164
|
+
- [explain-algorand-x402-python-examples.md](./explain-algorand-x402-python-examples.md) - Complete code examples
|
|
165
|
+
- [x402-avm on PyPI](https://pypi.org/project/x402-avm/)
|
|
166
|
+
- [x402-avm Examples Repository](https://github.com/GoPlausible/x402-avm/tree/branch-v2-algorand-publish/examples/)
|
|
167
|
+
- [x402 Algorand Documentation](https://github.com/GoPlausible/.github/blob/main/profile/algorand-x402-documentation/)
|