@btc-vision/btc-runtime 1.1.11 → 1.2.1

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 CHANGED
@@ -1,10 +1,12 @@
1
1
  {
2
2
  "name": "@btc-vision/btc-runtime",
3
- "version": "1.1.11",
3
+ "version": "1.2.1",
4
4
  "description": "Bitcoin Smart Contract Runtime",
5
5
  "main": "btc/index.ts",
6
+ "scripts": {
7
+ "build": "cross-env node scripts/asc tests/tests.ts"
8
+ },
6
9
  "types": "btc/index.ts",
7
- "scripts": {},
8
10
  "keywords": [
9
11
  "bitcoin",
10
12
  "smart",
@@ -17,9 +19,12 @@
17
19
  "author": "BlobMaster41",
18
20
  "license": "MIT",
19
21
  "devDependencies": {
22
+ "@types/mocha": "^10.0.8",
20
23
  "@types/node": "^22.5.5",
21
- "assemblyscript": "^0.27.25",
22
- "prettier": "^3.2.5"
24
+ "assemblyscript": "^0.27.30",
25
+ "cross-env": "^7.0.3",
26
+ "ts-mocha": "^10.0.0",
27
+ "fs-extra": "^11.2.0"
23
28
  },
24
29
  "repository": {
25
30
  "type": "git",
@@ -32,10 +37,11 @@
32
37
  "runtime/*.ts",
33
38
  "runtime/**/*.ts",
34
39
  "!**/*.js.map",
35
- "!**/*.tsbuildinfo"
40
+ "!**/*.tsbuildinfo",
41
+ "test/*.ts"
36
42
  ],
37
43
  "dependencies": {
38
- "@assemblyscript/loader": "^0.27.25",
44
+ "@assemblyscript/loader": "^0.27.30",
39
45
  "@eslint/js": "^9.10.0",
40
46
  "as-bignum": "^0.3.0",
41
47
  "gulplog": "^2.2.0",
@@ -3,6 +3,8 @@ import { Selector } from '../math/abi';
3
3
  import { u256 } from 'as-bignum/assembly';
4
4
  import { Revert } from '../types/Revert';
5
5
  import { Map } from '../generic/Map';
6
+ import { TransactionInput, TransactionOutput } from '../env/classes/UTXO';
7
+ import { StaticArray } from 'staticarray';
6
8
 
7
9
  @final
8
10
  export class BytesReader {
@@ -106,6 +108,36 @@ export class BytesReader {
106
108
  return String.UTF8.decode(bytes.buffer);
107
109
  }
108
110
 
111
+ public readTransactionInputs(): StaticArray<TransactionInput> {
112
+ const length = this.readU8();
113
+ const result = new StaticArray<TransactionInput>(length);
114
+
115
+ for (let i: u16 = 0; i < length; i++) {
116
+ const txId = this.readBytes(32);
117
+ const outputIndex = this.readU8();
118
+ const scriptSig = this.readBytesWithLength();
119
+
120
+ result[i] = new TransactionInput(txId, outputIndex, scriptSig);
121
+ }
122
+
123
+ return result;
124
+ }
125
+
126
+ public readTransactionOutputs(): StaticArray<TransactionOutput> {
127
+ const length = this.readU8();
128
+ const result = new StaticArray<TransactionOutput>(length);
129
+
130
+ for (let i: u16 = 0; i < length; i++) {
131
+ const index = this.readU8();
132
+ const scriptPubKey = this.readBytesWithLength();
133
+ const value = this.readU64();
134
+
135
+ result[i] = new TransactionOutput(index, scriptPubKey, value);
136
+ }
137
+
138
+ return result;
139
+ }
140
+
109
141
  public readTuple(): u256[] {
110
142
  const length = this.readU32();
111
143
  const result: u256[] = new Array<u256>(length);
@@ -11,10 +11,11 @@ export class BytesWriter {
11
11
  private currentOffset: u32 = 0;
12
12
  private buffer: DataView;
13
13
 
14
- constructor(length: i32) {
15
- const arrayBuffer = new ArrayBuffer(length);
14
+ private readonly typedArray: Uint8Array;
16
15
 
17
- this.buffer = new DataView(arrayBuffer);
16
+ constructor(length: i32) {
17
+ const typedArray = (this.typedArray = new Uint8Array(length));
18
+ this.buffer = new DataView(typedArray.buffer);
18
19
  }
19
20
 
20
21
  public bufferLength(): u32 {
@@ -63,12 +64,7 @@ export class BytesWriter {
63
64
  }
64
65
 
65
66
  public writeU256(value: u256): void {
66
- this.allocSafe(32);
67
-
68
- const bytes = value.toUint8Array(true);
69
- for (let i: i32 = 0; i < 32; i++) {
70
- this.writeU8(bytes[i] || 0);
71
- }
67
+ this.writeBytesU8Array(value.toUint8Array());
72
68
  }
73
69
 
74
70
  public writeTuple(value: u256[]): void {
@@ -81,14 +77,11 @@ export class BytesWriter {
81
77
  }
82
78
 
83
79
  public writeBytes(value: Uint8Array): void {
84
- this.allocSafe(value.length);
85
-
86
- for (let i = 0; i < value.length; i++) {
87
- this.writeU8(value[i]);
88
- }
80
+ this.writeBytesU8Array(value);
89
81
  }
90
82
 
91
- public writeBytesU8Array(value: u8[]): void {
83
+ @inline
84
+ public writeBytesU8Array(value: Uint8Array): void {
92
85
  this.allocSafe(value.length);
93
86
 
94
87
  for (let i = 0; i < value.length; i++) {
@@ -196,7 +189,7 @@ export class BytesWriter {
196
189
  }
197
190
 
198
191
  public getBuffer(): Uint8Array {
199
- return Uint8Array.wrap(this.buffer.buffer, this.buffer.byteOffset, this.buffer.byteLength);
192
+ return this.typedArray;
200
193
  }
201
194
 
202
195
  public toBytesReader(): BytesReader {
@@ -11,10 +11,11 @@ import { StoredU256 } from '../storage/StoredU256';
11
11
  import { Address } from '../types/Address';
12
12
  import { Revert } from '../types/Revert';
13
13
  import { SafeMath } from '../types/SafeMath';
14
- import { Calldata } from '../universal/ABIRegistry';
14
+
15
15
  import { IOP_20 } from './interfaces/IOP_20';
16
16
  import { OP20InitParameters } from './interfaces/OP20InitParameters';
17
17
  import { OP_NET } from './OP_NET';
18
+ import { Calldata } from '../types';
18
19
 
19
20
  const maxSupplyPointer: u16 = Blockchain.nextPointer;
20
21
  const decimalsPointer: u16 = Blockchain.nextPointer;
@@ -94,7 +95,7 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
94
95
  throw new Revert('Already initialized');
95
96
  }
96
97
 
97
- if (!skipOwnerVerification) this.onlyOwner(Blockchain.msgSender);
98
+ if (!skipOwnerVerification) this.onlyOwner(Blockchain.tx.sender);
98
99
 
99
100
  if (params.decimals > 32) {
100
101
  throw new Revert('Decimals can not be more than 32');
@@ -118,7 +119,7 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
118
119
 
119
120
  public approve(callData: Calldata): BytesWriter {
120
121
  // Define the owner and spender
121
- const owner = Blockchain.msgSender;
122
+ const owner = Blockchain.tx.sender;
122
123
  const spender: Address = callData.readAddress();
123
124
  const value = callData.readU256();
124
125
 
@@ -180,28 +181,7 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
180
181
  return response;
181
182
  }
182
183
 
183
- public callMethod(method: Selector, calldata: Calldata): BytesWriter {
184
- switch (method) {
185
- case encodeSelector('allowance'):
186
- return this.allowance(calldata);
187
- case encodeSelector('approve'):
188
- return this.approve(calldata);
189
- case encodeSelector('balanceOf'):
190
- return this.balanceOf(calldata);
191
- case encodeSelector('burn'):
192
- return this.burn(calldata);
193
- case encodeSelector('mint'):
194
- return this.mint(calldata);
195
- case encodeSelector('transfer'):
196
- return this.transfer(calldata);
197
- case encodeSelector('transferFrom'):
198
- return this.transferFrom(calldata);
199
- default:
200
- return super.callMethod(method, calldata);
201
- }
202
- }
203
-
204
- public callView(method: Selector): BytesWriter {
184
+ public execute(method: Selector, calldata: Calldata): BytesWriter {
205
185
  let response: BytesWriter;
206
186
 
207
187
  switch (method) {
@@ -225,8 +205,22 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
225
205
  response = new BytesWriter(32);
226
206
  response.writeU256(this.maxSupply);
227
207
  break;
208
+ case encodeSelector('allowance'):
209
+ return this.allowance(calldata);
210
+ case encodeSelector('approve'):
211
+ return this.approve(calldata);
212
+ case encodeSelector('balanceOf'):
213
+ return this.balanceOf(calldata);
214
+ case encodeSelector('burn'):
215
+ return this.burn(calldata);
216
+ case encodeSelector('mint'):
217
+ return this.mint(calldata);
218
+ case encodeSelector('transfer'):
219
+ return this.transfer(calldata);
220
+ case encodeSelector('transferFrom'):
221
+ return this.transferFrom(calldata);
228
222
  default:
229
- return super.callView(method);
223
+ return super.execute(method, calldata);
230
224
  }
231
225
 
232
226
  return response;
@@ -264,16 +258,16 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
264
258
  throw new Revert(`No tokens`);
265
259
  }
266
260
 
267
- if (onlyOwner) this.onlyOwner(Blockchain.msgSender);
261
+ if (onlyOwner) this.onlyOwner(Blockchain.tx.sender);
268
262
 
269
263
  if (this._totalSupply.value < value) throw new Revert(`Insufficient total supply.`);
270
- if (!this.balanceOfMap.has(Blockchain.msgSender)) throw new Revert('No balance');
264
+ if (!this.balanceOfMap.has(Blockchain.tx.sender)) throw new Revert('No balance');
271
265
 
272
- const balance: u256 = this.balanceOfMap.get(Blockchain.msgSender);
266
+ const balance: u256 = this.balanceOfMap.get(Blockchain.tx.sender);
273
267
  if (balance < value) throw new Revert(`Insufficient balance`);
274
268
 
275
269
  const newBalance: u256 = SafeMath.sub(balance, value);
276
- this.balanceOfMap.set(Blockchain.msgSender, newBalance);
270
+ this.balanceOfMap.set(Blockchain.tx.sender, newBalance);
277
271
 
278
272
  // @ts-expect-error TODO: Fix the typing because this is valid assembly-script syntax
279
273
  this._totalSupply -= value;
@@ -283,7 +277,7 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
283
277
  }
284
278
 
285
279
  protected _mint(to: Address, value: u256, onlyOwner: boolean = true): boolean {
286
- if (onlyOwner) this.onlyOwner(Blockchain.msgSender);
280
+ if (onlyOwner) this.onlyOwner(Blockchain.tx.sender);
287
281
 
288
282
  if (!this.balanceOfMap.has(to)) {
289
283
  this.balanceOfMap.set(to, value);
@@ -304,7 +298,7 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
304
298
  }
305
299
 
306
300
  protected _transfer(to: string, value: u256): boolean {
307
- const sender = Blockchain.msgSender;
301
+ const sender = Blockchain.tx.sender;
308
302
 
309
303
  if (!this.balanceOfMap.has(sender)) throw new Revert();
310
304
  if (this.isSelf(sender)) throw new Revert('Can not transfer from self account');
@@ -359,7 +353,7 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
359
353
  throw new Revert('Cannot transfer to or from dead address');
360
354
  }
361
355
 
362
- this._spendAllowance(from, Blockchain.msgSender, value);
356
+ this._spendAllowance(from, Blockchain.tx.sender, value);
363
357
  this._unsafeTransferFrom(from, to, value);
364
358
 
365
359
  return true;
@@ -1,19 +1,13 @@
1
1
  import { IBTC } from '../interfaces/IBTC';
2
2
  import { Address, ADDRESS_BYTE_LENGTH } from '../types/Address';
3
3
  import { Blockchain } from '../env';
4
- import { Calldata } from '../universal/ABIRegistry';
5
4
  import { BytesWriter } from '../buffer/BytesWriter';
6
5
  import { encodeSelector, Selector } from '../math/abi';
7
6
  import { Revert } from '../types/Revert';
8
7
  import { MAX_EVENT_DATA_SIZE, NetEvent } from '../events/NetEvent';
9
- import { StoredBoolean } from '../storage/StoredBoolean';
8
+ import { Calldata } from '../types';
10
9
 
11
10
  export class OP_NET implements IBTC {
12
- protected readonly instantiated: StoredBoolean = new StoredBoolean(
13
- Blockchain.nextPointer,
14
- false,
15
- );
16
-
17
11
  public get address(): string {
18
12
  return Blockchain.contractAddress;
19
13
  }
@@ -22,24 +16,9 @@ export class OP_NET implements IBTC {
22
16
  return Blockchain.owner;
23
17
  }
24
18
 
25
- public get isInstantiated(): bool {
26
- return this.instantiated.value;
27
- }
28
-
29
- public onInstantiated(): void {
30
- if (!this.isInstantiated) {
31
- this.instantiated.value = true;
32
- }
33
- }
34
-
35
- public callMethod(method: Selector, _calldata: Calldata): BytesWriter {
36
- switch (method) {
37
- default:
38
- throw new Revert('Method not found');
39
- }
40
- }
19
+ public onDeployment(_calldata: Calldata): void {}
41
20
 
42
- public callView(method: Selector): BytesWriter {
21
+ public execute(method: Selector, _calldata: Calldata): BytesWriter {
43
22
  let response: BytesWriter;
44
23
 
45
24
  switch (method) {
@@ -63,7 +42,7 @@ export class OP_NET implements IBTC {
63
42
  throw new Error('Event data length exceeds maximum length.');
64
43
  }
65
44
 
66
- Blockchain.addEvent(event);
45
+ Blockchain.emit(event);
67
46
  }
68
47
 
69
48
  protected isSelf(address: Address): boolean {
@@ -1,12 +1,11 @@
1
- import { Address, ADDRESS_BYTE_LENGTH, PotentialAddress } from '../types/Address';
1
+ import { Address, ADDRESS_BYTE_LENGTH } from '../types/Address';
2
2
  import { MemorySlotPointer } from '../memory/MemorySlotPointer';
3
3
  import { MemorySlotData } from '../memory/MemorySlot';
4
4
  import { u256 } from 'as-bignum/assembly';
5
- import { ABIRegistry } from '../universal/ABIRegistry';
6
5
  import { BytesReader } from '../buffer/BytesReader';
7
6
  import { encodePointerHash } from '../math/abi';
8
7
  import { BytesWriter } from '../buffer/BytesWriter';
9
- import { MAX_EVENTS, NetEvent } from '../events/NetEvent';
8
+ import { NetEvent } from '../events/NetEvent';
10
9
  import { Potential } from '../lang/Definitions';
11
10
  import { OP_NET } from '../contracts/OP_NET';
12
11
  import { PointerStorage } from '../types';
@@ -14,6 +13,7 @@ import {
14
13
  callContract,
15
14
  deploy,
16
15
  deployFromAddress,
16
+ emit,
17
17
  encodeAddress,
18
18
  loadPointer,
19
19
  log,
@@ -21,44 +21,44 @@ import {
21
21
  } from './global';
22
22
  import { DeployContractResponse } from '../interfaces/DeployContractResponse';
23
23
  import { MapU256 } from '../generic/MapU256';
24
+ import { Block } from './classes/Block';
25
+ import { Transaction } from './classes/Transaction';
24
26
 
25
27
  export * from '../env/global';
26
28
 
29
+ export function runtimeError(msg: string): Error {
30
+ return new Error(`RuntimeException: ${msg}`);
31
+ }
32
+
27
33
  @final
28
34
  export class BlockchainEnvironment {
29
35
  private static readonly MAX_U16: u16 = 65535;
30
- private static readonly runtimeException: string = 'RuntimeException';
36
+
31
37
  public readonly DEAD_ADDRESS: Address = 'bc1dead';
32
- private storage: PointerStorage = new MapU256();
33
- private events: NetEvent[] = [];
34
- private currentBlock: u256 = u256.Zero;
35
38
 
39
+ private storage: PointerStorage = new MapU256();
36
40
  private _selfContract: Potential<OP_NET> = null;
37
41
 
38
- private _txOrigin: PotentialAddress = null;
42
+ private _block: Potential<Block> = null;
39
43
 
40
- public get txOrigin(): Address {
41
- if (!this._txOrigin) {
42
- throw this.error('Callee is required');
44
+ @inline
45
+ public get block(): Block {
46
+ if (!this._block) {
47
+ throw this.error('Block is required');
43
48
  }
44
49
 
45
- return this._txOrigin as Address;
50
+ return this._block as Block;
46
51
  }
47
52
 
48
- private _msgSender: PotentialAddress = null;
53
+ private _tx: Potential<Transaction> = null;
49
54
 
50
- public get msgSender(): Address {
51
- if (!this._msgSender) {
52
- throw this.error('Caller is required');
55
+ @inline
56
+ public get tx(): Transaction {
57
+ if (!this._tx) {
58
+ throw this.error('Transaction is required');
53
59
  }
54
60
 
55
- return this._msgSender as Address;
56
- }
57
-
58
- private _timestamp: u64 = 0;
59
-
60
- public get timestamp(): u64 {
61
- return this._timestamp;
61
+ return this._tx as Transaction;
62
62
  }
63
63
 
64
64
  private _contract: Potential<() => OP_NET> = null;
@@ -105,25 +105,24 @@ export class BlockchainEnvironment {
105
105
  return this._contractAddress as Address;
106
106
  }
107
107
 
108
- public get blockNumber(): u256 {
109
- return this.currentBlock;
110
- }
111
-
112
- public get blockNumberU64(): u64 {
113
- return this.currentBlock.toU64();
114
- }
115
-
116
108
  public setEnvironment(data: Uint8Array): void {
117
109
  const reader: BytesReader = new BytesReader(data);
118
110
 
119
- this._msgSender = reader.readAddress();
120
- this._txOrigin = reader.readAddress(); // "leftmost thing in the call chain"
121
- this.currentBlock = reader.readU256();
111
+ this._tx = new Transaction(
112
+ reader.readAddress(),
113
+ reader.readAddress(),
114
+ reader.readBytes(32),
115
+ );
116
+
117
+ const currentBlock = reader.readU256();
122
118
 
123
119
  this._owner = reader.readAddress();
124
120
  this._contractAddress = reader.readAddress();
125
121
 
126
- this._timestamp = reader.readU64();
122
+ const medianTimestamp = reader.readU64();
123
+ const safeRnd64 = reader.readU64();
124
+
125
+ this._block = new Block(currentBlock, medianTimestamp, safeRnd64);
127
126
 
128
127
  this.createContractIfNotExists();
129
128
  }
@@ -154,32 +153,14 @@ export class BlockchainEnvironment {
154
153
  log(buffer);
155
154
  }
156
155
 
157
- public addEvent(event: NetEvent): void {
158
- if (this.events.length >= i32(MAX_EVENTS)) {
159
- throw this.error(`Too many events in the same transaction.`);
160
- }
156
+ public emit(event: NetEvent): void {
157
+ const data = event.getEventData();
158
+ const buffer = new BytesWriter(event.eventType.length + 6 + data.byteLength);
161
159
 
162
- this.events.push(event);
163
- }
164
-
165
- public getEvents(): Uint8Array {
166
- const eventLength: u16 = u16(this.events.length);
167
- if (eventLength > MAX_EVENTS) {
168
- throw this.error('Too many events');
169
- }
170
-
171
- const buffer: BytesWriter = new BytesWriter(this.getEventSize());
172
- buffer.writeU16(eventLength);
173
-
174
- for (let i: u8 = 0; i < eventLength; i++) {
175
- const event: NetEvent = this.events[i];
176
-
177
- buffer.writeStringWithLength(event.eventType);
178
- buffer.writeU64(0); //event.getEventDataSelector()
179
- buffer.writeBytesWithLength(event.getEventData());
180
- }
160
+ buffer.writeStringWithLength(event.eventType);
161
+ buffer.writeBytesWithLength(data);
181
162
 
182
- return buffer.getBuffer();
163
+ emit(buffer.getBuffer());
183
164
  }
184
165
 
185
166
  public encodeVirtualAddress(virtualAddress: Uint8Array): Address {
@@ -260,21 +241,6 @@ export class BlockchainEnvironment {
260
241
  this._internalSetStorageAt(pointerHash, value);
261
242
  }
262
243
 
263
- public getMethodSelectors(): Uint8Array {
264
- return ABIRegistry.getMethodSelectors();
265
- }
266
-
267
- private getEventSize(): u32 {
268
- let size: u32 = 2;
269
-
270
- for (let i: u32 = 0; i < <u32>this.events.length; i++) {
271
- const event: NetEvent = this.events[i];
272
- size += 2 + event.eventType.length + 8 + event.length + 4;
273
- }
274
-
275
- return size;
276
- }
277
-
278
244
  private createContractIfNotExists(): void {
279
245
  if (!this._contract) {
280
246
  throw this.error('Contract is required');
@@ -286,7 +252,7 @@ export class BlockchainEnvironment {
286
252
  }
287
253
 
288
254
  private error(msg: string): Error {
289
- return new Error(`${BlockchainEnvironment.runtimeException}: ${msg}`);
255
+ return runtimeError(msg);
290
256
  }
291
257
 
292
258
  private _internalSetStorageAt(pointerHash: u256, value: MemorySlotData<u256>): void {
@@ -0,0 +1,18 @@
1
+ import { u256 } from 'as-bignum/assembly';
2
+
3
+ @final
4
+ export class Block {
5
+ private readonly u64BlockNumber: u64;
6
+
7
+ public constructor(
8
+ public readonly number: u256,
9
+ public readonly medianTimestamp: u64,
10
+ public readonly safeRnd64: u64,
11
+ ) {
12
+ this.u64BlockNumber = number.toU64();
13
+ }
14
+
15
+ public get numberU64(): u64 {
16
+ return this.u64BlockNumber;
17
+ }
18
+ }
@@ -0,0 +1,47 @@
1
+ import { Address } from '../../types/Address';
2
+ import { TransactionInput, TransactionOutput } from './UTXO';
3
+ import { Potential } from '../../lang/Definitions';
4
+ import { StaticArray } from 'staticarray';
5
+ import { BytesReader } from '../../buffer/BytesReader';
6
+ import { inputs, outputs } from '../global';
7
+
8
+ @final
9
+ export class Transaction {
10
+ public constructor(
11
+ public readonly sender: Address, // "immediate caller"
12
+ public readonly origin: Address, // "leftmost thing in the call chain"
13
+ public readonly id: Uint8Array,
14
+ ) {}
15
+
16
+ private _inputs: Potential<StaticArray<TransactionInput>> = null;
17
+
18
+ public get inputs(): StaticArray<TransactionInput> {
19
+ if (!this._inputs) {
20
+ this._inputs = this.loadInputs();
21
+ }
22
+
23
+ return this._inputs;
24
+ }
25
+
26
+ private _outputs: Potential<StaticArray<TransactionOutput>> = null;
27
+
28
+ public get outputs(): StaticArray<TransactionOutput> {
29
+ if (!this._outputs) {
30
+ this._outputs = this.loadOutputs();
31
+ }
32
+
33
+ return this._outputs;
34
+ }
35
+
36
+ private loadInputs(): StaticArray<TransactionInput> {
37
+ const buffer = new BytesReader(inputs());
38
+
39
+ return buffer.readTransactionInputs();
40
+ }
41
+
42
+ private loadOutputs(): StaticArray<TransactionOutput> {
43
+ const buffer = new BytesReader(outputs());
44
+
45
+ return buffer.readTransactionOutputs();
46
+ }
47
+ }
@@ -0,0 +1,17 @@
1
+ @final
2
+ export class TransactionInput {
3
+ public constructor(
4
+ public readonly txId: Uint8Array,
5
+ public readonly outputIndex: u8,
6
+ public readonly scriptSig: Uint8Array,
7
+ ) {}
8
+ }
9
+
10
+ @final
11
+ export class TransactionOutput {
12
+ public constructor(
13
+ public readonly index: u8,
14
+ public readonly scriptPubKey: Uint8Array,
15
+ public readonly value: u64,
16
+ ) {}
17
+ }
@@ -22,11 +22,26 @@ export declare function callContract(data: Uint8Array): Uint8Array;
22
22
  @external('env', 'log')
23
23
  export declare function log(data: Uint8Array): void;
24
24
 
25
+ // @ts-ignore
26
+ @external('env', 'emit')
27
+ export declare function emit(data: Uint8Array): void;
28
+
25
29
  // @ts-ignore
26
30
  @external('env', 'encodeAddress')
27
31
  export declare function encodeAddress(data: Uint8Array): Uint8Array;
28
32
 
29
-
30
33
  // @ts-ignore
31
34
  @external('env', 'sha256')
32
35
  export declare function sha256(data: Uint8Array): Uint8Array;
36
+
37
+ // @ts-ignore
38
+ @external('env', 'ripemd160')
39
+ export declare function ripemd160(data: Uint8Array): Uint8Array;
40
+
41
+ // @ts-ignore
42
+ @external('env', 'inputs')
43
+ export declare function inputs(): Uint8Array;
44
+
45
+ // @ts-ignore
46
+ @external('env', 'outputs')
47
+ export declare function outputs(): Uint8Array;
@@ -1,3 +1,3 @@
1
- import { BlockchainEnvironment } from './BTCEnvironment';
1
+ import { BlockchainEnvironment } from './BlockchainEnvironment';
2
2
 
3
3
  export const Blockchain: BlockchainEnvironment = new BlockchainEnvironment();
@@ -2,7 +2,6 @@ import { BytesWriter } from '../buffer/BytesWriter';
2
2
  import { Revert } from '../types/Revert';
3
3
 
4
4
  export const MAX_EVENT_DATA_SIZE: u32 = 352; // 352 bytes max per event.
5
- export const MAX_EVENTS: u16 = 1000; // 1000 events max per transactions.
6
5
 
7
6
  export abstract class NetEvent {
8
7
  private readonly buffer: Uint8Array;
@@ -7,6 +7,7 @@ import { Address, ADDRESS_BYTE_LENGTH } from '../../types/Address';
7
7
  export class MintEvent extends NetEvent {
8
8
  constructor(address: Address, amount: u256) {
9
9
  const data: BytesWriter = new BytesWriter(32 + ADDRESS_BYTE_LENGTH);
10
+
10
11
  data.writeAddress(address);
11
12
  data.writeU256(amount);
12
13
 
@@ -1,27 +1,21 @@
1
- import { Calldata } from '../universal/ABIRegistry';
2
1
  import { Blockchain } from '../env';
3
- import { Selector } from '../math/abi';
4
2
  import { BytesWriter } from '../buffer/BytesWriter';
5
3
  import { BytesReader } from '../buffer/BytesReader';
4
+ import { Selector } from '../math/abi';
5
+ import { Calldata } from '../types';
6
6
 
7
- export function readMethod(method: Selector, data: Uint8Array): Uint8Array {
7
+ export function execute(data: Uint8Array): Uint8Array {
8
8
  const calldata: Calldata = new BytesReader(data);
9
- const result: BytesWriter = Blockchain.contract.callMethod(method, calldata);
9
+ const selector: Selector = calldata.readSelector();
10
+ const result: BytesWriter = Blockchain.contract.execute(selector, calldata);
10
11
 
11
12
  return result.getBuffer();
12
13
  }
13
14
 
14
- export function readView(method: Selector): Uint8Array {
15
- const result: BytesWriter = Blockchain.contract.callView(method);
16
- return result.getBuffer();
17
- }
18
-
19
- export function getEvents(): Uint8Array {
20
- return Blockchain.getEvents();
21
- }
15
+ export function onDeploy(data: Uint8Array): void {
16
+ const calldata: Calldata = new BytesReader(data);
22
17
 
23
- export function getMethodABI(): Uint8Array {
24
- return Blockchain.getMethodSelectors();
18
+ Blockchain.contract.onDeployment(calldata);
25
19
  }
26
20
 
27
21
  export function setEnvironment(data: Uint8Array): void {
package/runtime/index.ts CHANGED
@@ -19,9 +19,15 @@ export * from './interfaces/DeployContractResponse';
19
19
  export * from './events/NetEvent';
20
20
  export * from './events/predefined';
21
21
 
22
+ /** Env */
23
+ export * from './env/classes/UTXO';
24
+ export * from './env/classes/Transaction';
25
+ export * from './env/classes/Block';
26
+
22
27
  /** Types */
23
28
  export * from './generic/Map';
24
29
  export * from './interfaces/IBTC';
30
+ export * from './types';
25
31
 
26
32
  /** Definitions */
27
33
  export * from './lang/Definitions';
@@ -49,9 +55,10 @@ export * from './storage/StoredString';
49
55
  export * from './storage/StoredBoolean';
50
56
  export * from './storage/Serializable';
51
57
 
52
- /** Universal */
53
- export * from './universal/ABIRegistry';
54
-
55
58
  /** Shared libraries */
56
59
  export * from './shared-libraries/TransferHelper';
57
60
  export * from './shared-libraries/OP20Utils';
61
+
62
+ /** Utils */
63
+ export * from './utils/hex';
64
+ export * from './utils/box';
@@ -0,0 +1,8 @@
1
+ export function assert(condition: boolean, e: string): void {
2
+ if (!condition) throw new Error(e);
3
+ }
4
+
5
+ export function assertEq<T>(a: T, b: T): void {
6
+ assert(a === b, "expected " + a.toString() + " to equal " + b.toString());
7
+ }
8
+
@@ -0,0 +1,7 @@
1
+ // @ts-ignore
2
+ @external("env", "logStatic")
3
+ export declare function __logStatic(ptr: ArrayBuffer): void;
4
+
5
+ export function log(v: string): void {
6
+ return __logStatic(String.UTF8.encode(v));
7
+ }
@@ -0,0 +1,25 @@
1
+ import { BytesWriter } from "../buffer/BytesWriter";
2
+ import { u256 } from "as-bignum/assembly";
3
+ import { Blockchain } from "../env";
4
+ import { Box } from "../utils/box";
5
+ import { log } from "./env";
6
+ import { assertEq } from "./assert";
7
+
8
+ export function test_encode(): void {
9
+ const writer = new BytesWriter(64);
10
+ writer.writeU256(u256.from(10));
11
+ writer.writeU256(u256.from(20));
12
+ const buffer = writer.getBuffer().buffer;
13
+ assertEq(Box.from(buffer).toHexString(), "0x0a000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000");
14
+ }
15
+
16
+ export function test_log(): void {
17
+ Blockchain.log("test logging test: OK!");
18
+ }
19
+
20
+ export function test_writeStringWithLength(): void {
21
+ const s = 'test write';
22
+ const writer = new BytesWriter(s.length + 2);
23
+ writer.writeStringWithLength(s);
24
+ assertEq(Box.from(writer.getBuffer().buffer).toHexString(), "0x0a0074657374207772697465");
25
+ }
@@ -35,8 +35,8 @@ export class SafeMath {
35
35
  throw new Error('SafeMath: modulo by zero');
36
36
  }
37
37
 
38
- let divResult = SafeMath.div(a, b);
39
- let product = SafeMath.mul(divResult, b);
38
+ const divResult = SafeMath.div(a, b);
39
+ const product = SafeMath.mul(divResult, b);
40
40
  return SafeMath.sub(a, product);
41
41
  }
42
42
 
@@ -121,16 +121,16 @@ export class SafeMath {
121
121
  if (u256.gt(y, u256.fromU32(3))) {
122
122
  let z = y;
123
123
 
124
- let u246_2 = u256.fromU32(2);
124
+ const u246_2 = u256.fromU32(2);
125
125
 
126
- let d = SafeMath.div(y, u246_2);
126
+ const d = SafeMath.div(y, u246_2);
127
127
  let x = SafeMath.add(d, u256.One);
128
128
 
129
129
  while (u256.lt(x, z)) {
130
130
  z = x;
131
131
 
132
- let u = SafeMath.div(y, x);
133
- let y2 = u256.add(u, x);
132
+ const u = SafeMath.div(y, x);
133
+ const y2 = u256.add(u, x);
134
134
 
135
135
  x = SafeMath.div(y2, u246_2);
136
136
  }
@@ -150,8 +150,8 @@ export class SafeMath {
150
150
  return value.clone();
151
151
  }
152
152
 
153
- let totalBits = 256;
154
- let bitsPerSegment = 64;
153
+ const totalBits = 256;
154
+ const bitsPerSegment = 64;
155
155
 
156
156
  // Normalize shift to be within 0-255 range
157
157
  shift &= 255;
@@ -161,12 +161,12 @@ export class SafeMath {
161
161
  }
162
162
 
163
163
  // Determine how many full 64-bit segments we are shifting
164
- let segmentShift = (shift / bitsPerSegment) | 0;
165
- let bitShift = shift % bitsPerSegment;
164
+ const segmentShift = (shift / bitsPerSegment) | 0;
165
+ const bitShift = shift % bitsPerSegment;
166
166
 
167
- let segments = [value.lo1, value.lo2, value.hi1, value.hi2];
167
+ const segments = [value.lo1, value.lo2, value.hi1, value.hi2];
168
168
 
169
- let result = new Array<u64>(4).fill(0);
169
+ const result = new Array<u64>(4).fill(0);
170
170
 
171
171
  for (let i = 0; i < segments.length; i++) {
172
172
  if (i + segmentShift < segments.length) {
@@ -2,7 +2,7 @@ import { Map } from '../generic/Map';
2
2
  import { MemorySlotPointer } from '../memory/MemorySlotPointer';
3
3
  import { MemorySlotData } from '../memory/MemorySlot';
4
4
  import { u256 } from 'as-bignum/assembly';
5
- import { Address } from './Address';
5
+ import { BytesReader } from '../buffer/BytesReader';
6
6
 
7
7
  export type PointerStorage = Map<MemorySlotPointer, MemorySlotData<u256>>;
8
- export type BlockchainStorage = Map<Address, PointerStorage>;
8
+ export type Calldata = NonNullable<BytesReader>;
@@ -0,0 +1,115 @@
1
+ import { encodeHex, encodeHexUTF8 } from "./hex";
2
+
3
+ export function nullptr<T>(): T {
4
+ return changetype<T>(0);
5
+ }
6
+
7
+ export class Box {
8
+ public start: usize;
9
+ public len: usize;
10
+ constructor(start: usize, len: usize) {
11
+ this.start = start;
12
+ this.len = len;
13
+ }
14
+ toHexString(): string {
15
+ return encodeHex(this.start, this.len);
16
+ }
17
+ toHexUTF8(): ArrayBuffer {
18
+ return encodeHexUTF8(this.start, this.len);
19
+ }
20
+ static concat(data: Array<Box>): ArrayBuffer {
21
+ const result = new ArrayBuffer(
22
+ data.reduce<i32>((r: i32, v: Box, i: i32, ary: Array<Box>) => {
23
+ return r + <i32>v.len;
24
+ }, 0),
25
+ );
26
+ data.reduce<usize>((r: usize, v: Box, i: i32, ary: Array<Box>) => {
27
+ memory.copy(r, v.start, v.len);
28
+ return r + v.len;
29
+ }, changetype<usize>(result));
30
+ return result;
31
+ }
32
+
33
+ shift(): Box {
34
+ if (this.len == 0) {
35
+ return nullptr<Box>();
36
+ }
37
+ this.start = this.start + 1;
38
+ this.len = this.len - 1;
39
+ return this;
40
+ }
41
+
42
+ sliceFrom(start: usize): Box {
43
+ return new Box(this.start + start, this.len - start);
44
+ }
45
+
46
+ sliceTo(ptr: usize): Box {
47
+ if (ptr > this.start + this.len) {
48
+ throw new Error("ptr is out of bounds");
49
+ }
50
+ return new Box(this.start, ptr - this.start);
51
+ }
52
+ shrinkFront(distance: usize): Box {
53
+ this.start = this.start + distance;
54
+ this.len = this.len - distance;
55
+ return this;
56
+ }
57
+ growFront(distance: usize): Box {
58
+ this.start = this.start - distance;
59
+ this.len = this.len + distance;
60
+ return this;
61
+ }
62
+ shrinkBack(distance: usize): Box {
63
+ this.len = this.len - distance;
64
+ return this;
65
+ }
66
+ growBack(distance: usize): Box {
67
+ this.len = this.len + distance;
68
+ return this;
69
+ }
70
+ setLength(len: usize): Box {
71
+ this.len = len;
72
+ return this;
73
+ }
74
+ toArrayBuffer(): ArrayBuffer {
75
+ const result = new ArrayBuffer(<i32>this.len);
76
+ memory.copy(changetype<usize>(result), this.start, this.len);
77
+ return result;
78
+ }
79
+ isEmpty(): boolean {
80
+ return this.len == 0;
81
+ }
82
+ static from(data: ArrayBuffer): Box {
83
+ return new Box(changetype<usize>(data), data.byteLength);
84
+ }
85
+ static copy(data: ArrayBuffer): Box {
86
+ const ptr = heap.alloc(data.byteLength);
87
+ memory.copy(ptr, changetype<usize>(data), <usize>data.byteLength);
88
+ return new Box(ptr, <usize>data.byteLength);
89
+ }
90
+ static freeCopy(v: Box): void {
91
+ heap.free(v.start);
92
+ }
93
+ static fromTyped<T>(v: T): Box {
94
+ // const buffer = new ArrayBuffer(sizeof<T>(v));
95
+ const buffer = new ArrayBuffer(offsetof<T>());
96
+ store<T>(changetype<usize>(buffer), v);
97
+ return Box.copy(buffer);
98
+ }
99
+ }
100
+ export class RCBox extends Box {
101
+ public buffer: ArrayBuffer;
102
+ constructor(v: ArrayBuffer) {
103
+ super(changetype<usize>(v), <usize>v.byteLength);
104
+ this.buffer = v;
105
+ }
106
+ static from(v: ArrayBuffer): RCBox {
107
+ return new RCBox(v);
108
+ }
109
+ static fromTyped<T>(v: T): RCBox {
110
+ // const buffer = new ArrayBuffer(sizeof<T>(v));
111
+ const buffer = new ArrayBuffer(offsetof<T>());
112
+ store<T>(changetype<usize>(buffer), v);
113
+ return RCBox.from(buffer);
114
+ }
115
+ }
@@ -0,0 +1,60 @@
1
+ const hexLookupTable: StaticArray<u8> = [
2
+ 48, 48, 48, 49, 48, 50, 48, 51, 48, 52, 48, 53, 48, 54, 48, 55, 48, 56, 48,
3
+ 57, 48, 97, 48, 98, 48, 99, 48, 100, 48, 101, 48, 102, 49, 48, 49, 49, 49, 50,
4
+ 49, 51, 49, 52, 49, 53, 49, 54, 49, 55, 49, 56, 49, 57, 49, 97, 49, 98, 49,
5
+ 99, 49, 100, 49, 101, 49, 102, 50, 48, 50, 49, 50, 50, 50, 51, 50, 52, 50, 53,
6
+ 50, 54, 50, 55, 50, 56, 50, 57, 50, 97, 50, 98, 50, 99, 50, 100, 50, 101, 50,
7
+ 102, 51, 48, 51, 49, 51, 50, 51, 51, 51, 52, 51, 53, 51, 54, 51, 55, 51, 56,
8
+ 51, 57, 51, 97, 51, 98, 51, 99, 51, 100, 51, 101, 51, 102, 52, 48, 52, 49, 52,
9
+ 50, 52, 51, 52, 52, 52, 53, 52, 54, 52, 55, 52, 56, 52, 57, 52, 97, 52, 98,
10
+ 52, 99, 52, 100, 52, 101, 52, 102, 53, 48, 53, 49, 53, 50, 53, 51, 53, 52, 53,
11
+ 53, 53, 54, 53, 55, 53, 56, 53, 57, 53, 97, 53, 98, 53, 99, 53, 100, 53, 101,
12
+ 53, 102, 54, 48, 54, 49, 54, 50, 54, 51, 54, 52, 54, 53, 54, 54, 54, 55, 54,
13
+ 56, 54, 57, 54, 97, 54, 98, 54, 99, 54, 100, 54, 101, 54, 102, 55, 48, 55, 49,
14
+ 55, 50, 55, 51, 55, 52, 55, 53, 55, 54, 55, 55, 55, 56, 55, 57, 55, 97, 55,
15
+ 98, 55, 99, 55, 100, 55, 101, 55, 102, 56, 48, 56, 49, 56, 50, 56, 51, 56, 52,
16
+ 56, 53, 56, 54, 56, 55, 56, 56, 56, 57, 56, 97, 56, 98, 56, 99, 56, 100, 56,
17
+ 101, 56, 102, 57, 48, 57, 49, 57, 50, 57, 51, 57, 52, 57, 53, 57, 54, 57, 55,
18
+ 57, 56, 57, 57, 57, 97, 57, 98, 57, 99, 57, 100, 57, 101, 57, 102, 97, 48, 97,
19
+ 49, 97, 50, 97, 51, 97, 52, 97, 53, 97, 54, 97, 55, 97, 56, 97, 57, 97, 97,
20
+ 97, 98, 97, 99, 97, 100, 97, 101, 97, 102, 98, 48, 98, 49, 98, 50, 98, 51, 98,
21
+ 52, 98, 53, 98, 54, 98, 55, 98, 56, 98, 57, 98, 97, 98, 98, 98, 99, 98, 100,
22
+ 98, 101, 98, 102, 99, 48, 99, 49, 99, 50, 99, 51, 99, 52, 99, 53, 99, 54, 99,
23
+ 55, 99, 56, 99, 57, 99, 97, 99, 98, 99, 99, 99, 100, 99, 101, 99, 102, 100,
24
+ 48, 100, 49, 100, 50, 100, 51, 100, 52, 100, 53, 100, 54, 100, 55, 100, 56,
25
+ 100, 57, 100, 97, 100, 98, 100, 99, 100, 100, 100, 101, 100, 102, 101, 48,
26
+ 101, 49, 101, 50, 101, 51, 101, 52, 101, 53, 101, 54, 101, 55, 101, 56, 101,
27
+ 57, 101, 97, 101, 98, 101, 99, 101, 100, 101, 101, 101, 102, 102, 48, 102, 49,
28
+ 102, 50, 102, 51, 102, 52, 102, 53, 102, 54, 102, 55, 102, 56, 102, 57, 102,
29
+ 97, 102, 98, 102, 99, 102, 100, 102, 101, 102, 102,
30
+ ];
31
+
32
+ export function encodeHexUTF8(start: usize, len: usize): ArrayBuffer {
33
+ let result = new ArrayBuffer(2 + <i32>len * 2);
34
+ store<u16>(changetype<usize>(result), <u16>0x7830);
35
+ for (let i: usize = 0; i < len; i++) {
36
+ store<u16>(
37
+ 2 + changetype<usize>(result) + i * 2,
38
+ load<u16>(changetype<usize>(hexLookupTable) + 2 * load<u8>(start + i)),
39
+ );
40
+ }
41
+ return result;
42
+ }
43
+ export function encodeHex(start: usize, len: usize): string {
44
+ return String.UTF8.decode(encodeHexUTF8(start, len));
45
+ }
46
+
47
+ export function encodeHexFromBuffer(data: ArrayBuffer): string {
48
+ return encodeHex(changetype<usize>(data), data.byteLength);
49
+ }
50
+
51
+ export function decodeHex(hex: string): ArrayBuffer {
52
+ const result = new ArrayBuffer(hex.length / 2);
53
+ for (let i = 0; i < hex.length; i += 2) {
54
+ store<u8>(
55
+ changetype<usize>(result) + i / 2,
56
+ <u8>parseInt(hex.substring(i, i + 2), 16),
57
+ );
58
+ }
59
+ return result;
60
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./hex";
2
+ export * from "./box";
@@ -1,27 +0,0 @@
1
- import { encodeSelector, Selector } from '../math/abi';
2
- import { BytesReader } from '../buffer/BytesReader';
3
- import { BytesWriter } from '../buffer/BytesWriter';
4
-
5
- export type Calldata = NonNullable<BytesReader>;
6
-
7
- class ABIRegistryBase {
8
- private methodMap: Selector[] = [];
9
-
10
- public getMethodSelectors(): Uint8Array {
11
- const writer: BytesWriter = new BytesWriter(2 + this.methodMap.length * 4);
12
- writer.writeMethodSelectorsMap(this.methodMap);
13
-
14
- return writer.getBuffer();
15
- }
16
-
17
- // Register methods with their selectors and handlers
18
- public defineMethodSelector(name: string): void {
19
- const selector: u32 = encodeSelector(name);
20
-
21
- if (!this.methodMap.includes(selector)) {
22
- this.methodMap.push(selector);
23
- }
24
- }
25
- }
26
-
27
- export const ABIRegistry = new ABIRegistryBase();