@btc-vision/btc-runtime 1.0.11 → 1.0.13

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 CHANGED
@@ -11,8 +11,8 @@
11
11
 
12
12
  ## Introduction
13
13
 
14
- The OPNet Smart Contract Runtime is a WebAssembly runtime that is designed to help developers write smart contracts for the OPNet blockchain.
15
- The runtime is written in AssemblyScript, a subset of TypeScript that compiles to WebAssembly.
14
+ The OPNet Smart Contract Runtime contains all the necessary components to effectively create smart contracts for Bitcoin
15
+ L1. The runtime is written in AssemblyScript.
16
16
 
17
17
  ### Installation
18
18
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@btc-vision/btc-runtime",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "description": "Bitcoin Smart Contract Runtime",
5
5
  "main": "btc/index.ts",
6
6
  "types": "btc/index.ts",
@@ -23,29 +23,28 @@ const transferSelector = encodeSelector('transfer');
23
23
  const transferFromSelector = encodeSelector('transferFrom');
24
24
 
25
25
  export abstract class OP_20 extends OP_NET implements IOP_20 {
26
- public readonly decimals: u8 = 8;
27
-
28
- public readonly name: string = `OP_20`;
29
- public readonly symbol: string = `OP`;
30
-
31
26
  protected readonly allowanceMap: MultiAddressMemoryMap<Address, Address, MemorySlotData<u256>>;
32
27
  protected readonly balanceOfMap: AddressMemoryMap<Address, MemorySlotData<u256>>;
33
28
 
34
- protected constructor(public readonly maxSupply: u256) {
29
+ protected constructor(
30
+ protected readonly maxSupply: u256,
31
+ protected readonly decimals: u8,
32
+ protected readonly name: string,
33
+ protected readonly symbol: string,
34
+ ) {
35
35
  super();
36
36
 
37
37
  this.allowanceMap = new MultiAddressMemoryMap<Address, Address, MemorySlotData<u256>>(
38
38
  Blockchain.nextPointer,
39
39
  u256.Zero,
40
40
  );
41
+
41
42
  this.balanceOfMap = new AddressMemoryMap<Address, MemorySlotData<u256>>(
42
43
  Blockchain.nextPointer,
43
44
  u256.Zero,
44
45
  );
45
46
 
46
- const supplyPointer = Blockchain.nextPointer;
47
- const supply: u256 = Blockchain.getStorageAt(supplyPointer, u256.Zero, u256.Zero);
48
- this._totalSupply = new StoredU256(supplyPointer, u256.Zero, supply);
47
+ this._totalSupply = new StoredU256(Blockchain.nextPointer, u256.Zero, u256.Zero);
49
48
  }
50
49
 
51
50
  public _totalSupply: StoredU256;
@@ -156,10 +155,10 @@ export abstract class OP_20 extends OP_NET implements IOP_20 {
156
155
  response.writeU8(this.decimals);
157
156
  break;
158
157
  case encodeSelector('name'):
159
- response.writeString(this.name);
158
+ response.writeStringWithLength(this.name);
160
159
  break;
161
160
  case encodeSelector('symbol'):
162
- response.writeString(this.symbol);
161
+ response.writeStringWithLength(this.symbol);
163
162
  break;
164
163
  case encodeSelector('totalSupply'):
165
164
  response.writeU256(this.totalSupply);
@@ -229,9 +228,7 @@ export abstract class OP_20 extends OP_NET implements IOP_20 {
229
228
  const caller = Blockchain.caller();
230
229
 
231
230
  if (onlyOwner) this.onlyOwner(callee);
232
-
233
231
  if (caller !== callee) throw new Revert(`callee != caller`);
234
- if (callee !== this.owner) throw new Revert('Only indexers can mint tokens');
235
232
 
236
233
  if (!this.balanceOfMap.has(to)) {
237
234
  this.balanceOfMap.set(to, value);
@@ -271,14 +268,10 @@ export abstract class OP_20 extends OP_NET implements IOP_20 {
271
268
  const newBalance: u256 = SafeMath.sub(balance, value);
272
269
  this.balanceOfMap.set(caller, newBalance);
273
270
 
274
- if (!this.balanceOfMap.has(to)) {
275
- this.balanceOfMap.set(to, value);
276
- } else {
277
- const toBalance: u256 = this.balanceOfMap.get(to);
278
- const newToBalance: u256 = SafeMath.add(toBalance, value);
271
+ const toBalance: u256 = this.balanceOfMap.get(to);
272
+ const newToBalance: u256 = SafeMath.add(toBalance, value);
279
273
 
280
- this.balanceOfMap.set(to, newToBalance);
281
- }
274
+ this.balanceOfMap.set(to, newToBalance);
282
275
 
283
276
  this.createTransferEvent(caller, to, value);
284
277
 
@@ -345,19 +338,19 @@ export abstract class OP_20 extends OP_NET implements IOP_20 {
345
338
  this.emitEvent(burnEvent);
346
339
  }
347
340
 
348
- private createApproveEvent(owner: Address, spender: Address, value: u256): void {
341
+ protected createApproveEvent(owner: Address, spender: Address, value: u256): void {
349
342
  const approveEvent = new ApproveEvent(owner, spender, value);
350
343
 
351
344
  this.emitEvent(approveEvent);
352
345
  }
353
346
 
354
- private createMintEvent(owner: Address, value: u256): void {
347
+ protected createMintEvent(owner: Address, value: u256): void {
355
348
  const mintEvent = new MintEvent(owner, value);
356
349
 
357
350
  this.emitEvent(mintEvent);
358
351
  }
359
352
 
360
- private createTransferEvent(from: Address, to: Address, value: u256): void {
353
+ protected createTransferEvent(from: Address, to: Address, value: u256): void {
361
354
  const transferEvent = new TransferEvent(from, to, value);
362
355
 
363
356
  this.emitEvent(transferEvent);
@@ -3,10 +3,6 @@ import { Calldata } from '../../universal/ABIRegistry';
3
3
  import { StoredU256 } from '../../storage/StoredU256';
4
4
 
5
5
  export interface IOP_20 {
6
- readonly name: string;
7
- readonly symbol: string;
8
-
9
- readonly decimals: u8;
10
6
  readonly _totalSupply: StoredU256;
11
7
 
12
8
  balanceOf(callData: Calldata): BytesWriter;
@@ -11,7 +11,7 @@ import { Potential } from '../lang/Definitions';
11
11
  import { Map } from '../generic/Map';
12
12
  import { OP_NET } from '../contracts/OP_NET';
13
13
  import { PointerStorage } from '../types';
14
- import { deploy, deployFromAddress, loadPointer, storePointer } from './global';
14
+ import { callContract, deploy, deployFromAddress, loadPointer, log, storePointer } from './global';
15
15
  import { DeployContractResponse } from '../interfaces/DeployContractResponse';
16
16
 
17
17
  export * from '../env/global';
@@ -111,25 +111,25 @@ export class BlockchainEnvironment {
111
111
  }
112
112
 
113
113
  public call(destinationContract: Address, calldata: BytesWriter): BytesReader {
114
- /*if (destinationContract === this._callee) {
114
+ if (destinationContract === this._callee) {
115
115
  throw this.error('Cannot call self');
116
116
  }
117
117
 
118
- const externalCalls = this.externalCalls.get(destinationContract);
119
- const buffer = calldata.getBuffer();
120
- externalCalls.push(buffer);
118
+ const call = new BytesWriter();
119
+ call.writeAddress(destinationContract);
120
+ call.writeBytesWithLength(calldata.getBuffer());
121
121
 
122
- const response: Potential<Uint8Array> = this.getExternalCallResponse(
123
- destinationContract,
124
- externalCalls.length - 1,
125
- );
126
- if (!response) {
127
- throw this.error('external call failed');
128
- }
122
+ const response: Uint8Array = callContract(call.getBuffer());
123
+
124
+ return new BytesReader(response);
125
+ }
129
126
 
130
- return new BytesReader(response);*/
127
+ public log(data: string): void {
128
+ const writer = new BytesWriter();
129
+ writer.writeStringWithLength(data);
131
130
 
132
- throw this.error('Not implemented');
131
+ const buffer = writer.getBuffer();
132
+ log(buffer);
133
133
  }
134
134
 
135
135
  public addEvent(event: NetEvent): void {
@@ -221,10 +221,8 @@ export class BlockchainEnvironment {
221
221
  pointer: u16,
222
222
  keyPointer: MemorySlotPointer,
223
223
  value: MemorySlotData<u256>,
224
- defaultValue: MemorySlotData<u256>,
225
224
  ): void {
226
225
  const pointerHash: u256 = encodePointerHash(pointer, keyPointer);
227
- this.ensureStorageAtPointer(pointerHash, defaultValue);
228
226
 
229
227
  this._internalSetStorageAt(pointerHash, value);
230
228
  }
@@ -241,11 +239,6 @@ export class BlockchainEnvironment {
241
239
  return ABIRegistry.getWriteMethods();
242
240
  }
243
241
 
244
- private purgeMemory(): void {
245
- this.storage.clear();
246
- this.events = [];
247
- }
248
-
249
242
  private error(msg: string): Error {
250
243
  return new Error(`${BlockchainEnvironment.runtimeException}: ${msg}`);
251
244
  }
@@ -264,10 +257,6 @@ export class BlockchainEnvironment {
264
257
 
265
258
  this.storage.set(pointerHash, value);
266
259
 
267
- if (u256.eq(value, u256.Zero)) {
268
- return;
269
- }
270
-
271
260
  const writer: BytesWriter = new BytesWriter();
272
261
  writer.writeU256(pointerHash);
273
262
  writer.writeU256(value);
@@ -305,6 +294,10 @@ export class BlockchainEnvironment {
305
294
  defaultValue: MemorySlotData<u256>,
306
295
  ): void {
307
296
  if (!this.hasPointerStorageHash(pointerHash)) {
297
+ if (u256.eq(defaultValue, u256.Zero)) {
298
+ return;
299
+ }
300
+
308
301
  this._internalSetStorageAt(pointerHash, defaultValue);
309
302
  }
310
303
  }
@@ -17,3 +17,7 @@ export declare function deployFromAddress(data: Uint8Array): Uint8Array;
17
17
  // @ts-ignore
18
18
  @external('env', 'call')
19
19
  export declare function callContract(data: Uint8Array): Uint8Array;
20
+
21
+ // @ts-ignore
22
+ @external('env', 'log')
23
+ export declare function log(data: Uint8Array): void;
@@ -17,7 +17,7 @@ export class AddressMemoryMap<K extends string, V extends MemorySlotData<u256>>
17
17
 
18
18
  public set(key: K, value: V): this {
19
19
  const keyHash: MemorySlotPointer = encodePointer(key);
20
- Blockchain.setStorageAt(this.pointer, keyHash, value, this.defaultValue);
20
+ Blockchain.setStorageAt(this.pointer, keyHash, value);
21
21
 
22
22
  return this;
23
23
  }
@@ -24,7 +24,7 @@ export class KeyMerger<K extends string, K2 extends string, V extends MemorySlot
24
24
  const mergedKey: string = `${this.parentKey}${key2}`;
25
25
  const keyHash: MemorySlotPointer = encodePointer(mergedKey);
26
26
 
27
- Blockchain.setStorageAt(this.pointer, keyHash, value, this.defaultValue);
27
+ Blockchain.setStorageAt(this.pointer, keyHash, value);
28
28
 
29
29
  return this;
30
30
  }
@@ -0,0 +1,145 @@
1
+ import { u256 } from 'as-bignum/assembly';
2
+ import { Blockchain } from '../env';
3
+ import { SafeMath } from '../types/SafeMath';
4
+
5
+ @final
6
+ export class StoredString {
7
+ constructor(
8
+ public pointer: u16,
9
+ private defaultValue?: string,
10
+ ) {}
11
+
12
+ private _value: string = '';
13
+
14
+ @inline
15
+ public get value(): string {
16
+ if (!this._value) {
17
+ this.load();
18
+ }
19
+
20
+ return this._value;
21
+ }
22
+
23
+ @inline
24
+ public set value(value: string) {
25
+ this._value = value;
26
+ this.save();
27
+ }
28
+
29
+ private min(a: u32, b: u32): u32 {
30
+ return a < b ? a : b;
31
+ }
32
+
33
+ private max(a: u32, b: u32): u32 {
34
+ return a > b ? a : b;
35
+ }
36
+
37
+ private save(): void {
38
+ const length: u32 = this._value.length;
39
+ if (length == 0) {
40
+ return;
41
+ }
42
+
43
+ if (length > 2048) {
44
+ throw new Error('StoredString: value is too long');
45
+ }
46
+
47
+ // Prepare the header with the length of the string in the first 4 bytes
48
+ let header: u256 = u256.fromU32(length);
49
+ header = SafeMath.shl(header, 224);
50
+
51
+ let currentPointer: u256 = u256.Zero;
52
+ let remainingLength: u32 = length;
53
+ let offset: u32 = 0;
54
+
55
+ // Save the initial chunk (first 28 bytes) in the header
56
+ let bytesToWrite: u32 = this.min(remainingLength, 28);
57
+ header = this.saveChunk(header, this._value, offset, bytesToWrite, 4);
58
+ Blockchain.setStorageAt(this.pointer, currentPointer, header);
59
+
60
+ remainingLength -= bytesToWrite;
61
+ offset += bytesToWrite;
62
+
63
+ // Save the remaining chunks in subsequent storage slots
64
+ while (remainingLength > 0) {
65
+ bytesToWrite = this.min(remainingLength, 32);
66
+ let storageValue: u256 = this.saveChunk(
67
+ u256.Zero,
68
+ this._value,
69
+ offset,
70
+ bytesToWrite,
71
+ 0,
72
+ );
73
+ currentPointer = u256.add(currentPointer, u256.One);
74
+ Blockchain.setStorageAt(this.pointer, currentPointer, storageValue);
75
+
76
+ remainingLength -= bytesToWrite;
77
+ offset += bytesToWrite;
78
+ }
79
+ }
80
+
81
+ // Helper method to save a chunk of the string into the storage slot
82
+ private saveChunk(
83
+ storage: u256,
84
+ value: string,
85
+ offset: u32,
86
+ length: u32,
87
+ storageOffset: u32,
88
+ ): u256 {
89
+ let bytes = storage.toBytes(true);
90
+ for (let i: u32 = 0; i < length; i++) {
91
+ let index: i32 = i32(offset + i);
92
+ bytes[i + storageOffset] = u8(value.charCodeAt(index));
93
+ }
94
+ return u256.fromBytes(bytes, true);
95
+ }
96
+
97
+ private load(): void {
98
+ const header: u256 = Blockchain.getStorageAt(this.pointer, u256.Zero, u256.Zero);
99
+ if (u256.eq(header, u256.Zero)) {
100
+ if (this.defaultValue) {
101
+ this.value = this.defaultValue;
102
+ }
103
+
104
+ return;
105
+ }
106
+
107
+ // the length of the string is stored in the first 4 bytes of the header
108
+ const bits: u256 = u256.shr(header, 224);
109
+ const length: u32 = bits.toU32();
110
+
111
+ // the rest contains the string itself
112
+ let currentPointer: u256 = u256.Zero;
113
+ let remainingLength: u32 = length;
114
+ let currentStorage: u256 = header;
115
+
116
+ let bytesToRead: u32 = this.min(remainingLength, 28);
117
+ let str: string = this.loadChunk(currentStorage, 4, bytesToRead);
118
+ remainingLength -= bytesToRead;
119
+
120
+ while (remainingLength > 0) {
121
+ // Move to the next storage slot
122
+ currentPointer = u256.add(currentPointer, u256.One);
123
+ currentStorage = Blockchain.getStorageAt(this.pointer, currentPointer, u256.Zero);
124
+
125
+ // Extract the relevant portion of the string from the current storage slot
126
+ let bytesToRead: u32 = this.min(remainingLength, 32);
127
+ str += this.loadChunk(currentStorage, 0, bytesToRead);
128
+
129
+ remainingLength -= bytesToRead;
130
+ }
131
+
132
+ this._value = str;
133
+ }
134
+
135
+ private loadChunk(value: u256, offset: u32, length: u32): string {
136
+ const bytes = value.toBytes(true);
137
+
138
+ let str: string = '';
139
+ for (let i: u32 = 0; i < length; i++) {
140
+ str += String.fromCharCode(bytes[i + offset]);
141
+ }
142
+
143
+ return str;
144
+ }
145
+ }
@@ -9,9 +9,7 @@ export class StoredU256 {
9
9
  public pointer: u16,
10
10
  public subPointer: MemorySlotPointer,
11
11
  private defaultValue: u256,
12
- ) {
13
- this._value = Blockchain.getStorageAt(this.pointer, this.subPointer, this.defaultValue);
14
- }
12
+ ) {}
15
13
 
16
14
  private _value: u256 = u256.Zero;
17
15
 
@@ -26,7 +24,7 @@ export class StoredU256 {
26
24
  public set value(value: u256) {
27
25
  this._value = value;
28
26
 
29
- Blockchain.setStorageAt(this.pointer, this.subPointer, this._value, this.defaultValue);
27
+ Blockchain.setStorageAt(this.pointer, this.subPointer, this._value);
30
28
  }
31
29
 
32
30
  @inline
@@ -40,7 +38,7 @@ export class StoredU256 {
40
38
  this.ensureValue();
41
39
 
42
40
  this._value = SafeMath.add(this._value, value);
43
- Blockchain.setStorageAt(this.pointer, this.subPointer, this._value, this.defaultValue);
41
+ Blockchain.setStorageAt(this.pointer, this.subPointer, this._value);
44
42
 
45
43
  return this;
46
44
  }
@@ -51,7 +49,7 @@ export class StoredU256 {
51
49
  this.ensureValue();
52
50
 
53
51
  this._value = SafeMath.sub(this._value, value);
54
- Blockchain.setStorageAt(this.pointer, this.subPointer, this._value, this.defaultValue);
52
+ Blockchain.setStorageAt(this.pointer, this.subPointer, this._value);
55
53
 
56
54
  return this;
57
55
  }
@@ -62,7 +60,7 @@ export class StoredU256 {
62
60
  this.ensureValue();
63
61
 
64
62
  this._value = SafeMath.mul(this._value, value);
65
- Blockchain.setStorageAt(this.pointer, this.subPointer, this._value, this.defaultValue);
63
+ Blockchain.setStorageAt(this.pointer, this.subPointer, this._value);
66
64
 
67
65
  return this;
68
66
  }
@@ -121,7 +119,7 @@ export class StoredU256 {
121
119
  this.ensureValue();
122
120
 
123
121
  this._value = u256.shr(this._value, value);
124
- Blockchain.setStorageAt(this.pointer, this.subPointer, this._value, this.defaultValue);
122
+ Blockchain.setStorageAt(this.pointer, this.subPointer, this._value);
125
123
 
126
124
  return this;
127
125
  }
@@ -132,7 +130,7 @@ export class StoredU256 {
132
130
  this.ensureValue();
133
131
 
134
132
  this._value = u256.and(this._value, value);
135
- Blockchain.setStorageAt(this.pointer, this.subPointer, this._value, this.defaultValue);
133
+ Blockchain.setStorageAt(this.pointer, this.subPointer, this._value);
136
134
 
137
135
  return this;
138
136
  }
@@ -143,7 +141,7 @@ export class StoredU256 {
143
141
  this.ensureValue();
144
142
 
145
143
  this._value = u256.or(this._value, value);
146
- Blockchain.setStorageAt(this.pointer, this.subPointer, this._value, this.defaultValue);
144
+ Blockchain.setStorageAt(this.pointer, this.subPointer, this._value);
147
145
 
148
146
  return this;
149
147
  }
@@ -154,7 +152,7 @@ export class StoredU256 {
154
152
  this.ensureValue();
155
153
 
156
154
  this._value = u256.xor(this._value, value);
157
- Blockchain.setStorageAt(this.pointer, this.subPointer, this._value, this.defaultValue);
155
+ Blockchain.setStorageAt(this.pointer, this.subPointer, this._value);
158
156
 
159
157
  return this;
160
158
  }
@@ -176,7 +174,7 @@ export class StoredU256 {
176
174
  value = u256.shr(value, 1);
177
175
  }
178
176
 
179
- Blockchain.setStorageAt(this.pointer, this.subPointer, this._value, this.defaultValue);
177
+ Blockchain.setStorageAt(this.pointer, this.subPointer, this._value);
180
178
 
181
179
  return this;
182
180
  }
@@ -201,7 +199,7 @@ export class StoredU256 {
201
199
  }
202
200
 
203
201
  this._value = result;
204
- Blockchain.setStorageAt(this.pointer, this.subPointer, this._value, this.defaultValue);
202
+ Blockchain.setStorageAt(this.pointer, this.subPointer, this._value);
205
203
 
206
204
  return this;
207
205
  }
@@ -212,7 +210,7 @@ export class StoredU256 {
212
210
  this.ensureValue();
213
211
 
214
212
  this._value = SafeMath.add(this._value, u256.One);
215
- Blockchain.setStorageAt(this.pointer, this.subPointer, this._value, this.defaultValue);
213
+ Blockchain.setStorageAt(this.pointer, this.subPointer, this._value);
216
214
 
217
215
  return this;
218
216
  }
@@ -223,7 +221,7 @@ export class StoredU256 {
223
221
  this.ensureValue();
224
222
 
225
223
  this._value = SafeMath.sub(this._value, u256.One);
226
- Blockchain.setStorageAt(this.pointer, this.subPointer, this._value, this.defaultValue);
224
+ Blockchain.setStorageAt(this.pointer, this.subPointer, this._value);
227
225
 
228
226
  return this;
229
227
  }
@@ -232,7 +230,7 @@ export class StoredU256 {
232
230
  public set(value: u256): this {
233
231
  this._value = value;
234
232
 
235
- Blockchain.setStorageAt(this.pointer, this.subPointer, this._value, this.defaultValue);
233
+ Blockchain.setStorageAt(this.pointer, this.subPointer, this._value);
236
234
 
237
235
  return this;
238
236
  }
@@ -64,7 +64,7 @@ export class SafeMath {
64
64
  for (let i = shift; i >= 0; i--) {
65
65
  if (u256.ge(n, d)) {
66
66
  n = u256.sub(n, d);
67
- result = u256.or(result, SafeMath.shl(new u256(1), i));
67
+ result = u256.or(result, SafeMath.shl(u256.One, i));
68
68
  }
69
69
  d = u256.shr(d, 1); // restore d to original by shifting right
70
70
  }
@@ -86,16 +86,23 @@ export class SafeMath {
86
86
  if (u256.gt(y, u256.fromU32(3))) {
87
87
  let z = y;
88
88
 
89
- let x = SafeMath.add(SafeMath.div(y, u256.fromU32(2)), u256.fromU32(1));
89
+ let u246_2 = u256.fromU32(2);
90
+
91
+ let d = SafeMath.div(y, u246_2);
92
+ let x = SafeMath.add(d, u256.One);
93
+
90
94
  while (u256.lt(x, z)) {
91
95
  z = x;
92
96
 
93
97
  let u = SafeMath.div(y, x);
94
- x = SafeMath.div(u256.add(u, x), u256.fromU32(2));
98
+ let y2 = u256.add(u, x);
99
+
100
+ x = SafeMath.div(y2, u246_2);
95
101
  }
102
+
96
103
  return z;
97
104
  } else if (!u256.eq(y, u256.Zero)) {
98
- return u256.fromU32(1);
105
+ return u256.One;
99
106
  } else {
100
107
  return u256.Zero;
101
108
  }