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.
Files changed (112) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +112 -0
  3. package/index.ts +361 -0
  4. package/lib/mcp-servers.ts +14 -0
  5. package/lib/x402-fetch.ts +213 -0
  6. package/memory/algorand-plugin.md +82 -0
  7. package/openclaw.plugin.json +30 -0
  8. package/package.json +38 -0
  9. package/setup.ts +80 -0
  10. package/skills/algorand-development/SKILL.md +90 -0
  11. package/skills/algorand-development/references/build-smart-contracts-reference.md +79 -0
  12. package/skills/algorand-development/references/build-smart-contracts.md +52 -0
  13. package/skills/algorand-development/references/create-project-reference.md +86 -0
  14. package/skills/algorand-development/references/create-project.md +89 -0
  15. package/skills/algorand-development/references/implement-arc-standards-arc32-arc56.md +396 -0
  16. package/skills/algorand-development/references/implement-arc-standards-arc4.md +265 -0
  17. package/skills/algorand-development/references/implement-arc-standards.md +92 -0
  18. package/skills/algorand-development/references/search-algorand-examples-reference.md +119 -0
  19. package/skills/algorand-development/references/search-algorand-examples.md +89 -0
  20. package/skills/algorand-development/references/troubleshoot-errors-contract.md +373 -0
  21. package/skills/algorand-development/references/troubleshoot-errors-transaction.md +599 -0
  22. package/skills/algorand-development/references/troubleshoot-errors.md +105 -0
  23. package/skills/algorand-development/references/use-algokit-cli-reference.md +228 -0
  24. package/skills/algorand-development/references/use-algokit-cli.md +64 -0
  25. package/skills/algorand-interaction/SKILL.md +223 -0
  26. package/skills/algorand-interaction/references/algorand-mcp.md +743 -0
  27. package/skills/algorand-interaction/references/examples-algorand-mcp.md +647 -0
  28. package/skills/algorand-python/SKILL.md +95 -0
  29. package/skills/algorand-python/references/build-smart-contracts-decorators.md +413 -0
  30. package/skills/algorand-python/references/build-smart-contracts-reference.md +55 -0
  31. package/skills/algorand-python/references/build-smart-contracts-storage.md +452 -0
  32. package/skills/algorand-python/references/build-smart-contracts-transactions.md +445 -0
  33. package/skills/algorand-python/references/build-smart-contracts-types.md +438 -0
  34. package/skills/algorand-python/references/build-smart-contracts.md +82 -0
  35. package/skills/algorand-python/references/create-project-reference.md +55 -0
  36. package/skills/algorand-python/references/create-project.md +75 -0
  37. package/skills/algorand-python/references/implement-arc-standards-arc32-arc56.md +101 -0
  38. package/skills/algorand-python/references/implement-arc-standards-arc4.md +154 -0
  39. package/skills/algorand-python/references/implement-arc-standards.md +39 -0
  40. package/skills/algorand-python/references/troubleshoot-errors-contract.md +355 -0
  41. package/skills/algorand-python/references/troubleshoot-errors-transaction.md +430 -0
  42. package/skills/algorand-python/references/troubleshoot-errors.md +46 -0
  43. package/skills/algorand-python/references/use-algokit-utils-reference.md +350 -0
  44. package/skills/algorand-python/references/use-algokit-utils.md +76 -0
  45. package/skills/algorand-typescript/SKILL.md +131 -0
  46. package/skills/algorand-typescript/references/algorand-ts-migration-from-beta.md +448 -0
  47. package/skills/algorand-typescript/references/algorand-ts-migration-from-tealscript.md +487 -0
  48. package/skills/algorand-typescript/references/algorand-ts-migration.md +102 -0
  49. package/skills/algorand-typescript/references/algorand-typescript-syntax-methods-and-abi.md +134 -0
  50. package/skills/algorand-typescript/references/algorand-typescript-syntax-reference.md +58 -0
  51. package/skills/algorand-typescript/references/algorand-typescript-syntax-storage.md +154 -0
  52. package/skills/algorand-typescript/references/algorand-typescript-syntax-transactions.md +187 -0
  53. package/skills/algorand-typescript/references/algorand-typescript-syntax-types-and-values.md +150 -0
  54. package/skills/algorand-typescript/references/algorand-typescript-syntax.md +84 -0
  55. package/skills/algorand-typescript/references/build-smart-contracts-reference.md +52 -0
  56. package/skills/algorand-typescript/references/build-smart-contracts.md +74 -0
  57. package/skills/algorand-typescript/references/call-smart-contracts-reference.md +237 -0
  58. package/skills/algorand-typescript/references/call-smart-contracts.md +183 -0
  59. package/skills/algorand-typescript/references/create-project-reference.md +53 -0
  60. package/skills/algorand-typescript/references/create-project.md +86 -0
  61. package/skills/algorand-typescript/references/deploy-react-frontend-examples.md +527 -0
  62. package/skills/algorand-typescript/references/deploy-react-frontend-reference.md +412 -0
  63. package/skills/algorand-typescript/references/deploy-react-frontend.md +239 -0
  64. package/skills/algorand-typescript/references/implement-arc-standards-arc32-arc56.md +73 -0
  65. package/skills/algorand-typescript/references/implement-arc-standards-arc4.md +126 -0
  66. package/skills/algorand-typescript/references/implement-arc-standards.md +44 -0
  67. package/skills/algorand-typescript/references/test-smart-contracts-examples.md +245 -0
  68. package/skills/algorand-typescript/references/test-smart-contracts-unit-tests.md +147 -0
  69. package/skills/algorand-typescript/references/test-smart-contracts.md +127 -0
  70. package/skills/algorand-typescript/references/troubleshoot-errors-contract.md +296 -0
  71. package/skills/algorand-typescript/references/troubleshoot-errors-transaction.md +438 -0
  72. package/skills/algorand-typescript/references/troubleshoot-errors.md +56 -0
  73. package/skills/algorand-typescript/references/use-algokit-utils-reference.md +342 -0
  74. package/skills/algorand-typescript/references/use-algokit-utils.md +74 -0
  75. package/skills/algorand-x402-python/SKILL.md +113 -0
  76. package/skills/algorand-x402-python/references/create-python-x402-client-examples.md +469 -0
  77. package/skills/algorand-x402-python/references/create-python-x402-client-reference.md +313 -0
  78. package/skills/algorand-x402-python/references/create-python-x402-client.md +207 -0
  79. package/skills/algorand-x402-python/references/create-python-x402-facilitator-examples.md +924 -0
  80. package/skills/algorand-x402-python/references/create-python-x402-facilitator-reference.md +629 -0
  81. package/skills/algorand-x402-python/references/create-python-x402-facilitator.md +408 -0
  82. package/skills/algorand-x402-python/references/create-python-x402-server-examples.md +703 -0
  83. package/skills/algorand-x402-python/references/create-python-x402-server-reference.md +303 -0
  84. package/skills/algorand-x402-python/references/create-python-x402-server.md +221 -0
  85. package/skills/algorand-x402-python/references/explain-algorand-x402-python-examples.md +605 -0
  86. package/skills/algorand-x402-python/references/explain-algorand-x402-python-reference.md +315 -0
  87. package/skills/algorand-x402-python/references/explain-algorand-x402-python.md +167 -0
  88. package/skills/algorand-x402-python/references/use-python-x402-core-avm-examples.md +554 -0
  89. package/skills/algorand-x402-python/references/use-python-x402-core-avm-reference.md +278 -0
  90. package/skills/algorand-x402-python/references/use-python-x402-core-avm.md +166 -0
  91. package/skills/algorand-x402-typescript/SKILL.md +129 -0
  92. package/skills/algorand-x402-typescript/references/create-typescript-x402-client-examples.md +879 -0
  93. package/skills/algorand-x402-typescript/references/create-typescript-x402-client-reference.md +371 -0
  94. package/skills/algorand-x402-typescript/references/create-typescript-x402-client.md +236 -0
  95. package/skills/algorand-x402-typescript/references/create-typescript-x402-facilitator-examples.md +875 -0
  96. package/skills/algorand-x402-typescript/references/create-typescript-x402-facilitator-reference.md +461 -0
  97. package/skills/algorand-x402-typescript/references/create-typescript-x402-facilitator.md +270 -0
  98. package/skills/algorand-x402-typescript/references/create-typescript-x402-nextjs-examples.md +1181 -0
  99. package/skills/algorand-x402-typescript/references/create-typescript-x402-nextjs-reference.md +360 -0
  100. package/skills/algorand-x402-typescript/references/create-typescript-x402-nextjs.md +251 -0
  101. package/skills/algorand-x402-typescript/references/create-typescript-x402-paywall-examples.md +870 -0
  102. package/skills/algorand-x402-typescript/references/create-typescript-x402-paywall-reference.md +323 -0
  103. package/skills/algorand-x402-typescript/references/create-typescript-x402-paywall.md +281 -0
  104. package/skills/algorand-x402-typescript/references/create-typescript-x402-server-examples.md +1135 -0
  105. package/skills/algorand-x402-typescript/references/create-typescript-x402-server-reference.md +382 -0
  106. package/skills/algorand-x402-typescript/references/create-typescript-x402-server.md +216 -0
  107. package/skills/algorand-x402-typescript/references/explain-algorand-x402-typescript-examples.md +616 -0
  108. package/skills/algorand-x402-typescript/references/explain-algorand-x402-typescript-reference.md +323 -0
  109. package/skills/algorand-x402-typescript/references/explain-algorand-x402-typescript.md +232 -0
  110. package/skills/algorand-x402-typescript/references/use-typescript-x402-core-avm-examples.md +1417 -0
  111. package/skills/algorand-x402-typescript/references/use-typescript-x402-core-avm-reference.md +504 -0
  112. package/skills/algorand-x402-typescript/references/use-typescript-x402-core-avm.md +158 -0
