@btc-vision/btc-runtime 1.10.10 → 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 +51 -26
- package/runtime/memory/MapOfMap.ts +1 -0
- package/LICENSE.md +0 -21
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
# Decorators
|
|
2
|
+
|
|
3
|
+
Decorators are essential for OPNet smart contracts. They define the ABI (Application Binary Interface) that allows external callers to interact with your contract methods.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
OPNet uses three main decorators:
|
|
8
|
+
|
|
9
|
+
| Decorator | Purpose |
|
|
10
|
+
|-----------|---------|
|
|
11
|
+
| `@method()` | Defines input parameters for a contract method |
|
|
12
|
+
| `@returns()` | Defines return values for a contract method |
|
|
13
|
+
| `@emit()` | Specifies which event a method emits |
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { OP_NET, Calldata, BytesWriter, ABIDataTypes } from '@btc-vision/btc-runtime/runtime';
|
|
17
|
+
|
|
18
|
+
@final
|
|
19
|
+
class MyContract extends OP_NET {
|
|
20
|
+
public constructor() {
|
|
21
|
+
super();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@method({ name: 'recipient', type: ABIDataTypes.ADDRESS })
|
|
25
|
+
@returns({ name: 'success', type: ABIDataTypes.BOOL })
|
|
26
|
+
@emit('Transferred')
|
|
27
|
+
public transfer(calldata: Calldata): BytesWriter {
|
|
28
|
+
const recipient = calldata.readAddress();
|
|
29
|
+
// ... implementation
|
|
30
|
+
const writer = new BytesWriter(1);
|
|
31
|
+
writer.writeBoolean(true);
|
|
32
|
+
return writer;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Decorator Flow and ABI Generation
|
|
38
|
+
|
|
39
|
+
```mermaid
|
|
40
|
+
---
|
|
41
|
+
config:
|
|
42
|
+
theme: dark
|
|
43
|
+
---
|
|
44
|
+
flowchart LR
|
|
45
|
+
Start["Contract Source Code"] --> Parse["Compiler"]
|
|
46
|
+
Parse --> Extract["Extract Decorators"]
|
|
47
|
+
Extract --> Build["Build ABI Entry"]
|
|
48
|
+
Build --> Gen["Generate Selector<br/>SHA256 -> u32"]
|
|
49
|
+
Gen --> Output["abi.json"]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Solidity Comparison
|
|
53
|
+
|
|
54
|
+
OPNet decorators serve the same purpose as Solidity's function signatures but are more explicit:
|
|
55
|
+
|
|
56
|
+
| Solidity | OPNet |
|
|
57
|
+
|----------|-------|
|
|
58
|
+
| `function name() public view returns (string)` | `@method() @returns({ name: 'name', type: ABIDataTypes.STRING })` |
|
|
59
|
+
| `function balanceOf(address owner) public view returns (uint256)` | `@method({ name: 'owner', type: ABIDataTypes.ADDRESS }) @returns({ name: 'balance', type: ABIDataTypes.UINT256 })` |
|
|
60
|
+
| `function transfer(address to, uint256 amount) public` | `@method({ name: 'to', type: ABIDataTypes.ADDRESS }, { name: 'amount', type: ABIDataTypes.UINT256 })` |
|
|
61
|
+
| `event Transfer(address from, address to, uint256 amount)` | `@emit('Transferred')` |
|
|
62
|
+
|
|
63
|
+
## ABIDataTypes
|
|
64
|
+
|
|
65
|
+
The `ABIDataTypes` enum defines all supported parameter and return types:
|
|
66
|
+
|
|
67
|
+
### Numeric Types
|
|
68
|
+
|
|
69
|
+
| Type | Description | Size |
|
|
70
|
+
|------|-------------|------|
|
|
71
|
+
| `ABIDataTypes.UINT8` | Unsigned 8-bit integer | 1 byte |
|
|
72
|
+
| `ABIDataTypes.UINT16` | Unsigned 16-bit integer | 2 bytes |
|
|
73
|
+
| `ABIDataTypes.UINT32` | Unsigned 32-bit integer | 4 bytes |
|
|
74
|
+
| `ABIDataTypes.UINT64` | Unsigned 64-bit integer | 8 bytes |
|
|
75
|
+
| `ABIDataTypes.UINT128` | Unsigned 128-bit integer | 16 bytes |
|
|
76
|
+
| `ABIDataTypes.UINT256` | Unsigned 256-bit integer | 32 bytes |
|
|
77
|
+
|
|
78
|
+
### Address and Bytes Types
|
|
79
|
+
|
|
80
|
+
| Type | Description | Size |
|
|
81
|
+
|------|-------------|------|
|
|
82
|
+
| `ABIDataTypes.ADDRESS` | OPNet address | 32 bytes |
|
|
83
|
+
| `ABIDataTypes.BYTES` | Variable-length bytes | Variable |
|
|
84
|
+
| `ABIDataTypes.BYTES32` | Fixed 32-byte value | 32 bytes |
|
|
85
|
+
|
|
86
|
+
### Other Types
|
|
87
|
+
|
|
88
|
+
| Type | Description | Size |
|
|
89
|
+
|------|-------------|------|
|
|
90
|
+
| `ABIDataTypes.BOOL` | Boolean value | 1 byte |
|
|
91
|
+
| `ABIDataTypes.STRING` | UTF-8 string | Variable |
|
|
92
|
+
|
|
93
|
+
### Array Types
|
|
94
|
+
|
|
95
|
+
| Type | Description |
|
|
96
|
+
|------|-------------|
|
|
97
|
+
| `ABIDataTypes.ADDRESS_ARRAY` | Array of addresses |
|
|
98
|
+
| `ABIDataTypes.BYTES_ARRAY` | Array of byte arrays |
|
|
99
|
+
| `ABIDataTypes.UINT256_ARRAY` | Array of u256 values |
|
|
100
|
+
|
|
101
|
+
## @method Decorator
|
|
102
|
+
|
|
103
|
+
The `@method` decorator defines input parameters for a contract method.
|
|
104
|
+
|
|
105
|
+
### No Parameters
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
@method()
|
|
109
|
+
@returns({ name: 'supply', type: ABIDataTypes.UINT256 })
|
|
110
|
+
public totalSupply(_: Calldata): BytesWriter {
|
|
111
|
+
const writer = new BytesWriter(32);
|
|
112
|
+
writer.writeU256(this._totalSupply.value);
|
|
113
|
+
return writer;
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Single Parameter
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
@method({ name: 'owner', type: ABIDataTypes.ADDRESS })
|
|
121
|
+
@returns({ name: 'balance', type: ABIDataTypes.UINT256 })
|
|
122
|
+
public balanceOf(calldata: Calldata): BytesWriter {
|
|
123
|
+
const owner = calldata.readAddress();
|
|
124
|
+
const balance = this._balances.get(owner);
|
|
125
|
+
|
|
126
|
+
const writer = new BytesWriter(32);
|
|
127
|
+
writer.writeU256(balance);
|
|
128
|
+
return writer;
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Multiple Parameters
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
@method(
|
|
136
|
+
{ name: 'to', type: ABIDataTypes.ADDRESS },
|
|
137
|
+
{ name: 'amount', type: ABIDataTypes.UINT256 },
|
|
138
|
+
)
|
|
139
|
+
@emit('Transferred')
|
|
140
|
+
public transfer(calldata: Calldata): BytesWriter {
|
|
141
|
+
const to = calldata.readAddress();
|
|
142
|
+
const amount = calldata.readU256();
|
|
143
|
+
|
|
144
|
+
this._transfer(Blockchain.tx.sender, to, amount);
|
|
145
|
+
|
|
146
|
+
return new BytesWriter(0);
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Complex Parameters
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
@method(
|
|
154
|
+
{ name: 'owner', type: ABIDataTypes.ADDRESS },
|
|
155
|
+
{ name: 'spender', type: ABIDataTypes.ADDRESS },
|
|
156
|
+
{ name: 'value', type: ABIDataTypes.UINT256 },
|
|
157
|
+
{ name: 'deadline', type: ABIDataTypes.UINT64 },
|
|
158
|
+
{ name: 'signature', type: ABIDataTypes.BYTES },
|
|
159
|
+
)
|
|
160
|
+
@emit('Approved')
|
|
161
|
+
public permit(calldata: Calldata): BytesWriter {
|
|
162
|
+
const owner = calldata.readAddress();
|
|
163
|
+
const spender = calldata.readAddress();
|
|
164
|
+
const value = calldata.readU256();
|
|
165
|
+
const deadline = calldata.readU64();
|
|
166
|
+
const signature = calldata.readBytesWithLength();
|
|
167
|
+
|
|
168
|
+
// ... implementation
|
|
169
|
+
return new BytesWriter(0);
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Named Method Override
|
|
174
|
+
|
|
175
|
+
When your method name differs from the ABI name:
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
@method('name') // ABI will use 'name' as the method name
|
|
179
|
+
@returns({ name: 'name', type: ABIDataTypes.STRING })
|
|
180
|
+
public fn_name(_: Calldata): BytesWriter {
|
|
181
|
+
// Method is called 'fn_name' in code but 'name' in ABI
|
|
182
|
+
const writer = new BytesWriter(this._name.value.length + 4);
|
|
183
|
+
writer.writeString(this._name.value);
|
|
184
|
+
return writer;
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## @returns Decorator
|
|
189
|
+
|
|
190
|
+
The `@returns` decorator defines return values for a contract method.
|
|
191
|
+
|
|
192
|
+
### Single Return Value
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
@method()
|
|
196
|
+
@returns({ name: 'decimals', type: ABIDataTypes.UINT8 })
|
|
197
|
+
public decimals(_: Calldata): BytesWriter {
|
|
198
|
+
const writer = new BytesWriter(1);
|
|
199
|
+
writer.writeU8(this._decimals.value);
|
|
200
|
+
return writer;
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Multiple Return Values
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
@method()
|
|
208
|
+
@returns(
|
|
209
|
+
{ name: 'name', type: ABIDataTypes.STRING },
|
|
210
|
+
{ name: 'symbol', type: ABIDataTypes.STRING },
|
|
211
|
+
{ name: 'decimals', type: ABIDataTypes.UINT8 },
|
|
212
|
+
{ name: 'totalSupply', type: ABIDataTypes.UINT256 },
|
|
213
|
+
)
|
|
214
|
+
public metadata(_: Calldata): BytesWriter {
|
|
215
|
+
const writer = new BytesWriter(256);
|
|
216
|
+
writer.writeString(this._name.value);
|
|
217
|
+
writer.writeString(this._symbol.value);
|
|
218
|
+
writer.writeU8(this._decimals.value);
|
|
219
|
+
writer.writeU256(this._totalSupply.value);
|
|
220
|
+
return writer;
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### No Return Value
|
|
225
|
+
|
|
226
|
+
Methods that only mutate state:
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
@method(
|
|
230
|
+
{ name: 'to', type: ABIDataTypes.ADDRESS },
|
|
231
|
+
{ name: 'amount', type: ABIDataTypes.UINT256 },
|
|
232
|
+
)
|
|
233
|
+
@emit('Transferred')
|
|
234
|
+
public transfer(calldata: Calldata): BytesWriter {
|
|
235
|
+
const to = calldata.readAddress();
|
|
236
|
+
const amount = calldata.readU256();
|
|
237
|
+
|
|
238
|
+
this._transfer(Blockchain.tx.sender, to, amount);
|
|
239
|
+
|
|
240
|
+
return new BytesWriter(0); // Empty return
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## @emit Decorator
|
|
245
|
+
|
|
246
|
+
The `@emit` decorator specifies which event a method emits. This is used for ABI generation but doesn't automatically emit the event - you must call `this.emitEvent()` in your implementation.
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
@method(
|
|
250
|
+
{ name: 'to', type: ABIDataTypes.ADDRESS },
|
|
251
|
+
{ name: 'amount', type: ABIDataTypes.UINT256 },
|
|
252
|
+
)
|
|
253
|
+
@emit('Transferred') // Indicates this method emits Transferred event
|
|
254
|
+
public transfer(calldata: Calldata): BytesWriter {
|
|
255
|
+
const to = calldata.readAddress();
|
|
256
|
+
const amount = calldata.readU256();
|
|
257
|
+
const from = Blockchain.tx.sender;
|
|
258
|
+
|
|
259
|
+
this._transfer(from, to, amount);
|
|
260
|
+
|
|
261
|
+
// You must still emit the event manually
|
|
262
|
+
this.emitEvent(new TransferredEvent(from, from, to, amount));
|
|
263
|
+
|
|
264
|
+
return new BytesWriter(0);
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### How Decorators Work Together
|
|
269
|
+
|
|
270
|
+
```mermaid
|
|
271
|
+
---
|
|
272
|
+
config:
|
|
273
|
+
theme: dark
|
|
274
|
+
---
|
|
275
|
+
flowchart LR
|
|
276
|
+
Code["Method with Decorators"] --> Extract["Extract Metadata"]
|
|
277
|
+
Extract --> GenSig["Generate Signature"]
|
|
278
|
+
GenSig --> Selector["Selector: 0xABCD1234"]
|
|
279
|
+
Selector --> ABI["ABI Entry"]
|
|
280
|
+
ABI --> Call["External Call"]
|
|
281
|
+
Call --> Match{"Match?"}
|
|
282
|
+
Match -->|Yes| Execute["Execute Method"]
|
|
283
|
+
Match -->|No| Next["Next Method"]
|
|
284
|
+
Execute --> Return["Return Result"]
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## Complete Examples
|
|
288
|
+
|
|
289
|
+
### Simple Getter
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
@method()
|
|
293
|
+
@returns({ name: 'owner', type: ABIDataTypes.ADDRESS })
|
|
294
|
+
public owner(_: Calldata): BytesWriter {
|
|
295
|
+
const writer = new BytesWriter(32);
|
|
296
|
+
writer.writeAddress(this._owner.value);
|
|
297
|
+
return writer;
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Getter with Parameter
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
@method({ name: 'tokenId', type: ABIDataTypes.UINT256 })
|
|
305
|
+
@returns({ name: 'owner', type: ABIDataTypes.ADDRESS })
|
|
306
|
+
public ownerOf(calldata: Calldata): BytesWriter {
|
|
307
|
+
const tokenId = calldata.readU256();
|
|
308
|
+
const owner = this._owners.get(tokenId);
|
|
309
|
+
|
|
310
|
+
if (owner.isZero()) {
|
|
311
|
+
throw new Revert('Token does not exist');
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const writer = new BytesWriter(32);
|
|
315
|
+
writer.writeAddress(owner);
|
|
316
|
+
return writer;
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### State-Mutating Method
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
@method(
|
|
324
|
+
{ name: 'spender', type: ABIDataTypes.ADDRESS },
|
|
325
|
+
{ name: 'amount', type: ABIDataTypes.UINT256 },
|
|
326
|
+
)
|
|
327
|
+
@emit('Approved')
|
|
328
|
+
public approve(calldata: Calldata): BytesWriter {
|
|
329
|
+
const spender = calldata.readAddress();
|
|
330
|
+
const amount = calldata.readU256();
|
|
331
|
+
const owner = Blockchain.tx.sender;
|
|
332
|
+
|
|
333
|
+
this._approve(owner, spender, amount);
|
|
334
|
+
this.emitEvent(new ApprovedEvent(owner, spender, amount));
|
|
335
|
+
|
|
336
|
+
return new BytesWriter(0);
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Method with Bytes Input
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
@method(ABIDataTypes.BYTES) // Shorthand for { name: 'data', type: ABIDataTypes.BYTES }
|
|
344
|
+
@returns({ name: 'valid', type: ABIDataTypes.BOOL })
|
|
345
|
+
public verifySignature(calldata: Calldata): BytesWriter {
|
|
346
|
+
const signature = calldata.readBytesWithLength();
|
|
347
|
+
|
|
348
|
+
const message = new BytesWriter(32);
|
|
349
|
+
message.writeString('Sign this message');
|
|
350
|
+
const messageHash = sha256(message.getBuffer());
|
|
351
|
+
|
|
352
|
+
const isValid = Blockchain.verifySignature(
|
|
353
|
+
Blockchain.tx.origin,
|
|
354
|
+
signature,
|
|
355
|
+
messageHash,
|
|
356
|
+
true
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
const writer = new BytesWriter(1);
|
|
360
|
+
writer.writeBoolean(isValid);
|
|
361
|
+
return writer;
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### Full Token Transfer
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
@method(
|
|
369
|
+
{ name: 'from', type: ABIDataTypes.ADDRESS },
|
|
370
|
+
{ name: 'to', type: ABIDataTypes.ADDRESS },
|
|
371
|
+
{ name: 'amount', type: ABIDataTypes.UINT256 },
|
|
372
|
+
)
|
|
373
|
+
@emit('Transferred')
|
|
374
|
+
public transferFrom(calldata: Calldata): BytesWriter {
|
|
375
|
+
const from = calldata.readAddress();
|
|
376
|
+
const to = calldata.readAddress();
|
|
377
|
+
const amount = calldata.readU256();
|
|
378
|
+
const spender = Blockchain.tx.sender;
|
|
379
|
+
|
|
380
|
+
// Check and update allowance
|
|
381
|
+
const currentAllowance = this._allowances.get(from).get(spender);
|
|
382
|
+
if (currentAllowance < amount) {
|
|
383
|
+
throw new Revert('Insufficient allowance');
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Deduct from allowance (unless unlimited)
|
|
387
|
+
if (currentAllowance != u256.Max) {
|
|
388
|
+
this._allowances.get(from).set(spender, SafeMath.sub(currentAllowance, amount));
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Transfer
|
|
392
|
+
this._transfer(from, to, amount);
|
|
393
|
+
this.emitEvent(new TransferredEvent(spender, from, to, amount));
|
|
394
|
+
|
|
395
|
+
return new BytesWriter(0);
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
## Best Practices
|
|
400
|
+
|
|
401
|
+
### 1. Always Use Decorators for Public Methods
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
// Good - properly decorated
|
|
405
|
+
@method({ name: 'amount', type: ABIDataTypes.UINT256 })
|
|
406
|
+
@emit('Burned')
|
|
407
|
+
public burn(calldata: Calldata): BytesWriter {
|
|
408
|
+
// ...
|
|
409
|
+
return new BytesWriter(0);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Bad - no decorators
|
|
413
|
+
public burn(calldata: Calldata): BytesWriter {
|
|
414
|
+
// Callers won't know the ABI
|
|
415
|
+
return new BytesWriter(0);
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### 2. Match Read Order with Parameter Order
|
|
420
|
+
|
|
421
|
+
```typescript
|
|
422
|
+
@method(
|
|
423
|
+
{ name: 'to', type: ABIDataTypes.ADDRESS },
|
|
424
|
+
{ name: 'amount', type: ABIDataTypes.UINT256 },
|
|
425
|
+
)
|
|
426
|
+
public transfer(calldata: Calldata): BytesWriter {
|
|
427
|
+
// Read in same order as @method parameters
|
|
428
|
+
const to = calldata.readAddress(); // First
|
|
429
|
+
const amount = calldata.readU256(); // Second
|
|
430
|
+
// ...
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### 3. Use Descriptive Names
|
|
435
|
+
|
|
436
|
+
```typescript
|
|
437
|
+
// Good - clear names
|
|
438
|
+
@method({ name: 'recipient', type: ABIDataTypes.ADDRESS })
|
|
439
|
+
@returns({ name: 'success', type: ABIDataTypes.BOOL })
|
|
440
|
+
|
|
441
|
+
// Less clear
|
|
442
|
+
@method({ name: 'a', type: ABIDataTypes.ADDRESS })
|
|
443
|
+
@returns({ name: 'r', type: ABIDataTypes.BOOL })
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### 4. Group Related Returns
|
|
447
|
+
|
|
448
|
+
```typescript
|
|
449
|
+
@method()
|
|
450
|
+
@returns(
|
|
451
|
+
{ name: 'name', type: ABIDataTypes.STRING },
|
|
452
|
+
{ name: 'symbol', type: ABIDataTypes.STRING },
|
|
453
|
+
{ name: 'decimals', type: ABIDataTypes.UINT8 },
|
|
454
|
+
{ name: 'totalSupply', type: ABIDataTypes.UINT256 },
|
|
455
|
+
{ name: 'domainSeparator', type: ABIDataTypes.BYTES32 },
|
|
456
|
+
)
|
|
457
|
+
public metadata(_: Calldata): BytesWriter {
|
|
458
|
+
// Single call returns all token metadata
|
|
459
|
+
}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
---
|
|
463
|
+
|
|
464
|
+
**Navigation:**
|
|
465
|
+
- Previous: [Events](./events.md)
|
|
466
|
+
- Next: [Security](./security.md)
|