@btc-vision/btc-runtime 1.10.10 → 1.10.12

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.
Files changed (45) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +258 -137
  3. package/SECURITY.md +226 -0
  4. package/docs/README.md +614 -0
  5. package/docs/advanced/bitcoin-scripts.md +939 -0
  6. package/docs/advanced/cross-contract-calls.md +579 -0
  7. package/docs/advanced/plugins.md +1006 -0
  8. package/docs/advanced/quantum-resistance.md +660 -0
  9. package/docs/advanced/signature-verification.md +715 -0
  10. package/docs/api-reference/blockchain.md +729 -0
  11. package/docs/api-reference/events.md +642 -0
  12. package/docs/api-reference/op20.md +902 -0
  13. package/docs/api-reference/op721.md +819 -0
  14. package/docs/api-reference/safe-math.md +510 -0
  15. package/docs/api-reference/storage.md +731 -0
  16. package/docs/contracts/op-net-base.md +786 -0
  17. package/docs/contracts/op20-token.md +687 -0
  18. package/docs/contracts/op20s-signatures.md +614 -0
  19. package/docs/contracts/op721-nft.md +785 -0
  20. package/docs/contracts/reentrancy-guard.md +787 -0
  21. package/docs/core-concepts/blockchain-environment.md +724 -0
  22. package/docs/core-concepts/decorators.md +466 -0
  23. package/docs/core-concepts/events.md +652 -0
  24. package/docs/core-concepts/pointers.md +391 -0
  25. package/docs/core-concepts/security.md +370 -0
  26. package/docs/core-concepts/storage-system.md +938 -0
  27. package/docs/examples/basic-token.md +745 -0
  28. package/docs/examples/nft-with-reservations.md +1210 -0
  29. package/docs/examples/oracle-integration.md +1212 -0
  30. package/docs/examples/stablecoin.md +1180 -0
  31. package/docs/getting-started/first-contract.md +575 -0
  32. package/docs/getting-started/installation.md +384 -0
  33. package/docs/getting-started/project-structure.md +630 -0
  34. package/docs/storage/memory-maps.md +721 -0
  35. package/docs/storage/stored-arrays.md +714 -0
  36. package/docs/storage/stored-maps.md +686 -0
  37. package/docs/storage/stored-primitives.md +608 -0
  38. package/docs/types/address.md +773 -0
  39. package/docs/types/bytes-writer-reader.md +938 -0
  40. package/docs/types/calldata.md +744 -0
  41. package/docs/types/safe-math.md +403 -0
  42. package/package.json +51 -26
  43. package/runtime/memory/MapOfMap.ts +1 -0
  44. package/runtime/types/SafeMath.ts +121 -1
  45. package/LICENSE.md +0 -21