@@ -0,0 +1,605 @@
1
+ # x402-avm Python AVM Mechanism Examples
2
+
3
+ ## ClientAvmSigner Protocol
4
+
5
+ ```python
6
+ from x402.mechanisms.avm.signer import ClientAvmSigner
7
+
8
+ class ClientAvmSigner(Protocol):
9
+ @property
10
+ def address(self) -> str:
11
+ """58-character Algorand address."""
12
+ ...
13
+
14
+ def sign_transactions(
15
+ self,
16
+ unsigned_txns: list[bytes],
17
+ indexes_to_sign: list[int],
18
+ ) -> list[bytes | None]:
19
+ """Sign specified transactions in a group.
20
+
21
+ Args:
22
+ unsigned_txns: List of unsigned transaction bytes (msgpack encoded).
23
+ indexes_to_sign: Indexes of transactions this signer should sign.
24
+
25
+ Returns:
26
+ List parallel to unsigned_txns, with signed bytes at
27
+ indexes_to_sign and None elsewhere.
28
+ """
29
+ ...
30
+ ```
31
+
32
+ ## FacilitatorAvmSigner Protocol
33
+
34
+ ```python
35
+ from x402.mechanisms.avm.signer import FacilitatorAvmSigner
36
+
37
+ class FacilitatorAvmSigner(Protocol):
38
+ def get_addresses(self) -> list[str]:
39
+ """Get all managed fee payer addresses."""
40
+ ...
41
+
42
+ def sign_transaction(
43
+ self, txn_bytes: bytes, fee_payer: str, network: str,
44
+ ) -> bytes:
45
+ """Sign a single transaction with the fee payer's key."""
46
+ ...
47
+
48
+ def sign_group(
49
+ self,
50
+ group_bytes: list[bytes],
51
+ fee_payer: str,
52
+ indexes_to_sign: list[int],
53
+ network: str,
54
+ ) -> list[bytes]:
55
+ """Sign specified transactions in a group."""
56
+ ...
57
+
58
+ def simulate_group(
59
+ self, group_bytes: list[bytes], network: str,
60
+ ) -> None:
61
+ """Simulate a transaction group (raises on failure)."""
62
+ ...
63
+
64
+ def send_group(
65
+ self, group_bytes: list[bytes], network: str,
66
+ ) -> str:
67
+ """Send a transaction group, returns txid."""
68
+ ...
69
+
70
+ def confirm_transaction(
71
+ self, txid: str, network: str, rounds: int = 4,
72
+ ) -> None:
73
+ """Wait for transaction confirmation."""
74
+ ...
75
+ ```
76
+
77
+ ## Client Signer Implementation
78
+
79
+ ```python
80
+ import os
81
+ import base64
82
+ from x402.mechanisms.avm.signer import ClientAvmSigner
83
+ from algosdk import encoding
84
+
85
+
86
+ class PrivateKeySigner:
87
+ """ClientAvmSigner implementation using a base64-encoded private key."""
88
+
89
+ def __init__(self, private_key_b64: str):
90
+ self._secret_key = base64.b64decode(private_key_b64)
91
+ if len(self._secret_key) != 64:
92
+ raise ValueError(
93
+ f"Invalid key length: expected 64, got {len(self._secret_key)}"
94
+ )
95
+ self._address = encoding.encode_address(self._secret_key[32:])
96
+ self._signing_key = base64.b64encode(self._secret_key).decode()
97
+
98
+ @property
99
+ def address(self) -> str:
100
+ return self._address
101
+
102
+ def sign_transactions(
103
+ self,
104
+ unsigned_txns: list[bytes],
105
+ indexes_to_sign: list[int],
106
+ ) -> list[bytes | None]:
107
+ result: list[bytes | None] = []
108
+ for i, txn_bytes in enumerate(unsigned_txns):
109
+ if i not in indexes_to_sign:
110
+ result.append(None)
111
+ continue
112
+
113
+ b64_txn = base64.b64encode(txn_bytes).decode("utf-8")
114
+ txn_obj = encoding.msgpack_decode(b64_txn)
115
+ signed_txn = txn_obj.sign(self._signing_key)
116
+ signed_b64 = encoding.msgpack_encode(signed_txn)
117
+ signed_bytes = base64.b64decode(signed_b64)
118
+ result.append(signed_bytes)
119
+ return result
120
+
121
+
122
+ signer = PrivateKeySigner(os.environ["AVM_PRIVATE_KEY"])
123
+ print(f"Signer address: {signer.address}")
124
+ ```
125
+
126
+ ## Facilitator Signer Implementation
127
+
128
+ ```python
129
+ import base64
130
+ from x402.mechanisms.avm.signer import FacilitatorAvmSigner
131
+ from x402.mechanisms.avm.constants import (
132
+ ALGORAND_TESTNET_CAIP2,
133
+ ALGORAND_MAINNET_CAIP2,
134
+ NETWORK_CONFIGS,
135
+ )
136
+ from algosdk import encoding, transaction
137
+ from algosdk.v2client import algod
138
+
139
+
140
+ class AlgorandFacilitatorSigner:
141
+ """Production FacilitatorAvmSigner implementation."""
142
+
143
+ def __init__(self, private_key_b64: str, algod_url: str = "", algod_token: str = ""):
144
+ self._secret_key = base64.b64decode(private_key_b64)
145
+ self._address = encoding.encode_address(self._secret_key[32:])
146
+ self._signing_key = base64.b64encode(self._secret_key).decode()
147
+ self._clients: dict[str, algod.AlgodClient] = {}
148
+ if algod_url:
149
+ self._default_client = algod.AlgodClient(algod_token, algod_url)
150
+ else:
151
+ self._default_client = None
152
+
153
+ def _get_client(self, network: str) -> algod.AlgodClient:
154
+ if network not in self._clients:
155
+ if self._default_client:
156
+ self._clients[network] = self._default_client
157
+ else:
158
+ config = NETWORK_CONFIGS.get(network, {})
159
+ url = config.get("algod_url", "https://testnet-api.algonode.cloud")
160
+ self._clients[network] = algod.AlgodClient("", url)
161
+ return self._clients[network]
162
+
163
+ def get_addresses(self) -> list[str]:
164
+ return [self._address]
165
+
166
+ def sign_transaction(
167
+ self, txn_bytes: bytes, fee_payer: str, network: str,
168
+ ) -> bytes:
169
+ b64_txn = base64.b64encode(txn_bytes).decode("utf-8")
170
+ txn_obj = encoding.msgpack_decode(b64_txn)
171
+ signed = txn_obj.sign(self._signing_key)
172
+ return base64.b64decode(encoding.msgpack_encode(signed))
173
+
174
+ def sign_group(
175
+ self,
176
+ group_bytes: list[bytes],
177
+ fee_payer: str,
178
+ indexes_to_sign: list[int],
179
+ network: str,
180
+ ) -> list[bytes]:
181
+ result = list(group_bytes)
182
+ for i in indexes_to_sign:
183
+ result[i] = self.sign_transaction(group_bytes[i], fee_payer, network)
184
+ return result
185
+
186
+ def simulate_group(self, group_bytes: list[bytes], network: str) -> None:
187
+ client = self._get_client(network)
188
+ stxns = []
189
+ for txn_bytes in group_bytes:
190
+ b64 = base64.b64encode(txn_bytes).decode("utf-8")
191
+ obj = encoding.msgpack_decode(b64)
192
+ if isinstance(obj, transaction.SignedTransaction):
193
+ stxns.append(obj)
194
+ elif isinstance(obj, transaction.Transaction):
195
+ stxns.append(transaction.SignedTransaction(obj, None))
196
+ else:
197
+ stxns.append(obj)
198
+
199
+ request = transaction.SimulateRequest(
200
+ txn_groups=[
201
+ transaction.SimulateRequestTransactionGroup(txns=stxns)
202
+ ],
203
+ allow_empty_signatures=True,
204
+ )
205
+ result = client.simulate_raw_transactions(request)
206
+ for group in result.get("txn-groups", []):
207
+ if group.get("failure-message"):
208
+ raise Exception(f"Simulation failed: {group['failure-message']}")
209
+
210
+ def send_group(self, group_bytes: list[bytes], network: str) -> str:
211
+ client = self._get_client(network)
212
+ raw = base64.b64encode(b"".join(group_bytes))
213
+ return client.send_raw_transaction(raw)
214
+
215
+ def confirm_transaction(
216
+ self, txid: str, network: str, rounds: int = 4,
217
+ ) -> None:
218
+ client = self._get_client(network)
219
+ transaction.wait_for_confirmation(client, txid, rounds)
220
+
221
+
222
+ import os
223
+
224
+ signer = AlgorandFacilitatorSigner(
225
+ private_key_b64=os.environ["AVM_PRIVATE_KEY"],
226
+ algod_url="https://testnet-api.algonode.cloud",
227
+ )
228
+ print(f"Fee payer addresses: {signer.get_addresses()}")
229
+ ```
230
+
231
+ ## Network Constants
232
+
233
+ ```python
234
+ from x402.mechanisms.avm.constants import (
235
+ ALGORAND_MAINNET_CAIP2,
236
+ ALGORAND_TESTNET_CAIP2,
237
+ SUPPORTED_NETWORKS,
238
+ MAINNET_GENESIS_HASH,
239
+ TESTNET_GENESIS_HASH,
240
+ V1_NETWORKS,
241
+ V1_TO_V2_NETWORK_MAP,
242
+ V2_TO_V1_NETWORK_MAP,
243
+ )
244
+ ```
245
+
246
+ ## USDC Configuration
247
+
248
+ ```python
249
+ from x402.mechanisms.avm.constants import (
250
+ USDC_MAINNET_ASA_ID,
251
+ USDC_TESTNET_ASA_ID,
252
+ DEFAULT_DECIMALS,
253
+ NETWORK_CONFIGS,
254
+ )
255
+
256
+ testnet_config = NETWORK_CONFIGS[ALGORAND_TESTNET_CAIP2]
257
+ usdc_info = testnet_config["default_asset"]
258
+ ```
259
+
260
+ ## Algod Endpoints
261
+
262
+ ```python
263
+ from x402.mechanisms.avm.constants import (
264
+ MAINNET_ALGOD_URL,
265
+ TESTNET_ALGOD_URL,
266
+ FALLBACK_ALGOD_MAINNET,
267
+ FALLBACK_ALGOD_TESTNET,
268
+ MAINNET_INDEXER_URL,
269
+ TESTNET_INDEXER_URL,
270
+ )
271
+ ```
272
+
273
+ ## Transaction Limits
274
+
275
+ ```python
276
+ from x402.mechanisms.avm.constants import (
277
+ MAX_GROUP_SIZE,
278
+ MIN_TXN_FEE,
279
+ )
280
+ ```
281
+
282
+ ## Address Validation
283
+
284
+ ```python
285
+ from x402.mechanisms.avm.constants import AVM_ADDRESS_REGEX
286
+ from x402.mechanisms.avm.utils import is_valid_address
287
+ import re
288
+
289
+ is_valid = bool(re.match(AVM_ADDRESS_REGEX, some_address))
290
+ is_valid_address("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
291
+ ```
292
+
293
+ ## Amount Conversion
294
+
295
+ ```python
296
+ from x402.mechanisms.avm.utils import to_atomic_amount, from_atomic_amount
297
+
298
+ to_atomic_amount(1.50)
299
+ from_atomic_amount(1500000)
300
+ ```
301
+
302
+ ## Transaction Encoding/Decoding
303
+
304
+ ```python
305
+ from x402.mechanisms.avm.utils import (
306
+ decode_transaction_bytes,
307
+ decode_base64_transaction,
308
+ decode_payment_group,
309
+ encode_transaction_group,
310
+ )
311
+
312
+ info = decode_transaction_bytes(raw_txn_bytes)
313
+ print(info.type, info.sender, info.fee, info.is_signed)
314
+
315
+ group_info = decode_payment_group(
316
+ payment_group=["base64txn1...", "base64txn2..."],
317
+ payment_index=0,
318
+ )
319
+ print(group_info.transactions, group_info.group_id, group_info.total_fee)
320
+
321
+ encoded = encode_transaction_group([raw_bytes_1, raw_bytes_2])
322
+ ```
323
+
324
+ ## Network Utilities
325
+
326
+ ```python
327
+ from x402.mechanisms.avm.utils import (
328
+ normalize_network,
329
+ is_valid_network,
330
+ get_network_config,
331
+ get_usdc_asa_id,
332
+ get_genesis_hash,
333
+ network_from_genesis_hash,
334
+ )
335
+
336
+ normalize_network("algorand-testnet")
337
+ is_valid_network("algorand-testnet")
338
+ config = get_network_config("algorand-testnet")
339
+ get_usdc_asa_id("algorand-testnet")
340
+ network_from_genesis_hash("SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=")
341
+ ```
342
+
343
+ ## Security Validation
344
+
345
+ ```python
346
+ from x402.mechanisms.avm.utils import (
347
+ validate_no_security_risks,
348
+ validate_fee_payer_transaction,
349
+ is_blocked_transaction_type,
350
+ )
351
+
352
+ info = decode_transaction_bytes(txn_bytes)
353
+ error_code = validate_no_security_risks(info)
354
+ error_code = validate_fee_payer_transaction(info, expected_fee_payer="ABC...")
355
+ is_blocked_transaction_type("keyreg")
356
+ ```
357
+
358
+ ## Simple Payment Group
359
+
360
+ ```python
361
+ import base64
362
+ from algosdk import transaction, encoding
363
+ from algosdk.v2client import algod
364
+ from x402.mechanisms.avm.constants import (
365
+ ALGORAND_TESTNET_CAIP2,
366
+ USDC_TESTNET_ASA_ID,
367
+ NETWORK_CONFIGS,
368
+ )
369
+
370
+
371
+ def create_simple_payment(sender: str, receiver: str, amount: int) -> list[bytes]:
372
+ config = NETWORK_CONFIGS[ALGORAND_TESTNET_CAIP2]
373
+ client = algod.AlgodClient("", config["algod_url"])
374
+ params = client.suggested_params()
375
+
376
+ txn = transaction.AssetTransferTxn(
377
+ sender=sender,
378
+ sp=params,
379
+ receiver=receiver,
380
+ amt=amount,
381
+ index=USDC_TESTNET_ASA_ID,
382
+ )
383
+ return [base64.b64decode(encoding.msgpack_encode(txn))]
384
+ ```
385
+
386
+ ## Fee-Abstracted Payment Group
387
+
388
+ ```python
389
+ import base64
390
+ from algosdk import transaction, encoding
391
+ from algosdk.v2client import algod
392
+ from x402.mechanisms.avm.constants import (
393
+ ALGORAND_TESTNET_CAIP2,
394
+ USDC_TESTNET_ASA_ID,
395
+ MIN_TXN_FEE,
396
+ NETWORK_CONFIGS,
397
+ )
398
+ from x402.mechanisms.avm.utils import encode_transaction_group
399
+
400
+
401
+ def create_fee_abstracted_payment(
402
+ sender: str, receiver: str, fee_payer: str, amount: int,
403
+ ) -> dict:
404
+ config = NETWORK_CONFIGS[ALGORAND_TESTNET_CAIP2]
405
+ client = algod.AlgodClient("", config["algod_url"])
406
+ params = client.suggested_params()
407
+
408
+ payment_params = transaction.SuggestedParams(
409
+ fee=0, first=params.first, last=params.last,
410
+ gh=params.gh, gen=params.gen, flat_fee=True,
411
+ )
412
+ payment_txn = transaction.AssetTransferTxn(
413
+ sender=sender, sp=payment_params, receiver=receiver,
414
+ amt=amount, index=USDC_TESTNET_ASA_ID,
415
+ )
416
+
417
+ fee_params = transaction.SuggestedParams(
418
+ fee=MIN_TXN_FEE * 2, first=params.first, last=params.last,
419
+ gh=params.gh, gen=params.gen, flat_fee=True,
420
+ )
421
+ fee_payer_txn = transaction.PaymentTxn(
422
+ sender=fee_payer, sp=fee_params, receiver=fee_payer, amt=0,
423
+ )
424
+
425
+ gid = transaction.calculate_group_id([payment_txn, fee_payer_txn])
426
+ payment_txn.group = gid
427
+ fee_payer_txn.group = gid
428
+
429
+ payment_bytes = base64.b64decode(encoding.msgpack_encode(payment_txn))
430
+ fee_payer_bytes = base64.b64decode(encoding.msgpack_encode(fee_payer_txn))
431
+
432
+ return {
433
+ "paymentGroup": encode_transaction_group([payment_bytes, fee_payer_bytes]),
434
+ "paymentIndex": 0,
435
+ "rawBytes": [payment_bytes, fee_payer_bytes],
436
+ }
437
+ ```
438
+
439
+ ## ExactAvmScheme Client Registration
440
+
441
+ ```python
442
+ from x402 import x402Client
443
+ from x402.mechanisms.avm.exact import register_exact_avm_client
444
+
445
+ client = x402Client()
446
+ register_exact_avm_client(client, signer)
447
+ ```
448
+
449
+ ## ExactAvmScheme Server Registration
450
+
451
+ ```python
452
+ from x402 import x402ResourceServer
453
+ from x402.mechanisms.avm.exact import register_exact_avm_server
454
+
455
+ server = x402ResourceServer(facilitator_url="https://facilitator.example.com")
456
+ register_exact_avm_server(server)
457
+ ```
458
+
459
+ ## ExactAvmScheme Facilitator Registration
460
+
461
+ ```python
462
+ from x402 import x402Facilitator
463
+ from x402.mechanisms.avm.exact import register_exact_avm_facilitator
464
+ from x402.mechanisms.avm import ALGORAND_TESTNET_CAIP2, ALGORAND_MAINNET_CAIP2
465
+
466
+ facilitator = x402Facilitator()
467
+
468
+ register_exact_avm_facilitator(
469
+ facilitator, signer, networks=[ALGORAND_TESTNET_CAIP2]
470
+ )
471
+
472
+ register_exact_avm_facilitator(
473
+ facilitator, signer, networks=[ALGORAND_TESTNET_CAIP2, ALGORAND_MAINNET_CAIP2]
474
+ )
475
+ ```
476
+
477
+ ## algosdk Encoding Boundary Conversions
478
+
479
+ ```python
480
+ import base64
481
+ from algosdk import encoding
482
+
483
+ # Raw bytes -> algosdk object (DECODE)
484
+ raw_bytes: bytes = ...
485
+ b64_string = base64.b64encode(raw_bytes).decode("utf-8")
486
+ txn_obj = encoding.msgpack_decode(b64_string)
487
+
488
+ # algosdk object -> raw bytes (ENCODE)
489
+ b64_string = encoding.msgpack_encode(txn_obj)
490
+ raw_bytes = base64.b64decode(b64_string)
491
+ ```
492
+
493
+ ## Complete Example: FastAPI Facilitator Service
494
+
495
+ ```python
496
+ import os
497
+ import base64
498
+ from fastapi import FastAPI, Request
499
+ from fastapi.responses import JSONResponse
500
+ from x402 import x402Facilitator
501
+ from x402.mechanisms.avm.exact import register_exact_avm_facilitator
502
+ from x402.mechanisms.avm import ALGORAND_TESTNET_CAIP2, ALGORAND_MAINNET_CAIP2
503
+ from x402.mechanisms.avm.constants import NETWORK_CONFIGS
504
+ from algosdk import encoding, transaction
505
+ from algosdk.v2client import algod
506
+
507
+ app = FastAPI(title="x402-avm Facilitator Service")
508
+
509
+ SECRET_KEY = base64.b64decode(os.environ["AVM_PRIVATE_KEY"])
510
+ ADDRESS = encoding.encode_address(SECRET_KEY[32:])
511
+ SIGNING_KEY = base64.b64encode(SECRET_KEY).decode()
512
+
513
+
514
+ class FacilitatorSigner:
515
+ def __init__(self):
516
+ self._clients: dict[str, algod.AlgodClient] = {}
517
+
518
+ def _client(self, network: str) -> algod.AlgodClient:
519
+ if network not in self._clients:
520
+ config = NETWORK_CONFIGS.get(network, {})
521
+ url = config.get("algod_url", "https://testnet-api.algonode.cloud")
522
+ self._clients[network] = algod.AlgodClient("", url)
523
+ return self._clients[network]
524
+
525
+ def get_addresses(self) -> list[str]:
526
+ return [ADDRESS]
527
+
528
+ def sign_transaction(self, txn_bytes: bytes, fee_payer: str, network: str) -> bytes:
529
+ b64 = base64.b64encode(txn_bytes).decode()
530
+ txn_obj = encoding.msgpack_decode(b64)
531
+ signed = txn_obj.sign(SIGNING_KEY)
532
+ return base64.b64decode(encoding.msgpack_encode(signed))
533
+
534
+ def sign_group(self, group_bytes, fee_payer, indexes, network):
535
+ result = list(group_bytes)
536
+ for i in indexes:
537
+ result[i] = self.sign_transaction(group_bytes[i], fee_payer, network)
538
+ return result
539
+
540
+ def simulate_group(self, group_bytes, network):
541
+ client = self._client(network)
542
+ stxns = []
543
+ for txn_bytes in group_bytes:
544
+ b64 = base64.b64encode(txn_bytes).decode()
545
+ obj = encoding.msgpack_decode(b64)
546
+ if isinstance(obj, transaction.SignedTransaction):
547
+ stxns.append(obj)
548
+ else:
549
+ stxns.append(transaction.SignedTransaction(obj, None))
550
+ req = transaction.SimulateRequest(
551
+ txn_groups=[transaction.SimulateRequestTransactionGroup(txns=stxns)],
552
+ allow_empty_signatures=True,
553
+ )
554
+ result = client.simulate_raw_transactions(req)
555
+ for group in result.get("txn-groups", []):
556
+ if group.get("failure-message"):
557
+ raise Exception(f"Simulation failed: {group['failure-message']}")
558
+
559
+ def send_group(self, group_bytes, network):
560
+ client = self._client(network)
561
+ return client.send_raw_transaction(
562
+ base64.b64encode(b"".join(group_bytes))
563
+ )
564
+
565
+ def confirm_transaction(self, txid, network, rounds=4):
566
+ client = self._client(network)
567
+ transaction.wait_for_confirmation(client, txid, rounds)
568
+
569
+
570
+ signer = FacilitatorSigner()
571
+ facilitator = x402Facilitator()
572
+ register_exact_avm_facilitator(
573
+ facilitator, signer,
574
+ networks=[ALGORAND_TESTNET_CAIP2, ALGORAND_MAINNET_CAIP2],
575
+ )
576
+
577
+
578
+ @app.get("/supported")
579
+ async def supported():
580
+ return facilitator.get_supported_networks()
581
+
582
+
583
+ @app.post("/verify")
584
+ async def verify(request: Request):
585
+ body = await request.json()
586
+ try:
587
+ result = await facilitator.verify(
588
+ body["paymentPayload"], body["paymentRequirements"]
589
+ )
590
+ return result
591
+ except Exception as e:
592
+ return JSONResponse(status_code=400, content={"error": str(e)})
593
+
594
+
595
+ @app.post("/settle")
596
+ async def settle(request: Request):
597
+ body = await request.json()
598
+ try:
599
+ result = await facilitator.settle(
600
+ body["paymentPayload"], body["paymentRequirements"]
601
+ )
602
+ return result
603
+ except Exception as e:
604
+ return JSONResponse(status_code=400, content={"error": str(e)})
605
+ ```