@btc-vision/btc-runtime 1.10.12 → 1.11.0-alpha

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.
@@ -5,7 +5,7 @@ Plugins extend contract functionality through lifecycle hooks. They allow modula
5
5
  ## Overview
6
6
 
7
7
  ```typescript
8
- import { Plugin, Blockchain, Calldata, Selector } from '@btc-vision/btc-runtime/runtime';
8
+ import { Plugin, Blockchain, Calldata, Selector, BytesWriter } from '@btc-vision/btc-runtime/runtime';
9
9
 
10
10
  // Create a plugin by extending Plugin class
11
11
  class MyPlugin extends Plugin {
@@ -13,6 +13,10 @@ class MyPlugin extends Plugin {
13
13
  // Called during contract deployment
14
14
  }
15
15
 
16
+ public override onUpdate(calldata: Calldata): void {
17
+ // Called when contract bytecode is updated
18
+ }
19
+
16
20
  public override onExecutionStarted(selector: Selector, calldata: Calldata): void {
17
21
  // Called before each method execution
18
22
  }
@@ -20,15 +24,20 @@ class MyPlugin extends Plugin {
20
24
  public override onExecutionCompleted(selector: Selector, calldata: Calldata): void {
21
25
  // Called after each successful method execution
22
26
  }
27
+
28
+ public override execute(method: Selector, calldata: Calldata): BytesWriter | null {
29
+ // Handle method selectors - return BytesWriter if handled, null if not
30
+ return null;
31
+ }
23
32
  }
24
33
 
25
34
  // Register with contract
26
- Blockchain.registerPlugin(new MyPlugin());
35
+ this.registerPlugin(new MyPlugin());
27
36
  ```
28
37
 
29
38
  ## Plugin Lifecycle
30
39
 
31
- Plugins are initialized when the contract is deployed and can intercept method calls:
40
+ Plugins are initialized when the contract is deployed and can intercept method calls or handle them directly:
32
41
 
33
42
  ```mermaid
34
43
  ---
@@ -44,7 +53,7 @@ sequenceDiagram
44
53
  Note over Deployer,Blockchain: Contract Deployment
45
54
  Deployer->>Contract: constructor()
46
55
  Contract->>Plugin: new Plugin()
47
- Contract->>Blockchain: registerPlugin(plugin)
56
+ Contract->>Contract: registerPlugin(plugin)
48
57
 
49
58
  Deployer->>Contract: deploy(calldata)
50
59
  Blockchain->>Plugin: onDeployment(calldata)
@@ -62,9 +71,14 @@ sequenceDiagram
62
71
  Note over Plugin: Pre-execution checks<br/>(access control, pausing, etc.)
63
72
  Plugin-->>Blockchain: continue
64
73
 
65
- Blockchain->>Contract: method executes
66
- Note over Contract: Core business logic
67
- Contract-->>Blockchain: result
74
+ alt Plugin handles selector
75
+ Blockchain->>Plugin: execute(selector, calldata)
76
+ Plugin-->>Blockchain: BytesWriter result
77
+ else Contract handles selector
78
+ Blockchain->>Contract: method executes
79
+ Note over Contract: Core business logic
80
+ Contract-->>Blockchain: result
81
+ end
68
82
 
69
83
  Blockchain->>Plugin: onExecutionCompleted(selector, calldata)
70
84
  Note over Plugin: Post-execution tasks<br/>(metrics, logging, etc.)
@@ -86,8 +100,10 @@ classDiagram
86
100
  class Plugin {
87
101
  <<base>>
88
102
  +onDeployment(calldata: Calldata) void
103
+ +onUpdate(calldata: Calldata) void
89
104
  +onExecutionStarted(selector: Selector, calldata: Calldata) void
90
105
  +onExecutionCompleted(selector: Selector, calldata: Calldata) void
106
+ +execute(method: Selector, calldata: Calldata) BytesWriter|null
91
107
  }
92
108
 
93
109
  class RoleBasedAccessPlugin {
@@ -121,18 +137,26 @@ classDiagram
121
137
 
122
138
  ### Base Plugin Class
123
139
 
124
- The `Plugin` class provides three lifecycle hooks:
140
+ The `Plugin` class provides lifecycle hooks and method handling:
125
141
 
126
142
  ```typescript
127
143
  export class Plugin {
128
144
  // Called once during contract deployment, before contract's onDeployment
129
145
  public onDeployment(_calldata: Calldata): void {}
130
146
 
147
+ // Called when contract bytecode is updated via updateContractFromExisting
148
+ public onUpdate(_calldata: Calldata): void {}
149
+
131
150
  // Called before each method execution
132
151
  public onExecutionStarted(_selector: Selector, _calldata: Calldata): void {}
133
152
 
134
153
  // Called after each successful method execution
135
154
  public onExecutionCompleted(_selector: Selector, _calldata: Calldata): void {}
155
+
156
+ // Handle method selectors - return BytesWriter if handled, null if not
157
+ public execute(_method: Selector, _calldata: Calldata): BytesWriter | null {
158
+ return null;
159
+ }
136
160
  }
