@btc-vision/btc-runtime 1.5.1 → 1.5.2

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,6 +1,6 @@
1
1
  {
2
2
  "name": "@btc-vision/btc-runtime",
3
- "version": "1.5.1",
3
+ "version": "1.5.2",
4
4
  "description": "Bitcoin Smart Contract Runtime",
5
5
  "main": "btc/index.ts",
6
6
  "scripts": {},
@@ -15,6 +15,7 @@ import {
15
15
  U64_BYTE_LENGTH,
16
16
  U8_BYTE_LENGTH,
17
17
  } from '../utils';
18
+ import { sizeof } from 'builtins';
18
19
 
19
20
  @final
20
21
  export class BytesReader {
@@ -29,6 +30,76 @@ export class BytesReader {
29
30
  return this.buffer.byteLength;
30
31
  }
31
32
 
33
+ public read<T>(): T {
34
+ const id = idof<T>();
35
+
36
+ if (isBoolean<T>()) {
37
+ return this.readBoolean() as T;
38
+ } else if (isString<T>()) {
39
+ return this.readStringWithLength() as T;
40
+ } else if (isInteger<T>()) {
41
+ if (isSigned<T>()) {
42
+ const size = sizeof<T>();
43
+
44
+ switch (size) {
45
+ case 8:
46
+ return this.readU8() as T;
47
+ case 16:
48
+ return this.readI16() as T;
49
+ case 32:
50
+ return this.readI32() as T;
51
+ case 64:
52
+ return this.readI64() as T;
53
+ default:
54
+ throw new Error(`Invalid size ${size}`);
55
+ }
56
+ } else {
57
+ const size = sizeof<T>();
58
+
59
+ switch (size) {
60
+ case 8:
61
+ return this.readU8() as T;
62
+ case 16:
63
+ return this.readU16() as T;
64
+ case 32:
65
+ return this.readU32() as T;
66
+ case 64:
67
+ return this.readU64() as T;
68
+ default:
69
+ throw new Error(`Invalid size ${size}`);
70
+ }
71
+ }
72
+ } else if (id === idof<u256>()) {
73
+ return this.readU256() as T;
74
+ } else if (id === idof<u128>()) {
75
+ return this.readU128() as T;
76
+ } else if (id === idof<i128>()) {
77
+ return this.readI128() as T;
78
+ } else if (id === idof<Address>()) {
79
+ return this.readAddress() as T;
80
+ } else if (id === idof<Uint8Array>()) {
81
+ return this.readBytesWithLength() as T;
82
+ } else if (id === idof<string>()) {
83
+ return this.readStringWithLength() as T;
84
+ } else {
85
+ throw new Error(`Unsupported type ${id}`);
86
+ }
87
+ }
88
+
89
+ public readI16(): i16 {
90
+ this.verifyEnd(this.currentOffset + 2);
91
+ const value = this.buffer.getInt16(this.currentOffset, true);
92
+ this.currentOffset += 2;
93
+ return value;
94
+ }
95
+
96
+ public readI32(): i32 {
97
+ this.verifyEnd(this.currentOffset + 4);
98
+ const value = this.buffer.getInt32(this.currentOffset, true);
99
+ this.currentOffset += 4;
100
+ return value;
101
+ }
102
+
32
103
  public readU8(): u8 {
33
104
  this.verifyEnd(this.currentOffset + U8_BYTE_LENGTH);
34
105
  const value = this.buffer.getUint8(this.currentOffset);
@@ -169,7 +240,7 @@ export class BytesReader {
169
240
  return addr;
170
241
  }
171
242
 
172
- // ------------------- Arrays ------------------- //
243
+ // ------------------- Arrays ------------------- //
173
244
 
174
245
  /**
175
246
  * The AS writer does `writeU32(length)` for U256 arrays, so we read a u32.
@@ -30,6 +30,62 @@ export class BytesWriter {
30
30
  return this.buffer.byteLength;
31
31
  }
32
32
 
33
+ public write<T>(value: T): void {
34
+ if (isInteger<T>()) {
35
+ const size = sizeof<T>();
36
+ if (size === 1) {
37
+ this.writeU8(<u8>value);
38
+ return;
39
+ }
40
+
41
+ if (isSigned<T>()) {
42
+ switch (size) {
43
+ case 2:
44
+ this.writeI16(<i16>value);
45
+ break;
46
+ case 4:
47
+ this.writeI32(<i32>value);
48
+ break;
49
+ case 8:
50
+ this.writeI64(<i64>value);
51
+ break;
52
+ default:
53
+ throw new Revert(`Unsupported integer size: ${size}`);
54
+ }
55
+ } else {
56
+ switch (size) {
57
+ case 2:
58
+ this.writeU16(<u16>value);
59
+ break;
60
+ case 4:
61
+ this.writeU32(<u32>value);
62
+ break;
63
+ case 8:
64
+ this.writeU64(<u64>value);
65
+ break;
66
+ default:
67
+ throw new Revert(`Unsupported integer size: ${size}`);
68
+ }
69
+ }
70
+ } else if (isBoolean<T>()) {
71
+ this.writeBoolean(<boolean>value);
72
+ } else if (isString<T>()) {
73
+ this.writeStringWithLength(<string>value);
74
+ } else if (value instanceof Uint8Array) {
75
+ this.writeBytesWithLength(<Uint8Array>value);
76
+ } else if (value instanceof Address) {
77
+ this.writeAddress(<Address>value);
78
+ } else if (value instanceof u128) {
79
+ this.writeU128(<u128>value);
80
+ } else if (value instanceof u256) {
81
+ this.writeU256(<u256>value);
82
+ } else if (value instanceof i128) {
83
+ this.writeI128(<i128>value);
84
+ } else {
85
+ throw new Revert(`Unsupported type: ${typeof value}`);
86
+ }
87
+ }
88
+
33
89
  public writeU8(value: u8): void {
34
90
  this.allocSafe(U8_BYTE_LENGTH);
35
91
  this.buffer.setUint8(this.currentOffset, value);
@@ -64,6 +120,24 @@ export class BytesWriter {
64
120
  this.currentOffset += U64_BYTE_LENGTH;
65
121
  }
66
122
 
123
+ public writeI64(value: i64, be: boolean = true): void {
124
+ this.allocSafe(U64_BYTE_LENGTH);
125
+ this.buffer.setInt64(this.currentOffset, value, !be);
126
+ this.currentOffset += U64_BYTE_LENGTH;
127
+ }
128
+
129
+ public writeI32(value: i32, be: boolean = true): void {
130
+ this.allocSafe(U32_BYTE_LENGTH);
131
+ this.buffer.setInt32(this.currentOffset, value, !be);
132
+ this.currentOffset += U32_BYTE_LENGTH;
133
+ }
134
+
135
+ public writeI16(value: i16, be: boolean = true): void {
136
+ this.allocSafe(U16_BYTE_LENGTH);
137
+ this.buffer.setInt16(this.currentOffset, value, !be);
138
+ this.currentOffset += U16_BYTE_LENGTH;
139
+ }
140
+
67
141
  /**
68
142
  * Writes a 32-bit selector.
69
143
  * @param value
@@ -3,8 +3,6 @@ import { BytesWriter } from '../buffer/BytesWriter';
3
3
  import { Blockchain } from '../env';
4
4
  import { ApproveEvent, BurnEvent, MintEvent, TransferEvent } from '../events/predefined';
5
5
  import { encodeSelector, Selector } from '../math/abi';
6
- import { AddressMemoryMap } from '../memory/AddressMemoryMap';
7
- import { MultiAddressMemoryMap } from '../memory/MultiAddressMemoryMap';
8
6
  import { StoredString } from '../storage/StoredString';
9
7
  import { StoredU256 } from '../storage/StoredU256';
10
8
  import { Address } from '../types/Address';
@@ -19,6 +17,8 @@ import { OP20InitParameters } from './interfaces/OP20InitParameters';
19
17
  import { OP_NET } from './OP_NET';
20
18
  import { sha256 } from '../env/global';
21
19
  import { EMPTY_POINTER } from '../math/bytes';
20
+ import { MapOfMap } from '../memory/MapOfMap';
21
+ import { AddressMemoryMap } from '../memory/AddressMemoryMap';
22
22
 
23
23
  const nonceMapPointer: u16 = Blockchain.nextPointer;
24
24
  const maxSupplyPointer: u16 = Blockchain.nextPointer;
@@ -29,7 +29,7 @@ const allowanceMapPointer: u16 = Blockchain.nextPointer;
29
29
  const balanceOfMapPointer: u16 = Blockchain.nextPointer;
30
30
 
31
31
  export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
32
- protected readonly allowanceMap: MultiAddressMemoryMap;
32
+ protected readonly allowanceMap: MapOfMap<u256>;
33
33
  protected readonly balanceOfMap: AddressMemoryMap;
34
34
 
35
35
  protected readonly _maxSupply: StoredU256;
@@ -43,14 +43,15 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
43
43
  super();
44
44
 
45
45
  // Initialize main storage structures
46
- this.allowanceMap = new MultiAddressMemoryMap(allowanceMapPointer);
46
+ this.allowanceMap = new MapOfMap(allowanceMapPointer);
47
47
  this.balanceOfMap = new AddressMemoryMap(balanceOfMapPointer);
48
+ this._nonceMap = new AddressMemoryMap(nonceMapPointer);
49
+
48
50
  this._totalSupply = new StoredU256(totalSupplyPointer, EMPTY_POINTER);
49
51
  this._maxSupply = new StoredU256(maxSupplyPointer, EMPTY_POINTER);
50
52
  this._decimals = new StoredU256(decimalsPointer, EMPTY_POINTER);
51
53
  this._name = new StoredString(stringPointer, 0);
52
54
  this._symbol = new StoredString(stringPointer, 1);
53
- this._nonceMap = new AddressMemoryMap(nonceMapPointer);
54
55
 
55
56
  if (params && this._maxSupply.value.isZero()) {
56
57
  this.instantiate(params, true);
@@ -23,6 +23,8 @@ import {
23
23
  } from './global';
24
24
  import { eqUint, MapUint8Array } from '../generic/MapUint8Array';
25
25
  import { EMPTY_BUFFER } from '../math/bytes';
26
+ import { Plugin } from '../plugins/Plugin';
27
+ import { Calldata } from '../types';
26
28
 
27
29
  export * from '../env/global';
28
30
 
@@ -38,6 +40,7 @@ export class BlockchainEnvironment {
38
40
 
39
41
  private storage: MapUint8Array = new MapUint8Array();
40
42
  private _selfContract: Potential<OP_NET> = null;
43
+ private _plugins: Plugin[] = [];
41
44
 
42
45
  private _block: Potential<Block> = null;
43
46
 
@@ -105,6 +108,40 @@ export class BlockchainEnvironment {
105
108
  return this._contractAddress as Address;
106
109
  }
107
110
 
111
+ public registerPlugin(plugin: Plugin): void {
112
+ this._plugins.push(plugin);
113
+ }
114
+
115
+ public onDeployment(calldata: Calldata): void {
116
+ for (let i: i32 = 0; i < this._plugins.length; i++) {
117
+ const plugin = this._plugins[i];
118
+
119
+ plugin.onDeployment(calldata);
120
+ }
121
+
122
+ this.contract.onDeployment(calldata);
123
+ }
124
+
125
+ public onExecutionStarted(): void {
126
+ for (let i: i32 = 0; i < this._plugins.length; i++) {
127
+ const plugin = this._plugins[i];
128
+
129
+ plugin.onExecutionStarted();
130
+ }
131
+
132
+ this.contract.onExecutionStarted();
133
+ }
134
+
135
+ public onExecutionCompleted(): void {
136
+ for (let i: i32 = 0; i < this._plugins.length; i++) {
137
+ const plugin = this._plugins[i];
138
+
139
+ plugin.onExecutionCompleted();
140
+ }
141
+
142
+ this.contract.onExecutionCompleted();
143
+ }
144
+
108
145
  public setEnvironmentVariables(data: Uint8Array): void {
109
146
  const reader: BytesReader = new BytesReader(data);
110
147
 
@@ -18,11 +18,11 @@ export function execute(calldataLength: u32): u32 {
18
18
  const calldata: Calldata = new BytesReader(Uint8Array.wrap(calldataBuffer));
19
19
  const selector: Selector = calldata.readSelector();
20
20
 
21
- Blockchain.contract.onExecutionStarted();
21
+ Blockchain.onExecutionStarted();
22
22
 
23
23
  const result: BytesWriter = Blockchain.contract.execute(selector, calldata);
24
24
 
25
- Blockchain.contract.onExecutionCompleted();
25
+ Blockchain.onExecutionCompleted();
26
26
 
27
27
  const resultBuffer = result.getBuffer().buffer;
28
28
  const resultLength = resultBuffer.byteLength;
@@ -43,9 +43,9 @@ export function onDeploy(calldataLength: u32): u32 {
43
43
 
44
44
  const calldata: Calldata = new BytesReader(Uint8Array.wrap(calldataBuffer));
45
45
 
46
- Blockchain.contract.onExecutionStarted();
47
- Blockchain.contract.onDeployment(calldata);
48
- Blockchain.contract.onExecutionCompleted();
46
+ Blockchain.onExecutionStarted();
47
+ Blockchain.onDeployment(calldata);
48
+ Blockchain.onExecutionCompleted();
49
49
 
50
50
  return 0;
51
51
  }
package/runtime/index.ts CHANGED
@@ -45,12 +45,18 @@ export * from './math/bytes';
45
45
  export * from './secp256k1/ECPoint';
46
46
 
47
47
  /** Memory */
48
- export * from './memory/AddressMemoryMap';
49
- export * from './memory/StringMemoryMap';
50
- export * from './memory/MultiStringMemoryMap';
51
- export * from './memory/KeyMerger';
52
- export * from './memory/MultiAddressMemoryMap';
53
- export * from './memory/Uint8ArrayMerger';
48
+ export * from './memory/Nested';
49
+ export * from './nested/PointerManager';
50
+ export * from './nested/storage/StorageMap';
51
+ export * from './nested/storage/StorageSet';
52
+
53
+ /** Codecs */
54
+ export * from './nested/codecs/U256Codec';
55
+ export * from './nested/codecs/AddressCodec';
56
+ export * from './nested/codecs/NumericCodec';
57
+ export * from './nested/codecs/BooleanCodec';
58
+ export * from './nested/codecs/StringCodec';
59
+ export * from './nested/codecs/VariableBytesCodec';
54
60
 
55
61
  /** Storage */
56
62
  export * from './storage/StoredU256';
@@ -58,7 +64,6 @@ export * from './storage/StoredU64';
58
64
  export * from './storage/StoredString';
59
65
  export * from './storage/StoredAddress';
60
66
  export * from './storage/StoredBoolean';
61
- export * from './storage/Serializable';
62
67
 
63
68
  /** Arrays */
64
69
  export * from './storage/arrays/StoredAddressArray';
@@ -71,8 +76,6 @@ export * from './storage/arrays/StoredU64Array';
71
76
  export * from './storage/arrays/StoredU128Array';
72
77
  export * from './storage/arrays/StoredU256Array';
73
78
 
74
- export * from './memory/FastUint8Array';
75
-
76
79
  /** Shared libraries */
77
80
  export * from './shared-libraries/TransferHelper';
78
81
  export * from './shared-libraries/OP20Utils';
@@ -1,10 +1,10 @@
1
- import { Uint8ArrayMerger } from './Uint8ArrayMerger';
2
1
  import { Address } from '../types/Address';
2
+ import { Nested } from './Nested';
3
3
 
4
4
  @final
5
- export class MultiAddressMemoryMap extends Map<
5
+ export class MapOfMap<T> extends Map<
6
6
  Address,
7
- Uint8ArrayMerger
7
+ Nested<T>
8
8
  > {
9
9
  public pointer: u16;
10
10
 
@@ -17,14 +17,14 @@ export class MultiAddressMemoryMap extends Map<
17
17
  }
18
18
 
19
19
  @inline
20
- public get(key: Address): Uint8ArrayMerger {
20
+ public get(key: Address): Nested<T> {
21
21
  this.createKeyMerger(key);
22
22
 
23
23
  return super.get(key);
24
24
  }
25
25
 
26
26
  @inline
27
- public set(key: Address, value: Uint8ArrayMerger): this {
27
+ public set(key: Address, value: Nested<T>): this {
28
28
  this.createKeyMerger(key);
29
29
 
30
30
  return <this>super.set(key, value);
@@ -48,7 +48,7 @@ export class MultiAddressMemoryMap extends Map<
48
48
  @inline
49
49
  private createKeyMerger(key: Address): void {
50
50
  if (!super.has(key)) {
51
- super.set(key, new Uint8ArrayMerger(key, this.pointer));
51
+ super.set(key, new Nested<T>(key, this.pointer));
52
52
  }
53
53
  }
54
54
  }
@@ -0,0 +1,113 @@
1
+ import { Blockchain } from '../env';
2
+ import { encodePointerUnknownLength } from '../math/abi';
3
+ import { BytesWriter } from '../buffer/BytesWriter';
4
+ import { u256 } from '@btc-vision/as-bignum/assembly';
5
+ import { Address } from '../types/Address';
6
+
7
+ @final
8
+ export class Nested<T> {
9
+ public parentKey: Uint8Array;
10
+
11
+ public pointer: u16;
12
+
13
+ constructor(
14
+ parent: Uint8Array,
15
+ pointer: u16,
16
+ ) {
17
+ this.pointer = pointer;
18
+
19
+ this.parentKey = parent;
20
+ }
21
+
22
+ public set(key: Uint8Array, value: T): this {
23
+ const keyHash: Uint8Array = this.getKeyHash(key);
24
+ Blockchain.setStorageAt(keyHash, this.from(value));
25
+
26
+ return this;
27
+ }
28
+
29
+ public get(key: Uint8Array): T {
30
+ const keyHash: Uint8Array = this.getKeyHash(key);
31
+
32
+ return this.toValue(Blockchain.getStorageAt(keyHash));
33
+ }
34
+
35
+ public has(key: Uint8Array): bool {
36
+ const mergedKey: Uint8Array = this.getKeyHash(key);
37
+
38
+ return Blockchain.hasStorageAt(mergedKey);
39
+ }
40
+
41
+ @unsafe
42
+ public delete(_key: Uint8Array): bool {
43
+ throw new Error('Method not implemented.');
44
+ }
45
+
46
+ @unsafe
47
+ public clear(): void {
48
+ throw new Error('Clear method not implemented.');
49
+ }
50
+
51
+ /**
52
+ * Converts raw bytes from storage into type T.
53
+ */
54
+ private toValue(value: Uint8Array): T {
55
+ // Check T's compile-time type ID
56
+ if (idof<T>() == idof<u256>()) {
57
+ // We know T is u256
58
+ return changetype<T>(u256.fromUint8ArrayBE(value));
59
+ } else if (idof<T>() == idof<Uint8Array>()) {
60
+ // We know T is Uint8Array
61
+ return changetype<T>(value);
62
+ } else if (idof<T>() == idof<Address>()) {
63
+ // We know T is Address
64
+ return changetype<T>(value);
65
+ } else if (isInteger<T>()) {
66
+ // For a simple integer, just pull out the first byte
67
+ return value[0] as T;
68
+ } else if (isString<T>()) {
69
+ // T is a string
70
+ return changetype<T>(String.UTF8.decode(value.buffer));
71
+ }
72
+
73
+ throw new Error('Unsupported type');
74
+ }
75
+
76
+ /**
77
+ * Converts type T into raw bytes for storage.
78
+ */
79
+ private from(value: T): Uint8Array {
80
+ if (idof<T>() == idof<u256>()) {
81
+ // Cast T to u256, then convert to bytes
82
+ return changetype<u256>(value).toUint8Array(true);
83
+ } else if (idof<T>() == idof<Uint8Array>()) {
84
+ // Just return it
85
+ return changetype<Uint8Array>(value);
86
+ } else if (idof<T>() == idof<Address>()) {
87
+ // Address is already bytes
88
+ return changetype<Uint8Array>(value);
89
+ } else if (isInteger<T>()) {
90
+ const writer = new BytesWriter(sizeof<T>());
91
+ writer.write<T>(value);
92
+ return writer.getBuffer();
93
+ } else if (isString<T>()) {
94
+ const str = changetype<string>(value);
95
+ return Uint8Array.wrap(String.UTF8.encode(str));
96
+ }
97
+
98
+ throw new Error('Unsupported type');
99
+ }
100
+
101
+ private getKeyHash(key: Uint8Array): Uint8Array {
102
+ const writer: BytesWriter = new BytesWriter(key.byteLength + this.parentKey.byteLength);
103
+
104
+ writer.writeBytes(this.parentKey);
105
+ writer.writeBytes(key);
106
+
107
+ return this.encodePointer(writer);
108
+ }
109
+
110
+ private encodePointer(writer: BytesWriter): Uint8Array {
111
+ return encodePointerUnknownLength(this.pointer, writer.getBuffer());
112
+ }
113
+ }
@@ -0,0 +1,55 @@
1
+ import { Blockchain } from '../env';
2
+ import { Potential } from '../lang/Definitions';
3
+ import { bigEndianAdd } from '../math/bytes';
4
+ import { Plugin } from '../plugins/Plugin';
5
+
6
+ /**
7
+ * A unique key in storage that holds the next free "offset" as a 256-bit counter.
8
+ * You can change this to any 32-byte constant you like.
9
+ */
10
+ const ALLOCATOR_KEY = new Uint8Array(32);
11
+ for (let i = 0; i < 32; i++) {
12
+ ALLOCATOR_KEY[i] = 0xff;
13
+ }
14
+
15
+ /**
16
+ * PointerManager: ensures we never collide while allocating variable-length data.
17
+ * - We store a global "offset" in ALLOCATOR_KEY (as a big-endian u256).
18
+ * - Each time we allocate N slots (N * 32 bytes), we do:
19
+ * oldOffset = currentGlobalOffset
20
+ * newOffset = oldOffset + N
21
+ * store newOffset back
22
+ * return oldOffset as the base pointer
23
+ */
24
+ class _PointerManager extends Plugin {
25
+ private _cachedOffset: Potential<Uint8Array> = null;
26
+
27
+ private get cachedOffset(): Uint8Array {
28
+ if (!this._cachedOffset) {
29
+ this._cachedOffset = Blockchain.getStorageAt(ALLOCATOR_KEY);
30
+ }
31
+
32
+ return this._cachedOffset as Uint8Array;
33
+ }
34
+
35
+ /**
36
+ * Allocates `numSlots` (each 32 bytes). Returns a 32-byte pointer (u256 in big-endian).
37
+ *
38
+ * Each slot is conceptually:
39
+ * slot0 = pointer + 0
40
+ * slot1 = pointer + 1
41
+ * ...
42
+ * and so forth in big-endian arithmetic.
43
+ */
44
+ public allocateSlots(numSlots: u64): Uint8Array {
45
+ this._cachedOffset = bigEndianAdd(this.cachedOffset, numSlots);
46
+
47
+ const val = this.cachedOffset;
48
+ Blockchain.setStorageAt(ALLOCATOR_KEY, val);
49
+
50
+ return val;
51
+ }
52
+ }
53
+
54
+ export const PointerManager = new _PointerManager();
55
+ Blockchain.registerPlugin(PointerManager);
@@ -0,0 +1,19 @@
1
+ import { ICodec } from '../interfaces/ICodec';
2
+ import { Address } from '../../types/Address';
3
+
4
+ class _AddressCodec implements ICodec<Address> {
5
+ public encode(value: Address): Uint8Array {
6
+ return value;
7
+ }
8
+
9
+ public decode(buffer: Uint8Array): Address {
10
+ if (buffer.length == 0) {
11
+ return Address.zero();
12
+ }
13
+
14
+ return Address.fromUint8Array(buffer);
15
+ }
16
+ }
17
+
18
+ export const idOfAddressCodec = idof<_AddressCodec>();
19
+ export const AddressCodec = new _AddressCodec();
@@ -0,0 +1,17 @@
1
+ import { ICodec } from '../interfaces/ICodec';
2
+
3
+ class _BooleanCodec implements ICodec<bool> {
4
+ public encode(value: bool): Uint8Array {
5
+ const out = new Uint8Array(1);
6
+ out[0] = value ? 1 : 0;
7
+ return out;
8
+ }
9
+
10
+ public decode(buffer: Uint8Array): bool {
11
+ if (buffer.length == 0) return false;
12
+ return buffer[0] == 1;
13
+ }
14
+ }
15
+
16
+ export const idOfBoolCodec = idof<_BooleanCodec>();
17
+ export const BooleanCodec = new _BooleanCodec();
@@ -0,0 +1,10 @@
1
+ import { i128, u128, u256 } from '@btc-vision/as-bignum/assembly';
2
+ import { Uint8Array } from 'typedarray';
3
+ import { Address } from '../../types/Address';
4
+
5
+ export const idOfU256 = idof<u256>();
6
+ export const idOfU128 = idof<u128>();
7
+ export const idOfI128 = idof<i128>();
8
+ export const idOfUint8Array = idof<Uint8Array>();
9
+ export const idOfString = idof<string>();
10
+ export const idOfAddress = idof<Address>();
@@ -0,0 +1,58 @@
1
+ import { i128, u128, u256 } from '@btc-vision/as-bignum/assembly';
2
+ import { ICodec } from '../interfaces/ICodec';
3
+ import { BytesWriter } from '../../buffer/BytesWriter';
4
+ import { BytesReader } from '../../buffer/BytesReader';
5
+ import { idOfI128, idOfU128, idOfU256 } from './Ids';
6
+
7
+ /**
8
+ * A generic NumericCodec<T> that handles:
9
+ * - `u256` from @btc-vision/as-bignum (big-endian)
10
+ * - Any built-in integer type (i32, u32, i64, etc.) also stored big-endian
11
+ */
12
+ export class NumericCodec<T> implements ICodec<T> {
13
+ public encode(value: T): Uint8Array {
14
+ const id = idof<T>();
15
+ switch (id) {
16
+ case idOfU256: {
17
+ // T is `u256`
18
+ const val = changetype<u256>(value);
19
+ return val.toUint8Array(true); // big-endian
20
+ }
21
+ case idOfU128: {
22
+ // T is `u128`
23
+ const val = changetype<u128>(value);
24
+ return val.toUint8Array(true); // big-endian
25
+ }
26
+ case idOfI128: {
27
+ // T is `i128`
28
+ const val = changetype<i128>(value);
29
+ return val.toUint8Array(true); // big-endian
30
+ }
31
+ default: {
32
+ const writer = new BytesWriter(sizeof<T>());
33
+ writer.write<T>(value);
34
+
35
+ return writer.getBuffer();
36
+ }
37
+ }
38
+ }
39
+
40
+ public decode(buffer: Uint8Array): T {
41
+ const id = idof<T>();
42
+ switch (id) {
43
+ case idOfU256:
44
+ // T is `u256`
45
+ return changetype<T>(u256.fromBytes(buffer, true));
46
+ case idOfU128:
47
+ // T is `u128`
48
+ return changetype<T>(u128.fromBytes(buffer, true));
49
+ case idOfI128:
50
+ // T is `i128`
51
+ return changetype<T>(i128.fromBytes(buffer, true));
52
+ default: {
53
+ const writer = new BytesReader(buffer);
54
+ return writer.read<T>();
55
+ }
56
+ }
57
+ }
58
+ }