@btc-vision/btc-runtime 1.2.6 → 1.3.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/README.md +73 -37
- package/package.json +7 -4
- package/runtime/buffer/BytesReader.ts +11 -6
- package/runtime/buffer/BytesWriter.ts +9 -17
- package/runtime/contracts/DeployableOP_20.ts +4 -16
- package/runtime/contracts/OP_NET.ts +2 -2
- package/runtime/contracts/interfaces/IOP_20.ts +0 -2
- package/runtime/env/BlockchainEnvironment.ts +13 -10
- package/runtime/generic/AddressMap.ts +62 -0
- package/runtime/generic/Map.ts +9 -8
- package/runtime/index.ts +18 -4
- package/runtime/memory/AddressMemoryMap.ts +8 -7
- package/runtime/memory/MultiAddressMemoryMap.ts +7 -7
- package/runtime/memory/MultiStringMemoryMap.ts +62 -0
- package/runtime/memory/StringMemoryMap.ts +57 -0
- package/runtime/memory/Uint8ArrayMerger.ts +67 -0
- package/runtime/secp256k1/ECPoint.ts +121 -0
- package/runtime/storage/StorageBacked.ts +5 -0
- package/runtime/storage/StorageLayout.ts +7 -0
- package/runtime/storage/StorageSlot.ts +106 -0
- package/runtime/storage/StorageStruct.ts +23 -0
- package/runtime/storage/StorageValue.ts +36 -0
- package/runtime/storage/StoredAddress.ts +47 -0
- package/runtime/storage/StoredU256.ts +5 -0
- package/runtime/tests/assert.ts +6 -3
- package/runtime/tests/env.ts +2 -2
- package/runtime/tests/tests.ts +18 -16
- package/runtime/types/Address.ts +121 -2
- package/runtime/types/SafeMath.ts +24 -0
- package/runtime/utils/b32.ts +243 -0
- package/runtime/utils/box.ts +126 -107
- package/runtime/utils/encodings.ts +46 -0
- package/runtime/utils/hex.ts +50 -47
- package/runtime/utils/index.ts +3 -2
package/README.md
CHANGED
|
@@ -42,17 +42,17 @@ execution while integrating deeply with Bitcoin's decentralized architecture.
|
|
|
42
42
|
## Installation
|
|
43
43
|
|
|
44
44
|
1. Clone the repository:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
```bash
|
|
46
|
+
git clone https://github.com/btc-vision/btc-runtime.git
|
|
47
|
+
```
|
|
48
48
|
2. Navigate to the repository directory:
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
```bash
|
|
50
|
+
cd btc-runtime
|
|
51
|
+
```
|
|
52
52
|
3. Install the necessary dependencies:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
```bash
|
|
54
|
+
npm install
|
|
55
|
+
```
|
|
56
56
|
|
|
57
57
|
## Core Concepts
|
|
58
58
|
|
|
@@ -95,59 +95,95 @@ 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 { u128, u256 } from 'as-bignum/assembly';
|
|
99
98
|
import {
|
|
100
|
-
Address,
|
|
99
|
+
Address,
|
|
100
|
+
Blockchain,
|
|
101
101
|
BytesWriter,
|
|
102
102
|
Calldata,
|
|
103
|
+
DeployableOP_20,
|
|
103
104
|
encodeSelector,
|
|
104
105
|
Map,
|
|
105
106
|
OP20InitParameters,
|
|
106
107
|
Selector,
|
|
108
|
+
AddressMap
|
|
107
109
|
} from '@btc-vision/btc-runtime/runtime';
|
|
108
|
-
import {
|
|
110
|
+
import { u128, u256 } from 'as-bignum/assembly';
|
|
109
111
|
|
|
110
112
|
@final
|
|
111
113
|
export class MyToken extends DeployableOP_20 {
|
|
112
|
-
constructor() {
|
|
114
|
+
public constructor() {
|
|
113
115
|
super();
|
|
114
116
|
|
|
115
|
-
//
|
|
117
|
+
// IMPORTANT. THIS WILL RUN EVERYTIME THE CONTRACT IS INTERACTED WITH. FOR SPECIFIC INITIALIZATION, USE "onDeployment" METHOD.
|
|
116
118
|
}
|
|
117
119
|
|
|
118
|
-
// "solidityLikeConstructor" This is a solidity-like constructor. This method will only run once.
|
|
119
|
-
public
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const decimals: u8 = 18; // Your decimals.
|
|
125
|
-
const name: string = 'MyToken'; // Your token name.
|
|
126
|
-
const symbol: string = 'TOKEN'; // Your token symbol.
|
|
120
|
+
// "solidityLikeConstructor" This is a solidity-like constructor. This method will only run once when the contract is deployed.
|
|
121
|
+
public override onDeployment(_calldata: Calldata): void {
|
|
122
|
+
const maxSupply: u256 = u128.fromString('100000000000000000000000000').toU256(); // Your max supply.
|
|
123
|
+
const decimals: u8 = 18; // Your decimals.
|
|
124
|
+
const name: string = 'MyToken'; // Your token name.
|
|
125
|
+
const symbol: string = 'TOKEN'; // Your token symbol.
|
|
127
126
|
|
|
128
|
-
|
|
127
|
+
this.instantiate(new OP20InitParameters(maxSupply, decimals, name, symbol));
|
|
129
128
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
129
|
+
// Add your logic here. Eg, minting the initial supply:
|
|
130
|
+
this._mint(Blockchain.tx.origin, maxSupply);
|
|
133
131
|
}
|
|
134
132
|
|
|
135
|
-
public override
|
|
133
|
+
public override execute(method: Selector, calldata: Calldata): BytesWriter {
|
|
136
134
|
switch (method) {
|
|
135
|
+
case encodeSelector('airdrop'):
|
|
136
|
+
return this.airdrop(calldata);
|
|
137
|
+
case encodeSelector('airdropWithAmount'):
|
|
138
|
+
return this.airdropWithAmount(calldata);
|
|
137
139
|
default:
|
|
138
|
-
return super.
|
|
140
|
+
return super.execute(method, calldata);
|
|
139
141
|
}
|
|
140
142
|
}
|
|
141
|
-
}
|
|
142
|
-
```
|
|
143
143
|
|
|
144
|
-
|
|
144
|
+
private airdrop(calldata: Calldata): BytesWriter {
|
|
145
|
+
this.onlyOwner(Blockchain.tx.sender);
|
|
145
146
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
147
|
+
const drops: AddressMap<u256> = calldata.readAddressValueTuple();
|
|
148
|
+
|
|
149
|
+
const addresses: Address[] = drops.keys();
|
|
150
|
+
for (let i: i32 = 0; i < addresses.length; i++) {
|
|
151
|
+
const address = addresses[i];
|
|
152
|
+
const amount = drops.get(address);
|
|
153
|
+
|
|
154
|
+
this._mint(address, amount, false);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const writer: BytesWriter = new BytesWriter(1);
|
|
158
|
+
writer.writeBoolean(true);
|
|
159
|
+
|
|
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.onlyOwner(Blockchain.tx.sender);
|
|
173
|
+
|
|
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);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
this._totalSupply.commit();
|
|
182
|
+
|
|
183
|
+
const writer: BytesWriter = new BytesWriter(1);
|
|
184
|
+
writer.writeBoolean(true);
|
|
185
|
+
|
|
186
|
+
return writer;
|
|
151
187
|
}
|
|
152
188
|
}
|
|
153
189
|
```
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@btc-vision/btc-runtime",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Bitcoin Smart Contract Runtime",
|
|
5
5
|
"main": "btc/index.ts",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"build": "cross-env node scripts/asc tests/tests.ts"
|
|
7
|
+
"build": "cross-env node scripts/asc tests/tests.ts",
|
|
8
|
+
"test": "ts-mocha tests/**/*.spec.ts"
|
|
8
9
|
},
|
|
9
10
|
"types": "btc/index.ts",
|
|
10
11
|
"keywords": [
|
|
@@ -20,7 +21,7 @@
|
|
|
20
21
|
"license": "MIT",
|
|
21
22
|
"devDependencies": {
|
|
22
23
|
"@types/mocha": "^10.0.8",
|
|
23
|
-
"@types/node": "^22.
|
|
24
|
+
"@types/node": "^22.7.5",
|
|
24
25
|
"assemblyscript": "^0.27.30",
|
|
25
26
|
"cross-env": "^7.0.3",
|
|
26
27
|
"fs-extra": "^11.2.0",
|
|
@@ -45,8 +46,10 @@
|
|
|
45
46
|
"@eslint/js": "^9.10.0",
|
|
46
47
|
"as-bignum": "^0.3.0",
|
|
47
48
|
"gulplog": "^2.2.0",
|
|
49
|
+
"mocha": "^10.7.3",
|
|
48
50
|
"ts-node": "^10.9.2",
|
|
49
51
|
"typescript": "^5.6.2",
|
|
50
|
-
"typescript-eslint": "^8.6.0"
|
|
52
|
+
"typescript-eslint": "^8.6.0",
|
|
53
|
+
"yargs": "^17.7.2"
|
|
51
54
|
}
|
|
52
55
|
}
|
|
@@ -2,10 +2,10 @@ import { Address, ADDRESS_BYTE_LENGTH } from '../types/Address';
|
|
|
2
2
|
import { Selector } from '../math/abi';
|
|
3
3
|
import { i128, u128, u256 } from 'as-bignum/assembly';
|
|
4
4
|
import { Revert } from '../types/Revert';
|
|
5
|
-
import { Map } from '../generic/Map';
|
|
6
5
|
import { TransactionInput, TransactionOutput } from '../env/classes/UTXO';
|
|
7
6
|
import { StaticArray } from 'staticarray';
|
|
8
7
|
import { i256 } from '../math/i256';
|
|
8
|
+
import { AddressMap } from '../generic/AddressMap';
|
|
9
9
|
|
|
10
10
|
@final
|
|
11
11
|
export class BytesReader {
|
|
@@ -107,8 +107,8 @@ export class BytesReader {
|
|
|
107
107
|
return bytes;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
public readMultiBytesAddressMap():
|
|
111
|
-
const map:
|
|
110
|
+
public readMultiBytesAddressMap(): AddressMap<Uint8Array[]> {
|
|
111
|
+
const map: AddressMap<Uint8Array[]> = new AddressMap<Uint8Array[]>();
|
|
112
112
|
const size: u8 = this.readU8();
|
|
113
113
|
|
|
114
114
|
if (size > 8) throw new Revert('Too many contract called.');
|
|
@@ -192,9 +192,9 @@ export class BytesReader {
|
|
|
192
192
|
return result;
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
-
public readAddressValueTuple():
|
|
195
|
+
public readAddressValueTuple(): AddressMap<u256> {
|
|
196
196
|
const length: u16 = this.readU16();
|
|
197
|
-
const result = new
|
|
197
|
+
const result = new AddressMap<u256>();
|
|
198
198
|
|
|
199
199
|
for (let i: u16 = 0; i < length; i++) {
|
|
200
200
|
const address = this.readAddress();
|
|
@@ -230,7 +230,12 @@ export class BytesReader {
|
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
public readAddress(): Address {
|
|
233
|
-
|
|
233
|
+
const bytes: Address = new Address();
|
|
234
|
+
for (let i: u32 = 0; i < u32(ADDRESS_BYTE_LENGTH); i++) {
|
|
235
|
+
bytes[i] = this.readU8();
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return bytes;
|
|
234
239
|
}
|
|
235
240
|
|
|
236
241
|
public getOffset(): i32 {
|
|
@@ -3,9 +3,9 @@ import { Address, ADDRESS_BYTE_LENGTH } from '../types/Address';
|
|
|
3
3
|
import { Selector } from '../math/abi';
|
|
4
4
|
import { BytesReader } from './BytesReader';
|
|
5
5
|
import { Revert } from '../types/Revert';
|
|
6
|
-
import { Map } from '../generic/Map';
|
|
7
6
|
import { ArrayBuffer } from 'arraybuffer';
|
|
8
7
|
import { i256 } from '../math/i256';
|
|
8
|
+
import { AddressMap } from '../generic/AddressMap';
|
|
9
9
|
|
|
10
10
|
@final
|
|
11
11
|
export class BytesWriter {
|
|
@@ -154,7 +154,7 @@ export class BytesWriter {
|
|
|
154
154
|
this.writeString(value);
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
public writeAddressValueTupleMap(map:
|
|
157
|
+
public writeAddressValueTupleMap(map: AddressMap<u256>): void {
|
|
158
158
|
if (map.size > 65535) throw new Revert('Map size is too large');
|
|
159
159
|
|
|
160
160
|
/*const requiredSize: u32 = 2 + map.size * (ADDRESS_BYTE_LENGTH + 32);
|
|
@@ -177,7 +177,7 @@ export class BytesWriter {
|
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
-
public writeLimitedAddressBytesMap(map:
|
|
180
|
+
public writeLimitedAddressBytesMap(map: AddressMap<Uint8Array[]>): void {
|
|
181
181
|
if (map.size > 8) throw new Revert('Too many contract called.'); // no more than 8 different contracts.
|
|
182
182
|
|
|
183
183
|
/*let requiredSize: u32 = 1 + (map.size * ADDRESS_BYTE_LENGTH + 1);
|
|
@@ -269,22 +269,14 @@ export class BytesWriter {
|
|
|
269
269
|
return value1 < value2 ? value1 : value2;
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
-
private fromAddress(
|
|
273
|
-
if (
|
|
274
|
-
throw new Revert(
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const length: i32 = this.min(value.length + 1, ADDRESS_BYTE_LENGTH);
|
|
278
|
-
const bytes: Uint8Array = new Uint8Array(length);
|
|
279
|
-
for (let i: i32 = 0; i < value.length; i++) {
|
|
280
|
-
bytes[i] = value.charCodeAt(i);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
if (value.length < ADDRESS_BYTE_LENGTH) {
|
|
284
|
-
bytes[value.length] = 0;
|
|
272
|
+
private fromAddress(pubKey: Address): Uint8Array {
|
|
273
|
+
if (pubKey.byteLength > ADDRESS_BYTE_LENGTH) {
|
|
274
|
+
throw new Revert(
|
|
275
|
+
`Address is too long ${pubKey.byteLength} > ${ADDRESS_BYTE_LENGTH} bytes`,
|
|
276
|
+
);
|
|
285
277
|
}
|
|
286
278
|
|
|
287
|
-
return
|
|
279
|
+
return pubKey;
|
|
288
280
|
}
|
|
289
281
|
|
|
290
282
|
private resize(size: u32): void {
|
|
@@ -12,10 +12,10 @@ import { Address } from '../types/Address';
|
|
|
12
12
|
import { Revert } from '../types/Revert';
|
|
13
13
|
import { SafeMath } from '../types/SafeMath';
|
|
14
14
|
|
|
15
|
+
import { Calldata } from '../types';
|
|
15
16
|
import { IOP_20 } from './interfaces/IOP_20';
|
|
16
17
|
import { OP20InitParameters } from './interfaces/OP20InitParameters';
|
|
17
18
|
import { OP_NET } from './OP_NET';
|
|
18
|
-
import { Calldata } from '../types';
|
|
19
19
|
|
|
20
20
|
const maxSupplyPointer: u16 = Blockchain.nextPointer;
|
|
21
21
|
const decimalsPointer: u16 = Blockchain.nextPointer;
|
|
@@ -27,7 +27,7 @@ const balanceOfMapPointer: u16 = Blockchain.nextPointer;
|
|
|
27
27
|
|
|
28
28
|
export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
|
|
29
29
|
protected readonly allowanceMap: MultiAddressMemoryMap<Address, Address, MemorySlotData<u256>>;
|
|
30
|
-
protected readonly balanceOfMap: AddressMemoryMap<
|
|
30
|
+
protected readonly balanceOfMap: AddressMemoryMap<MemorySlotData<u256>>;
|
|
31
31
|
|
|
32
32
|
protected readonly _maxSupply: StoredU256;
|
|
33
33
|
protected readonly _decimals: StoredU256;
|
|
@@ -42,7 +42,7 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
|
|
|
42
42
|
u256.Zero,
|
|
43
43
|
);
|
|
44
44
|
|
|
45
|
-
this.balanceOfMap = new AddressMemoryMap<
|
|
45
|
+
this.balanceOfMap = new AddressMemoryMap<MemorySlotData<u256>>(
|
|
46
46
|
balanceOfMapPointer,
|
|
47
47
|
u256.Zero,
|
|
48
48
|
);
|
|
@@ -150,15 +150,6 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
|
|
|
150
150
|
return response;
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
public mint(callData: Calldata): BytesWriter {
|
|
154
|
-
const response = new BytesWriter(1);
|
|
155
|
-
const resp = this._mint(callData.readAddress(), callData.readU256());
|
|
156
|
-
|
|
157
|
-
response.writeBoolean(resp);
|
|
158
|
-
|
|
159
|
-
return response;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
153
|
public transfer(callData: Calldata): BytesWriter {
|
|
163
154
|
const response = new BytesWriter(1);
|
|
164
155
|
const resp = this._transfer(callData.readAddress(), callData.readU256());
|
|
@@ -213,8 +204,6 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
|
|
|
213
204
|
return this.balanceOf(calldata);
|
|
214
205
|
case encodeSelector('burn'):
|
|
215
206
|
return this.burn(calldata);
|
|
216
|
-
case encodeSelector('mint'):
|
|
217
|
-
return this.mint(calldata);
|
|
218
207
|
case encodeSelector('transfer'):
|
|
219
208
|
return this.transfer(calldata);
|
|
220
209
|
case encodeSelector('transferFrom'):
|
|
@@ -297,7 +286,7 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
|
|
|
297
286
|
return true;
|
|
298
287
|
}
|
|
299
288
|
|
|
300
|
-
protected _transfer(to:
|
|
289
|
+
protected _transfer(to: Address, value: u256): boolean {
|
|
301
290
|
const sender = Blockchain.tx.sender;
|
|
302
291
|
|
|
303
292
|
if (!this.balanceOfMap.has(sender)) throw new Revert();
|
|
@@ -317,7 +306,6 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
|
|
|
317
306
|
const newToBalance: u256 = SafeMath.add(toBalance, value);
|
|
318
307
|
|
|
319
308
|
this.balanceOfMap.set(to, newToBalance);
|
|
320
|
-
|
|
321
309
|
this.createTransferEvent(sender, to, value);
|
|
322
310
|
|
|
323
311
|
return true;
|
|
@@ -8,11 +8,11 @@ import { MAX_EVENT_DATA_SIZE, NetEvent } from '../events/NetEvent';
|
|
|
8
8
|
import { Calldata } from '../types';
|
|
9
9
|
|
|
10
10
|
export class OP_NET implements IBTC {
|
|
11
|
-
public get address():
|
|
11
|
+
public get address(): Address {
|
|
12
12
|
return Blockchain.contractAddress;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
public get owner():
|
|
15
|
+
public get owner(): Address {
|
|
16
16
|
return Blockchain.owner;
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -33,7 +33,7 @@ export function runtimeError(msg: string): Error {
|
|
|
33
33
|
export class BlockchainEnvironment {
|
|
34
34
|
private static readonly MAX_U16: u16 = 65535;
|
|
35
35
|
|
|
36
|
-
public readonly DEAD_ADDRESS: Address =
|
|
36
|
+
public readonly DEAD_ADDRESS: Address = Address.dead();
|
|
37
37
|
|
|
38
38
|
private storage: PointerStorage = new MapU256();
|
|
39
39
|
private _selfContract: Potential<OP_NET> = null;
|
|
@@ -127,7 +127,7 @@ export class BlockchainEnvironment {
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
public call(destinationContract: Address, calldata: BytesWriter): BytesReader {
|
|
130
|
-
if (destinationContract === this.
|
|
130
|
+
if (destinationContract === this.contractAddress) {
|
|
131
131
|
throw this.error('Cannot call self');
|
|
132
132
|
}
|
|
133
133
|
|
|
@@ -162,9 +162,10 @@ export class BlockchainEnvironment {
|
|
|
162
162
|
emit(buffer.getBuffer());
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
public encodeVirtualAddress(virtualAddress:
|
|
166
|
-
const writer: BytesWriter = new BytesWriter(virtualAddress.
|
|
167
|
-
writer.
|
|
165
|
+
public encodeVirtualAddress(virtualAddress: u8[]): Address {
|
|
166
|
+
const writer: BytesWriter = new BytesWriter(virtualAddress.length + 4);
|
|
167
|
+
writer.writeU32(virtualAddress.length);
|
|
168
|
+
writer.writeBytesU8Array(virtualAddress);
|
|
168
169
|
|
|
169
170
|
const buffer: Uint8Array = writer.getBuffer();
|
|
170
171
|
const cb: Potential<Uint8Array> = encodeAddress(buffer);
|
|
@@ -279,12 +280,14 @@ export class BlockchainEnvironment {
|
|
|
279
280
|
pointerHash: MemorySlotPointer,
|
|
280
281
|
defaultValue: MemorySlotData<u256>,
|
|
281
282
|
): void {
|
|
282
|
-
if (
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
283
|
+
if (this.hasPointerStorageHash(pointerHash)) {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
286
|
|
|
287
|
-
|
|
287
|
+
if (u256.eq(defaultValue, u256.Zero)) {
|
|
288
|
+
return;
|
|
288
289
|
}
|
|
290
|
+
|
|
291
|
+
this._internalSetStorageAt(pointerHash, defaultValue);
|
|
289
292
|
}
|
|
290
293
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Revert } from '../types/Revert';
|
|
2
|
+
import { Map } from './Map';
|
|
3
|
+
import { Address } from '../types/Address';
|
|
4
|
+
|
|
5
|
+
@final
|
|
6
|
+
export class AddressMap<V> extends Map<Address, V> {
|
|
7
|
+
public set(key: Address, value: V): void {
|
|
8
|
+
const index: i32 = this._keys.indexOf(key);
|
|
9
|
+
if (index == -1) {
|
|
10
|
+
this._keys.push(key);
|
|
11
|
+
this._values.push(value);
|
|
12
|
+
} else {
|
|
13
|
+
this._values[index] = value;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public indexOf(address: Address): i32 {
|
|
18
|
+
for (let i: i32 = 0; i < this._keys.length; i++) {
|
|
19
|
+
const key = this._keys[i];
|
|
20
|
+
|
|
21
|
+
if (address.equals(key)) {
|
|
22
|
+
return i;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return -1;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public has(key: Address): bool {
|
|
30
|
+
for (let i: i32 = 0; i < this._keys.length; i++) {
|
|
31
|
+
if (key.equals(this._keys[i])) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public get(key: Address): V {
|
|
40
|
+
const index: i32 = this.indexOf(key);
|
|
41
|
+
if (index == -1) {
|
|
42
|
+
throw new Revert('Key not found in map');
|
|
43
|
+
}
|
|
44
|
+
return this._values[index];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public delete(key: Address): bool {
|
|
48
|
+
const index: i32 = this.indexOf(key);
|
|
49
|
+
if (index == -1) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
this._keys.splice(index, 1);
|
|
54
|
+
this._values.splice(index, 1);
|
|
55
|
+
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public getAddresses(): Address[] {
|
|
60
|
+
return this._keys;
|
|
61
|
+
}
|
|
62
|
+
}
|
package/runtime/generic/Map.ts
CHANGED
|
@@ -27,7 +27,13 @@ export class Map<K, V> {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
public indexOf(key: K): i32 {
|
|
30
|
-
|
|
30
|
+
for (let i: i32 = 0; i < this._keys.length; i++) {
|
|
31
|
+
if (this._keys[i] == key) {
|
|
32
|
+
return i;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return -1;
|
|
31
37
|
}
|
|
32
38
|
|
|
33
39
|
public get(key: K): V {
|
|
@@ -39,13 +45,7 @@ export class Map<K, V> {
|
|
|
39
45
|
}
|
|
40
46
|
|
|
41
47
|
public has(key: K): bool {
|
|
42
|
-
|
|
43
|
-
if (this._keys[i] == key) {
|
|
44
|
-
return true;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return false;
|
|
48
|
+
return this.indexOf(key) != -1;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
public delete(key: K): bool {
|
|
@@ -53,6 +53,7 @@ export class Map<K, V> {
|
|
|
53
53
|
if (index == -1) {
|
|
54
54
|
return false;
|
|
55
55
|
}
|
|
56
|
+
|
|
56
57
|
this._keys.splice(index, 1);
|
|
57
58
|
this._values.splice(index, 1);
|
|
58
59
|
return true;
|
package/runtime/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ export * from './buffer/BytesReader';
|
|
|
13
13
|
export * from './buffer/BytesWriter';
|
|
14
14
|
|
|
15
15
|
/** Interfaces */
|
|
16
|
+
export * from './interfaces/IBTC';
|
|
16
17
|
export * from './interfaces/DeployContractResponse';
|
|
17
18
|
|
|
18
19
|
/** Events */
|
|
@@ -24,9 +25,12 @@ export * from './env/classes/UTXO';
|
|
|
24
25
|
export * from './env/classes/Transaction';
|
|
25
26
|
export * from './env/classes/Block';
|
|
26
27
|
|
|
27
|
-
/**
|
|
28
|
+
/** Maps */
|
|
28
29
|
export * from './generic/Map';
|
|
29
|
-
export * from './
|
|
30
|
+
export * from './generic/MapU256';
|
|
31
|
+
export * from './generic/AddressMap';
|
|
32
|
+
|
|
33
|
+
/** Types */
|
|
30
34
|
export * from './types';
|
|
31
35
|
|
|
32
36
|
/** Definitions */
|
|
@@ -42,24 +46,34 @@ export * from './math/cyrb53';
|
|
|
42
46
|
export * from './math/sha256';
|
|
43
47
|
export * from './math/rnd';
|
|
44
48
|
export * from './math/i256';
|
|
49
|
+
export * from './secp256k1/ECPoint';
|
|
45
50
|
|
|
46
51
|
/** Memory */
|
|
47
52
|
export * from './memory/AddressMemoryMap';
|
|
53
|
+
export * from './memory/StringMemoryMap';
|
|
54
|
+
export * from './memory/MultiStringMemoryMap';
|
|
48
55
|
export * from './memory/MemorySlot';
|
|
49
56
|
export * from './memory/MemorySlotPointer';
|
|
50
57
|
export * from './memory/KeyMerger';
|
|
51
58
|
export * from './memory/MultiAddressMemoryMap';
|
|
59
|
+
export * from './memory/Uint8ArrayMerger';
|
|
52
60
|
|
|
53
61
|
/** Storage */
|
|
54
62
|
export * from './storage/StoredU256';
|
|
55
63
|
export * from './storage/StoredString';
|
|
64
|
+
export * from './storage/StoredAddress';
|
|
56
65
|
export * from './storage/StoredBoolean';
|
|
57
66
|
export * from './storage/Serializable';
|
|
58
67
|
|
|
68
|
+
export * from './storage/StorageBacked';
|
|
69
|
+
export * from './storage/StorageSlot';
|
|
70
|
+
export * from './storage/StorageStruct';
|
|
71
|
+
export * from './storage/StorageLayout';
|
|
72
|
+
export * from './storage/StorageValue';
|
|
73
|
+
|
|
59
74
|
/** Shared libraries */
|
|
60
75
|
export * from './shared-libraries/TransferHelper';
|
|
61
76
|
export * from './shared-libraries/OP20Utils';
|
|
62
77
|
|
|
63
78
|
/** Utils */
|
|
64
|
-
export * from './utils
|
|
65
|
-
export * from './utils/box';
|
|
79
|
+
export * from './utils';
|
|
@@ -4,9 +4,10 @@ import { encodePointer } from '../math/abi';
|
|
|
4
4
|
import { MemorySlotData } from './MemorySlot';
|
|
5
5
|
import { u256 } from 'as-bignum/assembly';
|
|
6
6
|
import { BytesWriter } from '../buffer/BytesWriter';
|
|
7
|
+
import { Address } from '../types/Address';
|
|
7
8
|
|
|
8
9
|
@final
|
|
9
|
-
export class AddressMemoryMap<
|
|
10
|
+
export class AddressMemoryMap<V extends MemorySlotData<u256>> {
|
|
10
11
|
public pointer: u16;
|
|
11
12
|
|
|
12
13
|
constructor(
|
|
@@ -16,27 +17,27 @@ export class AddressMemoryMap<K extends string, V extends MemorySlotData<u256>>
|
|
|
16
17
|
this.pointer = pointer;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
public set(key:
|
|
20
|
+
public set(key: Address, value: V): this {
|
|
20
21
|
const keyHash: MemorySlotPointer = this.encodePointer(key);
|
|
21
22
|
Blockchain.setStorageAt(keyHash, value);
|
|
22
23
|
|
|
23
24
|
return this;
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
public get(key:
|
|
27
|
+
public get(key: Address): MemorySlotData<u256> {
|
|
27
28
|
const keyHash: MemorySlotPointer = this.encodePointer(key);
|
|
28
29
|
|
|
29
30
|
return Blockchain.getStorageAt(keyHash, this.defaultValue);
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
public has(key:
|
|
33
|
+
public has(key: Address): bool {
|
|
33
34
|
const keyHash: MemorySlotPointer = this.encodePointer(key);
|
|
34
35
|
|
|
35
36
|
return Blockchain.hasStorageAt(keyHash);
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
@unsafe
|
|
39
|
-
public delete(key:
|
|
40
|
+
public delete(key: Address): bool {
|
|
40
41
|
this.set(key, this.defaultValue);
|
|
41
42
|
|
|
42
43
|
return true;
|
|
@@ -47,10 +48,10 @@ export class AddressMemoryMap<K extends string, V extends MemorySlotData<u256>>
|
|
|
47
48
|
throw new Error('Method not implemented.');
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
private encodePointer(key:
|
|
51
|
+
private encodePointer(key: Address): MemorySlotPointer {
|
|
51
52
|
const writer = new BytesWriter(key.length + 2);
|
|
52
53
|
writer.writeU16(this.pointer);
|
|
53
|
-
writer.
|
|
54
|
+
writer.writeBytes(key);
|
|
54
55
|
|
|
55
56
|
return encodePointer(writer.getBuffer());
|
|
56
57
|
}
|