137
161
  ```
138
162
 
@@ -144,14 +168,21 @@ import {
144
168
  Calldata,
145
169
  Selector,
146
170
  Blockchain,
147
- Revert
171
+ BytesWriter,
172
+ Revert,
173
+ encodeSelector
148
174
  } from '@btc-vision/btc-runtime/runtime';
149
175
 
176
+ // Simple logging plugin using lifecycle hooks
150
177
  class LoggingPlugin extends Plugin {
151
178
  public override onDeployment(calldata: Calldata): void {
152
179
  // Log deployment
153
180
  }
154
181
 
182
+ public override onUpdate(calldata: Calldata): void {
183
+ // Handle contract upgrades
184
+ }
185
+
155
186
  public override onExecutionStarted(selector: Selector, calldata: Calldata): void {
156
187
  // Log method call before execution
157
188
  }
@@ -160,6 +191,23 @@ class LoggingPlugin extends Plugin {
160
191
  // Log method completion
161
192
  }
162
193
  }
194
+
195
+ // Plugin that handles method selectors (like UpgradeablePlugin)
196
+ class MethodHandlerPlugin extends Plugin {
197
+ public override execute(method: Selector, calldata: Calldata): BytesWriter | null {
198
+ switch (method) {
199
+ case encodeSelector('myPluginMethod()'):
200
+ return this.myPluginMethod();
201
+ default:
202
+ return null; // Not handled by this plugin
203
+ }
204
+ }
205
+
206
+ private myPluginMethod(): BytesWriter {
207
+ // Method implementation
208
+ return new BytesWriter(0);
209
+ }
210
+ }
163
211
  ```
164
212
 
165
213
  ## Registration
@@ -178,7 +226,7 @@ export class MyContract extends OP_NET {
178
226
 
179
227
  // Create and register plugins
180
228
  this.loggingPlugin = new LoggingPlugin();
181
- Blockchain.registerPlugin(this.loggingPlugin);
229
+ this.registerPlugin(this.loggingPlugin);
182
230
  }
183
231
  }
184
232
  ```
@@ -190,7 +238,7 @@ public override onDeployment(calldata: Calldata): void {
190
238
  const enableLogging = calldata.readBoolean();
191
239
 
192
240
  if (enableLogging) {
193
- Blockchain.registerPlugin(new LoggingPlugin());
241
+ this.registerPlugin(new LoggingPlugin());
194
242
  }
195
243
 
196
244
  // Continue with normal deployment
@@ -518,7 +566,7 @@ export class MyContract extends OP_NET {
518
566
  Blockchain.nextPointer,
519
567
  Blockchain.nextPointer
520
568
  );
521
- Blockchain.registerPlugin(this.feePlugin);
569
+ this.registerPlugin(this.feePlugin);
522
570
  }
523
571
 
524
572
  @method(
@@ -551,15 +599,21 @@ export class MyContract extends OP_NET {
551
599
  ### Execution Order
552
600
 
553
601
  ```
602
+ Deployment:
554
603
  1. Plugin.onDeployment (for each registered plugin, in order)
555
604
  2. Contract.onDeployment
556
605
 
557
- Then for each method call:
606
+ Upgrade (when bytecode is updated):
607
+ 1. Plugin.onUpdate (for each registered plugin, in order)
608
+ 2. Contract.onUpdate
609
+
610
+ Method execution:
558
611
  1. Plugin.onExecutionStarted (for each registered plugin, in order)
559
612
  2. Contract.onExecutionStarted
