@btc-vision/btc-runtime 1.5.6 → 1.5.7
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/package.json +44 -44
- package/runtime/contracts/DeployableOP_20.ts +1 -1
- package/runtime/contracts/OP_20.ts +1 -1
- package/runtime/env/BlockchainEnvironment.ts +9 -20
- package/runtime/storage/arrays/StoredPackedArray.ts +105 -9
- package/runtime/storage/arrays/StoredU128Array.ts +1 -1
- package/runtime/storage/arrays/StoredU16Array.ts +1 -1
- package/runtime/storage/arrays/StoredU256Array.ts +1 -1
- package/runtime/storage/arrays/StoredU32Array.ts +1 -1
- package/runtime/storage/arrays/StoredU64Array.ts +1 -1
- package/runtime/storage/arrays/StoredU8Array.ts +1 -1
package/package.json
CHANGED
|
@@ -1,46 +1,46 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
2
|
+
"name": "@btc-vision/btc-runtime",
|
|
3
|
+
"version": "1.5.7",
|
|
4
|
+
"description": "Bitcoin Smart Contract Runtime",
|
|
5
|
+
"main": "btc/index.ts",
|
|
6
|
+
"scripts": {},
|
|
7
|
+
"types": "btc/index.ts",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"bitcoin",
|
|
10
|
+
"smart",
|
|
11
|
+
"contract",
|
|
12
|
+
"runtime",
|
|
13
|
+
"opnet",
|
|
14
|
+
"OP_NET"
|
|
15
|
+
],
|
|
16
|
+
"homepage": "https://opnet.org",
|
|
17
|
+
"author": "BlobMaster41",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/node": "^22.13.10",
|
|
21
|
+
"assemblyscript": "^0.27.35"
|
|
22
|
+
},
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/btc-vision/btc-runtime"
|
|
26
|
+
},
|
|
27
|
+
"type": "module",
|
|
28
|
+
"files": [
|
|
29
|
+
"package.json",
|
|
30
|
+
"runtime",
|
|
31
|
+
"runtime/*.ts",
|
|
32
|
+
"runtime/**/*.ts",
|
|
33
|
+
"!**/*.js.map",
|
|
34
|
+
"!**/*.tsbuildinfo",
|
|
35
|
+
"test/*.ts"
|
|
36
|
+
],
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@assemblyscript/loader": "^0.27.35",
|
|
39
|
+
"@btc-vision/as-bignum": "^0.0.5",
|
|
40
|
+
"@eslint/js": "^9.22.0",
|
|
41
|
+
"gulplog": "^2.2.0",
|
|
42
|
+
"ts-node": "^10.9.2",
|
|
43
|
+
"typescript": "^5.8.2",
|
|
44
|
+
"typescript-eslint": "^8.26.1"
|
|
45
|
+
}
|
|
46
46
|
}
|
|
@@ -39,7 +39,7 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
|
|
|
39
39
|
|
|
40
40
|
protected readonly _nonceMap: AddressMemoryMap;
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
public constructor(params: OP20InitParameters | null = null) {
|
|
43
43
|
super();
|
|
44
44
|
|
|
45
45
|
// Initialize main storage structures
|
|
@@ -3,7 +3,7 @@ import { u256 } from '@btc-vision/as-bignum/assembly';
|
|
|
3
3
|
import { OP20InitParameters } from './interfaces/OP20InitParameters';
|
|
4
4
|
|
|
5
5
|
export abstract class OP_20 extends DeployableOP_20 {
|
|
6
|
-
|
|
6
|
+
public constructor(maxSupply: u256, decimals: u8, name: string, symbol: string) {
|
|
7
7
|
super(new OP20InitParameters(maxSupply, decimals, name, symbol));
|
|
8
8
|
}
|
|
9
9
|
}
|
|
@@ -25,13 +25,10 @@ import { eqUint, MapUint8Array } from '../generic/MapUint8Array';
|
|
|
25
25
|
import { EMPTY_BUFFER } from '../math/bytes';
|
|
26
26
|
import { Plugin } from '../plugins/Plugin';
|
|
27
27
|
import { Calldata } from '../types';
|
|
28
|
+
import { Revert } from '../types/Revert';
|
|
28
29
|
|
|
29
30
|
export * from '../env/global';
|
|
30
31
|
|
|
31
|
-
export function runtimeError(msg: string): Error {
|
|
32
|
-
return new Error(`RuntimeException: ${msg}`);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
32
|
@final
|
|
36
33
|
export class BlockchainEnvironment {
|
|
37
34
|
private static readonly MAX_U16: u16 = 65535;
|
|
@@ -47,7 +44,7 @@ export class BlockchainEnvironment {
|
|
|
47
44
|
@inline
|
|
48
45
|
public get block(): Block {
|
|
49
46
|
if (!this._block) {
|
|
50
|
-
throw
|
|
47
|
+
throw new Revert('Block is required');
|
|
51
48
|
}
|
|
52
49
|
|
|
53
50
|
return this._block as Block;
|
|
@@ -58,7 +55,7 @@ export class BlockchainEnvironment {
|
|
|
58
55
|
@inline
|
|
59
56
|
public get tx(): Transaction {
|
|
60
57
|
if (!this._tx) {
|
|
61
|
-
throw
|
|
58
|
+
throw new Revert('Transaction is required');
|
|
62
59
|
}
|
|
63
60
|
|
|
64
61
|
return this._tx as Transaction;
|
|
@@ -80,7 +77,7 @@ export class BlockchainEnvironment {
|
|
|
80
77
|
|
|
81
78
|
public get nextPointer(): u16 {
|
|
82
79
|
if (this._nextPointer === BlockchainEnvironment.MAX_U16) {
|
|
83
|
-
throw
|
|
80
|
+
throw new Revert(`Out of storage pointer.`);
|
|
84
81
|
}
|
|
85
82
|
|
|
86
83
|
this._nextPointer += 1;
|
|
@@ -92,7 +89,7 @@ export class BlockchainEnvironment {
|
|
|
92
89
|
|
|
93
90
|
public get contractDeployer(): Address {
|
|
94
91
|
if (!this._contractDeployer) {
|
|
95
|
-
throw
|
|
92
|
+
throw new Revert('Deployer is required');
|
|
96
93
|
}
|
|
97
94
|
|
|
98
95
|
return this._contractDeployer as Address;
|
|
@@ -102,7 +99,7 @@ export class BlockchainEnvironment {
|
|
|
102
99
|
|
|
103
100
|
public get contractAddress(): Address {
|
|
104
101
|
if (!this._contractAddress) {
|
|
105
|
-
throw
|
|
102
|
+
throw new Revert('Contract address is required');
|
|
106
103
|
}
|
|
107
104
|
|
|
108
105
|
return this._contractAddress as Address;
|
|
@@ -171,12 +168,8 @@ export class BlockchainEnvironment {
|
|
|
171
168
|
}
|
|
172
169
|
|
|
173
170
|
public call(destinationContract: Address, calldata: BytesWriter): BytesReader {
|
|
174
|
-
if (destinationContract === this.contractAddress) {
|
|
175
|
-
throw this.error('Cannot call self');
|
|
176
|
-
}
|
|
177
|
-
|
|
178
171
|
if (!destinationContract) {
|
|
179
|
-
throw
|
|
172
|
+
throw new Revert('Destination contract is required');
|
|
180
173
|
}
|
|
181
174
|
|
|
182
175
|
const resultLengthBuffer = new ArrayBuffer(32);
|
|
@@ -234,7 +227,7 @@ export class BlockchainEnvironment {
|
|
|
234
227
|
);
|
|
235
228
|
|
|
236
229
|
if (status !== 0) {
|
|
237
|
-
throw
|
|
230
|
+
throw new Revert('Failed to deploy contract');
|
|
238
231
|
}
|
|
239
232
|
|
|
240
233
|
const contractAddressReader = new BytesReader(Uint8Array.wrap(resultAddressBuffer));
|
|
@@ -283,7 +276,7 @@ export class BlockchainEnvironment {
|
|
|
283
276
|
|
|
284
277
|
private createContractIfNotExists(): void {
|
|
285
278
|
if (!this._contract) {
|
|
286
|
-
throw
|
|
279
|
+
throw new Revert('Contract is required');
|
|
287
280
|
}
|
|
288
281
|
|
|
289
282
|
if (!this._selfContract) {
|
|
@@ -291,10 +284,6 @@ export class BlockchainEnvironment {
|
|
|
291
284
|
}
|
|
292
285
|
}
|
|
293
286
|
|
|
294
|
-
private error(msg: string): Error {
|
|
295
|
-
return runtimeError(msg);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
287
|
private _internalSetStorageAt(pointerHash: Uint8Array, value: Uint8Array): void {
|
|
299
288
|
this.storage.set(pointerHash, value);
|
|
300
289
|
|
|
@@ -49,7 +49,7 @@ export abstract class StoredPackedArray<T> {
|
|
|
49
49
|
*/
|
|
50
50
|
protected readonly MAX_LENGTH: u64 = <u64>(u32.MAX_VALUE - 1);
|
|
51
51
|
|
|
52
|
-
protected constructor(public pointer: u16, public subPointer: Uint8Array) {
|
|
52
|
+
protected constructor(public pointer: u16, public subPointer: Uint8Array, protected defaultValue: T) {
|
|
53
53
|
assert(subPointer.length <= 30, `You must pass a 30 bytes sub-pointer. (Array, got ${subPointer.length})`);
|
|
54
54
|
|
|
55
55
|
const basePointer = encodeBasePointer(pointer, subPointer);
|
|
@@ -66,6 +66,24 @@ export abstract class StoredPackedArray<T> {
|
|
|
66
66
|
@inline
|
|
67
67
|
@operator('[]')
|
|
68
68
|
public get(index: u64): T {
|
|
69
|
+
// max length used on purpose to prevent unbounded usage
|
|
70
|
+
if (index > this.MAX_LENGTH) {
|
|
71
|
+
throw new Revert('get: out of range');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const realIndex = (this._startIndex + index) % this.MAX_LENGTH;
|
|
75
|
+
const cap = this.getSlotCapacity();
|
|
76
|
+
const slotIndex = realIndex / cap;
|
|
77
|
+
const subIndex = <u32>(realIndex % cap);
|
|
78
|
+
|
|
79
|
+
const slotData = this.ensureSlot(slotIndex);
|
|
80
|
+
const arr = this.unpackSlot(slotData);
|
|
81
|
+
|
|
82
|
+
return arr[subIndex];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@inline
|
|
86
|
+
public get_physical(index: u64): T {
|
|
69
87
|
if (index > this.MAX_LENGTH) {
|
|
70
88
|
throw new Revert('get: index exceeds MAX_LENGTH (packed array)');
|
|
71
89
|
}
|
|
@@ -74,11 +92,9 @@ export abstract class StoredPackedArray<T> {
|
|
|
74
92
|
const slotIndex = index / cap;
|
|
75
93
|
const subIndex = <u32>(index % cap);
|
|
76
94
|
|
|
77
|
-
// Load the slot if not cached
|
|
78
95
|
const slotData = this.ensureSlot(slotIndex);
|
|
79
|
-
|
|
80
|
-
// Unpack and return the subIndex
|
|
81
96
|
const arr = this.unpackSlot(slotData);
|
|
97
|
+
|
|
82
98
|
return arr[subIndex];
|
|
83
99
|
}
|
|
84
100
|
|
|
@@ -89,6 +105,28 @@ export abstract class StoredPackedArray<T> {
|
|
|
89
105
|
throw new Revert('set: index exceeds MAX_LENGTH (packed array)');
|
|
90
106
|
}
|
|
91
107
|
|
|
108
|
+
const realIndex = (this._startIndex + index) % this.MAX_LENGTH;
|
|
109
|
+
const cap = this.getSlotCapacity();
|
|
110
|
+
const slotIndex = realIndex / cap;
|
|
111
|
+
const subIndex = <u32>(realIndex % cap);
|
|
112
|
+
|
|
113
|
+
let slotData = this.ensureSlot(slotIndex);
|
|
114
|
+
const arr = this.unpackSlot(slotData);
|
|
115
|
+
|
|
116
|
+
if (!this.eq(arr[subIndex], value)) {
|
|
117
|
+
arr[subIndex] = value;
|
|
118
|
+
slotData = this.packSlot(arr);
|
|
119
|
+
this._slots.set(slotIndex, slotData);
|
|
120
|
+
this._isChanged.add(slotIndex);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
@inline
|
|
125
|
+
public set_physical(index: u64, value: T): void {
|
|
126
|
+
if (index > this.MAX_LENGTH) {
|
|
127
|
+
throw new Revert('set: index exceeds MAX_LENGTH (packed array)');
|
|
128
|
+
}
|
|
129
|
+
|
|
92
130
|
const cap = this.getSlotCapacity();
|
|
93
131
|
const slotIndex = index / cap;
|
|
94
132
|
const subIndex = <u32>(index % cap);
|
|
@@ -105,15 +143,15 @@ export abstract class StoredPackedArray<T> {
|
|
|
105
143
|
}
|
|
106
144
|
|
|
107
145
|
@inline
|
|
108
|
-
public push(value: T): void {
|
|
146
|
+
public push(value: T, isPhysical: bool = false): void {
|
|
109
147
|
if (this._length >= this.MAX_LENGTH) {
|
|
110
|
-
throw new Revert('push: array has reached MAX_LENGTH
|
|
148
|
+
throw new Revert('push: array has reached MAX_LENGTH');
|
|
111
149
|
}
|
|
112
150
|
|
|
113
|
-
const
|
|
151
|
+
const realIndex = ((isPhysical ? 0 : this._startIndex) + this._length) % this.MAX_LENGTH;
|
|
114
152
|
const cap = this.getSlotCapacity();
|
|
115
|
-
const slotIndex =
|
|
116
|
-
const subIndex = <u32>(
|
|
153
|
+
const slotIndex = realIndex / cap;
|
|
154
|
+
const subIndex = <u32>(realIndex % cap);
|
|
117
155
|
|
|
118
156
|
let slotData = this.ensureSlot(slotIndex);
|
|
119
157
|
const arr = this.unpackSlot(slotData);
|
|
@@ -129,12 +167,70 @@ export abstract class StoredPackedArray<T> {
|
|
|
129
167
|
this._isChangedLength = true;
|
|
130
168
|
}
|
|
131
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Remove the first element by zeroing it and shifting all other elements.
|
|
172
|
+
*/
|
|
173
|
+
@inline
|
|
174
|
+
public shift(): T {
|
|
175
|
+
if (this._length == 0) {
|
|
176
|
+
throw new Revert('shift: array is empty (packed array)');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const newIndex = this._startIndex;
|
|
180
|
+
const cap = this.getSlotCapacity();
|
|
181
|
+
const slotIndex = newIndex / cap;
|
|
182
|
+
const subIndex = <u32>(newIndex % cap);
|
|
183
|
+
|
|
184
|
+
let slotData = this.ensureSlot(slotIndex);
|
|
185
|
+
|
|
186
|
+
const arr = this.unpackSlot(slotData);
|
|
187
|
+
const currentData = arr[subIndex];
|
|
188
|
+
|
|
189
|
+
if (!this.eq(currentData, this.defaultValue)) {
|
|
190
|
+
arr[subIndex] = this.defaultValue;
|
|
191
|
+
slotData = this.packSlot(arr);
|
|
192
|
+
|
|
193
|
+
this._slots.set(slotIndex, slotData);
|
|
194
|
+
this._isChanged.add(slotIndex);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
this._length -= 1;
|
|
198
|
+
this._startIndex += 1;
|
|
199
|
+
this._isChangedLength = true;
|
|
200
|
+
this._isChangedStartIndex = true;
|
|
201
|
+
|
|
202
|
+
return currentData;
|
|
203
|
+
}
|
|
204
|
+
|
|
132
205
|
/**
|
|
133
206
|
* "Delete" by zeroing out the element at `index`,
|
|
134
207
|
* but does not reduce the length.
|
|
135
208
|
*/
|
|
136
209
|
@inline
|
|
137
210
|
public delete(index: u64): void {
|
|
211
|
+
const realIndex = (this._startIndex + index) % this.MAX_LENGTH;
|
|
212
|
+
const cap = this.getSlotCapacity();
|
|
213
|
+
const slotIndex = realIndex / cap;
|
|
214
|
+
const subIndex = <u32>(realIndex % cap);
|
|
215
|
+
|
|
216
|
+
let slotData = this.ensureSlot(slotIndex);
|
|
217
|
+
const arr = this.unpackSlot(slotData);
|
|
218
|
+
|
|
219
|
+
const zeroVal = this.zeroValue();
|
|
220
|
+
if (!this.eq(arr[subIndex], zeroVal)) {
|
|
221
|
+
arr[subIndex] = zeroVal;
|
|
222
|
+
slotData = this.packSlot(arr);
|
|
223
|
+
this._slots.set(slotIndex, slotData);
|
|
224
|
+
this._isChanged.add(slotIndex);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* "Delete" by zeroing out the element at `index`,
|
|
230
|
+
* but does not reduce the length.
|
|
231
|
+
*/
|
|
232
|
+
@inline
|
|
233
|
+
public delete_physical(index: u64): void {
|
|
138
234
|
const cap = this.getSlotCapacity();
|
|
139
235
|
const slotIndex = index / cap;
|
|
140
236
|
const subIndex = <u32>(index % cap);
|
|
@@ -10,7 +10,7 @@ import { bigEndianAdd } from '../../math/bytes';
|
|
|
10
10
|
@final
|
|
11
11
|
export class StoredU128Array extends StoredPackedArray<u128> {
|
|
12
12
|
public constructor(pointer: u16, subPointer: Uint8Array) {
|
|
13
|
-
super(pointer, subPointer);
|
|
13
|
+
super(pointer, subPointer, u128.Zero);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
protected getSlotCapacity(): u64 {
|
|
@@ -7,7 +7,7 @@ import { bigEndianAdd } from '../../math/bytes';
|
|
|
7
7
|
@final
|
|
8
8
|
export class StoredU16Array extends StoredPackedArray<u16> {
|
|
9
9
|
public constructor(pointer: u16, subPointer: Uint8Array) {
|
|
10
|
-
super(pointer, subPointer);
|
|
10
|
+
super(pointer, subPointer, 0);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
protected getSlotCapacity(): u64 {
|
|
@@ -9,7 +9,7 @@ import { bigEndianAdd } from '../../math/bytes';
|
|
|
9
9
|
@final
|
|
10
10
|
export class StoredU256Array extends StoredPackedArray<u256> {
|
|
11
11
|
public constructor(pointer: u16, subPointer: Uint8Array) {
|
|
12
|
-
super(pointer, subPointer);
|
|
12
|
+
super(pointer, subPointer, u256.Zero);
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
protected getSlotCapacity(): u64 {
|
|
@@ -8,7 +8,7 @@ import { bigEndianAdd } from '../../math/bytes';
|
|
|
8
8
|
@final
|
|
9
9
|
export class StoredU32Array extends StoredPackedArray<u32> {
|
|
10
10
|
public constructor(pointer: u16, subPointer: Uint8Array) {
|
|
11
|
-
super(pointer, subPointer);
|
|
11
|
+
super(pointer, subPointer, 0);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
protected getSlotCapacity(): u64 {
|
|
@@ -8,7 +8,7 @@ import { bigEndianAdd } from '../../math/bytes';
|
|
|
8
8
|
@final
|
|
9
9
|
export class StoredU64Array extends StoredPackedArray<u64> {
|
|
10
10
|
public constructor(pointer: u16, subPointer: Uint8Array) {
|
|
11
|
-
super(pointer, subPointer);
|
|
11
|
+
super(pointer, subPointer, 0);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
protected getSlotCapacity(): u64 {
|
|
@@ -8,7 +8,7 @@ import { bigEndianAdd } from '../../math/bytes';
|
|
|
8
8
|
@final
|
|
9
9
|
export class StoredU8Array extends StoredPackedArray<u8> {
|
|
10
10
|
public constructor(pointer: u16, subPointer: Uint8Array) {
|
|
11
|
-
super(pointer, subPointer);
|
|
11
|
+
super(pointer, subPointer, 0);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
protected getSlotCapacity(): u64 {
|