@btc-vision/btc-runtime 1.10.8 → 1.10.11
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 +190 -0
- package/README.md +258 -137
- package/SECURITY.md +226 -0
- package/docs/README.md +614 -0
- package/docs/advanced/bitcoin-scripts.md +939 -0
- package/docs/advanced/cross-contract-calls.md +579 -0
- package/docs/advanced/plugins.md +1006 -0
- package/docs/advanced/quantum-resistance.md +660 -0
- package/docs/advanced/signature-verification.md +715 -0
- package/docs/api-reference/blockchain.md +729 -0
- package/docs/api-reference/events.md +642 -0
- package/docs/api-reference/op20.md +902 -0
- package/docs/api-reference/op721.md +819 -0
- package/docs/api-reference/safe-math.md +510 -0
- package/docs/api-reference/storage.md +840 -0
- package/docs/contracts/op-net-base.md +786 -0
- package/docs/contracts/op20-token.md +687 -0
- package/docs/contracts/op20s-signatures.md +614 -0
- package/docs/contracts/op721-nft.md +785 -0
- package/docs/contracts/reentrancy-guard.md +787 -0
- package/docs/core-concepts/blockchain-environment.md +724 -0
- package/docs/core-concepts/decorators.md +466 -0
- package/docs/core-concepts/events.md +652 -0
- package/docs/core-concepts/pointers.md +391 -0
- package/docs/core-concepts/security.md +473 -0
- package/docs/core-concepts/storage-system.md +969 -0
- package/docs/examples/basic-token.md +745 -0
- package/docs/examples/nft-with-reservations.md +1440 -0
- package/docs/examples/oracle-integration.md +1212 -0
- package/docs/examples/stablecoin.md +1180 -0
- package/docs/getting-started/first-contract.md +575 -0
- package/docs/getting-started/installation.md +384 -0
- package/docs/getting-started/project-structure.md +630 -0
- package/docs/storage/memory-maps.md +764 -0
- package/docs/storage/stored-arrays.md +778 -0
- package/docs/storage/stored-maps.md +758 -0
- package/docs/storage/stored-primitives.md +655 -0
- package/docs/types/address.md +773 -0
- package/docs/types/bytes-writer-reader.md +938 -0
- package/docs/types/calldata.md +744 -0
- package/docs/types/safe-math.md +446 -0
- package/package.json +52 -27
- package/runtime/memory/MapOfMap.ts +1 -0
- package/LICENSE.md +0 -21
|
@@ -0,0 +1,902 @@
|
|
|
1
|
+
# OP20 API Reference
|
|
2
|
+
|
|
3
|
+
The `OP20` class implements the fungible token standard, equivalent to ERC20 on Ethereum.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { OP20, OP20InitParameters } from '@btc-vision/btc-runtime/runtime';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## OP20 Architecture
|
|
12
|
+
|
|
13
|
+
```mermaid
|
|
14
|
+
classDiagram
|
|
15
|
+
class OP20 {
|
|
16
|
+
<<abstract>>
|
|
17
|
+
+_totalSupply: StoredU256
|
|
18
|
+
#_maxSupply: StoredU256
|
|
19
|
+
#_decimals: StoredU256
|
|
20
|
+
#_name: StoredString
|
|
21
|
+
#_symbol: StoredString
|
|
22
|
+
#_icon: StoredString
|
|
23
|
+
#balanceOfMap: AddressMemoryMap
|
|
24
|
+
#allowanceMap: MapOfMap~u256~
|
|
25
|
+
#_nonceMap: AddressMemoryMap
|
|
26
|
+
+instantiate(params, skipDeployerVerification) void
|
|
27
|
+
+name(calldata) BytesWriter
|
|
28
|
+
+symbol(calldata) BytesWriter
|
|
29
|
+
+icon(calldata) BytesWriter
|
|
30
|
+
+decimals(calldata) BytesWriter
|
|
31
|
+
+totalSupply(calldata) BytesWriter
|
|
32
|
+
+maximumSupply(calldata) BytesWriter
|
|
33
|
+
+balanceOf(calldata) BytesWriter
|
|
34
|
+
+allowance(calldata) BytesWriter
|
|
35
|
+
+nonceOf(calldata) BytesWriter
|
|
36
|
+
+domainSeparator(calldata) BytesWriter
|
|
37
|
+
+metadata(calldata) BytesWriter
|
|
38
|
+
+transfer(calldata) BytesWriter
|
|
39
|
+
+transferFrom(calldata) BytesWriter
|
|
40
|
+
+safeTransfer(calldata) BytesWriter
|
|
41
|
+
+safeTransferFrom(calldata) BytesWriter
|
|
42
|
+
+increaseAllowance(calldata) BytesWriter
|
|
43
|
+
+decreaseAllowance(calldata) BytesWriter
|
|
44
|
+
+increaseAllowanceBySignature(calldata) BytesWriter
|
|
45
|
+
+decreaseAllowanceBySignature(calldata) BytesWriter
|
|
46
|
+
+burn(calldata) BytesWriter
|
|
47
|
+
#_mint(to, amount) void
|
|
48
|
+
#_burn(from, amount) void
|
|
49
|
+
#_transfer(from, to, amount) void
|
|
50
|
+
#_balanceOf(owner) u256
|
|
51
|
+
#_allowance(owner, spender) u256
|
|
52
|
+
#_increaseAllowance(owner, spender, amount) void
|
|
53
|
+
#_decreaseAllowance(owner, spender, amount) void
|
|
54
|
+
#_spendAllowance(owner, spender, amount) void
|
|
55
|
+
#_safeTransfer(from, to, amount, data) void
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
class ReentrancyGuard {
|
|
59
|
+
<<abstract>>
|
|
60
|
+
#reentrancyLevel: ReentrancyLevel
|
|
61
|
+
#isSelectorExcluded(selector) bool
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
class OP_NET {
|
|
65
|
+
<<abstract>>
|
|
66
|
+
+address: Address
|
|
67
|
+
+contractDeployer: Address
|
|
68
|
+
#emitEvent(event) void
|
|
69
|
+
#onlyDeployer(caller) void
|
|
70
|
+
Note: @method decorator handles routing
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
class MyToken {
|
|
74
|
+
+constructor()
|
|
75
|
+
+onDeployment(calldata) void
|
|
76
|
+
+mint(calldata) BytesWriter
|
|
77
|
+
Note: @method decorator handles routing
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
OP_NET <|-- ReentrancyGuard
|
|
81
|
+
ReentrancyGuard <|-- OP20
|
|
82
|
+
OP20 <|-- MyToken
|
|
83
|
+
|
|
84
|
+
note for OP20 "Fungible token standard\nwith safe math and\nreentrancy protection"
|
|
85
|
+
note for MyToken "Your implementation\nextends OP20"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Class Definition
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
@final
|
|
92
|
+
export class MyToken extends OP20 {
|
|
93
|
+
public constructor() {
|
|
94
|
+
super();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
public override onDeployment(calldata: Calldata): void {
|
|
98
|
+
this.instantiate(new OP20InitParameters(
|
|
99
|
+
maxSupply,
|
|
100
|
+
decimals,
|
|
101
|
+
name,
|
|
102
|
+
symbol,
|
|
103
|
+
icon
|
|
104
|
+
));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Initialization
|
|
110
|
+
|
|
111
|
+
### OP20InitParameters
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
class OP20InitParameters {
|
|
115
|
+
constructor(
|
|
116
|
+
maxSupply: u256,
|
|
117
|
+
decimals: u8,
|
|
118
|
+
name: string,
|
|
119
|
+
symbol: string,
|
|
120
|
+
icon: string = ''
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
| Parameter | Type | Description |
|
|
126
|
+
|-----------|------|-------------|
|
|
127
|
+
| `maxSupply` | `u256` | Maximum token supply (use `u256.Max` for unlimited) |
|
|
128
|
+
| `decimals` | `u8` | Token decimals (max 32) |
|
|
129
|
+
| `name` | `string` | Token name |
|
|
130
|
+
| `symbol` | `string` | Token symbol |
|
|
131
|
+
| `icon` | `string` | Token icon URL (optional, default empty string) |
|
|
132
|
+
|
|
133
|
+
### instantiate
|
|
134
|
+
|
|
135
|
+
Initializes the OP20 token. Must be called in `onDeployment`.
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
public instantiate(params: OP20InitParameters, skipDeployerVerification: boolean = false): void
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
public override onDeployment(calldata: Calldata): void {
|
|
143
|
+
this.instantiate(new OP20InitParameters(
|
|
144
|
+
u256.fromU64(1000000), // Max supply: 1M
|
|
145
|
+
18, // 18 decimals
|
|
146
|
+
'My Token', // Name
|
|
147
|
+
'MTK', // Symbol
|
|
148
|
+
'' // Icon URL (optional)
|
|
149
|
+
));
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**Solidity Comparison:**
|
|
154
|
+
| Solidity (ERC20) | OPNet (OP20) |
|
|
155
|
+
|------------------|--------------|
|
|
156
|
+
| `constructor(string name, string symbol)` | `onDeployment(calldata)` + `instantiate()` |
|
|
157
|
+
|
|
158
|
+
## View Methods
|
|
159
|
+
|
|
160
|
+
All view methods in OP20 take `Calldata` and return `BytesWriter`. The `@method` decorator handles ABI encoding/decoding.
|
|
161
|
+
|
|
162
|
+
### name
|
|
163
|
+
|
|
164
|
+
Returns the token name.
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
@method()
|
|
168
|
+
@returns({ name: 'name', type: ABIDataTypes.STRING })
|
|
169
|
+
public name(calldata: Calldata): BytesWriter
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### symbol
|
|
173
|
+
|
|
174
|
+
Returns the token symbol.
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
@method()
|
|
178
|
+
@returns({ name: 'symbol', type: ABIDataTypes.STRING })
|
|
179
|
+
public symbol(calldata: Calldata): BytesWriter
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### icon
|
|
183
|
+
|
|
184
|
+
Returns the token icon URL.
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
@method()
|
|
188
|
+
@returns({ name: 'icon', type: ABIDataTypes.STRING })
|
|
189
|
+
public icon(calldata: Calldata): BytesWriter
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### decimals
|
|
193
|
+
|
|
194
|
+
Returns the number of decimals.
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
@method()
|
|
198
|
+
@returns({ name: 'decimals', type: ABIDataTypes.UINT8 })
|
|
199
|
+
public decimals(calldata: Calldata): BytesWriter
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### nonceOf
|
|
203
|
+
|
|
204
|
+
Returns the current nonce for an address (used for signature verification).
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
@method({ name: 'owner', type: ABIDataTypes.ADDRESS })
|
|
208
|
+
@returns({ name: 'nonce', type: ABIDataTypes.UINT256 })
|
|
209
|
+
public nonceOf(calldata: Calldata): BytesWriter
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### domainSeparator
|
|
213
|
+
|
|
214
|
+
Returns the EIP-712 domain separator for signature verification.
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
@method()
|
|
218
|
+
@returns({ name: 'domainSeparator', type: ABIDataTypes.BYTES32 })
|
|
219
|
+
public domainSeparator(calldata: Calldata): BytesWriter
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### metadata
|
|
223
|
+
|
|
224
|
+
Returns all token metadata in a single call (name, symbol, icon, decimals, totalSupply, domainSeparator).
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
@method()
|
|
228
|
+
@returns(
|
|
229
|
+
{ name: 'name', type: ABIDataTypes.STRING },
|
|
230
|
+
{ name: 'symbol', type: ABIDataTypes.STRING },
|
|
231
|
+
{ name: 'icon', type: ABIDataTypes.STRING },
|
|
232
|
+
{ name: 'decimals', type: ABIDataTypes.UINT8 },
|
|
233
|
+
{ name: 'totalSupply', type: ABIDataTypes.UINT256 },
|
|
234
|
+
{ name: 'domainSeparator', type: ABIDataTypes.BYTES32 },
|
|
235
|
+
)
|
|
236
|
+
public metadata(calldata: Calldata): BytesWriter
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### totalSupply
|
|
240
|
+
|
|
241
|
+
Returns current total supply.
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
@method()
|
|
245
|
+
@returns({ name: 'totalSupply', type: ABIDataTypes.UINT256 })
|
|
246
|
+
public totalSupply(calldata: Calldata): BytesWriter
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### maximumSupply
|
|
250
|
+
|
|
251
|
+
Returns maximum possible supply.
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
@method()
|
|
255
|
+
@returns({ name: 'maximumSupply', type: ABIDataTypes.UINT256 })
|
|
256
|
+
public maximumSupply(calldata: Calldata): BytesWriter
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### balanceOf
|
|
260
|
+
|
|
261
|
+
Returns balance of an address.
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
@method({ name: 'owner', type: ABIDataTypes.ADDRESS })
|
|
265
|
+
@returns({ name: 'balance', type: ABIDataTypes.UINT256 })
|
|
266
|
+
public balanceOf(calldata: Calldata): BytesWriter
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Internal helper (for use within your contract):
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
protected _balanceOf(owner: Address): u256
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### allowance
|
|
276
|
+
|
|
277
|
+
Returns spending allowance.
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
@method(
|
|
281
|
+
{ name: 'owner', type: ABIDataTypes.ADDRESS },
|
|
282
|
+
{ name: 'spender', type: ABIDataTypes.ADDRESS },
|
|
283
|
+
)
|
|
284
|
+
@returns({ name: 'remaining', type: ABIDataTypes.UINT256 })
|
|
285
|
+
public allowance(calldata: Calldata): BytesWriter
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Internal helper (for use within your contract):
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
protected _allowance(owner: Address, spender: Address): u256
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
**Solidity Comparison:**
|
|
295
|
+
| Solidity (ERC20) | OPNet (OP20) |
|
|
296
|
+
|------------------|--------------|
|
|
297
|
+
| `function name() view returns (string)` | `name(calldata): BytesWriter` |
|
|
298
|
+
| `function balanceOf(address) view returns (uint256)` | `balanceOf(calldata): BytesWriter` |
|
|
299
|
+
| `function allowance(address, address) view returns (uint256)` | `allowance(calldata): BytesWriter` |
|
|
300
|
+
|
|
301
|
+
## Transfer Methods
|
|
302
|
+
|
|
303
|
+
### transfer (Calldata)
|
|
304
|
+
|
|
305
|
+
Transfers tokens from caller to recipient.
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
public transfer(calldata: Calldata): BytesWriter
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
**Calldata format:**
|
|
312
|
+
| Field | Type | Size |
|
|
313
|
+
|-------|------|------|
|
|
314
|
+
| to | Address | 32 bytes |
|
|
315
|
+
| amount | u256 | 32 bytes |
|
|
316
|
+
|
|
317
|
+
**Returns:** Boolean (success)
|
|
318
|
+
|
|
319
|
+
The following diagram shows the complete transfer validation and execution flow:
|
|
320
|
+
|
|
321
|
+
```mermaid
|
|
322
|
+
flowchart LR
|
|
323
|
+
subgraph "Validation"
|
|
324
|
+
Start([Transfer Request]) --> CheckFrom{from == zero?}
|
|
325
|
+
CheckFrom -->|Yes| Revert1[Revert: Invalid sender]
|
|
326
|
+
CheckFrom -->|No| CheckTo{to == zero?}
|
|
327
|
+
CheckTo -->|Yes| Revert2[Revert: Invalid receiver]
|
|
328
|
+
CheckTo -->|No| GetBal[Get balance of from]
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
subgraph "Balance Check"
|
|
332
|
+
GetBal --> CheckBal{balance >= amount?}
|
|
333
|
+
CheckBal -->|No| Revert3[Revert: Insufficient balance]
|
|
334
|
+
CheckBal -->|Yes| SubBal[balance from -= amount]
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
subgraph "Transfer Execution"
|
|
338
|
+
SubBal --> AddBal[balance to += amount]
|
|
339
|
+
AddBal --> Emit[Emit TransferredEvent]
|
|
340
|
+
Emit --> Success([Complete])
|
|
341
|
+
end
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### transferFrom (Calldata)
|
|
345
|
+
|
|
346
|
+
Transfers tokens using allowance.
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
public transferFrom(calldata: Calldata): BytesWriter
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
**Calldata format:**
|
|
353
|
+
| Field | Type | Size |
|
|
354
|
+
|-------|------|------|
|
|
355
|
+
| from | Address | 32 bytes |
|
|
356
|
+
| to | Address | 32 bytes |
|
|
357
|
+
| amount | u256 | 32 bytes |
|
|
358
|
+
|
|
359
|
+
**Returns:** Boolean (success)
|
|
360
|
+
|
|
361
|
+
The following sequence diagram illustrates both standard transfer and transferFrom with allowance:
|
|
362
|
+
|
|
363
|
+
```mermaid
|
|
364
|
+
sequenceDiagram
|
|
365
|
+
participant User as 👤 User
|
|
366
|
+
participant Contract as OP20 Contract
|
|
367
|
+
participant BalMap as Balance Map
|
|
368
|
+
participant Events as Event System
|
|
369
|
+
|
|
370
|
+
Note over User,Events: Standard Transfer Flow
|
|
371
|
+
|
|
372
|
+
User->>Contract: transfer(to, amount)
|
|
373
|
+
Contract->>Contract: _transfer(tx.sender, to, amount)
|
|
374
|
+
|
|
375
|
+
Contract->>Contract: Validate from != zero
|
|
376
|
+
Contract->>Contract: Validate to != zero
|
|
377
|
+
|
|
378
|
+
Contract->>BalMap: Get balance[from]
|
|
379
|
+
BalMap->>Contract: Return balance
|
|
380
|
+
|
|
381
|
+
Contract->>Contract: Check balance >= amount
|
|
382
|
+
|
|
383
|
+
Contract->>BalMap: Set balance[from] -= amount
|
|
384
|
+
Contract->>BalMap: Set balance[to] += amount
|
|
385
|
+
|
|
386
|
+
Contract->>Events: Emit TransferredEvent(operator, from, to, amount)
|
|
387
|
+
|
|
388
|
+
Contract->>User: Return success
|
|
389
|
+
|
|
390
|
+
Note over User,Events: TransferFrom with Allowance
|
|
391
|
+
|
|
392
|
+
User->>Contract: transferFrom(from, to, amount)
|
|
393
|
+
Contract->>Contract: _spendAllowance(from, tx.sender, amount)
|
|
394
|
+
|
|
395
|
+
alt Allowance is u256.Max
|
|
396
|
+
Contract->>Contract: Skip allowance deduction
|
|
397
|
+
else Normal allowance
|
|
398
|
+
Contract->>Contract: Check allowance >= amount
|
|
399
|
+
Contract->>Contract: Deduct from allowance
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
Contract->>Contract: _transfer(from, to, amount)
|
|
403
|
+
Contract->>Events: Emit TransferredEvent
|
|
404
|
+
Contract->>User: Return success
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
**Solidity Comparison:**
|
|
408
|
+
| Solidity (ERC20) | OPNet (OP20) |
|
|
409
|
+
|------------------|--------------|
|
|
410
|
+
| `function transfer(address to, uint256 amount) returns (bool)` | `transfer(calldata): BytesWriter` |
|
|
411
|
+
| `function transferFrom(address from, address to, uint256 amount) returns (bool)` | `transferFrom(calldata): BytesWriter` |
|
|
412
|
+
|
|
413
|
+
## Approval Methods
|
|
414
|
+
|
|
415
|
+
### increaseAllowance (Calldata)
|
|
416
|
+
|
|
417
|
+
Increases the allowance granted to a spender.
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
public increaseAllowance(calldata: Calldata): BytesWriter
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
**Calldata format:**
|
|
424
|
+
| Field | Type | Size |
|
|
425
|
+
|-------|------|------|
|
|
426
|
+
| spender | Address | 32 bytes |
|
|
427
|
+
| amount | u256 | 32 bytes |
|
|
428
|
+
|
|
429
|
+
**Returns:** Empty BytesWriter (emits ApprovedEvent)
|
|
430
|
+
|
|
431
|
+
**Note:** If overflow would occur, sets allowance to u256.Max (unlimited).
|
|
432
|
+
|
|
433
|
+
### decreaseAllowance (Calldata)
|
|
434
|
+
|
|
435
|
+
Decreases the allowance granted to a spender.
|
|
436
|
+
|
|
437
|
+
```typescript
|
|
438
|
+
public decreaseAllowance(calldata: Calldata): BytesWriter
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
**Calldata format:**
|
|
442
|
+
| Field | Type | Size |
|
|
443
|
+
|-------|------|------|
|
|
444
|
+
| spender | Address | 32 bytes |
|
|
445
|
+
| amount | u256 | 32 bytes |
|
|
446
|
+
|
|
447
|
+
**Returns:** Empty BytesWriter (emits ApprovedEvent)
|
|
448
|
+
|
|
449
|
+
**Note:** If underflow would occur, sets allowance to zero.
|
|
450
|
+
|
|
451
|
+
### increaseAllowanceBySignature (Calldata)
|
|
452
|
+
|
|
453
|
+
Increases allowance using an EIP-712 typed signature (gasless approval).
|
|
454
|
+
|
|
455
|
+
```typescript
|
|
456
|
+
public increaseAllowanceBySignature(calldata: Calldata): BytesWriter
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
**Calldata format:**
|
|
460
|
+
| Field | Type | Size |
|
|
461
|
+
|-------|------|------|
|
|
462
|
+
| owner | bytes32 | 32 bytes |
|
|
463
|
+
| ownerTweakedPublicKey | bytes32 | 32 bytes |
|
|
464
|
+
| spender | Address | 32 bytes |
|
|
465
|
+
| amount | u256 | 32 bytes |
|
|
466
|
+
| deadline | u64 | 8 bytes |
|
|
467
|
+
| signature | bytes | variable |
|
|
468
|
+
|
|
469
|
+
### decreaseAllowanceBySignature (Calldata)
|
|
470
|
+
|
|
471
|
+
Decreases allowance using an EIP-712 typed signature.
|
|
472
|
+
|
|
473
|
+
```typescript
|
|
474
|
+
public decreaseAllowanceBySignature(calldata: Calldata): BytesWriter
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
**Calldata format:**
|
|
478
|
+
| Field | Type | Size |
|
|
479
|
+
|-------|------|------|
|
|
480
|
+
| owner | bytes32 | 32 bytes |
|
|
481
|
+
| ownerTweakedPublicKey | bytes32 | 32 bytes |
|
|
482
|
+
| spender | Address | 32 bytes |
|
|
483
|
+
| amount | u256 | 32 bytes |
|
|
484
|
+
| deadline | u64 | 8 bytes |
|
|
485
|
+
| signature | bytes | variable |
|
|
486
|
+
|
|
487
|
+
The following diagram shows the allowance management flow including both direct and signature-based methods:
|
|
488
|
+
|
|
489
|
+
```mermaid
|
|
490
|
+
flowchart LR
|
|
491
|
+
subgraph "Allowance Method Selection"
|
|
492
|
+
A[User Allows Spender] --> B{Choose<br/>Method}
|
|
493
|
+
B -->|Direct| C[increaseAllowance]
|
|
494
|
+
B -->|Off-chain| D[increaseAllowanceBySignature]
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
subgraph "Direct Allowance Path"
|
|
498
|
+
C --> E[Get allowanceMap owner]
|
|
499
|
+
E --> F[Get current for spender]
|
|
500
|
+
F --> G[new = current + amount]
|
|
501
|
+
G --> H{Overflow?}
|
|
502
|
+
H -->|Yes| I[Set to u256.Max<br/>unlimited]
|
|
503
|
+
H -->|No| J[Set new allowance]
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
subgraph "Signature Path"
|
|
507
|
+
D --> L[Verify EIP-712<br/>signature]
|
|
508
|
+
L --> M{Valid?}
|
|
509
|
+
M -->|No| N[Revert:<br/>Invalid signature]
|
|
510
|
+
M -->|Yes| O[Increment nonce]
|
|
511
|
+
O --> E
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
I --> K[Emit ApprovedEvent]
|
|
515
|
+
J --> K
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
The complete allowance lifecycle from approval to spending:
|
|
519
|
+
|
|
520
|
+
```mermaid
|
|
521
|
+
sequenceDiagram
|
|
522
|
+
participant Owner
|
|
523
|
+
participant Contract as OP20
|
|
524
|
+
participant AllowMap as Allowance Map
|
|
525
|
+
participant Spender
|
|
526
|
+
|
|
527
|
+
Note over Owner,Spender: Standard Approval Flow
|
|
528
|
+
|
|
529
|
+
Owner->>Contract: increaseAllowance(spender, 1000)
|
|
530
|
+
Contract->>AllowMap: Get allowanceMap[owner]
|
|
531
|
+
AllowMap->>Contract: Return owner's map
|
|
532
|
+
Contract->>Contract: Get current allowance for spender
|
|
533
|
+
Contract->>Contract: newAllowance = current + 1000
|
|
534
|
+
Contract->>AllowMap: Set allowance[owner][spender] = newAllowance
|
|
535
|
+
Contract->>Contract: Emit ApprovedEvent
|
|
536
|
+
Contract->>Owner: Success
|
|
537
|
+
|
|
538
|
+
Note over Owner,Spender: Spender Uses Allowance
|
|
539
|
+
|
|
540
|
+
Spender->>Contract: transferFrom(owner, recipient, 500)
|
|
541
|
+
Contract->>AllowMap: Get allowance[owner][spender]
|
|
542
|
+
AllowMap->>Contract: Returns 1000
|
|
543
|
+
|
|
544
|
+
alt Allowance is u256.Max
|
|
545
|
+
Contract->>Contract: Skip deduction (infinite approval)
|
|
546
|
+
else Normal allowance
|
|
547
|
+
Contract->>Contract: Check 1000 >= 500
|
|
548
|
+
Contract->>AllowMap: Set allowance to 1000 - 500 = 500
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
Contract->>Contract: Execute transfer
|
|
552
|
+
Contract->>Spender: Success
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
**Solidity Comparison:**
|
|
556
|
+
| Solidity (ERC20) | OPNet (OP20) |
|
|
557
|
+
|------------------|--------------|
|
|
558
|
+
| `function approve(address, uint256) returns (bool)` | N/A (use increaseAllowance/decreaseAllowance) |
|
|
559
|
+
| `function increaseAllowance(address, uint256) returns (bool)` | `increaseAllowance(calldata): BytesWriter` |
|
|
560
|
+
| `function decreaseAllowance(address, uint256) returns (bool)` | `decreaseAllowance(calldata): BytesWriter` |
|
|
561
|
+
|
|
562
|
+
## Protected Methods
|
|
563
|
+
|
|
564
|
+
For internal contract use:
|
|
565
|
+
|
|
566
|
+
### _mint
|
|
567
|
+
|
|
568
|
+
Mints new tokens.
|
|
569
|
+
|
|
570
|
+
```typescript
|
|
571
|
+
protected _mint(to: Address, amount: u256): void
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
```typescript
|
|
575
|
+
@method(
|
|
576
|
+
{ name: 'to', type: ABIDataTypes.ADDRESS },
|
|
577
|
+
{ name: 'amount', type: ABIDataTypes.UINT256 },
|
|
578
|
+
)
|
|
579
|
+
@returns({ name: 'success', type: ABIDataTypes.BOOL })
|
|
580
|
+
@emit('Mint')
|
|
581
|
+
public mint(calldata: Calldata): BytesWriter {
|
|
582
|
+
this.onlyDeployer(Blockchain.tx.sender);
|
|
583
|
+
const to: Address = calldata.readAddress();
|
|
584
|
+
const amount: u256 = calldata.readU256();
|
|
585
|
+
this._mint(to, amount);
|
|
586
|
+
return new BytesWriter(0);
|
|
587
|
+
}
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
### _burn
|
|
591
|
+
|
|
592
|
+
Burns tokens.
|
|
593
|
+
|
|
594
|
+
```typescript
|
|
595
|
+
protected _burn(from: Address, amount: u256): void
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
```typescript
|
|
599
|
+
@method({ name: 'amount', type: ABIDataTypes.UINT256 })
|
|
600
|
+
@returns({ name: 'success', type: ABIDataTypes.BOOL })
|
|
601
|
+
@emit('Burn')
|
|
602
|
+
public burn(calldata: Calldata): BytesWriter {
|
|
603
|
+
const amount: u256 = calldata.readU256();
|
|
604
|
+
this._burn(Blockchain.tx.sender, amount);
|
|
605
|
+
return new BytesWriter(0);
|
|
606
|
+
}
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
### _transfer
|
|
610
|
+
|
|
611
|
+
Internal transfer.
|
|
612
|
+
|
|
613
|
+
```typescript
|
|
614
|
+
protected _transfer(from: Address, to: Address, amount: u256): void
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
### _increaseAllowance
|
|
618
|
+
|
|
619
|
+
Internal method to increase allowance with overflow protection.
|
|
620
|
+
|
|
621
|
+
```typescript
|
|
622
|
+
protected _increaseAllowance(owner: Address, spender: Address, amount: u256): void
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
### _decreaseAllowance
|
|
626
|
+
|
|
627
|
+
Internal method to decrease allowance with underflow protection.
|
|
628
|
+
|
|
629
|
+
```typescript
|
|
630
|
+
protected _decreaseAllowance(owner: Address, spender: Address, amount: u256): void
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
### _spendAllowance
|
|
634
|
+
|
|
635
|
+
Decrements allowance.
|
|
636
|
+
|
|
637
|
+
```typescript
|
|
638
|
+
protected _spendAllowance(owner: Address, spender: Address, amount: u256): void
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
**Solidity Comparison:**
|
|
642
|
+
| Solidity (ERC20) | OPNet (OP20) |
|
|
643
|
+
|------------------|--------------|
|
|
644
|
+
| `function _mint(address, uint256) internal` | `_mint(Address, u256): void` |
|
|
645
|
+
| `function _burn(address, uint256) internal` | `_burn(Address, u256): void` |
|
|
646
|
+
| `function _transfer(address, address, uint256) internal` | `_transfer(Address, Address, u256): void` |
|
|
647
|
+
|
|
648
|
+
## Safe Transfer Methods
|
|
649
|
+
|
|
650
|
+
### safeTransfer
|
|
651
|
+
|
|
652
|
+
Transfer with recipient callback.
|
|
653
|
+
|
|
654
|
+
```typescript
|
|
655
|
+
public safeTransfer(calldata: Calldata): BytesWriter
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
**Calldata format:**
|
|
659
|
+
| Field | Type | Size |
|
|
660
|
+
|-------|------|------|
|
|
661
|
+
| to | Address | 32 bytes |
|
|
662
|
+
| amount | u256 | 32 bytes |
|
|
663
|
+
| data | bytes | variable (with length prefix) |
|
|
664
|
+
|
|
665
|
+
Calls `onOP20Received` on recipient if it's a contract.
|
|
666
|
+
|
|
667
|
+
### safeTransferFrom
|
|
668
|
+
|
|
669
|
+
TransferFrom with callback.
|
|
670
|
+
|
|
671
|
+
```typescript
|
|
672
|
+
public safeTransferFrom(calldata: Calldata): BytesWriter
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
**Calldata format:**
|
|
676
|
+
| Field | Type | Size |
|
|
677
|
+
|-------|------|------|
|
|
678
|
+
| from | Address | 32 bytes |
|
|
679
|
+
| to | Address | 32 bytes |
|
|
680
|
+
| amount | u256 | 32 bytes |
|
|
681
|
+
| data | bytes | variable (with length prefix) |
|
|
682
|
+
|
|
683
|
+
The following diagram shows the safe transfer flow with recipient callback verification:
|
|
684
|
+
|
|
685
|
+
```mermaid
|
|
686
|
+
sequenceDiagram
|
|
687
|
+
participant User as 👤 User
|
|
688
|
+
participant Token as OP20 Contract
|
|
689
|
+
participant Target as Recipient Contract
|
|
690
|
+
participant Guard as ReentrancyGuard
|
|
691
|
+
|
|
692
|
+
Note over User,Guard: Safe Transfer Flow
|
|
693
|
+
|
|
694
|
+
User->>Token: safeTransfer(to, amount, data)
|
|
695
|
+
Token->>Guard: Check reentrancy depth
|
|
696
|
+
Guard->>Token: OK (depth allowed for callback)
|
|
697
|
+
|
|
698
|
+
Token->>Token: _transfer(from, to, amount)
|
|
699
|
+
Token->>Token: Update balances
|
|
700
|
+
Token->>Token: Emit TransferredEvent
|
|
701
|
+
|
|
702
|
+
Token->>Token: Check if recipient is contract
|
|
703
|
+
alt Recipient is Contract
|
|
704
|
+
Token->>Token: Build onOP20Received calldata
|
|
705
|
+
Token->>Target: call(onOP20Received(operator, from, amount, data))
|
|
706
|
+
|
|
707
|
+
alt Target implements onOP20Received
|
|
708
|
+
Target->>Target: Process receipt
|
|
709
|
+
Target->>Token: Return ON_OP20_RECEIVED_SELECTOR
|
|
710
|
+
|
|
711
|
+
Token->>Token: Verify return value
|
|
712
|
+
alt Return value matches selector
|
|
713
|
+
Token->>User: Success
|
|
714
|
+
else Invalid return value
|
|
715
|
+
Token->>User: Revert: Transfer rejected
|
|
716
|
+
end
|
|
717
|
+
else Target doesn't implement
|
|
718
|
+
Target->>Token: Error or wrong selector
|
|
719
|
+
Token->>User: Revert: Transfer rejected
|
|
720
|
+
end
|
|
721
|
+
else Recipient is EOA
|
|
722
|
+
Token->>User: Success (no callback needed)
|
|
723
|
+
end
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
## Events
|
|
727
|
+
|
|
728
|
+
### TransferredEvent
|
|
729
|
+
|
|
730
|
+
Emitted on transfers (transfer, transferFrom, safeTransfer, safeTransferFrom).
|
|
731
|
+
|
|
732
|
+
```typescript
|
|
733
|
+
class TransferredEvent extends NetEvent {
|
|
734
|
+
constructor(
|
|
735
|
+
operator: Address, // Address that initiated the transfer
|
|
736
|
+
from: Address, // Address tokens transferred from
|
|
737
|
+
to: Address, // Address tokens transferred to
|
|
738
|
+
amount: u256 // Amount transferred
|
|
739
|
+
)
|
|
740
|
+
}
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
### ApprovedEvent
|
|
744
|
+
|
|
745
|
+
Emitted on allowance changes (increaseAllowance, decreaseAllowance).
|
|
746
|
+
|
|
747
|
+
```typescript
|
|
748
|
+
class ApprovedEvent extends NetEvent {
|
|
749
|
+
constructor(
|
|
750
|
+
owner: Address,
|
|
751
|
+
spender: Address,
|
|
752
|
+
amount: u256
|
|
753
|
+
)
|
|
754
|
+
}
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
### MintedEvent
|
|
758
|
+
|
|
759
|
+
Emitted on minting.
|
|
760
|
+
|
|
761
|
+
```typescript
|
|
762
|
+
class MintedEvent extends NetEvent {
|
|
763
|
+
constructor(
|
|
764
|
+
to: Address,
|
|
765
|
+
amount: u256
|
|
766
|
+
)
|
|
767
|
+
}
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
### BurnedEvent
|
|
771
|
+
|
|
772
|
+
Emitted on burning.
|
|
773
|
+
|
|
774
|
+
```typescript
|
|
775
|
+
class BurnedEvent extends NetEvent {
|
|
776
|
+
constructor(
|
|
777
|
+
from: Address,
|
|
778
|
+
amount: u256
|
|
779
|
+
)
|
|
780
|
+
}
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
**Solidity Comparison:**
|
|
784
|
+
| Solidity (ERC20) | OPNet (OP20) |
|
|
785
|
+
|------------------|--------------|
|
|
786
|
+
| `event Transfer(address indexed from, address indexed to, uint256 value)` | `TransferredEvent(operator, from, to, amount)` |
|
|
787
|
+
| `event Approval(address indexed owner, address indexed spender, uint256 value)` | `ApprovedEvent(owner, spender, amount)` |
|
|
788
|
+
| `emit Transfer(from, to, amount)` | `emitEvent(new TransferredEvent(operator, from, to, amount))` |
|
|
789
|
+
|
|
790
|
+
## Storage Layout
|
|
791
|
+
|
|
792
|
+
OP20 uses 7 storage pointers:
|
|
793
|
+
|
|
794
|
+
| Pointer | Purpose |
|
|
795
|
+
|---------|---------|
|
|
796
|
+
| 0 | Nonce mapping (for signatures) |
|
|
797
|
+
| 1 | Max supply |
|
|
798
|
+
| 2 | Decimals |
|
|
799
|
+
| 3 | String pointer (shared for name/symbol/icon) |
|
|
800
|
+
| 4 | Total supply |
|
|
801
|
+
| 5 | Allowances mapping |
|
|
802
|
+
| 6 | Balances mapping |
|
|
803
|
+
|
|
804
|
+
## Method Selectors
|
|
805
|
+
|
|
806
|
+
| Selector | Method |
|
|
807
|
+
|----------|--------|
|
|
808
|
+
| `name` | Returns name |
|
|
809
|
+
| `symbol` | Returns symbol |
|
|
810
|
+
| `icon` | Returns icon URL |
|
|
811
|
+
| `decimals` | Returns decimals |
|
|
812
|
+
| `totalSupply` | Returns total supply |
|
|
813
|
+
| `maximumSupply` | Returns max supply |
|
|
814
|
+
| `balanceOf` | Returns balance |
|
|
815
|
+
| `allowance` | Returns allowance |
|
|
816
|
+
| `nonceOf` | Returns nonce for address |
|
|
817
|
+
| `domainSeparator` | Returns EIP-712 domain separator |
|
|
818
|
+
| `metadata` | Returns all metadata |
|
|
819
|
+
| `transfer` | Transfer tokens |
|
|
820
|
+
| `transferFrom` | Transfer with allowance |
|
|
821
|
+
| `safeTransfer` | Safe transfer with callback |
|
|
822
|
+
| `safeTransferFrom` | Safe transferFrom with callback |
|
|
823
|
+
| `increaseAllowance` | Increase spender allowance |
|
|
824
|
+
| `decreaseAllowance` | Decrease spender allowance |
|
|
825
|
+
| `increaseAllowanceBySignature` | Gasless allowance increase |
|
|
826
|
+
| `decreaseAllowanceBySignature` | Gasless allowance decrease |
|
|
827
|
+
| `burn` | Burn tokens from sender |
|
|
828
|
+
|
|
829
|
+
## Complete Example
|
|
830
|
+
|
|
831
|
+
```typescript
|
|
832
|
+
import { u256 } from '@btc-vision/as-bignum/assembly';
|
|
833
|
+
import {
|
|
834
|
+
OP20,
|
|
835
|
+
OP20InitParameters,
|
|
836
|
+
Blockchain,
|
|
837
|
+
Calldata,
|
|
838
|
+
BytesWriter,
|
|
839
|
+
ABIDataTypes,
|
|
840
|
+
} from '@btc-vision/btc-runtime/runtime';
|
|
841
|
+
|
|
842
|
+
@final
|
|
843
|
+
export class MyToken extends OP20 {
|
|
844
|
+
public constructor() {
|
|
845
|
+
super();
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
public override onDeployment(calldata: Calldata): void {
|
|
849
|
+
const maxSupply = calldata.readU256();
|
|
850
|
+
const decimals = calldata.readU8();
|
|
851
|
+
const name = calldata.readString();
|
|
852
|
+
const symbol = calldata.readString();
|
|
853
|
+
const initialMint = calldata.readU256();
|
|
854
|
+
|
|
855
|
+
this.instantiate(new OP20InitParameters(
|
|
856
|
+
maxSupply,
|
|
857
|
+
decimals,
|
|
858
|
+
name,
|
|
859
|
+
symbol,
|
|
860
|
+
'' // icon (optional)
|
|
861
|
+
));
|
|
862
|
+
|
|
863
|
+
if (!initialMint.isZero()) {
|
|
864
|
+
this._mint(Blockchain.tx.origin, initialMint);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
@method(
|
|
869
|
+
{ name: 'to', type: ABIDataTypes.ADDRESS },
|
|
870
|
+
{ name: 'amount', type: ABIDataTypes.UINT256 },
|
|
871
|
+
)
|
|
872
|
+
@returns({ name: 'success', type: ABIDataTypes.BOOL })
|
|
873
|
+
@emit('Minted')
|
|
874
|
+
public mint(calldata: Calldata): BytesWriter {
|
|
875
|
+
this.onlyDeployer(Blockchain.tx.sender);
|
|
876
|
+
|
|
877
|
+
const to = calldata.readAddress();
|
|
878
|
+
const amount = calldata.readU256();
|
|
879
|
+
|
|
880
|
+
this._mint(to, amount);
|
|
881
|
+
// Note: _mint already emits MintedEvent internally
|
|
882
|
+
|
|
883
|
+
return new BytesWriter(0);
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
```
|
|
887
|
+
|
|
888
|
+
## Solidity Comparison Summary
|
|
889
|
+
|
|
890
|
+
| Solidity (ERC20) | OPNet (OP20) |
|
|
891
|
+
|------------------|--------------|
|
|
892
|
+
| `constructor(...)` | `onDeployment(calldata)` |
|
|
893
|
+
| `function name()` | `name(): string` |
|
|
894
|
+
| `function balanceOf(address)` | `balanceOf(Address): u256` |
|
|
895
|
+
| `_mint(address, uint256)` | `_mint(Address, u256)` |
|
|
896
|
+
| `emit Transfer(...)` | `emitEvent(new TransferredEvent(...))` |
|
|
897
|
+
|
|
898
|
+
---
|
|
899
|
+
|
|
900
|
+
**Navigation:**
|
|
901
|
+
- Previous: [Blockchain API](./blockchain.md)
|
|
902
|
+
- Next: [OP721 API](./op721.md)
|