560
- 3. Contract method executes
561
- 4. Plugin.onExecutionCompleted (for each registered plugin, in order)
562
- 5. Contract.onExecutionCompleted
613
+ 3. Plugin.execute (check plugins first, return if handled)
614
+ 4. Contract method executes (if no plugin handled it)
615
+ 5. Plugin.onExecutionCompleted (for each registered plugin, in order)
616
+ 6. Contract.onExecutionCompleted
563
617
  ```
564
618
 
565
619
  ### Metrics Plugin Example
@@ -733,7 +787,7 @@ export class MyContract extends OP_NET {
733
787
  super();
734
788
  // Single plugin handles ALL access control
735
789
  this.accessPlugin = new RoleBasedAccessPlugin(Blockchain.nextPointer);
736
- Blockchain.registerPlugin(this.accessPlugin);
790
+ this.registerPlugin(this.accessPlugin);
737
791
  }
738
792
 
739
793
  @method()
@@ -812,7 +866,7 @@ export class MyContract extends OP_NET {
812
866
  public constructor() {
813
867
  super();
814
868
  this.pausablePlugin = new PausablePlugin(Blockchain.nextPointer);
815
- Blockchain.registerPlugin(this.pausablePlugin);
869
+ this.registerPlugin(this.pausablePlugin);
816
870
  }
817
871
 
818
872
  @method(ABIDataTypes.UINT256)
@@ -861,19 +915,19 @@ export class CompleteContract extends OP_NET {
861
915
 
862
916
  // Order matters - security checks first!
863
917
  this.accessPlugin = new RoleBasedAccessPlugin(Blockchain.nextPointer);
864
- Blockchain.registerPlugin(this.accessPlugin); // 1. Check permissions
918
+ this.registerPlugin(this.accessPlugin); // 1. Check permissions
865
919
 
866
920
  this.pausablePlugin = new PausablePlugin(Blockchain.nextPointer);
867
- Blockchain.registerPlugin(this.pausablePlugin); // 2. Check pause status
921
+ this.registerPlugin(this.pausablePlugin); // 2. Check pause status
868
922
 
869
923
  this.feePlugin = new FeeCollectorPlugin(
870
924
  Blockchain.nextPointer,
871
925
  Blockchain.nextPointer
872
926
  );
873
- Blockchain.registerPlugin(this.feePlugin); // 3. Fee calculations
927
+ this.registerPlugin(this.feePlugin); // 3. Fee calculations
874
928
 
875
929
  this.metricsPlugin = new MetricsPlugin(Blockchain.nextPointer);
876
- Blockchain.registerPlugin(this.metricsPlugin); // 4. Track metrics
930
+ this.registerPlugin(this.metricsPlugin); // 4. Track metrics
877
931
  }
878
932
 
879
933
  // All plugins execute their hooks automatically
@@ -886,7 +940,9 @@ export class CompleteContract extends OP_NET {
886
940
  | Lifecycle Event | Solidity | OPNet |
887
941
  |-----------------|----------|-------|
888
942
  | Contract deployment | Single constructor | `onDeployment` per plugin + contract |
943
+ | Contract upgrade | Proxy reinitialize | `onUpdate` per plugin + contract |
889
944
  | Before method call | Modifiers (manual per function) | `onExecutionStarted` (automatic) |
945
+ | Method handling | Function dispatch | `execute` returns BytesWriter or null |
890
946
  | After method call | No built-in hook | `onExecutionCompleted` (automatic) |
891
947
  | Error handling | try/catch (limited) | Revert in any hook |
892
948
 
@@ -928,6 +984,10 @@ class MyPlugin extends Plugin {
928
984
  // Implementation
929
985
  }
930
986
 
987
+ public override onUpdate(calldata: Calldata): void {
988
+ // Implementation
989
+ }
990
+
931
991
  public override onExecutionStarted(selector: Selector, calldata: Calldata): void {
932
992
  // Implementation
933
993
  }
@@ -935,6 +995,11 @@ class MyPlugin extends Plugin {
935
995
  public override onExecutionCompleted(selector: Selector, calldata: Calldata): void {
936
996
  // Implementation
937
997
  }
998
+
999
+ public override execute(method: Selector, calldata: Calldata): BytesWriter | null {
1000
+ // Return BytesWriter if handled, null if not
1001
+ return null;
1002
+ }
938
1003
  }
939
1004
  ```
940
1005
 
@@ -994,9 +1059,9 @@ public override onExecutionStarted(selector: Selector, calldata: Calldata): void
994
1059
 
995
1060
  ```typescript
996
1061
  // Security checks should come first
997
- Blockchain.registerPlugin(this.accessControl); // Check permissions first
998
- Blockchain.registerPlugin(this.pausable); // Then pausable
999
- Blockchain.registerPlugin(this.metrics); // Metrics last
1062
+ this.registerPlugin(this.accessControl); // Check permissions first
1063
+ this.registerPlugin(this.pausable); // Then pausable
1064
+ this.registerPlugin(this.metrics); // Metrics last
1000
1065
  ```
1001
1066
 
1002
1067
  ---
@@ -432,6 +432,34 @@ const newContract = Blockchain.deployContractFromExisting(
432
432
  );
