@btc-vision/btc-runtime 1.0.10 → 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.10",
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,41 +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
- Blockchain.contractAddress,
40
39
  u256.Zero,
41
40
  );
41
+
42
42
  this.balanceOfMap = new AddressMemoryMap<Address, MemorySlotData<u256>>(
43
43
  Blockchain.nextPointer,
44
- Blockchain.contractAddress,
45
44
  u256.Zero,
46
45
  );
47
46
 
48
- const supplyPointer = Blockchain.nextPointer;
49
- const supply: u256 = Blockchain.getStorageAt(
50
- Blockchain.contractAddress,
51
- supplyPointer,
52
- u256.Zero,
53
- u256.Zero,
54
- );
55
- this._totalSupply = new StoredU256(
56
- Blockchain.contractAddress,
57
- supplyPointer,
58
- u256.Zero,
59
- supply,
60
- );
47
+ this._totalSupply = new StoredU256(Blockchain.nextPointer, u256.Zero, u256.Zero);
61
48
  }
62
49
 
63
50
  public _totalSupply: StoredU256;
@@ -168,10 +155,10 @@ export abstract class OP_20 extends OP_NET implements IOP_20 {
168
155
  response.writeU8(this.decimals);
169
156
  break;
170
157
  case encodeSelector('name'):
171
- response.writeString(this.name);
158
+ response.writeStringWithLength(this.name);
172
159
  break;
173
160
  case encodeSelector('symbol'):
174
- response.writeString(this.symbol);
161
+ response.writeStringWithLength(this.symbol);
175
162
  break;
176
163
  case encodeSelector('totalSupply'):
177
164
  response.writeU256(this.totalSupply);
@@ -241,9 +228,7 @@ export abstract class OP_20 extends OP_NET implements IOP_20 {
241
228
  const caller = Blockchain.caller();
242
229
 
243
230
  if (onlyOwner) this.onlyOwner(callee);
244
-
245
231
  if (caller !== callee) throw new Revert(`callee != caller`);
246
- if (callee !== this.owner) throw new Revert('Only indexers can mint tokens');
247
232
 
248
233
  if (!this.balanceOfMap.has(to)) {
249
234
  this.balanceOfMap.set(to, value);
@@ -283,14 +268,10 @@ export abstract class OP_20 extends OP_NET implements IOP_20 {
283
268
  const newBalance: u256 = SafeMath.sub(balance, value);
284
269
  this.balanceOfMap.set(caller, newBalance);
285
270
 
286
- if (!this.balanceOfMap.has(to)) {
287
- this.balanceOfMap.set(to, value);
288
- } else {
289
- const toBalance: u256 = this.balanceOfMap.get(to);
290
- const newToBalance: u256 = SafeMath.add(toBalance, value);
271
+ const toBalance: u256 = this.balanceOfMap.get(to);
272
+ const newToBalance: u256 = SafeMath.add(toBalance, value);
291
273
 
292
- this.balanceOfMap.set(to, newToBalance);
293
- }
274
+ this.balanceOfMap.set(to, newToBalance);
294
275
 
295
276
  this.createTransferEvent(caller, to, value);
296
277
 
@@ -357,19 +338,19 @@ export abstract class OP_20 extends OP_NET implements IOP_20 {
357
338
  this.emitEvent(burnEvent);
358
339
  }
359
340
 
360
- private createApproveEvent(owner: Address, spender: Address, value: u256): void {
341
+ protected createApproveEvent(owner: Address, spender: Address, value: u256): void {
361
342
  const approveEvent = new ApproveEvent(owner, spender, value);
362
343
 
363
344
  this.emitEvent(approveEvent);
364
345
  }
365
346
 
366
- private createMintEvent(owner: Address, value: u256): void {
347
+ protected createMintEvent(owner: Address, value: u256): void {
367
348
  const mintEvent = new MintEvent(owner, value);
368
349
 
369
350
  this.emitEvent(mintEvent);
370
351
  }
371
352
 
372
- private createTransferEvent(from: Address, to: Address, value: u256): void {
353
+ protected createTransferEvent(from: Address, to: Address, value: u256): void {
373
354
  const transferEvent = new TransferEvent(from, to, value);
374
355
 
375
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;
@@ -10,8 +10,9 @@ import { MAX_EVENTS, NetEvent } from '../events/NetEvent';
10
10
  import { Potential } from '../lang/Definitions';
11
11
  import { Map } from '../generic/Map';
12
12
  import { OP_NET } from '../contracts/OP_NET';
13
- import { BlockchainStorage, PointerStorage } from '../types';
14
- import { deploy, deployFromAddress } from './global';
13
+ import { PointerStorage } from '../types';
14
+ import { callContract, deploy, deployFromAddress, loadPointer, log, storePointer } from './global';
15
+ import { DeployContractResponse } from '../interfaces/DeployContractResponse';
15
16
 
16
17
  export * from '../env/global';
17
18
 
@@ -19,12 +20,7 @@ export * from '../env/global';
19
20
  export class BlockchainEnvironment {
20
21
  private static readonly runtimeException: string = 'RuntimeException';
21
22
 
22
- private storage: BlockchainStorage = new Map();
23
- private initializedStorage: BlockchainStorage = new Map();
24
-
25
- private externalCalls: Map<Address, Uint8Array[]> = new Map();
26
- private externalCallsResponse: Map<Address, Uint8Array[]> = new Map();
27
-
23
+ private storage: PointerStorage = new Map();
28
24
  private events: NetEvent[] = [];
29
25
 
30
26
  private _callee: PotentialAddress = null;
@@ -119,38 +115,21 @@ export class BlockchainEnvironment {
119
115
  throw this.error('Cannot call self');
120
116
  }
121
117
 
122
- if (!this.externalCalls.has(destinationContract)) {
123
- this.externalCalls.set(destinationContract, []);
124
- }
125
-
126
- const externalCalls = this.externalCalls.get(destinationContract);
127
- const buffer = calldata.getBuffer();
128
- externalCalls.push(buffer);
118
+ const call = new BytesWriter();
119
+ call.writeAddress(destinationContract);
120
+ call.writeBytesWithLength(calldata.getBuffer());
129
121
 
130
- const response: Potential<Uint8Array> = this.getExternalCallResponse(
131
- destinationContract,
132
- externalCalls.length - 1,
133
- );
134
- if (!response) {
135
- throw this.error('external call failed');
136
- }
122
+ const response: Uint8Array = callContract(call.getBuffer());
137
123
 
138
124
  return new BytesReader(response);
139
125
  }
140
126
 
141
- public getCalls(): Uint8Array {
142
- const buffer: BytesWriter = new BytesWriter();
143
-
144
- buffer.writeLimitedAddressBytesMap(this.externalCalls);
145
- this.externalCalls.clear();
146
-
147
- return buffer.getBuffer();
148
- }
149
-
150
- public loadCallsResponse(responses: Uint8Array): void {
151
- const memoryReader: BytesReader = new BytesReader(responses);
127
+ public log(data: string): void {
128
+ const writer = new BytesWriter();
129
+ writer.writeStringWithLength(data);
152
130
 
153
- this.externalCallsResponse = memoryReader.readMultiBytesAddressMap();
131
+ const buffer = writer.getBuffer();
132
+ log(buffer);
154
133
  }
155
134
 
156
135
  public addEvent(event: NetEvent): void {
@@ -192,64 +171,60 @@ export class BlockchainEnvironment {
192
171
  return new BytesReader(cb as Uint8Array);
193
172
  }
194
173
 
195
- public deployContractFromExisting(hash: u256, existingAddress: Address): BytesReader {
174
+ public deployContractFromExisting(
175
+ existingAddress: Address,
176
+ salt: u256,
177
+ ): DeployContractResponse {
196
178
  const writer = new BytesWriter();
197
- writer.writeU256(hash);
198
179
  writer.writeAddress(existingAddress);
180
+ writer.writeU256(salt);
199
181
 
200
- const cb: Potential<Uint8Array> = deployFromAddress(writer.getBuffer());
182
+ const buffer: Uint8Array = writer.getBuffer();
183
+ const cb: Potential<Uint8Array> = deployFromAddress(buffer);
201
184
  if (!cb) throw this.error('Failed to deploy contract');
202
185
 
203
- return new BytesReader(cb as Uint8Array);
186
+ const reader: BytesReader = new BytesReader(cb as Uint8Array);
187
+ const virtualAddress: u256 = reader.readU256();
188
+ const contractAddress: Address = reader.readAddress();
189
+
190
+ return new DeployContractResponse(virtualAddress, contractAddress);
204
191
  }
205
192
 
206
193
  public getStorageAt(
207
- address: Address,
208
194
  pointer: u16,
209
195
  subPointer: MemorySlotPointer,
210
196
  defaultValue: MemorySlotData<u256>,
211
197
  ): MemorySlotData<u256> {
212
- this.ensureStorageAtAddress(address);
213
-
214
198
  const pointerHash: MemorySlotPointer = encodePointerHash(pointer, subPointer);
215
- this.ensureStorageAtPointer(address, pointerHash, defaultValue);
216
-
217
- const storage: PointerStorage = this.storage.get(address);
199
+ this.ensureStorageAtPointer(pointerHash, defaultValue);
218
200
 
219
201
  // maybe find a better way for this
220
- const allKeys: u256[] = storage.keys();
202
+ const allKeys: u256[] = this.storage.keys();
221
203
  for (let i: i32 = 0; i < allKeys.length; i++) {
222
204
  const v: u256 = allKeys[i];
223
205
 
224
206
  if (u256.eq(v, pointerHash)) {
225
- return storage.get(v);
207
+ return this.storage.get(v);
226
208
  }
227
209
  }
228
210
 
229
211
  return defaultValue;
230
212
  }
231
213
 
232
- public hasStorageAt(address: Address, pointer: u16, subPointer: MemorySlotPointer): bool {
233
- this.ensureStorageAtAddress(address);
234
-
214
+ public hasStorageAt(pointer: u16, subPointer: MemorySlotPointer): bool {
235
215
  // We mark zero as the default value for the storage, if something is 0, the storage slot get deleted or is non-existent
236
- const val: u256 = this.getStorageAt(address, pointer, subPointer, u256.Zero);
216
+ const val: u256 = this.getStorageAt(pointer, subPointer, u256.Zero);
237
217
  return val != u256.Zero;
238
218
  }
239
219
 
240
220
  public setStorageAt(
241
- address: Address,
242
221
  pointer: u16,
243
222
  keyPointer: MemorySlotPointer,
244
223
  value: MemorySlotData<u256>,
245
- defaultValue: MemorySlotData<u256>,
246
224
  ): void {
247
- this.ensureStorageAtAddress(address);
248
-
249
225
  const pointerHash: u256 = encodePointerHash(pointer, keyPointer);
250
- this.ensureStorageAtPointer(address, pointerHash, defaultValue);
251
226
 
252
- this._internalSetStorageAt(address, pointerHash, value);
227
+ this._internalSetStorageAt(pointerHash, value);
253
228
  }
254
229
 
255
230
  public getViewSelectors(): Uint8Array {
@@ -264,113 +239,34 @@ export class BlockchainEnvironment {
264
239
  return ABIRegistry.getWriteMethods();
265
240
  }
266
241
 
267
- public loadStorage(data: Uint8Array): void {
268
- this.purgeMemory();
269
-
270
- const memoryReader: BytesReader = new BytesReader(data);
271
- const contractsSize: u32 = memoryReader.readU32();
272
-
273
- for (let i: u32 = 0; i < contractsSize; i++) {
274
- const address: Address = memoryReader.readAddress();
275
- const storageSize: u32 = memoryReader.readU32();
276
-
277
- this.ensureStorageAtAddress(address);
278
- const storage: PointerStorage = this.storage.get(address);
279
-
280
- for (let j: u32 = 0; j < storageSize; j++) {
281
- const keyPointer: MemorySlotPointer = memoryReader.readU256();
282
- const value: MemorySlotData<u256> = memoryReader.readU256();
283
-
284
- storage.set(keyPointer, value);
285
- }
286
- }
287
- }
288
-
289
- public storageToBytes(): Uint8Array {
290
- const memoryWriter: BytesWriter = new BytesWriter();
291
- memoryWriter.writeStorage(this.storage);
292
-
293
- //this.storage.clear();
294
-
295
- return memoryWriter.getBuffer();
296
- }
297
-
298
- public initializedStorageToBytes(): Uint8Array {
299
- const memoryWriter: BytesWriter = new BytesWriter();
300
- memoryWriter.writeStorage(this.initializedStorage);
301
-
302
- //this.initializedStorage.clear();
303
-
304
- return memoryWriter.getBuffer();
305
- }
306
-
307
- private purgeMemory(): void {
308
- this.storage.clear();
309
- this.initializedStorage.clear();
310
-
311
- this.events = [];
312
-
313
- this.externalCallsResponse.clear();
314
- this.externalCalls.clear();
315
- }
316
-
317
- private requireInitialStorage(address: Address, pointerHash: u256, defaultValue: u256): void {
318
- if (!this.initializedStorage.has(address)) {
319
- this.initializedStorage.set(address, new Map<u256, MemorySlotData<u256>>());
320
- }
321
-
322
- //load(pointerHash);
323
-
324
- const storage = this.initializedStorage.get(address);
325
- storage.set(pointerHash, defaultValue);
326
- }
327
-
328
- private getExternalCallResponse(
329
- destinationContract: Address,
330
- index: i32,
331
- ): Potential<Uint8Array> {
332
- if (!this.externalCallsResponse.has(destinationContract)) {
333
- this.externalCallsResponse.set(destinationContract, []);
334
- }
335
-
336
- const externalCallsResponse = this.externalCallsResponse.get(destinationContract);
337
- return externalCallsResponse[index] || null;
338
- }
339
-
340
242
  private error(msg: string): Error {
341
243
  return new Error(`${BlockchainEnvironment.runtimeException}: ${msg}`);
342
244
  }
343
245
 
344
- private _internalSetStorageAt(
345
- address: Address,
346
- pointerHash: u256,
347
- value: MemorySlotData<u256>,
348
- ): void {
349
- const storage: PointerStorage = this.storage.get(address);
350
- const keys: u256[] = storage.keys();
246
+ private _internalSetStorageAt(pointerHash: u256, value: MemorySlotData<u256>): void {
247
+ const keys: u256[] = this.storage.keys();
351
248
 
352
249
  // Delete the old value, there is a bug with u256 and maps.
353
250
  for (let i = 0; i < keys.length; i++) {
354
251
  const key = keys[i];
355
252
 
356
253
  if (u256.eq(key, pointerHash)) {
357
- storage.delete(key);
254
+ this.storage.delete(key);
358
255
  }
359
256
  }
360
257
 
361
- //store(pointerHash, value);
258
+ this.storage.set(pointerHash, value);
362
259
 
363
- storage.set(pointerHash, value);
364
- }
260
+ const writer: BytesWriter = new BytesWriter();
261
+ writer.writeU256(pointerHash);
262
+ writer.writeU256(value);
365
263
 
366
- private ensureStorageAtAddress(address: Address): void {
367
- if (!this.storage.has(address)) {
368
- this.storage.set(address, new Map<u256, MemorySlotData<u256>>());
369
- }
264
+ const buffer: Uint8Array = writer.getBuffer();
265
+ storePointer(buffer);
370
266
  }
371
267
 
372
- private hasPointerStorageHash(storage: PointerStorage, pointer: MemorySlotPointer): bool {
373
- const keys = storage.keys();
268
+ private hasPointerStorageHash(pointer: MemorySlotPointer): bool {
269
+ const keys = this.storage.keys();
374
270
 
375
271
  for (let i = 0; i < keys.length; i++) {
376
272
  const key = keys[i];
@@ -380,24 +276,29 @@ export class BlockchainEnvironment {
380
276
  }
381
277
  }
382
278
 
383
- return false;
279
+ // we attempt to load the requested pointer.
280
+ const writer = new BytesWriter();
281
+ writer.writeU256(pointer);
282
+
283
+ const result: Uint8Array = loadPointer(writer.getBuffer());
284
+ const reader: BytesReader = new BytesReader(result);
285
+
286
+ const value: u256 = reader.readU256();
287
+ this.storage.set(pointer, value); // cache the value
288
+
289
+ return !u256.eq(value, u256.Zero);
384
290
  }
385
291
 
386
292
  private ensureStorageAtPointer(
387
- address: Address,
388
293
  pointerHash: MemorySlotPointer,
389
294
  defaultValue: MemorySlotData<u256>,
390
295
  ): void {
391
- if (!this.storage.has(address)) {
392
- throw this.error(`Storage slot not found for address ${address}`);
393
- }
394
-
395
- // !!! -- IMPORTANT -- !!!. We have to tell the indexer that we need this storage slot to continue even if it's already defined.
396
- this.requireInitialStorage(address, pointerHash, defaultValue);
296
+ if (!this.hasPointerStorageHash(pointerHash)) {
297
+ if (u256.eq(defaultValue, u256.Zero)) {
298
+ return;
299
+ }
397
300
 
398
- const storage: PointerStorage = this.storage.get(address);
399
- if (!this.hasPointerStorageHash(storage, pointerHash)) {
400
- this._internalSetStorageAt(address, pointerHash, defaultValue);
301
+ this._internalSetStorageAt(pointerHash, defaultValue);
401
302
  }
402
303
  }
403
304
  }
@@ -1,12 +1,10 @@
1
- import { u256 } from 'as-bignum/assembly/integer/u256';
2
-
3
1
  // @ts-ignore
4
2
  @external('env', 'load')
5
- export declare function load(data: Uint8Array): u256;
3
+ export declare function loadPointer(data: Uint8Array): Uint8Array;
6
4
 
7
5
  // @ts-ignore
8
6
  @external('env', 'store')
9
- export declare function store(data: Uint8Array): void;
7
+ export declare function storePointer(data: Uint8Array): Uint8Array;
10
8
 
11
9
  // @ts-ignore
12
10
  @external('env', 'deploy')
@@ -18,4 +16,8 @@ export declare function deployFromAddress(data: Uint8Array): Uint8Array;
18
16
 
19
17
  // @ts-ignore
20
18
  @external('env', 'call')
21
- export declare function call(data: Uint8Array): Uint8Array;
19
+ export declare function callContract(data: Uint8Array): Uint8Array;
20
+
21
+ // @ts-ignore
22
+ @external('env', 'log')
23
+ export declare function log(data: Uint8Array): void;
@@ -32,26 +32,6 @@ export function getWriteMethods(): Uint8Array {
32
32
  return Blockchain.getWriteMethods();
33
33
  }
34
34
 
35
- export function getModifiedStorage(): Uint8Array {
36
- return Blockchain.storageToBytes();
37
- }
38
-
39
- export function initializeStorage(): Uint8Array {
40
- return Blockchain.initializedStorageToBytes();
41
- }
42
-
43
- export function loadStorage(data: Uint8Array): void {
44
- Blockchain.loadStorage(data);
45
- }
46
-
47
- export function loadCallsResponse(data: Uint8Array): void {
48
- Blockchain.loadCallsResponse(data);
49
- }
50
-
51
- export function getCalls(): Uint8Array {
52
- return Blockchain.getCalls();
53
- }
54
-
55
35
  export function setEnvironment(data: Uint8Array): void {
56
36
  Blockchain.setEnvironment(data);
57
37
  }
package/runtime/index.ts CHANGED
@@ -10,6 +10,9 @@ export * from './contracts/OP_NET';
10
10
  export * from './buffer/BytesReader';
11
11
  export * from './buffer/BytesWriter';
12
12
 
13
+ /** Interfaces */
14
+ export * from './interfaces/DeployContractResponse';
15
+
13
16
  /** Events */
14
17
  export * from './events/NetEvent';
15
18
  export * from './events/predefined';
@@ -0,0 +1,12 @@
1
+ import { u256 } from 'as-bignum/assembly';
2
+ import { Address } from '../types/Address';
3
+
4
+ export class DeployContractResponse {
5
+ readonly virtualAddress: u256;
6
+ readonly contractAddress: Address;
7
+
8
+ constructor(virtualAddress: u256, contractAddress: Address) {
9
+ this.virtualAddress = virtualAddress;
10
+ this.contractAddress = contractAddress;
11
+ }
12
+ }
@@ -1,6 +1,5 @@
1
1
  import { MemorySlotPointer } from './MemorySlotPointer';
2
2
  import { Blockchain } from '../env';
3
- import { Address } from '../types/Address';
4
3
  import { encodePointer } from '../math/abi';
5
4
  import { MemorySlotData } from './MemorySlot';
6
5
  import { u256 } from 'as-bignum/assembly';
@@ -9,45 +8,26 @@ import { u256 } from 'as-bignum/assembly';
9
8
  export class AddressMemoryMap<K extends string, V extends MemorySlotData<u256>> {
10
9
  public pointer: u16;
11
10
 
12
- private readonly memoryAllocatorAddress: Address;
13
-
14
11
  constructor(
15
12
  pointer: u16,
16
- self: Address,
17
13
  private readonly defaultValue: V,
18
14
  ) {
19
15
  this.pointer = pointer;
20
- this.memoryAllocatorAddress = self;
21
16
  }
22
17
 
23
18
  public set(key: K, value: V): this {
24
19
  const keyHash: MemorySlotPointer = encodePointer(key);
25
- Blockchain.setStorageAt(
26
- this.memoryAllocatorAddress,
27
- this.pointer,
28
- keyHash,
29
- value,
30
- this.defaultValue,
31
- );
20
+ Blockchain.setStorageAt(this.pointer, keyHash, value);
32
21
 
33
22
  return this;
34
23
  }
35
24
 
36
25
  public get(key: K): MemorySlotData<u256> {
37
- return Blockchain.getStorageAt(
38
- this.memoryAllocatorAddress,
39
- this.pointer,
40
- encodePointer(key),
41
- this.defaultValue,
42
- );
26
+ return Blockchain.getStorageAt(this.pointer, encodePointer(key), this.defaultValue);
43
27
  }
44
28
 
45
29
  public has(key: K): bool {
46
- return Blockchain.hasStorageAt(
47
- this.memoryAllocatorAddress,
48
- this.pointer,
49
- encodePointer(key),
50
- );
30
+ return Blockchain.hasStorageAt(this.pointer, encodePointer(key));
51
31
  }
52
32
 
53
33
  @unsafe
@@ -1,7 +1,6 @@
1
1
  import { MemorySlotData } from './MemorySlot';
2
2
  import { u256 } from 'as-bignum/assembly';
3
3
  import { Blockchain } from '../env';
4
- import { Address } from '../types/Address';
5
4
  import { MemorySlotPointer } from './MemorySlotPointer';
6
5
  import { encodePointer } from '../math/abi';
7
6
 
@@ -10,16 +9,13 @@ export class KeyMerger<K extends string, K2 extends string, V extends MemorySlot
10
9
  public parentKey: K;
11
10
 
12
11
  public pointer: u16;
13
- private readonly memoryAllocatorAddress: Address;
14
12
 
15
13
  constructor(
16
14
  parent: K,
17
15
  pointer: u16,
18
- self: Address,
19
16
  private readonly defaultValue: V,
20
17
  ) {
21
18
  this.pointer = pointer;
22
- this.memoryAllocatorAddress = self;
23
19
 
24
20
  this.parentKey = parent;
25
21
  }
@@ -28,13 +24,7 @@ export class KeyMerger<K extends string, K2 extends string, V extends MemorySlot
28
24
  const mergedKey: string = `${this.parentKey}${key2}`;
29
25
  const keyHash: MemorySlotPointer = encodePointer(mergedKey);
30
26
 
31
- Blockchain.setStorageAt(
32
- this.memoryAllocatorAddress,
33
- this.pointer,
34
- keyHash,
35
- value,
36
- this.defaultValue,
37
- );
27
+ Blockchain.setStorageAt(this.pointer, keyHash, value);
38
28
 
39
29
  return this;
40
30
  }
@@ -42,22 +32,13 @@ export class KeyMerger<K extends string, K2 extends string, V extends MemorySlot
42
32
  public get(key: K): MemorySlotData<u256> {
43
33
  const mergedKey: string = `${this.parentKey}${key}`;
44
34
 
45
- return Blockchain.getStorageAt(
46
- this.memoryAllocatorAddress,
47
- this.pointer,
48
- encodePointer(mergedKey),
49
- this.defaultValue,
50
- );
35
+ return Blockchain.getStorageAt(this.pointer, encodePointer(mergedKey), this.defaultValue);
51
36
  }
52
37
 
53
38
  public has(key: K): bool {
54
39
  const mergedKey: string = `${this.parentKey}${key}`;
55
40
 
56
- return Blockchain.hasStorageAt(
57
- this.memoryAllocatorAddress,
58
- this.pointer,
59
- encodePointer(mergedKey),
60
- );
41
+ return Blockchain.hasStorageAt(this.pointer, encodePointer(mergedKey));
61
42
  }
62
43
 
63
44
  @unsafe
@@ -1,4 +1,3 @@
1
- import { Address } from '../types/Address';
2
1
  import { MemorySlotData } from './MemorySlot';
3
2
  import { u256 } from 'as-bignum/assembly';
4
3
  import { KeyMerger } from './KeyMerger';
@@ -11,17 +10,13 @@ export class MultiAddressMemoryMap<
11
10
  > extends Map<K, KeyMerger<K, K2, V>> {
12
11
  public pointer: u16;
13
12
 
14
- private readonly memoryAllocatorAddress: Address;
15
-
16
13
  constructor(
17
14
  pointer: u16,
18
- self: Address,
19
15
  private readonly defaultValue: V,
20
16
  ) {
21
17
  super();
22
18
 
23
19
  this.pointer = pointer;
24
- this.memoryAllocatorAddress = self;
25
20
  }
26
21
 
27
22
  public get(key: K): KeyMerger<K, K2, V> {
@@ -61,15 +56,7 @@ export class MultiAddressMemoryMap<
61
56
 
62
57
  private createKeyMerger(key: K): void {
63
58
  if (!super.has(key)) {
64
- super.set(
65
- key,
66
- new KeyMerger<K, K2, V>(
67
- key,
68
- this.pointer,
69
- this.memoryAllocatorAddress,
70
- this.defaultValue,
71
- ),
72
- );
59
+ super.set(key, new KeyMerger<K, K2, V>(key, this.pointer, this.defaultValue));
73
60
  }
74
61
  }
75
62
  }
@@ -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
+ }
@@ -1,24 +1,15 @@
1
1
  import { u256 } from 'as-bignum/assembly';
2
2
  import { SafeMath } from '../types/SafeMath';
3
- import { Address } from '../types/Address';
4
3
  import { MemorySlotPointer } from '../memory/MemorySlotPointer';
5
4
  import { Blockchain } from '../env';
6
5
 
7
6
  @final
8
7
  export class StoredU256 {
9
8
  constructor(
10
- public address: Address,
11
9
  public pointer: u16,
12
10
  public subPointer: MemorySlotPointer,
13
11
  private defaultValue: u256,
14
- ) {
15
- this._value = Blockchain.getStorageAt(
16
- this.address,
17
- this.pointer,
18
- this.subPointer,
19
- this.defaultValue,
20
- );
21
- }
12
+ ) {}
22
13
 
23
14
  private _value: u256 = u256.Zero;
24
15
 
@@ -33,13 +24,7 @@ export class StoredU256 {
33
24
  public set value(value: u256) {
34
25
  this._value = value;
35
26
 
36
- Blockchain.setStorageAt(
37
- this.address,
38
- this.pointer,
39
- this.subPointer,
40
- this._value,
41
- this.defaultValue,
42
- );
27
+ Blockchain.setStorageAt(this.pointer, this.subPointer, this._value);
43
28
  }
44
29
 
45
30
  @inline
@@ -53,13 +38,7 @@ export class StoredU256 {
53
38
  this.ensureValue();
54
39
 
55
40
  this._value = SafeMath.add(this._value, value);
56
- Blockchain.setStorageAt(
57
- this.address,
58
- this.pointer,
59
- this.subPointer,
60
- this._value,
61
- this.defaultValue,
62
- );
41
+ Blockchain.setStorageAt(this.pointer, this.subPointer, this._value);
63
42
 
64
43
  return this;
65
44
  }
@@ -70,13 +49,7 @@ export class StoredU256 {
70
49
  this.ensureValue();
71
50
 
72
51
  this._value = SafeMath.sub(this._value, value);
73
- Blockchain.setStorageAt(
74
- this.address,
75
- this.pointer,
76
- this.subPointer,
77
- this._value,
78
- this.defaultValue,
79
- );
52
+ Blockchain.setStorageAt(this.pointer, this.subPointer, this._value);
80
53
 
81
54
  return this;
82
55
  }
@@ -87,13 +60,7 @@ export class StoredU256 {
87
60
  this.ensureValue();
88
61
 
89
62
  this._value = SafeMath.mul(this._value, value);
90
- Blockchain.setStorageAt(
91
- this.address,
92
- this.pointer,
93
- this.subPointer,
94
- this._value,
95
- this.defaultValue,
96
- );
63
+ Blockchain.setStorageAt(this.pointer, this.subPointer, this._value);
97
64
 
98
65
  return this;
99
66
  }
@@ -152,13 +119,7 @@ export class StoredU256 {
152
119
  this.ensureValue();
153
120
 
154
121
  this._value = u256.shr(this._value, value);
155
- Blockchain.setStorageAt(
156
- this.address,
157
- this.pointer,
158
- this.subPointer,
159
- this._value,
160
- this.defaultValue,
161
- );
122
+ Blockchain.setStorageAt(this.pointer, this.subPointer, this._value);
162
123
 
163
124
  return this;
164
125
  }
@@ -169,13 +130,7 @@ export class StoredU256 {
169
130
  this.ensureValue();
170
131
 
171
132
  this._value = u256.and(this._value, value);
172
- Blockchain.setStorageAt(
173
- this.address,
174
- this.pointer,
175
- this.subPointer,
176
- this._value,
177
- this.defaultValue,
178
- );
133
+ Blockchain.setStorageAt(this.pointer, this.subPointer, this._value);
179
134
 
180
135
  return this;
181
136
  }
@@ -186,13 +141,7 @@ export class StoredU256 {
186
141
  this.ensureValue();
187
142
 
188
143
  this._value = u256.or(this._value, value);
189
- Blockchain.setStorageAt(
190
- this.address,
191
- this.pointer,
192
- this.subPointer,
193
- this._value,
194
- this.defaultValue,
195
- );
144
+ Blockchain.setStorageAt(this.pointer, this.subPointer, this._value);
196
145
 
197
146
  return this;
198
147
  }
@@ -203,13 +152,7 @@ export class StoredU256 {
203
152
  this.ensureValue();
204
153
 
205
154
  this._value = u256.xor(this._value, value);
206
- Blockchain.setStorageAt(
207
- this.address,
208
- this.pointer,
209
- this.subPointer,
210
- this._value,
211
- this.defaultValue,
212
- );
155
+ Blockchain.setStorageAt(this.pointer, this.subPointer, this._value);
213
156
 
214
157
  return this;
215
158
  }
@@ -231,13 +174,7 @@ export class StoredU256 {
231
174
  value = u256.shr(value, 1);
232
175
  }
233
176
 
234
- Blockchain.setStorageAt(
235
- this.address,
236
- this.pointer,
237
- this.subPointer,
238
- this._value,
239
- this.defaultValue,
240
- );
177
+ Blockchain.setStorageAt(this.pointer, this.subPointer, this._value);
241
178
 
242
179
  return this;
243
180
  }
@@ -262,13 +199,7 @@ export class StoredU256 {
262
199
  }
263
200
 
264
201
  this._value = result;
265
- Blockchain.setStorageAt(
266
- this.address,
267
- this.pointer,
268
- this.subPointer,
269
- this._value,
270
- this.defaultValue,
271
- );
202
+ Blockchain.setStorageAt(this.pointer, this.subPointer, this._value);
272
203
 
273
204
  return this;
274
205
  }
@@ -279,13 +210,7 @@ export class StoredU256 {
279
210
  this.ensureValue();
280
211
 
281
212
  this._value = SafeMath.add(this._value, u256.One);
282
- Blockchain.setStorageAt(
283
- this.address,
284
- this.pointer,
285
- this.subPointer,
286
- this._value,
287
- this.defaultValue,
288
- );
213
+ Blockchain.setStorageAt(this.pointer, this.subPointer, this._value);
289
214
 
290
215
  return this;
291
216
  }
@@ -296,13 +221,7 @@ export class StoredU256 {
296
221
  this.ensureValue();
297
222
 
298
223
  this._value = SafeMath.sub(this._value, u256.One);
299
- Blockchain.setStorageAt(
300
- this.address,
301
- this.pointer,
302
- this.subPointer,
303
- this._value,
304
- this.defaultValue,
305
- );
224
+ Blockchain.setStorageAt(this.pointer, this.subPointer, this._value);
306
225
 
307
226
  return this;
308
227
  }
@@ -311,13 +230,7 @@ export class StoredU256 {
311
230
  public set(value: u256): this {
312
231
  this._value = value;
313
232
 
314
- Blockchain.setStorageAt(
315
- this.address,
316
- this.pointer,
317
- this.subPointer,
318
- this._value,
319
- this.defaultValue,
320
- );
233
+ Blockchain.setStorageAt(this.pointer, this.subPointer, this._value);
321
234
 
322
235
  return this;
323
236
  }
@@ -328,11 +241,6 @@ export class StoredU256 {
328
241
  }
329
242
 
330
243
  private ensureValue(): void {
331
- this._value = Blockchain.getStorageAt(
332
- this.address,
333
- this.pointer,
334
- this.subPointer,
335
- this.defaultValue,
336
- );
244
+ this._value = Blockchain.getStorageAt(this.pointer, this.subPointer, this.defaultValue);
337
245
  }
338
246
  }
@@ -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
  }