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,58 @@
|
|
|
1
|
+
# Algorand TypeScript Reference Index
|
|
2
|
+
|
|
3
|
+
This skill includes detailed reference files for specific topics. Consult these when you need in-depth guidance.
|
|
4
|
+
|
|
5
|
+
## Reference Files
|
|
6
|
+
|
|
7
|
+
| File | Topics Covered |
|
|
8
|
+
|------|----------------|
|
|
9
|
+
| [types-and-values.md](./types-and-values.md) | AVM types, `uint64`, `bytes`, `clone()`, value semantics |
|
|
10
|
+
| [storage.md](./storage.md) | GlobalState, LocalState, BoxMap, Box, MBR funding |
|
|
11
|
+
| [methods-and-abi.md](./methods-and-abi.md) | Decorators, lifecycle methods, visibility, return types |
|
|
12
|
+
| [transactions.md](./transactions.md) | Group transactions (gtxn), inner transactions (itxn) |
|
|
13
|
+
|
|
14
|
+
## Quick Reference
|
|
15
|
+
|
|
16
|
+
### Types
|
|
17
|
+
|
|
18
|
+
| AVM Type | TypeScript | Creation |
|
|
19
|
+
|----------|------------|----------|
|
|
20
|
+
| uint64 | `uint64` | `Uint64(n)` |
|
|
21
|
+
| bytes | `bytes` | `Bytes('...')` |
|
|
22
|
+
| string | `string` | Native string |
|
|
23
|
+
| bool | `boolean` | Native boolean |
|
|
24
|
+
| biguint | `biguint` | `BigUint(n)` |
|
|
25
|
+
|
|
26
|
+
### Storage
|
|
27
|
+
|
|
28
|
+
| Type | Scope | Use Case |
|
|
29
|
+
|------|-------|----------|
|
|
30
|
+
| GlobalState | App-wide | Single values, app config |
|
|
31
|
+
| LocalState | Per-account | User-specific data |
|
|
32
|
+
| BoxMap | App-wide | Key-value storage, large data |
|
|
33
|
+
| Box | App-wide | Single large value |
|
|
34
|
+
|
|
35
|
+
### Method Decorators
|
|
36
|
+
|
|
37
|
+
| Decorator | Purpose |
|
|
38
|
+
|-----------|---------|
|
|
39
|
+
| `@abimethod()` | ABI-callable method |
|
|
40
|
+
| `@abimethod({ readonly: true })` | Read-only method |
|
|
41
|
+
| `@baremethod({ onCreate: 'require' })` | Create handler |
|
|
42
|
+
| `@baremethod({ onUpdate: 'require' })` | Update handler |
|
|
43
|
+
|
|
44
|
+
### Clone Rules
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
// Always clone when:
|
|
48
|
+
const value = clone(this.storage.value) // Reading from storage
|
|
49
|
+
this.storage.value = clone(updated) // Writing to storage
|
|
50
|
+
for (const item of clone(array)) { } // Iterating arrays
|
|
51
|
+
const copy = clone(original) // Copying complex types
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## External Documentation
|
|
55
|
+
|
|
56
|
+
- [Algorand TypeScript Documentation](https://dev.algorand.co/docs/get-started/algokit/typescript)
|
|
57
|
+
- [Puya Compiler](https://github.com/algorandfoundation/puya-ts)
|
|
58
|
+
- [AVM Specification](https://developer.algorand.org/docs/get-details/dapps/avm/)
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Storage Patterns
|
|
2
|
+
|
|
3
|
+
## Storage Types Overview
|
|
4
|
+
|
|
5
|
+
| Storage | Scope | Who Pays MBR | Use Case |
|
|
6
|
+
|---------|-------|--------------|----------|
|
|
7
|
+
| `GlobalState` | App-wide | App account | Shared app data |
|
|
8
|
+
| `LocalState` | Per-user | User (on opt-in) | Per-user data with opt-in |
|
|
9
|
+
| `Box` | App-wide | App account | Large data, single key |
|
|
10
|
+
| `BoxMap` | Per-key | App account | Per-user data without opt-in |
|
|
11
|
+
|
|
12
|
+
## Storage Access Patterns
|
|
13
|
+
|
|
14
|
+
### GlobalState
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
import { GlobalState, clone } from '@algorandfoundation/algorand-typescript'
|
|
18
|
+
|
|
19
|
+
export class MyContract extends Contract {
|
|
20
|
+
appState = GlobalState<MyData>({ key: 'state' })
|
|
21
|
+
|
|
22
|
+
public updateState(amount: uint64): void {
|
|
23
|
+
const state = clone(this.appState.value)
|
|
24
|
+
const updated = clone(state)
|
|
25
|
+
updated.counter = updated.counter + amount
|
|
26
|
+
this.appState.value = clone(updated)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### LocalState (Requires Opt-in)
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { LocalState, Txn, clone } from '@algorandfoundation/algorand-typescript'
|
|
35
|
+
|
|
36
|
+
export class MyContract extends Contract {
|
|
37
|
+
userData = LocalState<UserData>({ key: 'user' })
|
|
38
|
+
|
|
39
|
+
public optInToApplication(): void {
|
|
40
|
+
const initial: UserData = { balance: Uint64(0) }
|
|
41
|
+
this.userData(Txn.sender).value = clone(initial)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public getBalance(): uint64 {
|
|
45
|
+
return clone(this.userData(Txn.sender).value).balance
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### BoxMap (No Opt-in Required)
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { BoxMap, Account, clone } from '@algorandfoundation/algorand-typescript'
|
|
54
|
+
|
|
55
|
+
export class MyContract extends Contract {
|
|
56
|
+
userBoxes = BoxMap<Account, UserData>({ keyPrefix: 'u' })
|
|
57
|
+
|
|
58
|
+
public createUser(user: Account): void {
|
|
59
|
+
const initial: UserData = { balance: Uint64(0) }
|
|
60
|
+
this.userBoxes(user).value = clone(initial)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Box Storage MBR (CRITICAL)
|
|
66
|
+
|
|
67
|
+
Box storage increases the app account's Minimum Balance Requirement (MBR). The app account must be funded BEFORE boxes can be created.
|
|
68
|
+
|
|
69
|
+
### MBR Formula
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
(2500 per box) + (400 * (box size + key size)) microAlgos per box
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Funding Pattern in deploy-config.ts
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { AlgorandClient } from '@algorandfoundation/algokit-utils'
|
|
79
|
+
import { MyAppFactory } from '../artifacts/my_app/MyAppClient'
|
|
80
|
+
|
|
81
|
+
export async function deploy() {
|
|
82
|
+
const algorand = AlgorandClient.fromEnvironment()
|
|
83
|
+
const deployer = await algorand.account.fromEnvironment('DEPLOYER')
|
|
84
|
+
|
|
85
|
+
const factory = algorand.client.getTypedAppFactory(MyAppFactory, {
|
|
86
|
+
defaultSender: deployer.addr,
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
const { appClient, result } = await factory.deploy({
|
|
90
|
+
onUpdate: 'append',
|
|
91
|
+
onSchemaBreak: 'append'
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
// MANDATORY: Fund app account if BoxMap is used
|
|
95
|
+
if (['create', 'replace'].includes(result.operationPerformed)) {
|
|
96
|
+
await algorand.send.payment({
|
|
97
|
+
amount: (1).algo(), // Fund app account for box MBR
|
|
98
|
+
sender: deployer.addr,
|
|
99
|
+
receiver: appClient.appAddress,
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Note**: `operationPerformed` values are `'create'`, `'replace'`, `'update'`, or `'append'`. Only fund on `'create'` or `'replace'` to avoid redundant funding.
|
|
106
|
+
|
|
107
|
+
### Testing with BoxMap
|
|
108
|
+
|
|
109
|
+
E2E tests using BoxMap require app account funding before first box operation:
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
// Fund immediately after deployment in test setup
|
|
113
|
+
const { client } = await deploy(testAccount)
|
|
114
|
+
|
|
115
|
+
await localnet.algorand.send.payment({
|
|
116
|
+
amount: (1).algo(),
|
|
117
|
+
sender: testAccount,
|
|
118
|
+
receiver: client.appAddress,
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
// Now box operations will work
|
|
122
|
+
await client.send.createUser({ args: [userAccount] })
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Choosing Storage Type
|
|
126
|
+
|
|
127
|
+
| Scenario | Recommended | Reason |
|
|
128
|
+
|----------|-------------|--------|
|
|
129
|
+
| Per-user data, user pays | `LocalState` | User covers MBR on opt-in |
|
|
130
|
+
| Per-user data, app pays | `BoxMap` | No opt-in needed, app funds MBR |
|
|
131
|
+
| Shared app config | `GlobalState` | Simple, always available |
|
|
132
|
+
| Large data (>128 bytes) | `Box` or `BoxMap` | No size limits like GlobalState |
|
|
133
|
+
|
|
134
|
+
## Class Properties
|
|
135
|
+
|
|
136
|
+
Cannot define class properties or constants. Only storage proxies allowed on contract classes:
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
// CORRECT: Module-level constants
|
|
140
|
+
const MAX_ITEMS: uint64 = Uint64(100)
|
|
141
|
+
|
|
142
|
+
class MyContract extends Contract {
|
|
143
|
+
items = GlobalState<ItemList>({ key: 'items' }) // OK: storage proxy
|
|
144
|
+
|
|
145
|
+
public addItem(): void {
|
|
146
|
+
// Use MAX_ITEMS here
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// INCORRECT: Class properties
|
|
151
|
+
class MyContract extends Contract {
|
|
152
|
+
private readonly MAX_ITEMS: uint64 = Uint64(100) // Compiler error
|
|
153
|
+
}
|
|
154
|
+
```
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# Group and Inner Transactions
|
|
2
|
+
|
|
3
|
+
## Group Transactions (gtxn)
|
|
4
|
+
|
|
5
|
+
Access group transactions using typed `gtxn` functions with `uint64` indices:
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { gtxn, Global, Uint64 } from '@algorandfoundation/algorand-typescript'
|
|
9
|
+
|
|
10
|
+
// Verify group size
|
|
11
|
+
assert(Global.groupSize === Uint64(3), 'Must be group of 3 transactions')
|
|
12
|
+
|
|
13
|
+
// Access group transactions using typed functions
|
|
14
|
+
const assetTransfer = gtxn.AssetTransferTxn(Uint64(0)) // First transaction
|
|
15
|
+
const payment = gtxn.PaymentTxn(Uint64(1)) // Second transaction
|
|
16
|
+
|
|
17
|
+
// Verify transaction properties
|
|
18
|
+
assert(assetTransfer.sender.bytes === sellerBytes, 'Asset must come from seller')
|
|
19
|
+
assert(assetTransfer.assetReceiver.bytes === buyer.bytes, 'Asset must go to buyer')
|
|
20
|
+
assert(assetTransfer.xferAsset === asset, 'Asset ID mismatch')
|
|
21
|
+
assert(payment.amount === listing.price, 'Payment amount mismatch')
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**INCORRECT**: `const txn = gtxn[0]` — array indexing doesn't work.
|
|
25
|
+
|
|
26
|
+
### Available Typed Functions
|
|
27
|
+
|
|
28
|
+
| Function | Transaction Type |
|
|
29
|
+
|----------|------------------|
|
|
30
|
+
| `gtxn.PaymentTxn(n)` | Payment |
|
|
31
|
+
| `gtxn.AssetTransferTxn(n)` | Asset transfer |
|
|
32
|
+
| `gtxn.AssetConfigTxn(n)` | Asset configuration |
|
|
33
|
+
| `gtxn.ApplicationCallTxn(n)` | Application call |
|
|
34
|
+
| `gtxn.AssetFreezeTxn(n)` | Asset freeze |
|
|
35
|
+
| `gtxn.KeyRegistrationTxn(n)` | Key registration |
|
|
36
|
+
| `gtxn.Transaction(n)` | Untyped access |
|
|
37
|
+
|
|
38
|
+
## Inner Transactions (itxn)
|
|
39
|
+
|
|
40
|
+
### Method Selector Helper
|
|
41
|
+
|
|
42
|
+
Use `methodSelector` to get the 4-byte ARC-4 method selector for inner app calls:
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import { methodSelector } from '@algorandfoundation/algorand-typescript/arc4'
|
|
46
|
+
|
|
47
|
+
// Get selector from method signature
|
|
48
|
+
const selector = methodSelector('transfer(address,uint64)void')
|
|
49
|
+
|
|
50
|
+
// Use in inner app call
|
|
51
|
+
itxn.applicationCall({
|
|
52
|
+
appId: targetApp,
|
|
53
|
+
appArgs: [selector, encodedArg1, encodedArg2],
|
|
54
|
+
fee: Uint64(0),
|
|
55
|
+
}).submit()
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Method Names
|
|
59
|
+
|
|
60
|
+
Inner transaction methods use **lowercase**:
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import { itxn, Global, Uint64 } from '@algorandfoundation/algorand-typescript'
|
|
64
|
+
|
|
65
|
+
// CORRECT - lowercase
|
|
66
|
+
itxn.payment({ ... }).submit()
|
|
67
|
+
itxn.assetTransfer({ ... }).submit()
|
|
68
|
+
|
|
69
|
+
// INCORRECT
|
|
70
|
+
itxn.Payment({ ... }) // Wrong case
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Application Address
|
|
74
|
+
|
|
75
|
+
Use `Global.currentApplicationAddress`, not `this.appAddress()`:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
// CORRECT
|
|
79
|
+
assert(payment.receiver.bytes === Global.currentApplicationAddress.bytes)
|
|
80
|
+
|
|
81
|
+
// INCORRECT
|
|
82
|
+
assert(payment.receiver.bytes === this.appAddress().bytes)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Account from Bytes
|
|
86
|
+
|
|
87
|
+
When storing Account as bytes and converting back for inner transactions:
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
// Store Account as bytes (required for GlobalState/BoxMap)
|
|
91
|
+
const escrow = clone(this.escrow.value)
|
|
92
|
+
const sellerBytes = escrow.seller // bytes stored in state
|
|
93
|
+
|
|
94
|
+
// Convert bytes to Account for inner transaction
|
|
95
|
+
itxn.payment({
|
|
96
|
+
receiver: Account(sellerBytes), // Convert bytes to Account
|
|
97
|
+
amount: escrow.amount,
|
|
98
|
+
fee: Uint64(0),
|
|
99
|
+
}).submit()
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Fee Pooling (CRITICAL)
|
|
103
|
+
|
|
104
|
+
Always set `fee: Uint64(0)` for inner transactions. The app call sender covers fees through fee pooling:
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
itxn.payment({
|
|
108
|
+
receiver: Account(sellerBytes),
|
|
109
|
+
amount: escrow.amount,
|
|
110
|
+
fee: Uint64(0), // Caller covers fees
|
|
111
|
+
}).submit()
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
This prevents app account drain attacks where malicious callers force the app to pay fees.
|
|
115
|
+
|
|
116
|
+
### Composing Multiple Inner Transactions
|
|
117
|
+
|
|
118
|
+
Use `itxnCompose` for submitting multiple inner transactions atomically:
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import { itxn, itxnCompose, Global, Account, Uint64 } from '@algorandfoundation/algorand-typescript'
|
|
122
|
+
|
|
123
|
+
// Submit multiple inner transactions as a group
|
|
124
|
+
itxnCompose.begin()
|
|
125
|
+
|
|
126
|
+
itxnCompose.next(
|
|
127
|
+
itxn.payment({
|
|
128
|
+
receiver: Account(recipientBytes),
|
|
129
|
+
amount: Uint64(100_000),
|
|
130
|
+
fee: Uint64(0),
|
|
131
|
+
})
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
itxnCompose.next(
|
|
135
|
+
itxn.assetTransfer({
|
|
136
|
+
xferAsset: assetId,
|
|
137
|
+
assetReceiver: Account(recipientBytes),
|
|
138
|
+
assetAmount: Uint64(50),
|
|
139
|
+
fee: Uint64(0),
|
|
140
|
+
})
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
itxnCompose.submit()
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Note**: `itxnCompose.begin()` starts a new group, `itxnCompose.next()` adds transactions, and `itxnCompose.submit()` sends them all atomically.
|
|
147
|
+
|
|
148
|
+
## Asset Type
|
|
149
|
+
|
|
150
|
+
`Asset` constructor takes `uint64` directly:
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
// CORRECT
|
|
154
|
+
const asset = Asset(assetId) // if assetId is already uint64
|
|
155
|
+
const asset = Asset(Uint64(123))
|
|
156
|
+
|
|
157
|
+
// INCORRECT
|
|
158
|
+
const asset = Asset(123) // number literal
|
|
159
|
+
const asset = Asset({ id: 123 }) // object
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Complete Example: Escrow Release
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
import {
|
|
166
|
+
itxn, gtxn, Global, Account, Uint64, assert, clone
|
|
167
|
+
} from '@algorandfoundation/algorand-typescript'
|
|
168
|
+
|
|
169
|
+
public releaseEscrow(): void {
|
|
170
|
+
const escrow = clone(this.escrowData.value)
|
|
171
|
+
|
|
172
|
+
// Verify the payment came to app
|
|
173
|
+
assert(Global.groupSize === Uint64(2), 'Expected 2 txns')
|
|
174
|
+
const payment = gtxn.PaymentTxn(Uint64(0))
|
|
175
|
+
assert(
|
|
176
|
+
payment.receiver.bytes === Global.currentApplicationAddress.bytes,
|
|
177
|
+
'Payment must go to app'
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
// Send funds to seller via inner transaction
|
|
181
|
+
itxn.payment({
|
|
182
|
+
receiver: Account(escrow.seller),
|
|
183
|
+
amount: escrow.amount,
|
|
184
|
+
fee: Uint64(0), // Fee pooling
|
|
185
|
+
}).submit()
|
|
186
|
+
}
|
|
187
|
+
```
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# AVM Types and Value Semantics
|
|
2
|
+
|
|
3
|
+
## Basic AVM Types
|
|
4
|
+
|
|
5
|
+
| Type | Description | Constructor |
|
|
6
|
+
|------|-------------|-------------|
|
|
7
|
+
| `uint64` | 64-bit unsigned integer | `Uint64()` |
|
|
8
|
+
| `bytes` | Byte array | `Bytes()` |
|
|
9
|
+
| `bigint` | Up to 512-bit unsigned integer | `BigUInt` |
|
|
10
|
+
| `string` | UTF-8 string | Native strings |
|
|
11
|
+
| `bool` | Boolean | `true`/`false` |
|
|
12
|
+
|
|
13
|
+
## Type Mappings
|
|
14
|
+
|
|
15
|
+
AVM types don't map to JavaScript primitives:
|
|
16
|
+
- JavaScript `number` is signed and unbounded; AVM `uint64` is 64-bit unsigned
|
|
17
|
+
- JavaScript `Uint8Array`/`ArrayBuffer` don't exist; use `bytes`
|
|
18
|
+
|
|
19
|
+
## Objects and Arrays
|
|
20
|
+
|
|
21
|
+
**Plain TypeScript objects** are supported and mutable:
|
|
22
|
+
```typescript
|
|
23
|
+
type Point = { x: uint64; y: uint64 }
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Plain TypeScript arrays** are supported and mutable:
|
|
27
|
+
```typescript
|
|
28
|
+
const values: uint64[] = []
|
|
29
|
+
const items: Array<uint64> = []
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Prefer native types over ARC4**: Plain objects and arrays are more efficient for computations and mutations than `arc4.StaticArray`, `arc4.DynamicArray`, `arc4.Struct`.
|
|
33
|
+
|
|
34
|
+
## Numbers (CRITICAL)
|
|
35
|
+
|
|
36
|
+
Puya rejects JavaScript `number` entirely:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// CORRECT
|
|
40
|
+
const amount: uint64 = Uint64(20)
|
|
41
|
+
const total: uint64 = amount + Uint64(100)
|
|
42
|
+
return this.counter.value // Safe if already uint64
|
|
43
|
+
|
|
44
|
+
// INCORRECT - Compiler error
|
|
45
|
+
const amount = 20
|
|
46
|
+
const total = amount + 100
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Arithmetic type inference**: Explicitly type results to avoid inference as `number`:
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
// CORRECT
|
|
53
|
+
const timeout: uint64 = Global.latestTimestamp + timeoutSeconds
|
|
54
|
+
|
|
55
|
+
// INCORRECT - May infer as number
|
|
56
|
+
const timeout = Global.latestTimestamp + timeoutSeconds
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## String Operations
|
|
60
|
+
|
|
61
|
+
`string.length` is NOT supported. Use equality checks:
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
// CORRECT
|
|
65
|
+
assert(text !== '', 'Text cannot be empty')
|
|
66
|
+
|
|
67
|
+
// INCORRECT - Compiler error
|
|
68
|
+
assert(text.length > 0, 'Text cannot be empty')
|
|
69
|
+
assert(text.length <= 200, 'Text too long')
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Value Semantics (CRITICAL)
|
|
73
|
+
|
|
74
|
+
AVM uses value semantics only. Use `clone()` for complex types (structs, arrays, objects):
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { clone } from '@algorandfoundation/algorand-typescript'
|
|
78
|
+
|
|
79
|
+
// GlobalState: clone on read, modify, write
|
|
80
|
+
const state = clone(this.appState.value) // Clone on read
|
|
81
|
+
const updated = clone(state) // Clone for modification
|
|
82
|
+
updated.field1 = updated.field1 + amount
|
|
83
|
+
this.appState.value = clone(updated) // Clone on write
|
|
84
|
+
|
|
85
|
+
// Box: reads OK without clone, writes need clone
|
|
86
|
+
const stored = this.someBox.value // Read OK
|
|
87
|
+
const copy = clone(stored) // Clone for modification
|
|
88
|
+
copy.someField = copy.someField + amount
|
|
89
|
+
this.someBox.value = clone(copy) // Clone before write
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**When to clone**:
|
|
93
|
+
- ALWAYS when reading from `GlobalState`, `Box`, `BoxMap`, `LocalState` with complex types
|
|
94
|
+
- ALWAYS when assigning structs/arrays to storage
|
|
95
|
+
- Even if already cloned, clone again when assigning to storage
|
|
96
|
+
|
|
97
|
+
**Exception**: Primitive types (`uint64`, `bytes`, `string`, `bool`) stored directly (not in structs) do NOT require `clone()`.
|
|
98
|
+
|
|
99
|
+
## Union Types (Not Supported)
|
|
100
|
+
|
|
101
|
+
Cannot use union types like `Item | null` or `string | uint64`:
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// CORRECT - Use boolean flags
|
|
105
|
+
let found = false
|
|
106
|
+
let foundItem: Item = { /* default values */ }
|
|
107
|
+
for (const item of clone(items)) {
|
|
108
|
+
if (matches(item)) {
|
|
109
|
+
foundItem = clone(item)
|
|
110
|
+
found = true
|
|
111
|
+
break
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
assert(found, 'Item not found')
|
|
115
|
+
return foundItem
|
|
116
|
+
|
|
117
|
+
// INCORRECT - Compiler error
|
|
118
|
+
let foundItem: Item | null = null
|
|
119
|
+
let value: string | uint64 = someValue
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Array Operations
|
|
123
|
+
|
|
124
|
+
- **AVOID**: `forEach` — use `for...of`
|
|
125
|
+
- **AVOID**: `splice` on dynamic arrays — opcode-heavy
|
|
126
|
+
- **PREFER**: `StaticArray<uint64, N>` for fixed-size arrays
|
|
127
|
+
- **AVOID**: Nested dynamic types (`uint64[][]`) — encode as tuples
|
|
128
|
+
|
|
129
|
+
**Critical array rules**:
|
|
130
|
+
- Functions cannot mutate passed arrays
|
|
131
|
+
- Cannot specify array lengths with square brackets (`number[10]` invalid)
|
|
132
|
+
- Arrays in object literals must be cloned: `{ todos: clone(array) }`
|
|
133
|
+
- Clone arrays before iterating: `for (const item of clone(array))`
|
|
134
|
+
- Loop indices must be `uint64`, not `number`:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
let index = Uint64(0)
|
|
138
|
+
for (const item of clone(array)) {
|
|
139
|
+
// use index
|
|
140
|
+
index = index + Uint64(1)
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Unavailable APIs
|
|
145
|
+
|
|
146
|
+
These JavaScript APIs are NOT supported (AVM constraints):
|
|
147
|
+
- `Uint8Array` / `ArrayBuffer`
|
|
148
|
+
- Object methods (`.keys()`, `.values()`, etc.)
|
|
149
|
+
- Array length via square brackets
|
|
150
|
+
- Standard JavaScript APIs
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
|
|
2
|
+
# Algorand TypeScript Rules
|
|
3
|
+
|
|
4
|
+
Critical syntax rules for Algorand TypeScript (PuyaTs) that prevent compiler errors and runtime failures.
|
|
5
|
+
|
|
6
|
+
**File Extension**: Contract files must use `.algo.ts` extension (e.g., `Counter.algo.ts`).
|
|
7
|
+
|
|
8
|
+
## Overview / Core Workflow
|
|
9
|
+
|
|
10
|
+
1. Identify the syntax issue or pattern needed
|
|
11
|
+
2. Apply the correct AVM-compatible pattern
|
|
12
|
+
3. Use `clone()` for complex types
|
|
13
|
+
4. Verify no union types or JavaScript `number`
|
|
14
|
+
|
|
15
|
+
## How to proceed
|
|
16
|
+
|
|
17
|
+
1. **Check the most critical rules below**
|
|
18
|
+
2. **Consult detailed reference files** for specific topics
|
|
19
|
+
3. **Apply the correct pattern** with proper AVM types
|
|
20
|
+
4. **Build to verify**: `algokit project run build`
|
|
21
|
+
|
|
22
|
+
## Important Rules / Guidelines
|
|
23
|
+
|
|
24
|
+
### Numbers: No JavaScript `number`
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
// CORRECT
|
|
28
|
+
const amount: uint64 = Uint64(20)
|
|
29
|
+
const total: uint64 = amount + Uint64(100)
|
|
30
|
+
|
|
31
|
+
// INCORRECT - Compiler error
|
|
32
|
+
const amount = 20
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Numeric limits**: Algorand TypeScript supports integers up to 2^512. Use `biguint` for values exceeding uint64 (2^64 - 1).
|
|
36
|
+
|
|
37
|
+
### Value Semantics: Always `clone()`
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { clone } from '@algorandfoundation/algorand-typescript'
|
|
41
|
+
|
|
42
|
+
const state = clone(this.appState.value) // Read: clone
|
|
43
|
+
const updated = clone(state) // Modify: clone
|
|
44
|
+
this.appState.value = clone(updated) // Write: clone
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### No Union Types
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
// CORRECT - Use boolean flags
|
|
51
|
+
let found = false
|
|
52
|
+
let foundItem: Item = { /* defaults */ }
|
|
53
|
+
|
|
54
|
+
// INCORRECT - Compiler error
|
|
55
|
+
let foundItem: Item | null = null
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Arrays: Clone Before Iterating
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
// CORRECT
|
|
62
|
+
for (const item of clone(array)) { }
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Common Variations / Edge Cases
|
|
66
|
+
|
|
67
|
+
| Topic | Rule |
|
|
68
|
+
|-------|------|
|
|
69
|
+
| Numbers | Use `uint64` + `Uint64()`, never `number` |
|
|
70
|
+
| Strings | No `.length`; use `text !== ''` for empty check |
|
|
71
|
+
| Storage | Clone on read AND write for complex types |
|
|
72
|
+
| Arrays | Clone before iterating; indices must be `uint64` |
|
|
73
|
+
| Classes | No class properties; use module-level constants |
|
|
74
|
+
| Methods | Public = ABI method; private = subroutine |
|
|
75
|
+
|
|
76
|
+
## References / Further Reading
|
|
77
|
+
|
|
78
|
+
Detailed rules by topic:
|
|
79
|
+
|
|
80
|
+
- [Types and Values](./algorand-typescript-syntax-types-and-values.md) — AVM types, numbers, clone(), value semantics
|
|
81
|
+
- [Storage](./algorand-typescript-syntax-storage.md) — GlobalState, LocalState, BoxMap, MBR funding
|
|
82
|
+
- [Methods and ABI](./algorand-typescript-syntax-methods-and-abi.md) — Decorators, lifecycle methods, visibility
|
|
83
|
+
- [Transactions](./algorand-typescript-syntax-transactions.md) — Group transactions (gtxn), inner transactions (itxn)
|
|
84
|
+
- [Full Reference Index](./algorand-typescript-syntax-reference.md)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# TypeScript Smart Contract Repository and Pattern Reference
|
|
2
|
+
|
|
3
|
+
TypeScript-specific repositories, paths, and patterns for building Algorand smart contracts with PuyaTs.
|
|
4
|
+
|
|
5
|
+
## Priority Repositories
|
|
6
|
+
|
|
7
|
+
### Priority 1: DevPortal Code Examples (TypeScript)
|
|
8
|
+
**Repository:** `algorandfoundation/devportal-code-examples`
|
|
9
|
+
**Path:** `projects/typescript-examples/contracts/`
|
|
10
|
+
|
|
11
|
+
Common contract patterns available:
|
|
12
|
+
- `BoxStorage/` -- Box, BoxMap, BoxRef patterns
|
|
13
|
+
- State management examples (GlobalState, LocalState)
|
|
14
|
+
- ARC-4 method examples
|
|
15
|
+
- Inner transaction patterns
|
|
16
|
+
|
|
17
|
+
Always retrieve corresponding test files alongside contracts.
|
|
18
|
+
|
|
19
|
+
### Priority 2: Puya-TS Compiler Examples
|
|
20
|
+
**Repository:** `algorandfoundation/puya-ts`
|
|
21
|
+
**Path:** `examples/`
|
|
22
|
+
|
|
23
|
+
Key examples:
|
|
24
|
+
- `hello_world_arc4/` -- Basic ARC-4 contract structure
|
|
25
|
+
- `voting/` -- State management and complex logic
|
|
26
|
+
- `amm/` -- Advanced patterns (multi-asset, inner transactions)
|
|
27
|
+
- Search for `itxn` patterns for inner transaction examples
|
|
28
|
+
|
|
29
|
+
### Priority 3: AlgoKit TypeScript Template
|
|
30
|
+
**Repository:** `algorandfoundation/algokit-typescript-template`
|
|
31
|
+
|
|
32
|
+
Provides:
|
|
33
|
+
- Project structure and scaffolding
|
|
34
|
+
- Build and test configuration
|
|
35
|
+
- Integration test patterns with generated clients
|
|
36
|
+
|
|
37
|
+
## Pattern-Specific Lookups
|
|
38
|
+
|
|
39
|
+
| Pattern | Where to Look |
|
|
40
|
+
|---------|--------------|
|
|
41
|
+
| Box storage | `devportal-code-examples/projects/typescript-examples/contracts/BoxStorage/` |
|
|
42
|
+
| Inner transactions | `puya-ts/examples/` (search for itxn usage) |
|
|
43
|
+
| ARC-4 methods | `puya-ts/examples/hello_world_arc4/` |
|
|
44
|
+
| State management | `devportal-code-examples/projects/typescript-examples/contracts/` |
|
|
45
|
+
| AMM / DeFi | `puya-ts/examples/amm/` |
|
|
46
|
+
| Voting | `puya-ts/examples/voting/` |
|
|
47
|
+
|
|
48
|
+
## Supporting Repositories
|
|
49
|
+
|
|
50
|
+
- `algorandfoundation/algokit-cli` -- CLI tool reference
|
|
51
|
+
- `algorandfoundation/algokit-client-generator-ts` -- Generated client patterns
|
|
52
|
+
- `algorandfoundation/algokit-utils-ts` -- Utility functions for deployment and testing
|