@btc-vision/btc-runtime 1.8.1 → 1.9.0

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.
Files changed (69) hide show
  1. package/README.md +57 -79
  2. package/package.json +8 -8
  3. package/runtime/buffer/BytesReader.ts +36 -45
  4. package/runtime/buffer/BytesWriter.ts +60 -20
  5. package/runtime/contracts/OP20.ts +636 -0
  6. package/runtime/contracts/OP_NET.ts +3 -3
  7. package/runtime/contracts/interfaces/IOP20.ts +15 -0
  8. package/runtime/contracts/interfaces/OP20InitParameters.ts +3 -1
  9. package/runtime/env/BlockchainEnvironment.ts +28 -7
  10. package/runtime/env/classes/Transaction.ts +1 -2
  11. package/runtime/env/global.ts +171 -23
  12. package/runtime/events/NetEvent.ts +1 -1
  13. package/runtime/events/predefined/{ApproveEvent.ts → ApprovedEvent.ts} +3 -3
  14. package/runtime/events/predefined/{MintEvent.ts → BurnedEvent.ts} +5 -6
  15. package/runtime/events/predefined/{TransferEvent.ts → MintedEvent.ts} +5 -6
  16. package/runtime/events/predefined/TransferredEvent.ts +18 -0
  17. package/runtime/events/predefined/index.ts +4 -4
  18. package/runtime/exports/index.ts +4 -4
  19. package/runtime/index.ts +13 -3
  20. package/runtime/math/abi.ts +10 -6
  21. package/runtime/math/bytes.ts +5 -17
  22. package/runtime/memory/AddressMemoryMap.ts +4 -5
  23. package/runtime/memory/KeyMerger.ts +7 -7
  24. package/runtime/memory/MapOfMap.ts +2 -7
  25. package/runtime/memory/Nested.ts +6 -8
  26. package/runtime/nested/PointerManager.ts +1 -1
  27. package/runtime/nested/codecs/AddressCodec.ts +1 -1
  28. package/runtime/nested/codecs/BooleanCodec.ts +1 -1
  29. package/runtime/nested/codecs/Ids.ts +1 -1
  30. package/runtime/nested/codecs/NumericCodec.ts +1 -1
  31. package/runtime/nested/codecs/StringCodec.ts +1 -1
  32. package/runtime/nested/codecs/VariableBytesCodec.ts +3 -3
  33. package/runtime/nested/storage/StorageMap.ts +3 -3
  34. package/runtime/nested/storage/StorageSet.ts +2 -2
  35. package/runtime/plugins/Plugin.ts +5 -7
  36. package/runtime/script/Bech32.ts +369 -0
  37. package/runtime/script/BitcoinAddresses.ts +208 -0
  38. package/runtime/script/BitcoinCodec.ts +395 -0
  39. package/runtime/script/Networks.ts +94 -0
  40. package/runtime/script/Opcodes.ts +155 -0
  41. package/runtime/script/Script.ts +463 -0
  42. package/runtime/script/ScriptUtils.ts +101 -0
  43. package/runtime/script/Segwit.ts +185 -0
  44. package/runtime/script/reader/ScriptReader.ts +247 -0
  45. package/runtime/secp256k1/ECPoint.ts +6 -12
  46. package/runtime/shared-libraries/TransferHelper.ts +72 -31
  47. package/runtime/storage/AdvancedStoredString.ts +1 -1
  48. package/runtime/storage/StoredAddress.ts +1 -1
  49. package/runtime/storage/StoredBoolean.ts +2 -4
  50. package/runtime/storage/StoredString.ts +6 -3
  51. package/runtime/storage/StoredU256.ts +1 -3
  52. package/runtime/storage/StoredU32.ts +1 -6
  53. package/runtime/storage/StoredU64.ts +1 -4
  54. package/runtime/storage/arrays/StoredBooleanArray.ts +7 -12
  55. package/runtime/storage/arrays/StoredPackedArray.ts +3 -13
  56. package/runtime/storage/maps/StoredMapU256.ts +5 -5
  57. package/runtime/types/Address.ts +49 -39
  58. package/runtime/types/SafeMath.ts +19 -18
  59. package/runtime/types/SafeMathI128.ts +14 -13
  60. package/runtime/utils/hex.ts +41 -19
  61. package/runtime/utils/index.ts +0 -2
  62. package/runtime/contracts/DeployableOP_20.ts +0 -415
  63. package/runtime/contracts/OP_20.ts +0 -9
  64. package/runtime/contracts/interfaces/IOP_20.ts +0 -19
  65. package/runtime/events/predefined/BurnEvent.ts +0 -14
  66. package/runtime/utils/b32.ts +0 -243
  67. package/runtime/utils/box.ts +0 -134
  68. package/runtime/utils/encodings.ts +0 -45
  69. /package/{LICENSE → LICENSE.md} +0 -0
package/README.md CHANGED
@@ -31,13 +31,13 @@ execution while integrating deeply with Bitcoin's decentralized architecture.
31
31
 
32
32
  ### Features
33
33
 
34
- - **AssemblyScript and WebAssembly:** Efficient and high-performance contract execution using WebAssembly.
35
- - **Bitcoin Integration:** Direct interaction with Bitcoin L1, enabling the creation of decentralized applications that
36
- operate on the Bitcoin network.
37
- - **Comprehensive Storage Management:** Flexible and secure storage management using primary pointers and sub-pointers,
38
- ensuring data integrity through cryptographic proofs.
39
- - **Event Handling:** Sophisticated event system for contract state changes, allowing easy tracking and logging of
40
- contract activities.
34
+ - **AssemblyScript and WebAssembly:** Efficient and high-performance contract execution using WebAssembly.
35
+ - **Bitcoin Integration:** Direct interaction with Bitcoin L1, enabling the creation of decentralized applications that
36
+ operate on the Bitcoin network.
37
+ - **Comprehensive Storage Management:** Flexible and secure storage management using primary pointers and sub-pointers,
38
+ ensuring data integrity through cryptographic proofs.
39
+ - **Event Handling:** Sophisticated event system for contract state changes, allowing easy tracking and logging of
40
+ contract activities.
41
41
 
42
42
  ## Installation
43
43
 
@@ -95,22 +95,20 @@ Here is a real-world example of how to create a basic token contract using the O
95
95
  contract follows the OP20 standard.
96
96
 
97
97
  ```typescript
98
+ import { u256 } from '@btc-vision/as-bignum/assembly';
98
99
  import {
99
100
  Address,
101
+ AddressMap,
100
102
  Blockchain,
101
103
  BytesWriter,
102
104
  Calldata,
103
- DeployableOP_20,
104
- encodeSelector,
105
- Map,
105
+ OP20,
106
106
  OP20InitParameters,
107
- Selector,
108
- AddressMap,
107
+ SafeMath,
109
108
  } from '@btc-vision/btc-runtime/runtime';
110
- import { u128, u256 } from 'as-bignum/assembly';
111
109
 
112
110
  @final
113
- export class MyToken extends DeployableOP_20 {
111
+ export class MyToken extends OP20 {
114
112
  public constructor() {
115
113
  super();
116
114
 
@@ -119,71 +117,72 @@ export class MyToken extends DeployableOP_20 {
119
117
 
120
118
  // "solidityLikeConstructor" This is a solidity-like constructor. This method will only run once when the contract is deployed.
121
119
  public override onDeployment(_calldata: Calldata): void {
122
- const maxSupply: u256 = u128.fromString('100000000000000000000000000').toU256(); // Your max supply.
120
+ const maxSupply: u256 = u256.fromString('1000000000000000000000000000'); // Your max supply. (Here, 1 billion tokens)
123
121
  const decimals: u8 = 18; // Your decimals.
124
- const name: string = 'MyToken'; // Your token name.
125
- const symbol: string = 'TOKEN'; // Your token symbol.
122
+ const name: string = 'Test'; // Your token name.
123
+ const symbol: string = 'TEST'; // Your token symbol.
126
124
 
127
125
  this.instantiate(new OP20InitParameters(maxSupply, decimals, name, symbol));
128
126
 
129
127
  // Add your logic here. Eg, minting the initial supply:
130
- this._mint(Blockchain.tx.origin, maxSupply);
128
+ // this._mint(Blockchain.tx.origin, maxSupply);
131
129
  }
132
130
 
133
- public override execute(method: Selector, calldata: Calldata): BytesWriter {
134
- switch (method) {
135
- case encodeSelector('airdrop()'):
136
- return this.airdrop(calldata);
137
- case encodeSelector('airdropWithAmount()'):
138
- return this.airdropWithAmount(calldata);
139
- default:
140
- return super.execute(method, calldata);
141
- }
131
+ @method(
132
+ {
133
+ name: 'address',
134
+ type: ABIDataTypes.ADDRESS,
135
+ },
136
+ {
137
+ name: 'amount',
138
+ type: ABIDataTypes.UINT256,
139
+ },
140
+ )
141
+ @emit('Minted')
142
+ public mint(calldata: Calldata): BytesWriter {
143
+ this.onlyDeployer(Blockchain.tx.sender);
144
+ this._mint(calldata.readAddress(), calldata.readU256());
145
+ return new BytesWriter(0);
142
146
  }
143
147
 
144
- private airdrop(calldata: Calldata): BytesWriter {
148
+ /**
149
+ * Mints tokens to the specified addresses.
150
+ *
151
+ * @param calldata Calldata containing an `AddressMap<Address, u256>` to mint to.
152
+ */
153
+ @method({
154
+ name: 'addressAndAmount',
155
+ type: ABIDataTypes.ADDRESS_UINT256_TUPLE,
156
+ })
157
+ @emit('Minted')
158
+ public airdrop(calldata: Calldata): BytesWriter {
145
159
  this.onlyDeployer(Blockchain.tx.sender);
146
160
 
147
- const drops: AddressMap<u256> = calldata.readAddressValueTuple();
161
+ const addressAndAmount: AddressMap<u256> = calldata.readAddressMapU256();
162
+ const addresses: Address[] = addressAndAmount.keys();
163
+
164
+ let totalAirdropped: u256 = u256.Zero;
148
165
 
149
- const addresses: Address[] = drops.keys();
150
166
  for (let i: i32 = 0; i < addresses.length; i++) {
151
167
  const address = addresses[i];
152
- const amount = drops.get(address);
168
+ const amount = addressAndAmount.get(address);
153
169
 
154
- this._mint(address, amount, false);
155
- }
170
+ const currentBalance: u256 = this.balanceOfMap.get(address);
156
171
 
157
- const writer: BytesWriter = new BytesWriter(1);
158
- writer.writeBoolean(true);
172
+ if (currentBalance) {
173
+ this.balanceOfMap.set(address, SafeMath.add(currentBalance, amount));
174
+ } else {
175
+ this.balanceOfMap.set(address, amount);
176
+ }
159
177
 
160
- return writer;
161
- }
162
-
163
- private _optimizedMint(address: Address, amount: u256): void {
164
- this.balanceOfMap.set(address, amount);
165
-
166
- this._totalSupply.addNoCommit(amount);
167
-
168
- this.createMintEvent(address, amount);
169
- }
170
-
171
- private airdropWithAmount(calldata: Calldata): BytesWriter {
172
- this.onlyDeployer(Blockchain.tx.sender);
178
+ totalAirdropped = SafeMath.add(totalAirdropped, amount);
173
179
 
174
- const amount: u256 = calldata.readU256();
175
- const addresses: Address[] = calldata.readAddressArray();
176
-
177
- for (let i: i32 = 0; i < addresses.length; i++) {
178
- this._optimizedMint(addresses[i], amount);
180
+ this.createMintedEvent(address, amount);
179
181
  }
180
182
 
181
- this._totalSupply.commit();
182
-
183
- const writer: BytesWriter = new BytesWriter(1);
184
- writer.writeBoolean(true);
183
+ this._totalSupply.set(SafeMath.add(this._totalSupply.value, totalAirdropped));
185
184
 
186
- return writer;
185
+ return new BytesWriter(0);
187
186
  }
188
187
  }
189
188
  ```
