@btc-vision/btc-runtime 1.11.0-rc.9 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/docs/README.md +39 -39
- package/docs/advanced/bitcoin-scripts.md +17 -17
- package/docs/advanced/{contract-upgrades.md → contract-updates.md} +90 -98
- package/docs/advanced/cross-contract-calls.md +4 -4
- package/docs/advanced/plugins.md +21 -21
- package/docs/advanced/quantum-resistance.md +32 -32
- package/docs/advanced/signature-verification.md +22 -22
- package/docs/api-reference/blockchain.md +14 -14
- package/docs/api-reference/events.md +2 -2
- package/docs/api-reference/op20.md +7 -7
- package/docs/api-reference/op721.md +7 -7
- package/docs/api-reference/storage.md +2 -2
- package/docs/contracts/op-net-base.md +15 -15
- package/docs/contracts/op20-token.md +3 -3
- package/docs/contracts/op20s-signatures.md +2 -2
- package/docs/contracts/op721-nft.md +3 -3
- package/docs/contracts/reentrancy-guard.md +5 -7
- package/docs/contracts/updatable.md +384 -0
- package/docs/core-concepts/blockchain-environment.md +10 -10
- package/docs/core-concepts/decorators.md +5 -5
- package/docs/core-concepts/events.md +6 -6
- package/docs/core-concepts/pointers.md +5 -5
- package/docs/core-concepts/security.md +5 -5
- package/docs/core-concepts/storage-system.md +24 -24
- package/docs/examples/basic-token.md +8 -8
- package/docs/examples/nft-with-reservations.md +9 -9
- package/docs/examples/oracle-integration.md +13 -13
- package/docs/examples/stablecoin.md +10 -10
- package/docs/getting-started/first-contract.md +8 -8
- package/docs/getting-started/installation.md +2 -2
- package/docs/getting-started/project-structure.md +6 -6
- package/docs/storage/memory-maps.md +8 -8
- package/docs/storage/stored-arrays.md +6 -6
- package/docs/storage/stored-maps.md +8 -8
- package/docs/storage/stored-primitives.md +6 -6
- package/docs/types/address.md +13 -13
- package/docs/types/bytes-writer-reader.md +18 -18
- package/docs/types/calldata.md +12 -12
- package/package.json +10 -10
- package/runtime/constants/Exports.ts +0 -30
- package/runtime/contracts/OP20.ts +7 -7
- package/runtime/contracts/OP721.ts +60 -74
- package/runtime/contracts/OP_NET.ts +2 -2
- package/runtime/contracts/ReentrancyGuard.ts +1 -5
- package/runtime/contracts/Updatable.ts +241 -0
- package/runtime/contracts/interfaces/OP721InitParameters.ts +8 -8
- package/runtime/env/BlockchainEnvironment.ts +5 -5
- package/runtime/env/global.ts +7 -6
- package/runtime/events/predefined/{ApprovedEvent.ts → OP20ApprovedEvent.ts} +1 -1
- package/runtime/events/predefined/{BurnedEvent.ts → OP20BurnedEvent.ts} +1 -1
- package/runtime/events/predefined/{MintedEvent.ts → OP20MintedEvent.ts} +1 -1
- package/runtime/events/predefined/{TransferredEvent.ts → OP20TransferredEvent.ts} +1 -1
- package/runtime/events/predefined/OP721ApprovedEvent.ts +17 -0
- package/runtime/events/predefined/{ApprovedForAll.ts → OP721ApprovedForAllEvent.ts} +1 -1
- package/runtime/events/predefined/OP721BurnedEvent.ts +16 -0
- package/runtime/events/predefined/OP721MintedEvent.ts +16 -0
- package/runtime/events/predefined/OP721TransferredEvent.ts +18 -0
- package/runtime/events/predefined/index.ts +5 -5
- package/runtime/events/{upgradeable/UpgradeableEvents.ts → updatable/UpdatableEvents.ts} +9 -9
- package/runtime/hashing/keccak256.ts +1 -1
- package/runtime/index.ts +3 -5
- package/runtime/plugins/UpdatablePlugin.ts +276 -0
- package/runtime/script/Networks.ts +1 -1
- package/runtime/storage/StoredBoolean.ts +23 -12
- package/runtime/types/Address.ts +1 -1
- package/runtime/types/ExtendedAddress.ts +1 -1
- package/docs/contracts/upgradeable.md +0 -396
- package/runtime/contracts/Upgradeable.ts +0 -242
- package/runtime/contracts/interfaces/IOP1155.ts +0 -33
- package/runtime/contracts/interfaces/OP1155InitParameters.ts +0 -11
- package/runtime/plugins/UpgradeablePlugin.ts +0 -279
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
# Contract
|
|
1
|
+
# Contract Updates
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
OP_NET provides a native bytecode replacement mechanism that allows contracts to update their execution logic while preserving their address and storage state. This guide covers the update mechanism, security considerations, and the timelock pattern for safe updates.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
Unlike Ethereum's proxy patterns or Solana's upgrade authority model,
|
|
7
|
+
Unlike Ethereum's proxy patterns or Solana's upgrade authority model, OP_NET enables contracts to replace their own bytecode through a VM opcode. The mechanism uses an address-based replacement model where new bytecode is deployed to a temporary contract, and the target contract references that address to perform the update.
|
|
8
8
|
|
|
9
9
|
```typescript
|
|
10
10
|
import { Blockchain } from '@btc-vision/btc-runtime/runtime';
|
|
@@ -22,14 +22,14 @@ sequenceDiagram
|
|
|
22
22
|
participant Owner as Contract Owner
|
|
23
23
|
participant Target as Target Contract
|
|
24
24
|
participant Source as Source Contract<br/>(New Bytecode)
|
|
25
|
-
participant VM as
|
|
25
|
+
participant VM as OP_NET VM
|
|
26
26
|
|
|
27
27
|
Note over Owner: Step 1: Deploy new bytecode
|
|
28
28
|
Owner->>VM: Deploy new contract with updated code
|
|
29
29
|
VM->>Source: Create contract at new address
|
|
30
30
|
|
|
31
|
-
Note over Owner: Step 2: Call
|
|
32
|
-
Owner->>Target:
|
|
31
|
+
Note over Owner: Step 2: Call update function
|
|
32
|
+
Owner->>Target: update(sourceAddress)
|
|
33
33
|
Target->>Target: Validate permissions
|
|
34
34
|
Target->>Target: Validate source is a contract
|
|
35
35
|
|
|
@@ -63,7 +63,7 @@ Blockchain.updateContractFromExisting(
|
|
|
63
63
|
): void
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
### Simple
|
|
66
|
+
### Simple Update Function
|
|
67
67
|
|
|
68
68
|
```typescript
|
|
69
69
|
import {
|
|
@@ -80,15 +80,15 @@ import {
|
|
|
80
80
|
export class MyContract extends OP_NET {
|
|
81
81
|
public override execute(method: Selector, calldata: Calldata): BytesWriter {
|
|
82
82
|
switch (method) {
|
|
83
|
-
case encodeSelector('
|
|
84
|
-
return this.
|
|
83
|
+
case encodeSelector('update(address)'):
|
|
84
|
+
return this.update(calldata);
|
|
85
85
|
default:
|
|
86
86
|
return super.execute(method, calldata);
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
private
|
|
91
|
-
// Only deployer can
|
|
90
|
+
private update(calldata: Calldata): BytesWriter {
|
|
91
|
+
// Only deployer can update
|
|
92
92
|
this.onlyDeployer(Blockchain.tx.sender);
|
|
93
93
|
|
|
94
94
|
const sourceAddress = calldata.readAddress();
|
|
@@ -98,7 +98,7 @@ export class MyContract extends OP_NET {
|
|
|
98
98
|
throw new Revert('Source must be a deployed contract');
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
// Perform
|
|
101
|
+
// Perform update - takes effect next block
|
|
102
102
|
Blockchain.updateContractFromExisting(sourceAddress);
|
|
103
103
|
|
|
104
104
|
return new BytesWriter(0);
|
|
@@ -108,44 +108,44 @@ export class MyContract extends OP_NET {
|
|
|
108
108
|
|
|
109
109
|
## The Timelock Pattern
|
|
110
110
|
|
|
111
|
-
Immediate
|
|
111
|
+
Immediate updates are risky because users have no time to react to potentially malicious changes. The timelock pattern addresses this by requiring a delay between submitting and applying an update.
|
|
112
112
|
|
|
113
113
|
### Why Use a Timelock?
|
|
114
114
|
|
|
115
|
-
1. **User Protection**: Users can monitor for pending
|
|
116
|
-
2. **Attack Prevention**: Prevents instant malicious
|
|
117
|
-
3. **Transparency**: All pending
|
|
115
|
+
1. **User Protection**: Users can monitor for pending updates and exit if they distrust changes
|
|
116
|
+
2. **Attack Prevention**: Prevents instant malicious updates
|
|
117
|
+
3. **Transparency**: All pending updates are visible on-chain
|
|
118
118
|
|
|
119
119
|
### Timelock Flow
|
|
120
120
|
|
|
121
121
|
```mermaid
|
|
122
122
|
sequenceDiagram
|
|
123
123
|
participant Owner as Contract Owner
|
|
124
|
-
participant Contract as
|
|
124
|
+
participant Contract as Updatable Contract
|
|
125
125
|
participant Users as Users/Indexers
|
|
126
126
|
|
|
127
|
-
Note over Owner: Day 1: Submit
|
|
128
|
-
Owner->>Contract:
|
|
127
|
+
Note over Owner: Day 1: Submit update
|
|
128
|
+
Owner->>Contract: submitUpdate(sourceAddress)
|
|
129
129
|
Contract->>Contract: Store pending address + block
|
|
130
|
-
Contract-->>Users: Emit
|
|
130
|
+
Contract-->>Users: Emit UpdateSubmitted event
|
|
131
131
|
|
|
132
132
|
Note over Users: Users can monitor and exit
|
|
133
|
-
Users->>Users: Review pending
|
|
133
|
+
Users->>Users: Review pending update
|
|
134
134
|
Users->>Users: Exit if distrustful
|
|
135
135
|
|
|
136
136
|
Note over Owner: Day 2+: Apply after delay
|
|
137
|
-
Owner->>Contract:
|
|
137
|
+
Owner->>Contract: applyUpdate(sourceAddress, updateCalldata)
|
|
138
138
|
Contract->>Contract: Verify delay elapsed
|
|
139
139
|
Contract->>Contract: Verify address matches
|
|
140
|
-
Contract->>Contract: Execute
|
|
141
|
-
Contract-->>Users: Emit
|
|
140
|
+
Contract->>Contract: Execute update
|
|
141
|
+
Contract-->>Users: Emit UpdateApplied event
|
|
142
142
|
```
|
|
143
143
|
|
|
144
|
-
### Using the
|
|
144
|
+
### Using the Updatable Base Class
|
|
145
145
|
|
|
146
146
|
```typescript
|
|
147
147
|
import {
|
|
148
|
-
|
|
148
|
+
Updatable,
|
|
149
149
|
Calldata,
|
|
150
150
|
BytesWriter,
|
|
151
151
|
encodeSelector,
|
|
@@ -154,25 +154,21 @@ import {
|
|
|
154
154
|
} from '@btc-vision/btc-runtime/runtime';
|
|
155
155
|
|
|
156
156
|
@final
|
|
157
|
-
export class
|
|
158
|
-
// Set
|
|
159
|
-
protected readonly
|
|
157
|
+
export class MyUpdatableContract extends Updatable {
|
|
158
|
+
// Set update delay: 144 blocks = ~24 hours
|
|
159
|
+
protected readonly updateDelay: u64 = 144;
|
|
160
160
|
|
|
161
161
|
public override execute(method: Selector, calldata: Calldata): BytesWriter {
|
|
162
162
|
switch (method) {
|
|
163
|
-
case encodeSelector('
|
|
164
|
-
return this.
|
|
165
|
-
case encodeSelector('
|
|
163
|
+
case encodeSelector('submitUpdate'):
|
|
164
|
+
return this.submitUpdate(calldata.readAddress());
|
|
165
|
+
case encodeSelector('applyUpdate'): {
|
|
166
166
|
const sourceAddress = calldata.readAddress();
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
if (remainingLength > 0) {
|
|
170
|
-
updateCalldata.writeBytes(calldata.readBytes(remainingLength));
|
|
171
|
-
}
|
|
172
|
-
return this.applyUpgrade(sourceAddress, updateCalldata);
|
|
167
|
+
const updateCalldata = calldata.readBytesWithLength();
|
|
168
|
+
return this.applyUpdate(sourceAddress, updateCalldata);
|
|
173
169
|
}
|
|
174
|
-
case encodeSelector('
|
|
175
|
-
return this.
|
|
170
|
+
case encodeSelector('cancelUpdate'):
|
|
171
|
+
return this.cancelUpdate();
|
|
176
172
|
// ... other methods
|
|
177
173
|
default:
|
|
178
174
|
return super.execute(method, calldata);
|
|
@@ -181,14 +177,14 @@ export class MyUpgradeableContract extends Upgradeable {
|
|
|
181
177
|
}
|
|
182
178
|
```
|
|
183
179
|
|
|
184
|
-
### Using the
|
|
180
|
+
### Using the UpdatablePlugin
|
|
185
181
|
|
|
186
|
-
If you don't want to extend a base class (for example, if you're already extending `OP20` or `OP721`), use the `
|
|
182
|
+
If you don't want to extend a base class (for example, if you're already extending `OP20` or `OP721`), use the `UpdatablePlugin` instead. The plugin system is fully automatic - just register the plugin in your constructor:
|
|
187
183
|
|
|
188
184
|
```typescript
|
|
189
185
|
import {
|
|
190
186
|
OP20,
|
|
191
|
-
|
|
187
|
+
UpdatablePlugin,
|
|
192
188
|
} from '@btc-vision/btc-runtime/runtime';
|
|
193
189
|
|
|
194
190
|
@final
|
|
@@ -196,19 +192,19 @@ export class MyToken extends OP20 {
|
|
|
196
192
|
public constructor() {
|
|
197
193
|
super();
|
|
198
194
|
// Register the plugin - 144 blocks = ~24 hours (default)
|
|
199
|
-
this.registerPlugin(new
|
|
195
|
+
this.registerPlugin(new UpdatablePlugin(144));
|
|
200
196
|
}
|
|
201
197
|
|
|
202
|
-
// No need to modify execute() -
|
|
198
|
+
// No need to modify execute() - update methods are handled automatically!
|
|
203
199
|
}
|
|
204
200
|
```
|
|
205
201
|
|
|
206
202
|
The plugin automatically handles these methods:
|
|
207
|
-
- `
|
|
208
|
-
- `
|
|
209
|
-
- `
|
|
210
|
-
- `
|
|
211
|
-
- `
|
|
203
|
+
- `submitUpdate(address)` - Submit update for timelock
|
|
204
|
+
- `applyUpdate(address,bytes)` - Apply update after delay
|
|
205
|
+
- `cancelUpdate()` - Cancel pending update
|
|
206
|
+
- `pendingUpdate()` - Get pending update info
|
|
207
|
+
- `updateDelay()` - Get configured delay
|
|
212
208
|
|
|
213
209
|
**How it works:** When your contract's `execute()` falls through to `super.execute()`, the base `OP_NET` class automatically checks all registered plugins before throwing "Method not found".
|
|
214
210
|
|
|
@@ -217,30 +213,30 @@ The plugin automatically handles these methods:
|
|
|
217
213
|
| Delay | Blocks | Use Case |
|
|
218
214
|
|-------|--------|----------|
|
|
219
215
|
| ~1 hour | 6 | Emergency patches (not recommended for production) |
|
|
220
|
-
| ~24 hours | 144 | Standard
|
|
216
|
+
| ~24 hours | 144 | Standard updates (default) |
|
|
221
217
|
| ~1 week | 1008 | Critical infrastructure |
|
|
222
218
|
| ~1 month | 4320 | Governance-controlled contracts |
|
|
223
219
|
|
|
224
|
-
###
|
|
220
|
+
### Update Events
|
|
225
221
|
|
|
226
|
-
The `
|
|
222
|
+
The `Updatable` contract emits events for monitoring:
|
|
227
223
|
|
|
228
224
|
```typescript
|
|
229
|
-
// Emitted when an
|
|
230
|
-
class
|
|
225
|
+
// Emitted when an update is submitted
|
|
226
|
+
class UpdateSubmittedEvent {
|
|
231
227
|
sourceAddress: Address; // New bytecode contract
|
|
232
228
|
submitBlock: u64; // Block when submitted
|
|
233
|
-
effectiveBlock: u64; // Block when
|
|
229
|
+
effectiveBlock: u64; // Block when update can be applied
|
|
234
230
|
}
|
|
235
231
|
|
|
236
|
-
// Emitted when an
|
|
237
|
-
class
|
|
232
|
+
// Emitted when an update is applied
|
|
233
|
+
class UpdateAppliedEvent {
|
|
238
234
|
sourceAddress: Address; // New bytecode contract
|
|
239
235
|
appliedAtBlock: u64; // Block when applied
|
|
240
236
|
}
|
|
241
237
|
|
|
242
|
-
// Emitted when a pending
|
|
243
|
-
class
|
|
238
|
+
// Emitted when a pending update is cancelled
|
|
239
|
+
class UpdateCancelledEvent {
|
|
244
240
|
sourceAddress: Address; // Cancelled source contract
|
|
245
241
|
cancelledAtBlock: u64; // Block when cancelled
|
|
246
242
|
}
|
|
@@ -289,11 +285,11 @@ class MyContractV2Bad extends OP_NET {
|
|
|
289
285
|
1. **Never remove or reorder existing pointers**
|
|
290
286
|
2. **Always add new pointers at the end**
|
|
291
287
|
3. **Document pointer assignments**
|
|
292
|
-
4. **Test
|
|
288
|
+
4. **Test updates thoroughly on testnet**
|
|
293
289
|
|
|
294
290
|
## The onUpdate Lifecycle Hook
|
|
295
291
|
|
|
296
|
-
When a contract's bytecode is updated via `updateContractFromExisting`, the VM calls the `onUpdate` hook on the **new** bytecode. This allows the new contract version to perform migrations, initialize new storage, or validate the
|
|
292
|
+
When a contract's bytecode is updated via `updateContractFromExisting`, the VM calls the `onUpdate` hook on the **new** bytecode. This allows the new contract version to perform migrations, initialize new storage, or validate the update.
|
|
297
293
|
|
|
298
294
|
### Basic Usage
|
|
299
295
|
|
|
@@ -350,7 +346,7 @@ private migrateFromV2(): void {
|
|
|
350
346
|
Then pass the version when upgrading:
|
|
351
347
|
|
|
352
348
|
```typescript
|
|
353
|
-
// In the
|
|
349
|
+
// In the update transaction
|
|
354
350
|
const migrationData = new BytesWriter(8);
|
|
355
351
|
migrationData.writeU64(1); // Migrating from version 1
|
|
356
352
|
|
|
@@ -391,15 +387,15 @@ if (!Blockchain.isContract(sourceAddress)) {
|
|
|
391
387
|
This prevents an attacker from:
|
|
392
388
|
1. Submitting a not-yet-deployed address
|
|
393
389
|
2. Deploying malicious bytecode just before applying
|
|
394
|
-
3. Front-running the
|
|
390
|
+
3. Front-running the update
|
|
395
391
|
|
|
396
392
|
### Address Verification at Apply
|
|
397
393
|
|
|
398
|
-
The `
|
|
394
|
+
The `applyUpdate` function requires the address parameter to match the pending update:
|
|
399
395
|
|
|
400
396
|
```typescript
|
|
401
|
-
if (!sourceAddress.equals(this.
|
|
402
|
-
throw new Revert('Address does not match pending
|
|
397
|
+
if (!sourceAddress.equals(this.pendingUpdateAddress)) {
|
|
398
|
+
throw new Revert('Address does not match pending update');
|
|
403
399
|
}
|
|
404
400
|
```
|
|
405
401
|
|
|
@@ -414,7 +410,7 @@ The VM does not enforce any specific permission model. Implement appropriate acc
|
|
|
414
410
|
this.onlyDeployer(Blockchain.tx.sender);
|
|
415
411
|
|
|
416
412
|
// Advanced: Multisig or governance
|
|
417
|
-
if (!this.
|
|
413
|
+
if (!this.isAuthorizedUpdater(Blockchain.tx.sender)) {
|
|
418
414
|
throw new Revert('Not authorized');
|
|
419
415
|
}
|
|
420
416
|
```
|
|
@@ -429,7 +425,7 @@ This provides a clean transition with no mid-block ambiguity.
|
|
|
429
425
|
|
|
430
426
|
## Comparison with Other Platforms
|
|
431
427
|
|
|
432
|
-
| Feature |
|
|
428
|
+
| Feature | OP_NET | Ethereum | Solana |
|
|
433
429
|
|---------|-------|----------|--------|
|
|
434
430
|
| Mechanism | VM opcode | Proxy + delegatecall | Upgrade authority |
|
|
435
431
|
| Delay | Contract-level (recommended) | Contract-level only | None (instant) |
|
|
@@ -441,7 +437,7 @@ This provides a clean transition with no mid-block ambiguity.
|
|
|
441
437
|
|
|
442
438
|
```typescript
|
|
443
439
|
import {
|
|
444
|
-
|
|
440
|
+
Updatable,
|
|
445
441
|
Blockchain,
|
|
446
442
|
Calldata,
|
|
447
443
|
BytesWriter,
|
|
@@ -453,9 +449,9 @@ import {
|
|
|
453
449
|
} from '@btc-vision/btc-runtime/runtime';
|
|
454
450
|
|
|
455
451
|
@final
|
|
456
|
-
export class
|
|
457
|
-
// 1-week
|
|
458
|
-
protected readonly
|
|
452
|
+
export class UpdatableVault extends Updatable {
|
|
453
|
+
// 1-week update delay for security
|
|
454
|
+
protected readonly updateDelay: u64 = 1008;
|
|
459
455
|
|
|
460
456
|
// Storage pointers - never reorder these
|
|
461
457
|
private totalDepositedPointer: u16 = Blockchain.nextPointer;
|
|
@@ -471,26 +467,22 @@ export class UpgradeableVault extends Upgradeable {
|
|
|
471
467
|
|
|
472
468
|
public override execute(method: Selector, calldata: Calldata): BytesWriter {
|
|
473
469
|
switch (method) {
|
|
474
|
-
//
|
|
475
|
-
case encodeSelector('
|
|
476
|
-
return this.
|
|
477
|
-
case encodeSelector('
|
|
470
|
+
// Update methods
|
|
471
|
+
case encodeSelector('submitUpdate'):
|
|
472
|
+
return this.submitUpdate(calldata.readAddress());
|
|
473
|
+
case encodeSelector('applyUpdate'): {
|
|
478
474
|
const sourceAddress = calldata.readAddress();
|
|
479
|
-
const
|
|
480
|
-
|
|
481
|
-
if (remainingLength > 0) {
|
|
482
|
-
updateCalldata.writeBytes(calldata.readBytes(remainingLength));
|
|
483
|
-
}
|
|
484
|
-
return this.applyUpgrade(sourceAddress, updateCalldata);
|
|
475
|
+
const updateCalldata = calldata.readBytesWithLength();
|
|
476
|
+
return this.applyUpdate(sourceAddress, updateCalldata);
|
|
485
477
|
}
|
|
486
|
-
case encodeSelector('
|
|
487
|
-
return this.
|
|
478
|
+
case encodeSelector('cancelUpdate'):
|
|
479
|
+
return this.cancelUpdate();
|
|
488
480
|
|
|
489
|
-
// View methods for
|
|
490
|
-
case encodeSelector('
|
|
491
|
-
return this.
|
|
492
|
-
case encodeSelector('
|
|
493
|
-
return this.
|
|
481
|
+
// View methods for update status
|
|
482
|
+
case encodeSelector('pendingUpdate'):
|
|
483
|
+
return this.getPendingUpdate();
|
|
484
|
+
case encodeSelector('updateEffectiveBlock'):
|
|
485
|
+
return this.getUpdateEffectiveBlock();
|
|
494
486
|
|
|
495
487
|
// Business logic
|
|
496
488
|
case encodeSelector('deposit'):
|
|
@@ -501,16 +493,16 @@ export class UpgradeableVault extends Upgradeable {
|
|
|
501
493
|
}
|
|
502
494
|
}
|
|
503
495
|
|
|
504
|
-
private
|
|
496
|
+
private getPendingUpdate(): BytesWriter {
|
|
505
497
|
const response = new BytesWriter(40);
|
|
506
|
-
response.writeAddress(this.
|
|
507
|
-
response.writeU64(this.
|
|
498
|
+
response.writeAddress(this.pendingUpdateAddress);
|
|
499
|
+
response.writeU64(this.pendingUpdateBlock);
|
|
508
500
|
return response;
|
|
509
501
|
}
|
|
510
502
|
|
|
511
|
-
private
|
|
503
|
+
private getUpdateEffectiveBlock(): BytesWriter {
|
|
512
504
|
const response = new BytesWriter(8);
|
|
513
|
-
response.writeU64(this.
|
|
505
|
+
response.writeU64(this.updateEffectiveBlock);
|
|
514
506
|
return response;
|
|
515
507
|
}
|
|
516
508
|
|
|
@@ -521,13 +513,13 @@ export class UpgradeableVault extends Upgradeable {
|
|
|
521
513
|
}
|
|
522
514
|
```
|
|
523
515
|
|
|
524
|
-
##
|
|
516
|
+
## Update Workflow
|
|
525
517
|
|
|
526
518
|
1. **Develop and test new version** on testnet
|
|
527
519
|
2. **Deploy new bytecode** as a separate contract
|
|
528
|
-
3. **Submit
|
|
520
|
+
3. **Submit update** with `submitUpdate(newAddress)`
|
|
529
521
|
4. **Wait for delay** (users can exit during this period)
|
|
530
|
-
5. **Apply
|
|
522
|
+
5. **Apply update** with `applyUpdate(newAddress, updateCalldata)`
|
|
531
523
|
6. **Verify** new functionality works correctly
|
|
532
524
|
|
|
533
525
|
---
|
|
@@ -11,7 +11,7 @@ config:
|
|
|
11
11
|
---
|
|
12
12
|
sequenceDiagram
|
|
13
13
|
participant ContractA as Contract A
|
|
14
|
-
participant Blockchain as
|
|
14
|
+
participant Blockchain as OP_NET Runtime
|
|
15
15
|
participant ContractB as Contract B
|
|
16
16
|
|
|
17
17
|
Note over ContractA: Prepare calldata
|
|
@@ -93,7 +93,7 @@ const result = Blockchain.call(tokenContract, writer, true);
|
|
|
93
93
|
|
|
94
94
|
```mermaid
|
|
95
95
|
flowchart LR
|
|
96
|
-
subgraph
|
|
96
|
+
subgraph OP_NET["OP_NET Cross-Contract Call Flow"]
|
|
97
97
|
A["Blockchain.call<br/>target, data, stopOnFailure"] --> B{"stopOnFailure?"}
|
|
98
98
|
|
|
99
99
|
B -->|"true"| C["Execute call"]
|
|
@@ -312,7 +312,7 @@ public batchTransfer(calldata: Calldata): BytesWriter {
|
|
|
312
312
|
|
|
313
313
|
## Solidity Comparison
|
|
314
314
|
|
|
315
|
-
| Solidity |
|
|
315
|
+
| Solidity | OP_NET |
|
|
316
316
|
|----------|-------|
|
|
317
317
|
| `token.transfer(to, amount)` | `Blockchain.call(token, encodeTransfer(...), true)` |
|
|
318
318
|
| `(bool s, bytes memory d) = target.call(data)` | `Blockchain.call(target, data, false)` |
|
|
@@ -333,7 +333,7 @@ contract Router {
|
|
|
333
333
|
```
|
|
334
334
|
|
|
335
335
|
```typescript
|
|
336
|
-
//
|
|
336
|
+
// OP_NET
|
|
337
337
|
import { OP_NET, Blockchain, Address, Calldata, BytesWriter, ABIDataTypes, method, returns } from '@btc-vision/btc-runtime/runtime';
|
|
338
338
|
import { u256 } from '@btc-vision/as-bignum/assembly';
|
|
339
339
|
|
package/docs/advanced/plugins.md
CHANGED
|
@@ -48,7 +48,7 @@ sequenceDiagram
|
|
|
48
48
|
participant Deployer as 👤 Deployer
|
|
49
49
|
participant Contract as Contract
|
|
50
50
|
participant Plugin as Plugin
|
|
51
|
-
participant Blockchain as
|
|
51
|
+
participant Blockchain as OP_NET Runtime
|
|
52
52
|
|
|
53
53
|
Note over Deployer,Blockchain: Contract Deployment
|
|
54
54
|
Deployer->>Contract: constructor()
|
|
@@ -180,7 +180,7 @@ class LoggingPlugin extends Plugin {
|
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
public override onUpdate(calldata: Calldata): void {
|
|
183
|
-
// Handle contract
|
|
183
|
+
// Handle contract updates
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
public override onExecutionStarted(selector: Selector, calldata: Calldata): void {
|
|
@@ -192,7 +192,7 @@ class LoggingPlugin extends Plugin {
|
|
|
192
192
|
}
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
-
// Plugin that handles method selectors (like
|
|
195
|
+
// Plugin that handles method selectors (like UpdatablePlugin)
|
|
196
196
|
class MethodHandlerPlugin extends Plugin {
|
|
197
197
|
public override execute(method: Selector, calldata: Calldata): BytesWriter | null {
|
|
198
198
|
switch (method) {
|
|
@@ -256,7 +256,7 @@ config:
|
|
|
256
256
|
theme: dark
|
|
257
257
|
---
|
|
258
258
|
flowchart LR
|
|
259
|
-
subgraph
|
|
259
|
+
subgraph OP_NET["OP_NET Plugin-Based Access Control"]
|
|
260
260
|
A["👤 User calls method"] --> B["onExecutionStarted"]
|
|
261
261
|
B --> C{"Method requires role?"}
|
|
262
262
|
|
|
@@ -603,7 +603,7 @@ Deployment:
|
|
|
603
603
|
1. Plugin.onDeployment (for each registered plugin, in order)
|
|
604
604
|
2. Contract.onDeployment
|
|
605
605
|
|
|
606
|
-
|
|
606
|
+
Update (when bytecode is updated):
|
|
607
607
|
1. Plugin.onUpdate (for each registered plugin, in order)
|
|
608
608
|
2. Contract.onUpdate
|
|
609
609
|
|
|
@@ -658,13 +658,13 @@ class MetricsPlugin extends Plugin {
|
|
|
658
658
|
}
|
|
659
659
|
```
|
|
660
660
|
|
|
661
|
-
## Solidity vs
|
|
661
|
+
## Solidity vs OP_NET: Plugin System Comparison
|
|
662
662
|
|
|
663
|
-
|
|
663
|
+
OP_NET plugins provide a more flexible and powerful alternative to Solidity's inheritance and modifier patterns. They enable cross-cutting concerns without tight coupling.
|
|
664
664
|
|
|
665
665
|
### Feature Comparison Table
|
|
666
666
|
|
|
667
|
-
| Feature | Solidity/EVM |
|
|
667
|
+
| Feature | Solidity/EVM | OP_NET | OP_NET Advantage |
|
|
668
668
|
|---------|--------------|-------|-----------------|
|
|
669
669
|
| **Code Reuse Pattern** | Inheritance | Composition (Plugins) | Flexible, no diamond problem |
|
|
670
670
|
| **Pre-Execution Hooks** | Modifiers | `onExecutionStarted` | Centralized, selector-aware |
|
|
@@ -677,7 +677,7 @@ OPNet plugins provide a more flexible and powerful alternative to Solidity's inh
|
|
|
677
677
|
|
|
678
678
|
### Pattern Mapping Table
|
|
679
679
|
|
|
680
|
-
| Solidity Pattern |
|
|
680
|
+
| Solidity Pattern | OP_NET Plugin Equivalent | Improvement |
|
|
681
681
|
|-----------------|------------------------|-------------|
|
|
682
682
|
| OpenZeppelin `Ownable` | `RoleBasedAccessPlugin` with ADMIN role | Multi-role support |
|
|
683
683
|
| OpenZeppelin `Pausable` | `PausablePlugin` | Selector-specific pausing |
|
|
@@ -689,7 +689,7 @@ OPNet plugins provide a more flexible and powerful alternative to Solidity's inh
|
|
|
689
689
|
|
|
690
690
|
### Capability Matrix
|
|
691
691
|
|
|
692
|
-
| Capability | Solidity |
|
|
692
|
+
| Capability | Solidity | OP_NET |
|
|
693
693
|
|------------|:--------:|:-----:|
|
|
694
694
|
| Pre-execution hooks | Modifiers (per-function) | Plugins (centralized) |
|
|
695
695
|
| Post-execution hooks | Manual | Built-in |
|
|
@@ -718,7 +718,7 @@ flowchart TB
|
|
|
718
718
|
S6["Diamond Problem Risk"]
|
|
719
719
|
end
|
|
720
720
|
|
|
721
|
-
subgraph
|
|
721
|
+
subgraph OP_NET["OP_NET - Plugin Composition"]
|
|
722
722
|
O1["Contract"] --> O2["OP_NET base"]
|
|
723
723
|
O3["AccessPlugin"] -.->|"registered"| O1
|
|
724
724
|
O4["PausablePlugin"] -.->|"registered"| O1
|
|
@@ -729,7 +729,7 @@ flowchart TB
|
|
|
729
729
|
|
|
730
730
|
### Code Complexity Comparison
|
|
731
731
|
|
|
732
|
-
| Aspect | Solidity |
|
|
732
|
+
| Aspect | Solidity | OP_NET |
|
|
733
733
|
|--------|----------|-------|
|
|
734
734
|
| Adding access control | Import + inherit + add modifiers | Register plugin |
|
|
735
735
|
| Adding pausable | Import + inherit + add modifiers | Register plugin |
|
|
@@ -775,10 +775,10 @@ contract MyContract is Ownable, AccessControl {
|
|
|
775
775
|
}
|
|
776
776
|
```
|
|
777
777
|
|
|
778
|
-
####
|
|
778
|
+
#### OP_NET: Plugin-Based Composition
|
|
779
779
|
|
|
780
780
|
```typescript
|
|
781
|
-
//
|
|
781
|
+
// OP_NET with Plugin - Composition-based
|
|
782
782
|
@final
|
|
783
783
|
export class MyContract extends OP_NET {
|
|
784
784
|
private accessPlugin: RoleBasedAccessPlugin;
|
|
@@ -855,10 +855,10 @@ contract MyContract is Pausable {
|
|
|
855
855
|
}
|
|
856
856
|
```
|
|
857
857
|
|
|
858
|
-
####
|
|
858
|
+
#### OP_NET: Automatic Selector-Based Pausing
|
|
859
859
|
|
|
860
860
|
```typescript
|
|
861
|
-
//
|
|
861
|
+
// OP_NET with Plugin
|
|
862
862
|
@final
|
|
863
863
|
export class MyContract extends OP_NET {
|
|
864
864
|
private pausablePlugin: PausablePlugin;
|
|
@@ -902,7 +902,7 @@ export class MyContract extends OP_NET {
|
|
|
902
902
|
### Multiple Plugins Example
|
|
903
903
|
|
|
904
904
|
```typescript
|
|
905
|
-
//
|
|
905
|
+
// OP_NET - Composing multiple plugins (no inheritance conflicts)
|
|
906
906
|
@final
|
|
907
907
|
export class CompleteContract extends OP_NET {
|
|
908
908
|
private accessPlugin: RoleBasedAccessPlugin;
|
|
@@ -937,18 +937,18 @@ export class CompleteContract extends OP_NET {
|
|
|
937
937
|
|
|
938
938
|
### Lifecycle Hook Comparison
|
|
939
939
|
|
|
940
|
-
| Lifecycle Event | Solidity |
|
|
940
|
+
| Lifecycle Event | Solidity | OP_NET |
|
|
941
941
|
|-----------------|----------|-------|
|
|
942
942
|
| Contract deployment | Single constructor | `onDeployment` per plugin + contract |
|
|
943
|
-
| Contract
|
|
943
|
+
| Contract update | Proxy reinitialize | `onUpdate` per plugin + contract |
|
|
944
944
|
| Before method call | Modifiers (manual per function) | `onExecutionStarted` (automatic) |
|
|
945
945
|
| Method handling | Function dispatch | `execute` returns BytesWriter or null |
|
|
946
946
|
| After method call | No built-in hook | `onExecutionCompleted` (automatic) |
|
|
947
947
|
| Error handling | try/catch (limited) | Revert in any hook |
|
|
948
948
|
|
|
949
|
-
### Why
|
|
949
|
+
### Why OP_NET Plugins?
|
|
950
950
|
|
|
951
|
-
| Solidity Limitation |
|
|
951
|
+
| Solidity Limitation | OP_NET Solution |
|
|
952
952
|
|---------------------|----------------|
|
|
953
953
|
| Modifier on every function | Centralized selector-based routing |
|
|
954
954
|
| Multiple inheritance complexity | Simple composition |
|