433
433
  ```
434
434
 
435
+ ### updateContractFromExisting
436
+
437
+ Updates the calling contract's bytecode from another deployed contract. The new bytecode takes effect at the next block.
438
+
439
+ ```typescript
440
+ updateContractFromExisting(
441
+ sourceAddress: Address,
442
+ calldata?: BytesWriter | null
443
+ ): void
444
+ ```
445
+
446
+ | Parameter | Type | Description |
447
+ |-----------|------|-------------|
448
+ | `sourceAddress` | `Address` | Contract containing new bytecode |
449
+ | `calldata` | `BytesWriter \| null` | Optional calldata passed to VM (default: empty) |
450
+
451
+ ```typescript
452
+ // Basic upgrade (not recommended without access control)
453
+ Blockchain.updateContractFromExisting(newBytecodeAddress);
454
+
455
+ // With calldata
456
+ const upgradeData = new BytesWriter(32);
457
+ upgradeData.writeU256(migrationVersion);
458
+ Blockchain.updateContractFromExisting(newBytecodeAddress, upgradeData);
459
+ ```
460
+
461
+ > **Warning:** This is a privileged operation. Always implement access control (e.g., `onlyDeployer`) and consider using the `Upgradeable` base class or `UpgradeablePlugin` for timelock protection. See [Contract Upgrades](../advanced/contract-upgrades.md) for details.
462
+
435
463
  ## Cryptographic Operations
436
464
 
437
465
  ### sha256
@@ -698,20 +726,6 @@ log(data: string): void
698
726
  Blockchain.log('Debug: operation started');
699
727
  ```
700
728
 
701
- ## Plugin Methods
702
-
703
- ### registerPlugin
704
-
705
- Registers a plugin.
706
-
707
- ```typescript
708
- registerPlugin(plugin: Plugin): void
709
- ```
710
-
711
- ```typescript
712
- Blockchain.registerPlugin(new MyPlugin());
713
- ```
714
-
715
729
  ## Lifecycle Hooks
716
730
 
717
731
  These are called by the runtime:
@@ -719,9 +733,29 @@ These are called by the runtime:
719
733
  | Method | When Called |
720
734
  |--------|-------------|
721
735
  | `onDeployment(calldata)` | Contract deployment |
736
+ | `onUpdate(calldata)` | Contract bytecode update (via `updateContractFromExisting`) |
722
737
  | `onExecutionStarted(selector, calldata)` | Before method execution |
723
738
  | `onExecutionCompleted(selector, calldata)` | After successful execution |
724
739
 
740
+ ### onUpdate
741
+
742
+ Called when the contract's bytecode is updated via `updateContractFromExisting`. Use this hook to perform storage migrations or initialization logic when upgrading.
743
+
744
+ ```typescript
745
+ public override onUpdate(calldata: Calldata): void {
746
+ super.onUpdate(calldata); // Call plugins first
747
+
748
+ // Perform migration logic
749
+ const migrationVersion = calldata.readU64();
750
+ if (migrationVersion === 2) {
751
+ // Migrate from v1 to v2
752
+ this.migrateToV2();
753
+ }
754
+ }
755
+ ```
756
+
757
+ > **Note:** The calldata is the same data passed to `Blockchain.updateContractFromExisting(sourceAddress, calldata)`. If no calldata was provided, an empty reader is passed.
758
+
725
759
  ---
726
760
 
727
761
  **Navigation:**
@@ -62,6 +62,7 @@ classDiagram
62
62
  +address Address (getter)
63
63
  +contractDeployer Address (getter)
64
64
  +onDeployment(_calldata: Calldata) void
65
+ +onUpdate(_calldata: Calldata) void
65
66
  +onExecutionStarted(_selector: Selector, _calldata: Calldata) void
66
67
  +onExecutionCompleted(_selector: Selector, _calldata: Calldata) void
67
68
  +execute(method: Selector, _calldata: Calldata) BytesWriter
@@ -235,6 +236,27 @@ constructor(uint256 initialSupply, string memory tokenName) {
235
236
  // Constructor runs every call, onDeployment runs once
236
237
  ```
237
238
 
239
+ ### 2b. Update (onUpdate)
240
+
241
+ Runs when the contract's bytecode is updated via `updateContractFromExisting`:
242
+
243
+ ```typescript
244
+ public override onUpdate(calldata: Calldata): void {
245
+ super.onUpdate(calldata); // Notify plugins
246
+
247
+ // Read migration parameters
248
+ const fromVersion = calldata.readU64();
249
+
250
+ // Perform migration based on version
251
+ if (fromVersion === 1) {
252
+ // Initialize new storage added in this version
253
+ this._newFeature.value = u256.fromU64(100);
254
+ }
255
+ }
256
+ ```
257
+
258
+ > **Note:** This hook is called on the **new** bytecode, not the old one. See [Contract Upgrades](../advanced/contract-upgrades.md#the-onupdate-lifecycle-hook) for details.
259
+
238
260
  ### 3. Method Execution
239
261
 
240
262
  Methods are automatically routed via `@method` decorators: