@btc-vision/btc-runtime 1.0.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 (41) hide show
  1. package/LICENSE.md +62 -0
  2. package/README.md +34 -0
  3. package/package.json +46 -0
  4. package/runtime/buffer/BytesReader.ts +193 -0
  5. package/runtime/buffer/BytesWriter.ts +322 -0
  6. package/runtime/contracts/OP_20.ts +377 -0
  7. package/runtime/contracts/OP_NET.ts +77 -0
  8. package/runtime/contracts/interfaces/IOP_20.ts +25 -0
  9. package/runtime/env/BTCEnvironment.ts +372 -0
  10. package/runtime/env/global.ts +18 -0
  11. package/runtime/env/index.ts +3 -0
  12. package/runtime/events/NetEvent.ts +27 -0
  13. package/runtime/events/predefined/ApproveEvent.ts +16 -0
  14. package/runtime/events/predefined/BurnEvent.ts +13 -0
  15. package/runtime/events/predefined/ClaimEvent.ts +13 -0
  16. package/runtime/events/predefined/MintEvent.ts +15 -0
  17. package/runtime/events/predefined/StakeEvent.ts +13 -0
  18. package/runtime/events/predefined/TransferEvent.ts +16 -0
  19. package/runtime/events/predefined/UnstakeEvent.ts +13 -0
  20. package/runtime/events/predefined/index.ts +7 -0
  21. package/runtime/exports/index.ts +57 -0
  22. package/runtime/generic/Map.ts +55 -0
  23. package/runtime/index.ts +45 -0
  24. package/runtime/interfaces/IBTC.ts +6 -0
  25. package/runtime/lang/Definitions.ts +1 -0
  26. package/runtime/math/abi.ts +37 -0
  27. package/runtime/math/bytes.ts +34 -0
  28. package/runtime/math/cyrb53.ts +46 -0
  29. package/runtime/math/rnd.ts +51 -0
  30. package/runtime/math/sha256.ts +270 -0
  31. package/runtime/memory/AddressMemoryMap.ts +64 -0
  32. package/runtime/memory/KeyMerger.ts +72 -0
  33. package/runtime/memory/MemorySlot.ts +1 -0
  34. package/runtime/memory/MemorySlotPointer.ts +3 -0
  35. package/runtime/memory/MultiAddressMemoryMap.ts +76 -0
  36. package/runtime/storage/StoredU256.ts +320 -0
  37. package/runtime/types/Address.ts +5 -0
  38. package/runtime/types/Revert.ts +5 -0
  39. package/runtime/types/SafeMath.ts +137 -0
  40. package/runtime/types/index.ts +8 -0
  41. package/runtime/universal/ABIRegistry.ts +72 -0