@@ -196,27 +195,6 @@ Storage pointers and sub-pointers are encoded and hashed to create unique and se
196
195
  locations are managed using the `Blockchain` class's `setStorageAt` and `getStorageAt` methods, ensuring data integrity
197
196
  and preventing tampering.
198
197
 
199
- ### Using Serializable Data Structures
200
-
201
- For complex data types, the `Serializable` class allows you to manage and persist data structures across multiple
202
- storage slots.
203
-
204
- ```typescript
205
- class ComplexData extends Serializable {
206
- // Implementation
207
- }
208
- ```
209
-
210
- ## Additional Documentation
211
-
212
- For more detailed explanations on specific topics, refer to the individual documentation files:
213
-
214
- - [Blockchain.md](docs/Blockchain.md)
215
- - [Contract.md](docs/Contract.md)
216
- - [Events.md](docs/Events.md)
217
- - [Pointers.md](docs/Pointers.md)
218
- - [Storage.md](docs/Storage.md)
219
-
220
198
  ## License
221
199
 
222
200
  This project is licensed under the MIT License. View the full license [here](LICENSE.md).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@btc-vision/btc-runtime",
3
- "version": "1.8.1",
3
+ "version": "1.9.0",
4
4
  "description": "Bitcoin Smart Contract Runtime",
5
5
  "main": "btc/index.ts",
6
6
  "scripts": {},
@@ -17,8 +17,8 @@
17
17
  "author": "BlobMaster41",
18
18
  "license": "MIT",
19
19
  "devDependencies": {
20
- "@types/node": "^24.0.3",
21
- "assemblyscript": "^0.28.2"
20
+ "@types/node": "^24.2.1",
21
+ "assemblyscript": "^0.28.4"
22
22
  },
23
23
  "repository": {
24
24
  "type": "git",
@@ -35,13 +35,13 @@
35
35
  "test/*.ts"
36
36
  ],
37
37
  "dependencies": {
38
- "@assemblyscript/loader": "^0.28.2",
38
+ "@assemblyscript/loader": "^0.28.4",
39
39
  "@btc-vision/as-bignum": "^0.0.5",
40
- "@btc-vision/opnet-transform": "^0.1.6",
41
- "@eslint/js": "^9.29.0",
40
+ "@btc-vision/opnet-transform": "^0.1.10",
41
+ "@eslint/js": "^9.33.0",
42
42
  "gulplog": "^2.2.0",
43
43
  "ts-node": "^10.9.2",
44
- "typescript": "^5.8.3",
45
- "typescript-eslint": "^8.35.0"
44
+ "typescript": "^5.9.2",
45
+ "typescript-eslint": "^8.39.0"
46
46
  }
47
47
  }
