@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,642 @@
|
|
|
1
|
+
# Events API Reference
|
|
2
|
+
|
|
3
|
+
Events provide a way to emit state change notifications that off-chain applications can monitor.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import {
|
|
9
|
+
NetEvent,
|
|
10
|
+
TransferredEvent,
|
|
11
|
+
ApprovedEvent,
|
|
12
|
+
MintedEvent,
|
|
13
|
+
BurnedEvent,
|
|
14
|
+
TransferredSingleEvent,
|
|
15
|
+
TransferredBatchEvent,
|
|
16
|
+
ApprovedForAllEvent,
|
|
17
|
+
URIEvent,
|
|
18
|
+
BytesWriter,
|
|
19
|
+
} from '@btc-vision/btc-runtime/runtime';
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## NetEvent Base Class
|
|
23
|
+
|
|
24
|
+
All events extend the `NetEvent` base class.
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
abstract class NetEvent {
|
|
28
|
+
protected constructor(eventType: string, data: BytesWriter)
|
|
29
|
+
public getEventData(): Uint8Array
|
|
30
|
+
public get length(): u32
|
|
31
|
+
public readonly eventType: string
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Event Class Hierarchy
|
|
36
|
+
|
|
37
|
+
```mermaid
|
|
38
|
+
classDiagram
|
|
39
|
+
class NetEvent {
|
|
40
|
+
<<abstract>>
|
|
41
|
+
+string eventType
|
|
42
|
+
#BytesWriter data
|
|
43
|
+
-Uint8Array buffer
|
|
44
|
+
#constructor(eventType: string, data: BytesWriter)
|
|
45
|
+
+getEventData() Uint8Array
|
|
46
|
+
+get length() u32
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
class TransferredEvent {
|
|
50
|
+
+constructor(operator, from, to, amount)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
class TransferredSingleEvent {
|
|
54
|
+
+constructor(operator, from, to, id, value)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
class TransferredBatchEvent {
|
|
58
|
+
+constructor(operator, from, to, ids, values)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
class ApprovedEvent {
|
|
62
|
+
+constructor(owner, spender, amount)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
class ApprovedForAllEvent {
|
|
66
|
+
+constructor(account, operator, approved)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
class MintedEvent {
|
|
70
|
+
+constructor(to, amount)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
class BurnedEvent {
|
|
74
|
+
+constructor(from, amount)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
class URIEvent {
|
|
78
|
+
+constructor(value, id)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
class CustomEvent {
|
|
82
|
+
+constructor(...)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
NetEvent <|-- TransferredEvent
|
|
86
|
+
NetEvent <|-- TransferredSingleEvent
|
|
87
|
+
NetEvent <|-- TransferredBatchEvent
|
|
88
|
+
NetEvent <|-- ApprovedEvent
|
|
89
|
+
NetEvent <|-- ApprovedForAllEvent
|
|
90
|
+
NetEvent <|-- MintedEvent
|
|
91
|
+
NetEvent <|-- BurnedEvent
|
|
92
|
+
NetEvent <|-- URIEvent
|
|
93
|
+
NetEvent <|-- CustomEvent
|
|
94
|
+
|
|
95
|
+
note for NetEvent "All events must extend NetEvent\nand build data in constructor"
|
|
96
|
+
note for CustomEvent "User-defined events\nfor custom contract logic"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Creating Custom Events
|
|
100
|
+
|
|
101
|
+
### Basic Event
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
@final
|
|
105
|
+
class MyEvent extends NetEvent {
|
|
106
|
+
constructor(value: u256) {
|
|
107
|
+
const data = new BytesWriter(32);
|
|
108
|
+
data.writeU256(value);
|
|
109
|
+
super('MyEvent', data);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Emit
|
|
114
|
+
this.emitEvent(new MyEvent(u256.fromU64(100)));
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The following diagram shows the event system architecture:
|
|
118
|
+
|
|
119
|
+
```mermaid
|
|
120
|
+
graph LR
|
|
121
|
+
subgraph "Event Creation"
|
|
122
|
+
A[Contract Method] -->|Create| B[NetEvent Instance]
|
|
123
|
+
B -->|Extends| C[NetEvent Base Class]
|
|
124
|
+
C --> D[eventType: string]
|
|
125
|
+
C --> E[BytesWriter data]
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
subgraph "Validation"
|
|
129
|
+
E --> F{data.length<br/>352 bytes?}
|
|
130
|
+
F -->|Yes| G[Valid Event]
|
|
131
|
+
F -->|No| H[Revert:<br/>Too Large]
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
subgraph "Emission"
|
|
135
|
+
G --> I[contract.emitEvent]
|
|
136
|
+
I --> J[Blockchain.emit]
|
|
137
|
+
J --> K[Event Binary<br/>Format]
|
|
138
|
+
K --> L[Transaction<br/>Receipt]
|
|
139
|
+
end
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Multi-Field Event
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
import { ADDRESS_BYTE_LENGTH, U256_BYTE_LENGTH } from '@btc-vision/btc-runtime/runtime';
|
|
146
|
+
|
|
147
|
+
@final
|
|
148
|
+
class TransferEvent extends NetEvent {
|
|
149
|
+
constructor(from: Address, to: Address, amount: u256) {
|
|
150
|
+
const data = new BytesWriter(ADDRESS_BYTE_LENGTH * 2 + U256_BYTE_LENGTH);
|
|
151
|
+
data.writeAddress(from);
|
|
152
|
+
data.writeAddress(to);
|
|
153
|
+
data.writeU256(amount);
|
|
154
|
+
super('Transfer', data);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Event with Data Constructor
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
@final
|
|
163
|
+
class ComplexEvent extends NetEvent {
|
|
164
|
+
constructor(user: Address, action: u8, timestamp: u64, value: u256) {
|
|
165
|
+
const data = new BytesWriter(ADDRESS_BYTE_LENGTH + 1 + 8 + U256_BYTE_LENGTH);
|
|
166
|
+
data.writeAddress(user);
|
|
167
|
+
data.writeU8(action);
|
|
168
|
+
data.writeU64(timestamp);
|
|
169
|
+
data.writeU256(value);
|
|
170
|
+
super('ComplexEvent', data);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Predefined Events
|
|
176
|
+
|
|
177
|
+
### TransferredEvent
|
|
178
|
+
|
|
179
|
+
Standard token transfer event. Event type: `'Transferred'`. Note: OP20 transfers include an operator field.
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
class TransferredEvent extends NetEvent {
|
|
183
|
+
constructor(
|
|
184
|
+
operator: Address, // The address initiating the transfer
|
|
185
|
+
from: Address, // The sender address
|
|
186
|
+
to: Address, // The recipient address
|
|
187
|
+
amount: u256 // The amount transferred
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
this.emitEvent(new TransferredEvent(Blockchain.tx.sender, sender, recipient, amount));
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### TransferredSingleEvent
|
|
197
|
+
|
|
198
|
+
Single token transfer event for ERC1155-style tokens. Event type: `'TransferredSingle'`.
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
class TransferredSingleEvent extends NetEvent {
|
|
202
|
+
constructor(
|
|
203
|
+
operator: Address, // The address initiating the transfer
|
|
204
|
+
from: Address, // The sender address
|
|
205
|
+
to: Address, // The recipient address
|
|
206
|
+
id: u256, // The token ID
|
|
207
|
+
value: u256 // The amount transferred
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
this.emitEvent(new TransferredSingleEvent(Blockchain.tx.sender, from, to, tokenId, amount));
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### TransferredBatchEvent
|
|
217
|
+
|
|
218
|
+
Batch token transfer event for ERC1155-style tokens. Event type: `'TransferredBatch'`. Limited to 3 items due to the 352-byte event size limit.
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
class TransferredBatchEvent extends NetEvent {
|
|
222
|
+
constructor(
|
|
223
|
+
operator: Address, // The address initiating the transfer
|
|
224
|
+
from: Address, // The sender address
|
|
225
|
+
to: Address, // The recipient address
|
|
226
|
+
ids: u256[], // The token IDs (max 3 items)
|
|
227
|
+
values: u256[] // The amounts transferred (max 3 items)
|
|
228
|
+
)
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
this.emitEvent(new TransferredBatchEvent(Blockchain.tx.sender, from, to, tokenIds, amounts));
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### ApprovedEvent
|
|
237
|
+
|
|
238
|
+
Standard approval event. Event type: `'Approved'`.
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
class ApprovedEvent extends NetEvent {
|
|
242
|
+
constructor(
|
|
243
|
+
owner: Address, // The token owner
|
|
244
|
+
spender: Address, // The approved spender
|
|
245
|
+
amount: u256 // The approved amount
|
|
246
|
+
)
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
this.emitEvent(new ApprovedEvent(owner, spender, allowance));
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### ApprovedForAllEvent
|
|
255
|
+
|
|
256
|
+
Operator approval event for all tokens. Event type: `'ApprovedForAll'`.
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
class ApprovedForAllEvent extends NetEvent {
|
|
260
|
+
constructor(
|
|
261
|
+
account: Address, // The account granting approval
|
|
262
|
+
operator: Address, // The approved operator
|
|
263
|
+
approved: boolean // Whether approval is granted or revoked
|
|
264
|
+
)
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
this.emitEvent(new ApprovedForAllEvent(owner, operator, true));
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### MintedEvent
|
|
273
|
+
|
|
274
|
+
Token minting event. Event type: `'Minted'`.
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
class MintedEvent extends NetEvent {
|
|
278
|
+
constructor(
|
|
279
|
+
to: Address, // The recipient of minted tokens
|
|
280
|
+
amount: u256 // The amount minted
|
|
281
|
+
)
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
this.emitEvent(new MintedEvent(recipient, mintAmount));
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### BurnedEvent
|
|
290
|
+
|
|
291
|
+
Token burning event. Event type: `'Burned'`.
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
class BurnedEvent extends NetEvent {
|
|
295
|
+
constructor(
|
|
296
|
+
from: Address, // The address tokens are burned from
|
|
297
|
+
amount: u256 // The amount burned
|
|
298
|
+
)
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
this.emitEvent(new BurnedEvent(burner, burnAmount));
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### URIEvent
|
|
307
|
+
|
|
308
|
+
URI update event for token metadata. Event type: `'URI'`. URI length is limited to 200 bytes.
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
class URIEvent extends NetEvent {
|
|
312
|
+
constructor(
|
|
313
|
+
value: string, // The URI string (max 200 bytes)
|
|
314
|
+
id: u256 // The token ID
|
|
315
|
+
)
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
this.emitEvent(new URIEvent('https://example.com/token/1', tokenId));
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
## Event Lifecycle
|
|
324
|
+
|
|
325
|
+
The following sequence diagram shows the complete event lifecycle from creation to storage:
|
|
326
|
+
|
|
327
|
+
```mermaid
|
|
328
|
+
sequenceDiagram
|
|
329
|
+
participant C as Contract
|
|
330
|
+
participant E as NetEvent
|
|
331
|
+
participant BC as Blockchain
|
|
332
|
+
participant R as Receipt Storage
|
|
333
|
+
|
|
334
|
+
Note over C,R: Event Lifecycle
|
|
335
|
+
|
|
336
|
+
C->>E: new TransferredEvent(operator, from, to, amount)
|
|
337
|
+
E->>E: Create BytesWriter with size
|
|
338
|
+
E->>E: writer.writeAddress(operator)
|
|
339
|
+
E->>E: writer.writeAddress(from)
|
|
340
|
+
E->>E: writer.writeAddress(to)
|
|
341
|
+
E->>E: writer.writeU256(amount)
|
|
342
|
+
E->>E: super('Transferred', data)
|
|
343
|
+
|
|
344
|
+
E->>E: Validate length <= 352 bytes
|
|
345
|
+
|
|
346
|
+
C->>C: this.emitEvent(event)
|
|
347
|
+
C->>BC: Blockchain.emit(event)
|
|
348
|
+
|
|
349
|
+
BC->>E: event.getEventData()
|
|
350
|
+
E->>BC: Return encoded bytes
|
|
351
|
+
|
|
352
|
+
BC->>R: Store in transaction receipt
|
|
353
|
+
R->>R: Format: [4B:typeLen][typeStr][4B:dataLen][data]
|
|
354
|
+
|
|
355
|
+
Note over C,R: Off-chain indexers can now parse events
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
## Event Encoding Flow
|
|
359
|
+
|
|
360
|
+
The following diagram shows how events are encoded into binary format:
|
|
361
|
+
|
|
362
|
+
```mermaid
|
|
363
|
+
flowchart LR
|
|
364
|
+
subgraph "Event Construction"
|
|
365
|
+
A[Event Instance] --> B[Constructor<br/>Parameters]
|
|
366
|
+
B --> C[Create BytesWriter<br/>in constructor]
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
subgraph "BytesWriter Encoding"
|
|
370
|
+
C --> D[BytesWriter]
|
|
371
|
+
D --> E[Write Address<br/>32 bytes]
|
|
372
|
+
D --> F[Write u256<br/>32 bytes]
|
|
373
|
+
D --> G[Write u64<br/>8 bytes]
|
|
374
|
+
D --> H[Write u8<br/>1 byte]
|
|
375
|
+
D --> I[Write bool<br/>1 byte]
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
subgraph "Validation"
|
|
379
|
+
E & F & G & H & I --> J[Combined Buffer]
|
|
380
|
+
J --> K{Size Check}
|
|
381
|
+
K -->|<= 352 bytes| L[Valid Event]
|
|
382
|
+
K -->|> 352 bytes| M[Revert]
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
subgraph "Serialization Format"
|
|
386
|
+
L --> N[4B: type name length]
|
|
387
|
+
N --> O[N bytes: type name UTF-8]
|
|
388
|
+
O --> P[4B: data length]
|
|
389
|
+
P --> Q[M bytes: encoded data]
|
|
390
|
+
end
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
## Event Size Limits
|
|
394
|
+
|
|
395
|
+
Events have a maximum payload size of **352 bytes**.
|
|
396
|
+
|
|
397
|
+
| Component | Size |
|
|
398
|
+
|-----------|------|
|
|
399
|
+
| Event type name | Variable |
|
|
400
|
+
| Event data | Up to 352 bytes |
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
// Calculate event data size
|
|
404
|
+
// Address: 32 bytes, u256: 32 bytes
|
|
405
|
+
|
|
406
|
+
@final
|
|
407
|
+
class LargeEvent extends NetEvent {
|
|
408
|
+
constructor(
|
|
409
|
+
addr1: Address, // 32 bytes
|
|
410
|
+
addr2: Address, // 32 bytes
|
|
411
|
+
value1: u256, // 32 bytes
|
|
412
|
+
value2: u256, // 32 bytes
|
|
413
|
+
value3: u256 // 32 bytes
|
|
414
|
+
) { // Total: 160 bytes - OK
|
|
415
|
+
const data = new BytesWriter(ADDRESS_BYTE_LENGTH * 2 + U256_BYTE_LENGTH * 3);
|
|
416
|
+
data.writeAddress(addr1);
|
|
417
|
+
data.writeAddress(addr2);
|
|
418
|
+
data.writeU256(value1);
|
|
419
|
+
data.writeU256(value2);
|
|
420
|
+
data.writeU256(value3);
|
|
421
|
+
super('LargeEvent', data);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
## Emitting Events
|
|
427
|
+
|
|
428
|
+
Use `emitEvent` from the contract:
|
|
429
|
+
|
|
430
|
+
```typescript
|
|
431
|
+
// In contract method
|
|
432
|
+
@method(
|
|
433
|
+
{ name: 'to', type: ABIDataTypes.ADDRESS },
|
|
434
|
+
{ name: 'amount', type: ABIDataTypes.UINT256 },
|
|
435
|
+
)
|
|
436
|
+
@emit('Transferred')
|
|
437
|
+
public transfer(calldata: Calldata): BytesWriter {
|
|
438
|
+
const to: Address = calldata.readAddress();
|
|
439
|
+
const amount: u256 = calldata.readU256();
|
|
440
|
+
const from: Address = Blockchain.tx.sender;
|
|
441
|
+
|
|
442
|
+
// Perform transfer
|
|
443
|
+
this._transfer(from, to, amount);
|
|
444
|
+
|
|
445
|
+
// Emit event (OP_NET base class handles emitting via internal _transfer)
|
|
446
|
+
// For custom events, use:
|
|
447
|
+
// this.emitEvent(new TransferredEvent(Blockchain.tx.sender, from, to, amount));
|
|
448
|
+
|
|
449
|
+
return new BytesWriter(0);
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
Or use `Blockchain.emit`:
|
|
454
|
+
|
|
455
|
+
```typescript
|
|
456
|
+
Blockchain.emit(new TransferredEvent(Blockchain.tx.sender, from, to, amount));
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
## Event Encoding Format
|
|
460
|
+
|
|
461
|
+
Events are encoded as:
|
|
462
|
+
|
|
463
|
+
```
|
|
464
|
+
[4 bytes: type name length]
|
|
465
|
+
[N bytes: type name (UTF-8)]
|
|
466
|
+
[4 bytes: data length]
|
|
467
|
+
[M bytes: event data]
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
## Common Event Patterns
|
|
471
|
+
|
|
472
|
+
### State Change Events
|
|
473
|
+
|
|
474
|
+
```typescript
|
|
475
|
+
@final
|
|
476
|
+
class OwnershipTransferred extends NetEvent {
|
|
477
|
+
constructor(previousOwner: Address, newOwner: Address) {
|
|
478
|
+
const data = new BytesWriter(ADDRESS_BYTE_LENGTH * 2);
|
|
479
|
+
data.writeAddress(previousOwner);
|
|
480
|
+
data.writeAddress(newOwner);
|
|
481
|
+
super('OwnershipTransferred', data);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
### Action Events
|
|
487
|
+
|
|
488
|
+
```typescript
|
|
489
|
+
@final
|
|
490
|
+
class Paused extends NetEvent {
|
|
491
|
+
constructor(account: Address) {
|
|
492
|
+
const data = new BytesWriter(ADDRESS_BYTE_LENGTH);
|
|
493
|
+
data.writeAddress(account);
|
|
494
|
+
super('Paused', data);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
@final
|
|
499
|
+
class Unpaused extends NetEvent {
|
|
500
|
+
constructor(account: Address) {
|
|
501
|
+
const data = new BytesWriter(ADDRESS_BYTE_LENGTH);
|
|
502
|
+
data.writeAddress(account);
|
|
503
|
+
super('Unpaused', data);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
### Indexed-Style Events
|
|
509
|
+
|
|
510
|
+
While OPNet doesn't have Solidity's indexed parameters, you can structure events for efficient filtering:
|
|
511
|
+
|
|
512
|
+
```typescript
|
|
513
|
+
@final
|
|
514
|
+
class OrderFilled extends NetEvent {
|
|
515
|
+
constructor(
|
|
516
|
+
orderId: u256,
|
|
517
|
+
maker: Address,
|
|
518
|
+
taker: Address,
|
|
519
|
+
amount: u256,
|
|
520
|
+
price: u256
|
|
521
|
+
) {
|
|
522
|
+
const data = new BytesWriter(U256_BYTE_LENGTH * 3 + ADDRESS_BYTE_LENGTH * 2);
|
|
523
|
+
// Put "indexed" fields first for consistent offset
|
|
524
|
+
data.writeU256(orderId);
|
|
525
|
+
data.writeAddress(maker);
|
|
526
|
+
data.writeAddress(taker);
|
|
527
|
+
data.writeU256(amount);
|
|
528
|
+
data.writeU256(price);
|
|
529
|
+
super('OrderFilled', data);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
## Event Best Practices
|
|
535
|
+
|
|
536
|
+
### 1. Emit After State Changes
|
|
537
|
+
|
|
538
|
+
```typescript
|
|
539
|
+
// Good - emit after state is updated
|
|
540
|
+
this._balances.set(to, newBalance);
|
|
541
|
+
this.emitEvent(new TransferredEvent(Blockchain.tx.sender, from, to, amount));
|
|
542
|
+
|
|
543
|
+
// Bad - emit before state change
|
|
544
|
+
this.emitEvent(new TransferredEvent(Blockchain.tx.sender, from, to, amount));
|
|
545
|
+
this._balances.set(to, newBalance); // Could fail
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
### 2. Use Descriptive Event Names
|
|
549
|
+
|
|
550
|
+
```typescript
|
|
551
|
+
// Good - clear event names
|
|
552
|
+
@final class TokensMinted extends NetEvent { ... }
|
|
553
|
+
@final class LiquidityAdded extends NetEvent { ... }
|
|
554
|
+
@final class StakeWithdrawn extends NetEvent { ... }
|
|
555
|
+
|
|
556
|
+
// Less clear
|
|
557
|
+
@final class Action1 extends NetEvent { ... }
|
|
558
|
+
@final class Update extends NetEvent { ... }
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
### 3. Include Relevant Context
|
|
562
|
+
|
|
563
|
+
```typescript
|
|
564
|
+
// Good - includes all relevant data
|
|
565
|
+
@final
|
|
566
|
+
class Swap extends NetEvent {
|
|
567
|
+
constructor(
|
|
568
|
+
user: Address,
|
|
569
|
+
tokenIn: Address,
|
|
570
|
+
tokenOut: Address,
|
|
571
|
+
amountIn: u256,
|
|
572
|
+
amountOut: u256
|
|
573
|
+
) {
|
|
574
|
+
const data = new BytesWriter(ADDRESS_BYTE_LENGTH * 3 + U256_BYTE_LENGTH * 2);
|
|
575
|
+
data.writeAddress(user);
|
|
576
|
+
data.writeAddress(tokenIn);
|
|
577
|
+
data.writeAddress(tokenOut);
|
|
578
|
+
data.writeU256(amountIn);
|
|
579
|
+
data.writeU256(amountOut);
|
|
580
|
+
super('Swap', data);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// Less useful - missing context
|
|
585
|
+
@final
|
|
586
|
+
class Swap extends NetEvent {
|
|
587
|
+
constructor(amount: u256) {
|
|
588
|
+
const data = new BytesWriter(U256_BYTE_LENGTH);
|
|
589
|
+
data.writeU256(amount);
|
|
590
|
+
super('Swap', data);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
### 4. Keep Events Consistent
|
|
596
|
+
|
|
597
|
+
```typescript
|
|
598
|
+
// Use consistent field ordering across similar events
|
|
599
|
+
@final
|
|
600
|
+
class Deposit extends NetEvent {
|
|
601
|
+
constructor(user: Address, token: Address, amount: u256) {
|
|
602
|
+
const data = new BytesWriter(ADDRESS_BYTE_LENGTH * 2 + U256_BYTE_LENGTH);
|
|
603
|
+
data.writeAddress(user);
|
|
604
|
+
data.writeAddress(token);
|
|
605
|
+
data.writeU256(amount);
|
|
606
|
+
super('Deposit', data);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
@final
|
|
611
|
+
class Withdraw extends NetEvent {
|
|
612
|
+
constructor(user: Address, token: Address, amount: u256) { // Same order
|
|
613
|
+
const data = new BytesWriter(ADDRESS_BYTE_LENGTH * 2 + U256_BYTE_LENGTH);
|
|
614
|
+
data.writeAddress(user);
|
|
615
|
+
data.writeAddress(token);
|
|
616
|
+
data.writeU256(amount);
|
|
617
|
+
super('Withdraw', data);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
## Solidity Comparison
|
|
623
|
+
|
|
624
|
+
| Solidity | OPNet |
|
|
625
|
+
|----------|-------|
|
|
626
|
+
| `event Transfer(address indexed from, address indexed to, uint256 value)` | `class TransferredEvent extends NetEvent` |
|
|
627
|
+
| `emit Transfer(from, to, value)` | `emitEvent(new TransferredEvent(operator, from, to, value))` |
|
|
628
|
+
| Indexed parameters | Structure data with important fields first |
|
|
629
|
+
| Anonymous events | Not supported |
|
|
630
|
+
|
|
631
|
+
## Event Limitations
|
|
632
|
+
|
|
633
|
+
1. **No indexed parameters** - All parameters are in data payload
|
|
634
|
+
2. **352-byte limit** - Plan data structure carefully
|
|
635
|
+
3. **Not accessible on-chain** - Events are for off-chain consumption only
|
|
636
|
+
4. **No topics** - Single event type string instead of topic hashes
|
|
637
|
+
|
|
638
|
+
---
|
|
639
|
+
|
|
640
|
+
**Navigation:**
|
|
641
|
+
- Previous: [Storage API](./storage.md)
|
|
642
|
+
- [Back to Documentation Index](../README.md)
|