@@ -0,0 +1,579 @@
1
+ # Cross-Contract Calls
2
+
3
+ Cross-contract calls enable contracts to interact with each other, building composable protocols. This guide covers making calls, handling responses, and security considerations.
4
+
5
+ ## Architecture Overview
6
+
7
+ ```mermaid
8
+ ---
9
+ config:
10
+ theme: dark
11
+ ---
12
+ sequenceDiagram
13
+ participant ContractA as Contract A
14
+ participant Blockchain as OPNet Runtime
15
+ participant ContractB as Contract B
16
+
17
+ Note over ContractA: Prepare calldata
18
+ ContractA->>ContractA: Create BytesWriter
19
+ ContractA->>ContractA: writeSelector(TRANSFER_SELECTOR)
20
+ ContractA->>ContractA: writeAddress(recipient)
21
+ ContractA->>ContractA: writeU256(amount)
22
+
23
+ Note over ContractA,Blockchain: Make cross-contract call
24
+ ContractA->>Blockchain: call(ContractB, calldata, stopOnFailure)
25
+
26
+ Note over Blockchain: Runtime validates and dispatches
27
+ Blockchain->>ContractB: execute(selector, calldata)
28
+
29
+ Note over ContractB: Process method
30
+ ContractB->>ContractB: Parse calldata
31
+ ContractB->>ContractB: Execute transfer logic
32
+ ContractB->>ContractB: Create BytesWriter response
33
+
34
+ Note over ContractB,Blockchain: Return result
35
+ ContractB-->>Blockchain: BytesWriter response
36
+
37
+ Note over Blockchain: Wrap in CallResult
38
+ Blockchain-->>ContractA: CallResult{success: true, data: bytes}
39
+
40
+ Note over ContractA: Process response
41
+ ContractA->>ContractA: Check result.success
42
+ ContractA->>ContractA: Parse result.data
43
+ ```
44
+
45
+ ## Overview
46
+
47
+ ```typescript
48
+ import { Blockchain, Address, CallResult, BytesWriter, encodeSelector } from '@btc-vision/btc-runtime/runtime';
49
+
50
+ // Define method selectors (sha256 first 4 bytes of method signature)
51
+ const TRANSFER_SELECTOR: u32 = 0xa9059cbb; // transfer(address,uint256)
52
+ const BALANCE_OF_SELECTOR: u32 = 0x70a08231; // balanceOf(address)
53
+
54
+ // Make a call to another contract
55
+ const result: CallResult = Blockchain.call(
56
+ targetContract, // Address of contract to call
57
+ calldata, // BytesWriter with encoded function call
58
+ stopOnFailure // Revert entire tx on failure?
59
+ );
60
+
61
+ if (result.success) {
62
+ // Process result.data
63
+ }
64
+ ```
65
+
66
+ ## Making Calls
67
+
68
+ ### Basic Call
69
+
70
+ ```typescript
71
+ // Define method selector
72
+ const TRANSFER_SELECTOR: u32 = 0xa9059cbb; // transfer(address,uint256)
73
+
74
+ // Encode the call
75
+ const writer = new BytesWriter(68);
76
+ writer.writeSelector(TRANSFER_SELECTOR);
77
+ writer.writeAddress(recipient);
78
+ writer.writeU256(amount);
79
+
80
+ // Make the call
81
+ const result = Blockchain.call(tokenContract, writer, true);
82
+ ```
83
+
84
+ ### Call Parameters
85
+
86
+ | Parameter | Type | Description |
87
+ |-----------|------|-------------|
88
+ | `destinationContract` | `Address` | Contract address to call |
89
+ | `calldata` | `BytesWriter` | Encoded method + parameters |
90
+ | `stopExecutionOnFailure` | `boolean` | If true, revert on call failure (default: `true`) |
91
+
92
+ ### stopOnFailure Behavior
93
+
94
+ ```mermaid
95
+ flowchart LR
96
+ subgraph OPNet["OPNet Cross-Contract Call Flow"]
97
+ A["Blockchain.call<br/>target, data, stopOnFailure"] --> B{"stopOnFailure?"}
98
+
99
+ B -->|"true"| C["Execute call"]
100
+ B -->|"false"| D["Execute call"]
101
+
102
+ C --> E{"Call successful?"}
103
+ D --> F{"Call successful?"}
104
+
105
+ E -->|"Yes"| G["Return CallResult<br/>success: true<br/>data: response"]
106
+ E -->|"No"| H["REVERT ENTIRE TX<br/>Execution stops here"]
107
+
108
+ F -->|"Yes"| I["Return CallResult<br/>success: true<br/>data: response"]
109
+ F -->|"No"| J["Return CallResult<br/>success: false<br/>data: empty"]
110
+
111
+ G --> K["Continue execution"]
112
+ I --> L["Continue execution"]
113
+ J --> M["Continue execution<br/>Check result.success"]
114
+ end
115
+ ```
116
+
117
+ ```typescript
118
+ // stopOnFailure = true: Entire transaction reverts if call fails
119
+ const result = Blockchain.call(target, data, true);
120
+ // If call fails, we never reach this line
121
+
122
+ // stopOnFailure = false: Continue execution on failure
123
+ const result = Blockchain.call(target, data, false);
124
+ if (!result.success) {
125
+ // Handle failure gracefully
126
+ // Transaction continues
127
+ }
128
+ ```
129
+
130
+ ## Handling Results
131
+
132
+ ### CallResult Structure
133
+
134
+ ```typescript
135
+ class CallResult {
136
+ public readonly success: boolean; // Did the call succeed?
137
+ public readonly data: BytesReader; // Return data reader for parsing response
138
+ }
139
+ ```
140
+
141
+ ### Processing Return Data
142
+
143
+ ```typescript
144
+ // Make call
145
+ const result = Blockchain.call(tokenContract, getBalanceCalldata, true);
146
+
147
+ // Parse return data (result.data is already a BytesReader)
148
+ const balance: u256 = result.data.readU256();
149
+ ```
150
+
151
+ ### Error Handling
152
+
153
+ ```typescript
154
+ // Option 1: Strict (revert on failure)
155
+ const result = Blockchain.call(target, data, true);
156
+ // Execution only continues if successful
157
+
158
+ // Option 2: Graceful handling
159
+ const result = Blockchain.call(target, data, false);
160
+ if (!result.success) {
161
+ // Log, emit event, try fallback, etc.
162
+ Blockchain.emit(new CallFailedEvent(target));
163
+ throw new Revert('External call failed');
164
+ }
165
+ ```
166
+
167
+ ## Common Patterns
168
+
169
+ ### Calling Token Transfers
170
+
171
+ ```typescript
172
+ // Define method selector at the top of your contract
173
+ const TRANSFER_SELECTOR: u32 = 0xa9059cbb; // transfer(address,uint256)
174
+
175
+ @method(
176
+ { name: 'token', type: ABIDataTypes.ADDRESS },
177
+ { name: 'to', type: ABIDataTypes.ADDRESS },
178
+ { name: 'amount', type: ABIDataTypes.UINT256 },
179
+ )
180
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
181
+ public transferToken(calldata: Calldata): BytesWriter {
182
+ const token = calldata.readAddress();
183
+ const to = calldata.readAddress();
184
+ const amount = calldata.readU256();
185
+
186
+ // Encode transfer(address,uint256)
187
+ const writer = new BytesWriter(68);
188
+ writer.writeSelector(TRANSFER_SELECTOR);
189
+ writer.writeAddress(to);
190
+ writer.writeU256(amount);
191
+
192
+ // Make call
193
+ const result = Blockchain.call(token, writer, true);
194
+
195
+ // Parse result (transfer returns bool in many implementations)
196
+ // result.data is already a BytesReader
197
+ if (result.data.byteLength > 0) {
198
+ const success = result.data.readBoolean();
199
+ if (!success) {
200
+ throw new Revert('Token transfer failed');
201
+ }
202
+ }
203
+
204
+ const response = new BytesWriter(1);
205
+ response.writeBoolean(true);
206
+ return response;
207
+ }
208
+ ```
209
+
210
+ ### Calling TransferFrom
211
+
212
+ ```typescript
213
+ // Define method selector at the top of your contract
214
+ const TRANSFER_FROM_SELECTOR: u32 = 0x23b872dd; // transferFrom(address,address,uint256)
215
+
216
+ @method(
217
+ { name: 'token', type: ABIDataTypes.ADDRESS },
218
+ { name: 'from', type: ABIDataTypes.ADDRESS },
219
+ { name: 'amount', type: ABIDataTypes.UINT256 },
220
+ )
221
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
222
+ public pullTokens(calldata: Calldata): BytesWriter {
223
+ const token = calldata.readAddress();
224
+ const from = calldata.readAddress();
225
+ const amount = calldata.readU256();
226
+
227
+ // Encode transferFrom(address,address,uint256)
228
+ const writer = new BytesWriter(100);
229
+ writer.writeSelector(TRANSFER_FROM_SELECTOR);
230
+ writer.writeAddress(from);
231
+ writer.writeAddress(Blockchain.contract.address);
232
+ writer.writeU256(amount);
233
+
234
+ const result = Blockchain.call(token, writer, true);
235
+
236
+ // Verify success - result.data is already a BytesReader
237
+ if (result.data.byteLength > 0) {
238
+ if (!result.data.readBoolean()) {
239
+ throw new Revert('TransferFrom failed');
240
+ }
241
+ }
242
+
243
+ const response = new BytesWriter(1);
244
+ response.writeBoolean(true);
245
+ return response;
246
+ }
247
+ ```
248
+
249
+ ### Querying Another Contract
250
+
251
+ ```typescript
252
+ // Define method selector at the top of your contract
253
+ const BALANCE_OF_SELECTOR: u32 = 0x70a08231; // balanceOf(address)
254
+
255
+ @method(
256
+ { name: 'token', type: ABIDataTypes.ADDRESS },
257
+ { name: 'account', type: ABIDataTypes.ADDRESS },
258
+ )
259
+ @returns({ name: 'balance', type: ABIDataTypes.UINT256 })
260
+ public getExternalBalance(calldata: Calldata): BytesWriter {
261
+ const token = calldata.readAddress();
262
+ const account = calldata.readAddress();
263
+
264
+ // Encode balanceOf(address)
265
+ const writer = new BytesWriter(36);
266
+ writer.writeSelector(BALANCE_OF_SELECTOR);
267
+ writer.writeAddress(account);
268
+
269
+ const result = Blockchain.call(token, writer, true);
270
+
271
+ // result.data is already a BytesReader
272
+ const balance = result.data.readU256();
273
+
274
+ const response = new BytesWriter(32);
275
+ response.writeU256(balance);
276
+ return response;
277
+ }
278
+ ```
279
+
280
+ ### Multi-Call Pattern
281
+
282
+ ```typescript
283
+ // Define method selector at the top of your contract
284
+ const TRANSFER_SELECTOR: u32 = 0xa9059cbb; // transfer(address,uint256)
285
+
286
+ @method(
287
+ { name: 'tokens', type: ABIDataTypes.ADDRESS_ARRAY },
288
+ { name: 'recipients', type: ABIDataTypes.ADDRESS_ARRAY },
289
+ { name: 'amounts', type: ABIDataTypes.UINT256_ARRAY },
290
+ )
291
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
292
+ public batchTransfer(calldata: Calldata): BytesWriter {
293
+ const tokens = calldata.readAddressArray();
294
+ const recipients = calldata.readAddressArray();
295
+ const amounts = calldata.readU256Array();
296
+
297
+ for (let i: i32 = 0; i < tokens.length; i++) {
298
+ // Encode transfer(address,uint256)
299
+ const writer = new BytesWriter(68);
300
+ writer.writeSelector(TRANSFER_SELECTOR);
301
+ writer.writeAddress(recipients[i]);
302
+ writer.writeU256(amounts[i]);
303
+
304
+ Blockchain.call(tokens[i], writer, true);
305
+ }
306
+
307
+ const response = new BytesWriter(1);
308
+ response.writeBoolean(true);
309
+ return response;
310
+ }
311
+ ```
312
+
313
+ ## Solidity Comparison
314
+
315
+ | Solidity | OPNet |
316
+ |----------|-------|
317
+ | `token.transfer(to, amount)` | `Blockchain.call(token, encodeTransfer(...), true)` |
318
+ | `(bool s, bytes memory d) = target.call(data)` | `Blockchain.call(target, data, false)` |
319
+ | `target.delegatecall(data)` | Not supported |
320
+ | `try/catch` | `stopOnFailure=false` + manual check |
321
+
322
+ ### Example Comparison
323
+
324
+ ```solidity
325
+ // Solidity
326
+ contract Router {
327
+ function swap(address token, uint256 amount) external {
328
+ IERC20(token).transferFrom(msg.sender, address(this), amount);
329
+ // ... swap logic ...
330
+ IERC20(outputToken).transfer(msg.sender, outputAmount);
331
+ }
332
+ }
333
+ ```
334
+
335
+ ```typescript
336
+ // OPNet
337
+ import { OP_NET, Blockchain, Address, Calldata, BytesWriter, ABIDataTypes, method, returns } from '@btc-vision/btc-runtime/runtime';
338
+ import { u256 } from '@btc-vision/as-bignum/assembly';
339
+
340
+ // Selectors for calling OTHER contracts (cross-contract calls only)
341
+ const TRANSFER_SELECTOR: u32 = 0xa9059cbb; // transfer(address,uint256)
342
+ const TRANSFER_FROM_SELECTOR: u32 = 0x23b872dd; // transferFrom(address,address,uint256)
343
+
344
+ @final
345
+ export class Router extends OP_NET {
346
+ public constructor() {
347
+ super();
348
+ }
349
+
350
+ @method(
351
+ { name: 'token', type: ABIDataTypes.ADDRESS },
352
+ { name: 'amount', type: ABIDataTypes.UINT256 },
353
+ )
354
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
355
+ public swap(calldata: Calldata): BytesWriter {
356
+ const token = calldata.readAddress();
357
+ const amount = calldata.readU256();
358
+
359
+ // Pull tokens
360
+ this.pullTokens(token, Blockchain.tx.sender, amount);
361
+
362
+ // ... swap logic ...
363
+
364
+ // Send output
365
+ this.transferToken(outputToken, Blockchain.tx.sender, outputAmount);
366
+
367
+ const response = new BytesWriter(1);
368
+ response.writeBoolean(true);
369
+ return response;
370
+ }
371
+
372
+ private pullTokens(token: Address, from: Address, amount: u256): void {
373
+ const writer = new BytesWriter(100);
374
+ writer.writeSelector(TRANSFER_FROM_SELECTOR);
375
+ writer.writeAddress(from);
376
+ writer.writeAddress(Blockchain.contract.address);
377
+ writer.writeU256(amount);
378
+
379
+ Blockchain.call(token, writer, true);
380
+ }
381
+
382
+ private transferToken(token: Address, to: Address, amount: u256): void {
383
+ const writer = new BytesWriter(68);
384
+ writer.writeSelector(TRANSFER_SELECTOR);
385
+ writer.writeAddress(to);
386
+ writer.writeU256(amount);
387
+
388
+ Blockchain.call(token, writer, true);
389
+ }
390
+ }
391
+ ```
392
+
393
+ ## Security Considerations
394
+
395
+ ### 1. Reentrancy Risk
396
+
397
+ External calls can trigger callbacks:
398
+
399
+ ```typescript
400
+ // Define method selector at the top
401
+ const TRANSFER_SELECTOR: u32 = 0xa9059cbb; // transfer(address,uint256)
402
+
403
+ // VULNERABLE
404
+ @method()
405
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
406
+ public withdraw(_calldata: Calldata): BytesWriter {
407
+ const sender = Blockchain.tx.sender;
408
+ const amount = balances.get(sender);
409
+
410
+ const writer = new BytesWriter(68);
411
+ writer.writeSelector(TRANSFER_SELECTOR);
412
+ writer.writeAddress(sender);
413
+ writer.writeU256(amount);
414
+
415
+ Blockchain.call(token, writer, true);
416
+ // ^ Called contract could call back into this function
417
+ balances.set(sender, u256.Zero);
418
+
419
+ return new BytesWriter(0);
420
+ }
421
+
422
+ // SAFE: Update state before call
423
+ @method()
424
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
425
+ public withdraw(_calldata: Calldata): BytesWriter {
426
+ const sender = Blockchain.tx.sender;
427
+ const amount = balances.get(sender);
428
+ balances.set(sender, u256.Zero); // State update first
429
+
430
+ const writer = new BytesWriter(68);
431
+ writer.writeSelector(TRANSFER_SELECTOR);
432
+ writer.writeAddress(sender);
433
+ writer.writeU256(amount);
434
+
435
+ Blockchain.call(token, writer, true);
436
+
437
+ return new BytesWriter(0);
438
+ }
439
+
440
+ // Or use ReentrancyGuard
441
+ ```
442
+
443
+ ### 2. Return Value Validation
444
+
445
+ ```typescript
446
+ // Always validate return data
447
+ const result = Blockchain.call(token, data, true);
448
+
449
+ // Don't assume success based only on not reverting
450
+ // result.data is a BytesReader with byteLength property
451
+ if (result.data.byteLength > 0) {
452
+ const success = result.data.readBoolean();
453
+ if (!success) {
454
+ throw new Revert('Call returned false');
455
+ }
456
+ }
457
+ ```
458
+
459
+ ### 3. Trust Assumptions
460
+
461
+ ```typescript
462
+ @method({ name: 'target', type: ABIDataTypes.ADDRESS })
463
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
464
+ public callExternalContract(calldata: Calldata): BytesWriter {
465
+ const target = calldata.readAddress();
466
+
467
+ // Only call trusted contracts
468
+ // Malicious contracts can:
469
+ // - Consume excessive resources
470
+ // - Return malicious data
471
+ // - Re-enter your contract
472
+
473
+ // Validate contract addresses
474
+ if (!this.trustedContracts.has(target)) {
475
+ throw new Revert('Untrusted contract');
476
+ }
477
+
478
+ // Make the call...
479
+ return new BytesWriter(0);
480
+ }
481
+ ```
482
+
483
+ ## Advanced Patterns
484
+
485
+ ### Interface Abstraction
486
+
487
+ ```typescript
488
+ // Define method selectors at the top
489
+ const TRANSFER_SELECTOR: u32 = 0xa9059cbb; // transfer(address,uint256)
490
+ const BALANCE_OF_SELECTOR: u32 = 0x70a08231; // balanceOf(address)
491
+
492
+ // Create helper class for common calls
493
+ class TokenInterface {
494
+ constructor(private address: Address) {}
495
+
496
+ public transfer(to: Address, amount: u256): void {
497
+ const writer = new BytesWriter(68);
498
+ writer.writeSelector(TRANSFER_SELECTOR);
499
+ writer.writeAddress(to);
500
+ writer.writeU256(amount);
501
+ Blockchain.call(this.address, writer, true);
502
+ }
503
+
504
+ public balanceOf(account: Address): u256 {
505
+ const writer = new BytesWriter(36);
506
+ writer.writeSelector(BALANCE_OF_SELECTOR);
507
+ writer.writeAddress(account);
508
+
509
+ const result = Blockchain.call(this.address, writer, true);
510
+ // result.data is already a BytesReader
511
+ return result.data.readU256();
512
+ }
513
+ }
514
+
515
+ // Usage
516
+ const token = new TokenInterface(tokenAddress);
517
+ token.transfer(recipient, amount);
518
+ const balance = token.balanceOf(user);
519
+ ```
520
+
521
+ ### Callback Pattern
522
+
523
+ ```typescript
524
+ // Define method selector at the top
525
+ const ON_TOKEN_RECEIVED_SELECTOR: u32 = 0x150b7a02; // onTokenReceived(address,uint256,bytes)
526
+
527
+ // Contract that accepts callbacks
528
+ @method(
529
+ { name: 'from', type: ABIDataTypes.ADDRESS },
530
+ { name: 'amount', type: ABIDataTypes.UINT256 },
531
+ { name: 'data', type: ABIDataTypes.BYTES },
532
+ )
533
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
534
+ public onTokenReceived(calldata: Calldata): BytesWriter {
535
+ const from = calldata.readAddress();
536
+ const amount = calldata.readU256();
537
+ const data = calldata.readBytesWithLength();
538
+
539
+ // Process callback
540
+ // ...
541
+
542
+ return new BytesWriter(0);
543
+ }
544
+
545
+ // Calling contract notifies via callback
546
+ @method(
547
+ { name: 'to', type: ABIDataTypes.ADDRESS },
548
+ { name: 'tokenId', type: ABIDataTypes.UINT256 },
549
+ { name: 'data', type: ABIDataTypes.BYTES },
550
+ )
551
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
552
+ public safeTransfer(calldata: Calldata): BytesWriter {
553
+ const to = calldata.readAddress();
554
+ const tokenId = calldata.readU256();
555
+ const data = calldata.readBytesWithLength();
556
+ const from = Blockchain.tx.sender;
557
+
558
+ this._transfer(from, to, tokenId);
559
+
560
+ // Check if receiver is a contract
561
+ // If so, call onTokenReceived
562
+ const writer = new BytesWriter(/* size */);
563
+ writer.writeSelector(ON_TOKEN_RECEIVED_SELECTOR);
564
+ writer.writeAddress(from);
565
+ writer.writeU256(tokenId);
566
+ writer.writeBytesWithLength(data);
567
+
568
+ const result = Blockchain.call(to, writer, false);
569
+ // Validate response...
570
+
571
+ return new BytesWriter(0);
572
+ }
573
+ ```
574
+
575
+ ---
576
+
577
+ **Navigation:**
578
+ - Previous: [Memory Maps](../storage/memory-maps.md)
579
+ - Next: [Signature Verification](./signature-verification.md)