@@ -6,7 +6,10 @@ import { Revert } from '../types/Revert';
6
6
  import {
7
7
  ADDRESS_BYTE_LENGTH,
8
8
  I128_BYTE_LENGTH,
9
+ I16_BYTE_LENGTH,
10
+ I32_BYTE_LENGTH,
9
11
  I64_BYTE_LENGTH,
12
+ I8_BYTE_LENGTH,
10
13
  U128_BYTE_LENGTH,
11
14
  U16_BYTE_LENGTH,
12
15
  U256_BYTE_LENGTH,
@@ -14,7 +17,6 @@ import {
14
17
  U64_BYTE_LENGTH,
15
18
  U8_BYTE_LENGTH,
16
19
  } from '../utils';
17
- import { sizeof } from 'builtins';
18
20
 
19
21
  @final
20
22
  export class BytesReader {
@@ -41,31 +43,31 @@ export class BytesReader {
41
43
  const size = sizeof<T>();
42
44
 
43
45
  switch (size) {
44
- case 8:
45
- return this.readU8() as T;
46
- case 16:
46
+ case I8_BYTE_LENGTH:
47
+ return this.readI8() as T;
48
+ case I16_BYTE_LENGTH:
47
49
  return this.readI16() as T;
48
- case 32:
50
+ case I32_BYTE_LENGTH:
49
51
  return this.readI32() as T;
50
- case 64:
52
+ case I64_BYTE_LENGTH:
51
53
  return this.readI64() as T;
52
54
  default:
53
- throw new Error(`Invalid size ${size}`);
55
+ throw new Revert(`Invalid size ${size}`);
54
56
  }
55
57
  } else {
56
58
  const size = sizeof<T>();
57
59
 
58
60
  switch (size) {
59
- case 8:
61
+ case U8_BYTE_LENGTH:
60
62
  return this.readU8() as T;
61
- case 16:
63
+ case U16_BYTE_LENGTH:
62
64
  return this.readU16() as T;
63
- case 32:
65
+ case U32_BYTE_LENGTH:
64
66
  return this.readU32() as T;
65
- case 64:
67
+ case U64_BYTE_LENGTH:
66
68
  return this.readU64() as T;
67
69
  default:
68
- throw new Error(`Invalid size ${size}`);
70
+ throw new Revert(`Invalid size ${size}`);
69
71
  }
70
72
  }
71
73
  } else if (id === idof<u256>()) {
@@ -81,21 +83,35 @@ export class BytesReader {
81
83
  } else if (id === idof<string>()) {
82
84
  return this.readStringWithLength() as T;
83
85
  } else {
84
- throw new Error(`Unsupported type ${id}`);
86
+ throw new Revert(`Unsupported type ${id}`);
85
87
  }
86
88
  }
87
89
 
90
+ public readI8(): i8 {
91
+ this.verifyEnd(this.currentOffset + I8_BYTE_LENGTH);
92
+ const value = this.buffer.getInt8(this.currentOffset);
93
+ this.currentOffset += I8_BYTE_LENGTH;
94
+ return value;
95
+ }
96
+
88
97
  public readI16(): i16 {
89
- this.verifyEnd(this.currentOffset + 2);
98
+ this.verifyEnd(this.currentOffset + I16_BYTE_LENGTH);
90
99
  const value = this.buffer.getInt16(this.currentOffset, true);
91
- this.currentOffset += 2;
100
+ this.currentOffset += I16_BYTE_LENGTH;
92
101
  return value;
93
102
  }
94
103
 
95
104
  public readI32(): i32 {
96
- this.verifyEnd(this.currentOffset + 4);
105
+ this.verifyEnd(this.currentOffset + I32_BYTE_LENGTH);
97
106
  const value = this.buffer.getInt32(this.currentOffset, true);
98
- this.currentOffset += 4;
107
+ this.currentOffset += I32_BYTE_LENGTH;
108
+ return value;
109
+ }
110
+
111
+ public readI64(be: boolean = true): i64 {
112
+ this.verifyEnd(this.currentOffset + I64_BYTE_LENGTH);
113
+ const value = this.buffer.getInt64(this.currentOffset, !be);
114
+ this.currentOffset += I64_BYTE_LENGTH;
99
115
  return value;
100
116
  }
101
117
 
@@ -146,13 +162,6 @@ export class BytesReader {
146
162
  return be ? u256.fromBytesBE(raw) : u256.fromBytesLE(raw);
147
163
  }
148
164
 
149
- public readI64(be: boolean = true): i64 {
150
- this.verifyEnd(this.currentOffset + I64_BYTE_LENGTH);
151
- const value = this.buffer.getInt64(this.currentOffset, !be);
152
- this.currentOffset += I64_BYTE_LENGTH;
153
- return value;
154
- }
155
-
156
165
  public readU128(be: boolean = true): u128 {
157
166
  this.verifyEnd(this.currentOffset + U128_BYTE_LENGTH);
158
167
  const raw: u8[] = this.readBytesArray(U128_BYTE_LENGTH);
@@ -211,10 +220,6 @@ export class BytesReader {
211
220
  return String.UTF8.decode(bytes.buffer);
212
221
  }
213
222
 
214
- /**
215
- * [u16 length][raw bytes].
216
- * The AS writer calls `writeStringWithLength(value: string)` => writes length big-endian by default.
217
- */
218
223
  public readStringWithLength(be: boolean = true): string {
219
224
  const length = this.readU32(be);
220
225
  return this.readString(length);
@@ -243,13 +248,8 @@ export class BytesReader {
243
248
 
244
249
  // ------------------- Arrays ------------------- //
245
250
 
246
- /**
247
- * The AS writer does `writeU32(length)` for U256 arrays, so we read a u32.
248
- * If you changed it to a `u16`, then do readU16() here.
249
- */
250
251
  public readU256Array(be: boolean = true): u256[] {
251
- // The AS writer currently writes a u32 length for U256 arrays
252
- const length = this.readU32();
252
+ const length = this.readU16();
253
253
  const result = new Array<u256>(length);
254
254
  for (let i: u32 = 0; i < length; i++) {
255
255
  result[i] = this.readU256(be);
@@ -257,9 +257,6 @@ export class BytesReader {
257
257
  return result;
258
258
  }
259
259
 
260
- /**
261
- * The AS writer uses a [u16 length] for U64 arrays.
262
- */
263
260
  public readU64Array(be: boolean = true): u64[] {
264
261
  const length = this.readU16(be);
265
262
  const result = new Array<u64>(length);
@@ -296,10 +293,6 @@ export class BytesReader {
296
293
  return result;
297
294
  }
298
295
 
299
- /**
300
- * The AS writer uses a [u8 length] for transaction inputs/outputs in the example,
301
- * but for an "AddressArray" we use [u16 length].
302
- */
303
296
  public readAddressArray(be: boolean = true): Address[] {
304
297
  const length = this.readU16(be);
305
298
  const result = new Array<Address>(length);
@@ -309,9 +302,6 @@ export class BytesReader {
309
302
  return result;
310
303
  }
311
304
 
312
- /**
313
- * Map of [u16 length] entries, each entry = [Address, U256], consistent with the writer’s `writeAddressMapU256`.
314
- */
315
305
  public readAddressMapU256(be: boolean = true): AddressMap<u256> {
316
306
  const length = this.readU16(be);
317
307
  const result = new AddressMap<u256>();
@@ -334,6 +324,7 @@ export class BytesReader {
334
324
  }
335
325
 
336
326
  public setOffset(offset: i32): void {
327
+ this.verifyEnd(offset);
337
328
  this.currentOffset = offset;
338
329
  }
339
330
 
@@ -342,7 +333,7 @@ export class BytesReader {
342
333
  */
343
334
  public verifyEnd(size: i32): void {
344
335
  if (size > this.buffer.byteLength) {
345
- throw new Error(
336
+ throw new Revert(
346
337
  `Attempt to read beyond buffer length. Requested up to offset ${size}, ` +
347
338
  `but buffer is only ${this.buffer.byteLength} bytes.`,
348
339
  );
@@ -6,6 +6,10 @@ import { Revert } from '../types/Revert';
6
6
  import {
7
7
  ADDRESS_BYTE_LENGTH,
8
8
  I128_BYTE_LENGTH,
9
+ I16_BYTE_LENGTH,
10
+ I32_BYTE_LENGTH,
11
+ I64_BYTE_LENGTH,
12
+ I8_BYTE_LENGTH,
9
13
  U128_BYTE_LENGTH,
10
14
  U16_BYTE_LENGTH,
11
15
  U256_BYTE_LENGTH,
@@ -33,20 +37,19 @@ export class BytesWriter {
33
37
  public write<T>(value: T): void {
34
38
  if (isInteger<T>()) {
35
39
  const size = sizeof<T>();
36
- if (size === 1) {
37
- this.writeU8(<u8>value);
38
- return;
39
- }
40
40
 
41
41
  if (isSigned<T>()) {
42
42
  switch (size) {
43
- case 2:
43
+ case I8_BYTE_LENGTH:
44
+ this.writeI8(<i8>value);
45
+ break;
46
+ case I16_BYTE_LENGTH:
44
47
  this.writeI16(<i16>value);
45
48
  break;
46
- case 4:
49
+ case I32_BYTE_LENGTH:
47
50
  this.writeI32(<i32>value);
48
51
  break;
49
- case 8:
52
+ case I64_BYTE_LENGTH:
50
53
  this.writeI64(<i64>value);
51
54
  break;
52
55
  default:
@@ -54,13 +57,16 @@ export class BytesWriter {
54
57
  }
55
58
  } else {
56
59
  switch (size) {
57
- case 2:
60
+ case U8_BYTE_LENGTH:
61
+ this.writeU8(<u8>value);
62
+ break;
63
+ case U16_BYTE_LENGTH:
58
64
  this.writeU16(<u16>value);
59
65
  break;
60
- case 4:
66
+ case U32_BYTE_LENGTH:
61
67
  this.writeU32(<u32>value);
62
68
  break;
63
- case 8:
69
+ case U64_BYTE_LENGTH:
64
70
  this.writeU64(<u64>value);
65
71
  break;
66
72
  default:
@@ -121,21 +127,27 @@ export class BytesWriter {
121
127
  }
122
128
 
123
129
  public writeI64(value: i64, be: boolean = true): void {
124
- this.allocSafe(U64_BYTE_LENGTH);
130
+ this.allocSafe(I64_BYTE_LENGTH);
125
131
  this.buffer.setInt64(this.currentOffset, value, !be);
126
- this.currentOffset += U64_BYTE_LENGTH;
132
+ this.currentOffset += I64_BYTE_LENGTH;
127
133
  }
128
134
 
129
135
  public writeI32(value: i32, be: boolean = true): void {
130
- this.allocSafe(U32_BYTE_LENGTH);
136
+ this.allocSafe(I32_BYTE_LENGTH);
131
137
  this.buffer.setInt32(this.currentOffset, value, !be);
132
- this.currentOffset += U32_BYTE_LENGTH;
138
+ this.currentOffset += I32_BYTE_LENGTH;
133
139
  }
134
140
 
135
141
  public writeI16(value: i16, be: boolean = true): void {
136
- this.allocSafe(U16_BYTE_LENGTH);
142
+ this.allocSafe(I16_BYTE_LENGTH);
137
143
  this.buffer.setInt16(this.currentOffset, value, !be);
138
- this.currentOffset += U16_BYTE_LENGTH;
144
+ this.currentOffset += I16_BYTE_LENGTH;
145
+ }
146
+
147
+ public writeI8(value: u8): void {
148
+ this.allocSafe(I8_BYTE_LENGTH);
149
+ this.buffer.setInt8(this.currentOffset, value);
150
+ this.currentOffset += I8_BYTE_LENGTH;
139
151
  }
140
152
 
141
153
  /**
@@ -289,6 +301,38 @@ export class BytesWriter {
289
301
  this.writeBytes(bytes);
290
302
  }
291
303
 
304
+ // zero-copy bulk writer
305
+ public writeRaw(data: Uint8Array): void {
306
+ const n = data.length;
307
+ this.allocSafe(n);
308
+
309
+ const off = this.currentOffset;
310
+ const dst = this.typedArray;
311
+
312
+ memory.copy(changetype<usize>(dst.buffer) + <usize>off, changetype<usize>(data.buffer), n);
313
+
314
+ this.currentOffset = off + n;
315
+ }
316
+
317
+ public writeRawSlice(data: Uint8Array, offset: i32, length: i32): void {
318
+ if (offset < 0 || length < 0 || offset + length > data.length) {
319
+ throw new Revert('writeRawSlice bounds');
320
+ }
321
+
322
+ this.allocSafe(length);
323
+
324
+ const off = this.currentOffset;
325
+ const dst = this.typedArray;
326
+
327
+ memory.copy(
328
+ changetype<usize>(dst.buffer) + <usize>off,
329
+ changetype<usize>(data.buffer) + <usize>offset,
330
+ length,
331
+ );
332
+
333
+ this.currentOffset = off + length;
334
+ }
335
+
292
336
  /**
293
337
  * Equivalent to TS’s writeAddressValueTuple, except specialized for u256 values.
294
338
  */
@@ -318,10 +362,6 @@ export class BytesWriter {
318
362
  return this.currentOffset;
319
363
  }
320
364
 
321
- public setOffset(offset: u32): void {
322
- this.currentOffset = offset;
323
- }
324
-
325
365
  /**
326
366
  * Ensures we have space for `size` more bytes without going past the current buffer.
327
367
  * If not, calls `resize()` which by default throws a Revert.