@@ -0,0 +1,372 @@
1
+ import { Address, PotentialAddress } from '../types/Address';
2
+ import { MemorySlotPointer } from '../memory/MemorySlotPointer';
3
+ import { MemorySlotData } from '../memory/MemorySlot';
4
+ import { u256 } from 'as-bignum/assembly';
5
+ import { ABIRegistry } from '../universal/ABIRegistry';
6
+ import { BytesReader } from '../buffer/BytesReader';
7
+ import { encodePointerHash } from '../math/abi';
8
+ import { BytesWriter } from '../buffer/BytesWriter';
9
+ import { MAX_EVENTS, NetEvent } from '../events/NetEvent';
10
+ import { Potential } from '../lang/Definitions';
11
+ import { Map } from '../generic/Map';
12
+ import { OP_NET } from '../contracts/OP_NET';
13
+ import { BlockchainStorage, PointerStorage } from '../types';
14
+
15
+ export * from '../env/global';
16
+
17
+ @final
18
+ export class BlockchainEnvironment {
19
+ private static readonly runtimeException: string = 'RuntimeException';
20
+
21
+ private storage: BlockchainStorage = new Map();
22
+ private initializedStorage: BlockchainStorage = new Map();
23
+
24
+ private externalCalls: Map<Address, Uint8Array[]> = new Map();
25
+ private externalCallsResponse: Map<Address, Uint8Array[]> = new Map();
26
+
27
+ private events: NetEvent[] = [];
28
+
29
+ private _callee: PotentialAddress = null;
30
+ private _caller: PotentialAddress = null;
31
+ private currentBlock: u256 = u256.Zero;
32
+
33
+ constructor() {}
34
+
35
+ private _contract: Potential<OP_NET> = null;
36
+
37
+ public get contract(): OP_NET {
38
+ if (!this._contract) {
39
+ throw this.error('Contract is required');
40
+ }
41
+
42
+ return this._contract as OP_NET;
43
+ }
44
+
45
+ public set contract(contract: OP_NET) {
46
+ this._contract = contract;
47
+ }
48
+
49
+ private _nextPointer: u8 = 0;
50
+
51
+ public get nextPointer(): u8 {
52
+ this._nextPointer += 1;
53
+
54
+ return this._nextPointer;
55
+ }
56
+
57
+ public _owner: Potential<Address> = null;
58
+
59
+ public get owner(): Address {
60
+ if (!this._owner) {
61
+ throw this.error('Owner is required');
62
+ }
63
+
64
+ return this._owner as Address;
65
+ }
66
+
67
+ public _contractAddress: Potential<Address> = null;
68
+
69
+ public get contractAddress(): Address {
70
+ if (!this._contractAddress) {
71
+ throw this.error('Contract address is required');
72
+ }
73
+
74
+ return this._contractAddress as Address;
75
+ }
76
+
77
+ public get blockNumber(): u256 {
78
+ return this.currentBlock;
79
+ }
80
+
81
+ public callee(): Address {
82
+ if (!this._callee) {
83
+ throw this.error('Callee is required');
84
+ }
85
+
86
+ return this._callee as Address;
87
+ }
88
+
89
+ public caller(): Address {
90
+ if (!this._caller) {
91
+ throw this.error('Caller is required');
92
+ }
93
+
94
+ return this._caller as Address;
95
+ }
96
+
97
+ public setEnvironment(data: Uint8Array): void {
98
+ const reader: BytesReader = new BytesReader(data);
99
+
100
+ this._caller = reader.readAddress();
101
+ this._callee = reader.readAddress();
102
+ this.currentBlock = reader.readU256();
103
+
104
+ this._owner = reader.readAddress();
105
+ this._contractAddress = reader.readAddress();
106
+ }
107
+
108
+ public call(destinationContract: Address, calldata: BytesWriter): BytesReader {
109
+ if (destinationContract === this._callee) {
110
+ throw this.error('Cannot call self');
111
+ }
112
+
113
+ if (!this.externalCalls.has(destinationContract)) {
114
+ this.externalCalls.set(destinationContract, []);
115
+ }
116
+
117
+ const externalCalls = this.externalCalls.get(destinationContract);
118
+ const buffer = calldata.getBuffer();
119
+ externalCalls.push(buffer);
120
+
121
+ const response: Potential<Uint8Array> = this.getExternalCallResponse(
122
+ destinationContract,
123
+ externalCalls.length - 1,
124
+ );
125
+ if (!response) {
126
+ throw this.error('external call failed');
127
+ }
128
+
129
+ return new BytesReader(response);
130
+ }
131
+
132
+ public getCalls(): Uint8Array {
133
+ const buffer: BytesWriter = new BytesWriter();
134
+
135
+ buffer.writeLimitedAddressBytesMap(this.externalCalls);
136
+ this.externalCalls.clear();
137
+
138
+ return buffer.getBuffer();
139
+ }
140
+
141
+ public loadCallsResponse(responses: Uint8Array): void {
142
+ const memoryReader: BytesReader = new BytesReader(responses);
143
+
144
+ this.externalCallsResponse = memoryReader.readMultiBytesAddressMap();
145
+ }
146
+
147
+ public addEvent(event: NetEvent): void {
148
+ if (this.events.length >= i32(MAX_EVENTS)) {
149
+ throw this.error(`Too many events in the same transaction.`);
150
+ }
151
+
152
+ this.events.push(event);
153
+ }
154
+
155
+ public getEvents(): Uint8Array {
156
+ const eventLength: u8 = u8(this.events.length);
157
+ if (eventLength > MAX_EVENTS) {
158
+ throw this.error('Too many events');
159
+ }
160
+
161
+ const buffer: BytesWriter = new BytesWriter();
162
+ buffer.writeU8(eventLength);
163
+
164
+ for (let i: u8 = 0; i < eventLength; i++) {
165
+ const event: NetEvent = this.events[i];
166
+
167
+ buffer.writeStringWithLength(event.eventType);
168
+ buffer.writeU64(event.getEventDataSelector());
169
+ buffer.writeBytesWithLength(event.getEventData());
170
+ }
171
+
172
+ return buffer.getBuffer();
173
+ }
174
+
175
+ public getStorageAt(
176
+ address: Address,
177
+ pointer: u16,
178
+ subPointer: MemorySlotPointer,
179
+ defaultValue: MemorySlotData<u256>,
180
+ ): MemorySlotData<u256> {
181
+ this.ensureStorageAtAddress(address);
182
+
183
+ const pointerHash: MemorySlotPointer = encodePointerHash(pointer, subPointer);
184
+ this.ensureStorageAtPointer(address, pointerHash, defaultValue);
185
+
186
+ const storage: PointerStorage = this.storage.get(address);
187
+
188
+ // maybe find a better way for this
189
+ const allKeys: u256[] = storage.keys();
190
+ for (let i: i32 = 0; i < allKeys.length; i++) {
191
+ const v: u256 = allKeys[i];
192
+
193
+ if (u256.eq(v, pointerHash)) {
194
+ return storage.get(v);
195
+ }
196
+ }
197
+
198
+ return defaultValue;
199
+ }
200
+
201
+ public hasStorageAt(address: Address, pointer: u16, subPointer: MemorySlotPointer): bool {
202
+ this.ensureStorageAtAddress(address);
203
+
204
+ // We mark zero as the default value for the storage, if something is 0, the storage slot get deleted or is non-existent
205
+ const val: u256 = this.getStorageAt(address, pointer, subPointer, u256.Zero);
206
+ return val != u256.Zero;
207
+ }
208
+
209
+ public setStorageAt(
210
+ address: Address,
211
+ pointer: u16,
212
+ keyPointer: MemorySlotPointer,
213
+ value: MemorySlotData<u256>,
214
+ defaultValue: MemorySlotData<u256>,
215
+ ): void {
216
+ this.ensureStorageAtAddress(address);
217
+
218
+ const pointerHash: u256 = encodePointerHash(pointer, keyPointer);
219
+ this.ensureStorageAtPointer(address, pointerHash, defaultValue);
220
+
221
+ this._internalSetStorageAt(address, pointerHash, value);
222
+ }
223
+
224
+ public getViewSelectors(): Uint8Array {
225
+ return ABIRegistry.getViewSelectors();
226
+ }
227
+
228
+ public getMethodSelectors(): Uint8Array {
229
+ return ABIRegistry.getMethodSelectors();
230
+ }
231
+
232
+ public getWriteMethods(): Uint8Array {
233
+ return ABIRegistry.getWriteMethods();
234
+ }
235
+
236
+ public loadStorage(data: Uint8Array): void {
237
+ this.purgeMemory();
238
+
239
+ const memoryReader: BytesReader = new BytesReader(data);
240
+ const contractsSize: u32 = memoryReader.readU32();
241
+
242
+ for (let i: u32 = 0; i < contractsSize; i++) {
243
+ const address: Address = memoryReader.readAddress();
244
+ const storageSize: u32 = memoryReader.readU32();
245
+
246
+ this.ensureStorageAtAddress(address);
247
+ const storage: PointerStorage = this.storage.get(address);
248
+
249
+ for (let j: u32 = 0; j < storageSize; j++) {
250
+ const keyPointer: MemorySlotPointer = memoryReader.readU256();
251
+ const value: MemorySlotData<u256> = memoryReader.readU256();
252
+
253
+ storage.set(keyPointer, value);
254
+ }
255
+ }
256
+ }
257
+
258
+ public storageToBytes(): Uint8Array {
259
+ const memoryWriter: BytesWriter = new BytesWriter();
260
+ memoryWriter.writeStorage(this.storage);
261
+
262
+ //this.storage.clear();
263
+
264
+ return memoryWriter.getBuffer();
265
+ }
266
+
267
+ public initializedStorageToBytes(): Uint8Array {
268
+ const memoryWriter: BytesWriter = new BytesWriter();
269
+ memoryWriter.writeStorage(this.initializedStorage);
270
+
271
+ //this.initializedStorage.clear();
272
+
273
+ return memoryWriter.getBuffer();
274
+ }
275
+
276
+ private purgeMemory(): void {
277
+ this.storage.clear();
278
+ this.initializedStorage.clear();
279
+
280
+ this.events = [];
281
+
282
+ this.externalCallsResponse.clear();
283
+ this.externalCalls.clear();
284
+ }
285
+
286
+ private requireInitialStorage(address: Address, pointerHash: u256, defaultValue: u256): void {
287
+ if (!this.initializedStorage.has(address)) {
288
+ this.initializedStorage.set(address, new Map<u256, MemorySlotData<u256>>());
289
+ }
290
+
291
+ //load(pointerHash);
292
+
293
+ const storage = this.initializedStorage.get(address);
294
+ storage.set(pointerHash, defaultValue);
295
+ }
296
+
297
+ private getExternalCallResponse(
298
+ destinationContract: Address,
299
+ index: i32,
300
+ ): Potential<Uint8Array> {
301
+ if (!this.externalCallsResponse.has(destinationContract)) {
302
+ this.externalCallsResponse.set(destinationContract, []);
303
+ }
304
+
305
+ const externalCallsResponse = this.externalCallsResponse.get(destinationContract);
306
+ return externalCallsResponse[index] || null;
307
+ }
308
+
309
+ private error(msg: string): Error {
310
+ return new Error(`${BlockchainEnvironment.runtimeException}: ${msg}`);
311
+ }
312
+
313
+ private _internalSetStorageAt(
314
+ address: Address,
315
+ pointerHash: u256,
316
+ value: MemorySlotData<u256>,
317
+ ): void {
318
+ const storage: PointerStorage = this.storage.get(address);
319
+ const keys: u256[] = storage.keys();
320
+
321
+ // Delete the old value, there is a bug with u256 and maps.
322
+ for (let i = 0; i < keys.length; i++) {
323
+ const key = keys[i];
324
+
325
+ if (u256.eq(key, pointerHash)) {
326
+ storage.delete(key);
327
+ }
328
+ }
329
+
330
+ //store(pointerHash, value);
331
+
332
+ storage.set(pointerHash, value);
333
+ }
334
+
335
+ private ensureStorageAtAddress(address: Address): void {
336
+ if (!this.storage.has(address)) {
337
+ this.storage.set(address, new Map<u256, MemorySlotData<u256>>());
338
+ }
339
+ }
340
+
341
+ private hasPointerStorageHash(storage: PointerStorage, pointer: MemorySlotPointer): bool {
342
+ const keys = storage.keys();
343
+
344
+ for (let i = 0; i < keys.length; i++) {
345
+ const key = keys[i];
346
+
347
+ if (u256.eq(key, pointer)) {
348
+ return true;
349
+ }
350
+ }
351
+
352
+ return false;
353
+ }
354
+
355
+ private ensureStorageAtPointer(
356
+ address: Address,
357
+ pointerHash: MemorySlotPointer,
358
+ defaultValue: MemorySlotData<u256>,
359
+ ): void {
360
+ if (!this.storage.has(address)) {
361
+ throw this.error(`Storage slot not found for address ${address}`);
362
+ }
363
+
364
+ // !!! -- IMPORTANT -- !!!. We have to tell the indexer that we need this storage slot to continue even if it's already defined.
365
+ this.requireInitialStorage(address, pointerHash, defaultValue);
366
+
367
+ const storage: PointerStorage = this.storage.get(address);
368
+ if (!this.hasPointerStorageHash(storage, pointerHash)) {
369
+ this._internalSetStorageAt(address, pointerHash, defaultValue);
370
+ }
371
+ }
372
+ }
@@ -0,0 +1,18 @@
1
+ import { u256 } from "as-bignum/assembly/integer/u256";
2
+ import { Address } from "../types/Address";
3
+
4
+ // @ts-ignore
5
+ @external('env', 'load')
6
+ export declare function load(pointer: u256): u256;
7
+
8
+ // @ts-ignore
9
+ @external('env', 'store')
10
+ export declare function store(pointer: u256, value: u256): void;
11
+
12
+ // @ts-ignore
13
+ @external('env', 'deploy')
14
+ export declare function deploy(bytecode: Uint8Array): Address;
15
+
16
+ // @ts-ignore
17
+ @external('env', 'call')
18
+ export declare function call(callee: Address, calldata: Uint8Array): Uint8Array;
@@ -0,0 +1,3 @@
1
+ import { BlockchainEnvironment } from './BTCEnvironment';
2
+
3
+ export const Blockchain: BlockchainEnvironment = new BlockchainEnvironment();
@@ -0,0 +1,27 @@
1
+ import { BytesWriter } from '../buffer/BytesWriter';
2
+
3
+ export const MAX_EVENT_DATA_SIZE: u32 = 256; // 256 bytes max per event.
4
+ export const MAX_EVENTS: u8 = 8; // 8 events max per transactions.
5
+
6
+ export abstract class NetEvent {
7
+ protected constructor(
8
+ public readonly eventType: string,
9
+ protected data: BytesWriter,
10
+ ) {}
11
+
12
+ public get length(): u32 {
13
+ return this.data.bufferLength();
14
+ }
15
+
16
+ public getEventDataSelector(): u64 {
17
+ return this.data.getSelectorDataType();
18
+ }
19
+
20
+ public getEventData(): Uint8Array {
21
+ if (this.data.bufferLength() > MAX_EVENT_DATA_SIZE) {
22
+ throw new Error('Event data length exceeds maximum length.');
23
+ }
24
+
25
+ return this.data.getBuffer();
26
+ }
27
+ }
@@ -0,0 +1,16 @@
1
+ import { u256 } from 'as-bignum/assembly';
2
+ import { Address } from '../../types/Address';
3
+ import { NetEvent } from '../NetEvent';
4
+ import { BytesWriter } from '../../buffer/BytesWriter';
5
+
6
+ @final
7
+ export class ApproveEvent extends NetEvent {
8
+ constructor(owner: Address, spender: Address, value: u256) {
9
+ const data: BytesWriter = new BytesWriter(1, true);
10
+ data.writeAddress(owner);
11
+ data.writeAddress(spender);
12
+ data.writeU256(value);
13
+
14
+ super('Approve', data);
15
+ }
16
+ }
@@ -0,0 +1,13 @@
1
+ import { u256 } from 'as-bignum/assembly';
2
+ import { NetEvent } from '../NetEvent';
3
+ import { BytesWriter } from '../../buffer/BytesWriter';
4
+
5
+ @final
6
+ export class BurnEvent extends NetEvent {
7
+ constructor(amount: u256) {
8
+ const data: BytesWriter = new BytesWriter(1, true);
9
+ data.writeU256(amount);
10
+
11
+ super('Burn', data);
12
+ }
13
+ }
@@ -0,0 +1,13 @@
1
+ import { u256 } from 'as-bignum/assembly';
2
+ import { NetEvent } from '../NetEvent';
3
+ import { BytesWriter } from '../../buffer/BytesWriter';
4
+
5
+ @final
6
+ export class ClaimEvent extends NetEvent {
7
+ constructor(amount: u256) {
8
+ const data: BytesWriter = new BytesWriter(1, true);
9
+ data.writeU256(amount);
10
+
11
+ super('Claim', data);
12
+ }
13
+ }
@@ -0,0 +1,15 @@
1
+ import { u256 } from 'as-bignum/assembly';
2
+ import { NetEvent } from '../NetEvent';
3
+ import { BytesWriter } from '../../buffer/BytesWriter';
4
+ import { Address } from '../../types/Address';
5
+
6
+ @final
7
+ export class MintEvent extends NetEvent {
8
+ constructor(address: Address, amount: u256) {
9
+ const data: BytesWriter = new BytesWriter(1, true);
10
+ data.writeAddress(address);
11
+ data.writeU256(amount);
12
+
13
+ super('Mint', data);
14
+ }
15
+ }
@@ -0,0 +1,13 @@
1
+ import { u256 } from 'as-bignum/assembly';
2
+ import { NetEvent } from '../NetEvent';
3
+ import { BytesWriter } from '../../buffer/BytesWriter';
4
+
5
+ @final
6
+ export class StakeEvent extends NetEvent {
7
+ constructor(amount: u256) {
8
+ const data: BytesWriter = new BytesWriter(1, true);
9
+ data.writeU256(amount);
10
+
11
+ super('Stake', data);
12
+ }
13
+ }
@@ -0,0 +1,16 @@
1
+ import { u256 } from 'as-bignum/assembly';
2
+ import { NetEvent } from '../NetEvent';
3
+ import { Address } from '../../types/Address';
4
+ import { BytesWriter } from '../../buffer/BytesWriter';
5
+
6
+ @final
7
+ export class TransferEvent extends NetEvent {
8
+ constructor(from: Address, to: Address, amount: u256) {
9
+ const data: BytesWriter = new BytesWriter(1, true);
10
+ data.writeAddress(from);
11
+ data.writeAddress(to);
12
+ data.writeU256(amount);
13
+
14
+ super('Transfer', data);
15
+ }
16
+ }
@@ -0,0 +1,13 @@
1
+ import { u256 } from 'as-bignum/assembly';
2
+ import { NetEvent } from '../NetEvent';
3
+ import { BytesWriter } from '../../buffer/BytesWriter';
4
+
5
+ @final
6
+ export class UnstakeEvent extends NetEvent {
7
+ constructor(amount: u256) {
8
+ const data: BytesWriter = new BytesWriter(1, true);
9
+ data.writeU256(amount);
10
+
11
+ super('Unstake', data);
12
+ }
13
+ }
@@ -0,0 +1,7 @@
1
+ export * from './ApproveEvent';
2
+ export * from './BurnEvent';
3
+ export * from './MintEvent';
4
+ export * from './TransferEvent';
5
+ export * from './ClaimEvent';
6
+ export * from './StakeEvent';
7
+ export * from './UnstakeEvent';
@@ -0,0 +1,57 @@
1
+ import { Calldata } from '../universal/ABIRegistry';
2
+ import { Blockchain } from '../env';
3
+ import { Selector } from '../math/abi';
4
+ import { BytesWriter } from '../buffer/BytesWriter';
5
+ import { BytesReader } from '../buffer/BytesReader';
6
+
7
+ export function readMethod(method: Selector, data: Uint8Array): Uint8Array {
8
+ const calldata: Calldata = new BytesReader(data);
9
+ const result: BytesWriter = Blockchain.contract.callMethod(method, calldata);
10
+
11
+ return result.getBuffer();
12
+ }
13
+
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
+ }
22
+
23
+ export function getViewABI(): Uint8Array {
24
+ return Blockchain.getViewSelectors();
25
+ }
26
+
27
+ export function getMethodABI(): Uint8Array {
28
+ return Blockchain.getMethodSelectors();
29
+ }
30
+
31
+ export function getWriteMethods(): Uint8Array {
32
+ return Blockchain.getWriteMethods();
33
+ }
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
+ export function setEnvironment(data: Uint8Array): void {
56
+ Blockchain.setEnvironment(data);
57
+ }
@@ -0,0 +1,55 @@
1
+ import { Revert } from '../types/Revert';
2
+
3
+ export class Map<K, V> {
4
+ private _keys: K[] = [];
5
+ private _values: V[] = [];
6
+
7
+ public get size(): i32 {
8
+ return this._keys.length;
9
+ }
10
+
11
+ public keys(): K[] {
12
+ return this._keys;
13
+ }
14
+
15
+ public values(): V[] {
16
+ return this._values;
17
+ }
18
+
19
+ public set(key: K, value: V): void {
20
+ const index: i32 = this._keys.indexOf(key);
21
+ if (index == -1) {
22
+ this._keys.push(key);
23
+ this._values.push(value);
24
+ } else {
25
+ this._values[index] = value;
26
+ }
27
+ }
28
+
29
+ public get(key: K): V {
30
+ const index: i32 = this._keys.indexOf(key);
31
+ if (index == -1) {
32
+ throw new Revert('Key not found in map');
33
+ }
34
+ return this._values[index];
35
+ }
36
+
37
+ public has(key: K): bool {
38
+ return this._keys.includes(key);
39
+ }
40
+
41
+ public delete(key: K): bool {
42
+ const index: i32 = this._keys.indexOf(key);
43
+ if (index == -1) {
44
+ return false;
45
+ }
46
+ this._keys.splice(index, 1);
47
+ this._values.splice(index, 1);
48
+ return true;
49
+ }
50
+
51
+ public clear(): void {
52
+ this._keys = [];
53
+ this._values = [];
54
+ }
55
+ }