@goplausible/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 +41 -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,445 @@
|
|
|
1
|
+
# Algorand Python Transactions
|
|
2
|
+
|
|
3
|
+
Create inner transactions and access group transactions in Algorand Python smart contracts.
|
|
4
|
+
|
|
5
|
+
## When to use this reference
|
|
6
|
+
|
|
7
|
+
Use this reference when:
|
|
8
|
+
|
|
9
|
+
- Creating inner transactions to send payments, transfer assets, or call other contracts
|
|
10
|
+
- Submitting multiple inner transactions atomically
|
|
11
|
+
- Accessing other transactions in an atomic group
|
|
12
|
+
- Requiring payment or asset transactions as method parameters
|
|
13
|
+
|
|
14
|
+
## Transaction Types Overview
|
|
15
|
+
|
|
16
|
+
| Group Transactions | Inner Transaction Params | Inner Transaction Result |
|
|
17
|
+
|-------------------|--------------------------|--------------------------|
|
|
18
|
+
| `gtxn.PaymentTransaction` | `itxn.Payment` | `PaymentInnerTransaction` |
|
|
19
|
+
| `gtxn.AssetTransferTransaction` | `itxn.AssetTransfer` | `AssetTransferInnerTransaction` |
|
|
20
|
+
| `gtxn.AssetConfigTransaction` | `itxn.AssetConfig` | `AssetConfigInnerTransaction` |
|
|
21
|
+
| `gtxn.AssetFreezeTransaction` | `itxn.AssetFreeze` | `AssetFreezeInnerTransaction` |
|
|
22
|
+
| `gtxn.ApplicationCallTransaction` | `itxn.ApplicationCall` | `ApplicationCallInnerTransaction` |
|
|
23
|
+
| `gtxn.KeyRegistrationTransaction` | `itxn.KeyRegistration` | `KeyRegistrationInnerTransaction` |
|
|
24
|
+
| `gtxn.Transaction` | `itxn.InnerTransaction` | `InnerTransactionResult` |
|
|
25
|
+
|
|
26
|
+
## Inner Transactions
|
|
27
|
+
|
|
28
|
+
Smart contracts can execute inner transactions to send payments, transfer assets, create assets, and call other applications.
|
|
29
|
+
|
|
30
|
+
### Basic Payment
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from algopy import ARC4Contract, UInt64, Txn, itxn, arc4
|
|
34
|
+
|
|
35
|
+
class MyContract(ARC4Contract):
|
|
36
|
+
@arc4.abimethod
|
|
37
|
+
def send_payment(self) -> UInt64:
|
|
38
|
+
# Create and submit a payment inner transaction
|
|
39
|
+
result = itxn.Payment(
|
|
40
|
+
amount=5000,
|
|
41
|
+
receiver=Txn.sender,
|
|
42
|
+
fee=0 # Always use 0 - caller covers via fee pooling
|
|
43
|
+
).submit()
|
|
44
|
+
|
|
45
|
+
return result.amount
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Asset Transfer
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from algopy import ARC4Contract, Asset, Account, UInt64, itxn, arc4
|
|
52
|
+
|
|
53
|
+
class MyContract(ARC4Contract):
|
|
54
|
+
@arc4.abimethod
|
|
55
|
+
def transfer_asset(self, asset: Asset, receiver: Account, amount: UInt64) -> None:
|
|
56
|
+
itxn.AssetTransfer(
|
|
57
|
+
xfer_asset=asset,
|
|
58
|
+
asset_receiver=receiver,
|
|
59
|
+
asset_amount=amount,
|
|
60
|
+
fee=0
|
|
61
|
+
).submit()
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Asset Opt-In (Self Transfer)
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from algopy import ARC4Contract, Asset, Global, itxn, arc4
|
|
68
|
+
|
|
69
|
+
class MyContract(ARC4Contract):
|
|
70
|
+
@arc4.abimethod
|
|
71
|
+
def opt_in_to_asset(self, asset: Asset) -> None:
|
|
72
|
+
# Opt the contract into an asset
|
|
73
|
+
itxn.AssetTransfer(
|
|
74
|
+
xfer_asset=asset,
|
|
75
|
+
asset_receiver=Global.current_application_address,
|
|
76
|
+
asset_amount=0,
|
|
77
|
+
fee=0
|
|
78
|
+
).submit()
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Create Fungible Asset
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from algopy import ARC4Contract, UInt64, Global, itxn, arc4
|
|
85
|
+
|
|
86
|
+
class MyContract(ARC4Contract):
|
|
87
|
+
@arc4.abimethod
|
|
88
|
+
def create_token(self) -> UInt64:
|
|
89
|
+
result = itxn.AssetConfig(
|
|
90
|
+
total=100_000_000_000,
|
|
91
|
+
decimals=2,
|
|
92
|
+
unit_name=b"TKN",
|
|
93
|
+
asset_name=b"My Token",
|
|
94
|
+
manager=Global.current_application_address,
|
|
95
|
+
reserve=Global.current_application_address,
|
|
96
|
+
fee=0
|
|
97
|
+
).submit()
|
|
98
|
+
|
|
99
|
+
return result.created_asset.id
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Create NFT
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
from algopy import ARC4Contract, UInt64, Global, itxn, arc4
|
|
106
|
+
|
|
107
|
+
class MyContract(ARC4Contract):
|
|
108
|
+
@arc4.abimethod
|
|
109
|
+
def create_nft(self) -> UInt64:
|
|
110
|
+
# ARC-3 NFT: total=1, decimals=0
|
|
111
|
+
result = itxn.AssetConfig(
|
|
112
|
+
total=1,
|
|
113
|
+
decimals=0,
|
|
114
|
+
unit_name=b"NFT",
|
|
115
|
+
asset_name=b"My NFT",
|
|
116
|
+
url=b"https://example.com/nft.json",
|
|
117
|
+
manager=Global.current_application_address,
|
|
118
|
+
reserve=Global.current_application_address,
|
|
119
|
+
freeze=Global.current_application_address,
|
|
120
|
+
clawback=Global.current_application_address,
|
|
121
|
+
fee=0
|
|
122
|
+
).submit()
|
|
123
|
+
|
|
124
|
+
return result.created_asset.id
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Call Another Application
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from algopy import ARC4Contract, Application, Bytes, arc4, itxn
|
|
131
|
+
|
|
132
|
+
class MyContract(ARC4Contract):
|
|
133
|
+
@arc4.abimethod
|
|
134
|
+
def call_other_app(self, app: Application) -> arc4.String:
|
|
135
|
+
# Call an ARC-4 method on another app
|
|
136
|
+
result = itxn.ApplicationCall(
|
|
137
|
+
app_id=app,
|
|
138
|
+
app_args=(
|
|
139
|
+
arc4.arc4_signature("hello(string)string"),
|
|
140
|
+
arc4.String("World")
|
|
141
|
+
),
|
|
142
|
+
fee=0
|
|
143
|
+
).submit()
|
|
144
|
+
|
|
145
|
+
# Extract return value from logs
|
|
146
|
+
return arc4.String.from_log(result.last_log)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Deploy Another Contract
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
from algopy import ARC4Contract, UInt64, arc4, itxn, compile_contract
|
|
153
|
+
from .other_contract import OtherContract
|
|
154
|
+
|
|
155
|
+
class MyContract(ARC4Contract):
|
|
156
|
+
@arc4.abimethod
|
|
157
|
+
def deploy_contract(self) -> UInt64:
|
|
158
|
+
# Compile and deploy another contract
|
|
159
|
+
compiled = compile_contract(OtherContract)
|
|
160
|
+
|
|
161
|
+
result = itxn.ApplicationCall(
|
|
162
|
+
approval_program=compiled.approval_program,
|
|
163
|
+
clear_state_program=compiled.clear_state_program,
|
|
164
|
+
fee=0
|
|
165
|
+
).submit()
|
|
166
|
+
|
|
167
|
+
return result.created_app.id
|
|
168
|
+
|
|
169
|
+
@arc4.abimethod
|
|
170
|
+
def deploy_with_arc4(self) -> UInt64:
|
|
171
|
+
# Simpler: use arc4.arc4_create
|
|
172
|
+
result = arc4.arc4_create(OtherContract)
|
|
173
|
+
return result.created_app.id
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Grouped Inner Transactions
|
|
177
|
+
|
|
178
|
+
Submit multiple inner transactions atomically using `itxn.submit_txns()`.
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
from algopy import ARC4Contract, Application, UInt64, Txn, arc4, itxn
|
|
182
|
+
|
|
183
|
+
class MyContract(ARC4Contract):
|
|
184
|
+
@arc4.abimethod
|
|
185
|
+
def multi_txn(self, app: Application) -> tuple[UInt64, arc4.String]:
|
|
186
|
+
# Create transaction parameters (not submitted yet)
|
|
187
|
+
payment_params = itxn.Payment(
|
|
188
|
+
amount=5000,
|
|
189
|
+
receiver=Txn.sender,
|
|
190
|
+
fee=0
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
app_call_params = itxn.ApplicationCall(
|
|
194
|
+
app_id=app,
|
|
195
|
+
app_args=(
|
|
196
|
+
arc4.arc4_signature("hello(string)string"),
|
|
197
|
+
arc4.String("World")
|
|
198
|
+
),
|
|
199
|
+
fee=0
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
# Submit both atomically
|
|
203
|
+
pay_txn, app_txn = itxn.submit_txns(payment_params, app_call_params)
|
|
204
|
+
|
|
205
|
+
# Access results
|
|
206
|
+
hello_result = arc4.String.from_log(app_txn.last_log)
|
|
207
|
+
return pay_txn.amount, hello_result
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Inner Transactions in Loops
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
from algopy import ARC4Contract, Account, UInt64, itxn, arc4
|
|
214
|
+
|
|
215
|
+
class MyContract(ARC4Contract):
|
|
216
|
+
@arc4.abimethod
|
|
217
|
+
def distribute(self, receivers: tuple[Account, Account, Account]) -> None:
|
|
218
|
+
for receiver in receivers:
|
|
219
|
+
itxn.Payment(
|
|
220
|
+
amount=UInt64(1_000_000),
|
|
221
|
+
receiver=receiver,
|
|
222
|
+
fee=0
|
|
223
|
+
).submit()
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Inner Transaction Result Properties
|
|
227
|
+
|
|
228
|
+
### Payment Result
|
|
229
|
+
|
|
230
|
+
| Property | Type | Description |
|
|
231
|
+
|----------|------|-------------|
|
|
232
|
+
| `amount` | `UInt64` | Payment amount in microAlgos |
|
|
233
|
+
| `receiver` | `Account` | Payment receiver |
|
|
234
|
+
| `close_remainder_to` | `Account` | Close remainder to address |
|
|
235
|
+
| `sender` | `Account` | Transaction sender |
|
|
236
|
+
| `txn_id` | `Bytes` | Transaction ID |
|
|
237
|
+
|
|
238
|
+
### Asset Config Result
|
|
239
|
+
|
|
240
|
+
| Property | Type | Description |
|
|
241
|
+
|----------|------|-------------|
|
|
242
|
+
| `created_asset` | `Asset` | Newly created asset |
|
|
243
|
+
| `config_asset` | `Asset` | Configured asset |
|
|
244
|
+
| `txn_id` | `Bytes` | Transaction ID |
|
|
245
|
+
|
|
246
|
+
### Application Call Result
|
|
247
|
+
|
|
248
|
+
| Property | Type | Description |
|
|
249
|
+
|----------|------|-------------|
|
|
250
|
+
| `created_app` | `Application` | Newly created application |
|
|
251
|
+
| `last_log` | `Bytes` | Last log entry |
|
|
252
|
+
| `logs(index)` | `Bytes` | Log entry at index |
|
|
253
|
+
| `txn_id` | `Bytes` | Transaction ID |
|
|
254
|
+
|
|
255
|
+
### Asset Transfer Result
|
|
256
|
+
|
|
257
|
+
| Property | Type | Description |
|
|
258
|
+
|----------|------|-------------|
|
|
259
|
+
| `asset_amount` | `UInt64` | Amount transferred |
|
|
260
|
+
| `asset_receiver` | `Account` | Asset receiver |
|
|
261
|
+
| `xfer_asset` | `Asset` | Transferred asset |
|
|
262
|
+
| `txn_id` | `Bytes` | Transaction ID |
|
|
263
|
+
|
|
264
|
+
## Group Transactions
|
|
265
|
+
|
|
266
|
+
Access other transactions in an atomic group.
|
|
267
|
+
|
|
268
|
+
### As ABI Method Parameters
|
|
269
|
+
|
|
270
|
+
```python
|
|
271
|
+
from algopy import ARC4Contract, arc4, gtxn, Txn, Global
|
|
272
|
+
|
|
273
|
+
class MyContract(ARC4Contract):
|
|
274
|
+
@arc4.abimethod
|
|
275
|
+
def process_payment(self, payment: gtxn.PaymentTransaction) -> None:
|
|
276
|
+
# Verify payment is to this app
|
|
277
|
+
assert payment.receiver == Global.current_application_address
|
|
278
|
+
assert payment.amount >= 1_000_000
|
|
279
|
+
|
|
280
|
+
# Process the payment...
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### By Group Index
|
|
284
|
+
|
|
285
|
+
```python
|
|
286
|
+
from algopy import ARC4Contract, arc4, gtxn, UInt64, Global
|
|
287
|
+
|
|
288
|
+
class MyContract(ARC4Contract):
|
|
289
|
+
@arc4.abimethod
|
|
290
|
+
def verify_group(self) -> None:
|
|
291
|
+
# Access transaction at specific index
|
|
292
|
+
pay_txn = gtxn.PaymentTransaction(0)
|
|
293
|
+
|
|
294
|
+
assert pay_txn.receiver == Global.current_application_address
|
|
295
|
+
assert pay_txn.amount >= UInt64(1_000_000)
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Untyped Access
|
|
299
|
+
|
|
300
|
+
```python
|
|
301
|
+
from algopy import ARC4Contract, arc4, gtxn, UInt64
|
|
302
|
+
|
|
303
|
+
class MyContract(ARC4Contract):
|
|
304
|
+
@arc4.abimethod
|
|
305
|
+
def check_any_txn(self, index: UInt64) -> None:
|
|
306
|
+
# Access any transaction type
|
|
307
|
+
txn = gtxn.Transaction(index)
|
|
308
|
+
|
|
309
|
+
# Access common properties
|
|
310
|
+
sender = txn.sender
|
|
311
|
+
fee = txn.fee
|
|
312
|
+
txn_type = txn.type
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
## Group Transaction Types
|
|
316
|
+
|
|
317
|
+
| Type | Usage |
|
|
318
|
+
|------|-------|
|
|
319
|
+
| `gtxn.PaymentTransaction` | Payment transactions |
|
|
320
|
+
| `gtxn.AssetTransferTransaction` | Asset transfers |
|
|
321
|
+
| `gtxn.AssetConfigTransaction` | Asset configuration |
|
|
322
|
+
| `gtxn.AssetFreezeTransaction` | Asset freeze/unfreeze |
|
|
323
|
+
| `gtxn.ApplicationCallTransaction` | App calls |
|
|
324
|
+
| `gtxn.KeyRegistrationTransaction` | Key registration |
|
|
325
|
+
| `gtxn.Transaction` | Any transaction type |
|
|
326
|
+
|
|
327
|
+
## Fee Pooling (CRITICAL)
|
|
328
|
+
|
|
329
|
+
Always set `fee=0` for inner transactions. The caller covers all fees through fee pooling.
|
|
330
|
+
|
|
331
|
+
```python
|
|
332
|
+
# CORRECT - Caller covers fees
|
|
333
|
+
itxn.Payment(
|
|
334
|
+
amount=1000,
|
|
335
|
+
receiver=Txn.sender,
|
|
336
|
+
fee=0 # Always 0
|
|
337
|
+
).submit()
|
|
338
|
+
|
|
339
|
+
# INCORRECT - App pays fees (security risk!)
|
|
340
|
+
itxn.Payment(
|
|
341
|
+
amount=1000,
|
|
342
|
+
receiver=Txn.sender,
|
|
343
|
+
fee=1000 # Don't do this!
|
|
344
|
+
).submit()
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
**Why?** If inner transactions specify non-zero fees, malicious callers can drain the app account by repeatedly invoking methods that execute inner transactions.
|
|
348
|
+
|
|
349
|
+
## Common Mistakes
|
|
350
|
+
|
|
351
|
+
### Forgetting Fee Pooling
|
|
352
|
+
|
|
353
|
+
```python
|
|
354
|
+
# INCORRECT - App pays fee (vulnerability!)
|
|
355
|
+
@arc4.abimethod
|
|
356
|
+
def bad_payment(self) -> None:
|
|
357
|
+
itxn.Payment(
|
|
358
|
+
amount=1000,
|
|
359
|
+
receiver=Txn.sender
|
|
360
|
+
# fee defaults to minimum, draining app account
|
|
361
|
+
).submit()
|
|
362
|
+
|
|
363
|
+
# CORRECT - Caller pays via fee pooling
|
|
364
|
+
@arc4.abimethod
|
|
365
|
+
def good_payment(self) -> None:
|
|
366
|
+
itxn.Payment(
|
|
367
|
+
amount=1000,
|
|
368
|
+
receiver=Txn.sender,
|
|
369
|
+
fee=0 # Explicit: caller covers
|
|
370
|
+
).submit()
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Not Checking Group Transaction Properties
|
|
374
|
+
|
|
375
|
+
```python
|
|
376
|
+
# INCORRECT - Trusts payment without verification
|
|
377
|
+
@arc4.abimethod
|
|
378
|
+
def accept_payment(self, payment: gtxn.PaymentTransaction) -> None:
|
|
379
|
+
self.balance += payment.amount # Who received it?
|
|
380
|
+
|
|
381
|
+
# CORRECT - Verify payment destination
|
|
382
|
+
@arc4.abimethod
|
|
383
|
+
def accept_payment_safe(self, payment: gtxn.PaymentTransaction) -> None:
|
|
384
|
+
assert payment.receiver == Global.current_application_address
|
|
385
|
+
self.balance += payment.amount
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Wrong Sender for Inner Transactions
|
|
389
|
+
|
|
390
|
+
```python
|
|
391
|
+
# Inner transactions are always sent from the app address
|
|
392
|
+
# The sender is automatically Global.current_application_address
|
|
393
|
+
# You cannot send from arbitrary accounts (unless rekeyed to app)
|
|
394
|
+
|
|
395
|
+
# CORRECT - Default sender is app address
|
|
396
|
+
itxn.Payment(
|
|
397
|
+
amount=1000,
|
|
398
|
+
receiver=some_account,
|
|
399
|
+
fee=0
|
|
400
|
+
).submit()
|
|
401
|
+
|
|
402
|
+
# To send from a different account, it must be rekeyed to app
|
|
403
|
+
itxn.Payment(
|
|
404
|
+
sender=rekeyed_account, # Must be rekeyed to app address
|
|
405
|
+
amount=1000,
|
|
406
|
+
receiver=some_account,
|
|
407
|
+
fee=0
|
|
408
|
+
).submit()
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### Not Funding App Before Inner Transactions
|
|
412
|
+
|
|
413
|
+
```python
|
|
414
|
+
# Inner transactions require the app account to have sufficient balance
|
|
415
|
+
# Fund the app account before executing inner transactions
|
|
416
|
+
|
|
417
|
+
# In deploy script:
|
|
418
|
+
algorand.send.payment(
|
|
419
|
+
sender=deployer.address,
|
|
420
|
+
receiver=app_client.app_address,
|
|
421
|
+
amount=1_000_000 # Fund app with 1 Algo
|
|
422
|
+
)
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Using Wrong Index for Group Transactions
|
|
426
|
+
|
|
427
|
+
```python
|
|
428
|
+
# INCORRECT - Hardcoded index may be wrong
|
|
429
|
+
@arc4.abimethod
|
|
430
|
+
def process(self) -> None:
|
|
431
|
+
payment = gtxn.PaymentTransaction(0) # Assumes payment is first
|
|
432
|
+
|
|
433
|
+
# CORRECT - Use transaction parameter
|
|
434
|
+
@arc4.abimethod
|
|
435
|
+
def process_safe(self, payment: gtxn.PaymentTransaction) -> None:
|
|
436
|
+
# ARC-4 router handles finding the correct transaction
|
|
437
|
+
assert payment.receiver == Global.current_application_address
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
## References
|
|
441
|
+
|
|
442
|
+
- [Algorand Python Transactions](https://dev.algorand.co/algokit/languages/python/lg-transactions/)
|
|
443
|
+
- [Inner Transactions Concepts](https://dev.algorand.co/concepts/smart-contracts/inner-txn/)
|
|
444
|
+
- [algopy.itxn API](https://dev.algorand.co/reference/algorand-python/api/api-algopyitxn/)
|
|
445
|
+
- [algopy.gtxn API](https://dev.algorand.co/reference/algorand-python/api/api-algopygtxn/)
|