@btc-vision/btc-runtime 1.2.5 → 1.3.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.
- 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 +17 -23
- package/runtime/generic/AddressMap.ts +62 -0
- package/runtime/generic/Map.ts +9 -8
- package/runtime/index.ts +18 -4
- package/runtime/math/abi.ts +1 -19
- package/runtime/memory/AddressMemoryMap.ts +23 -9
- package/runtime/memory/KeyMerger.ts +13 -4
- 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/Serializable.ts +19 -9
- 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/StoredBoolean.ts +8 -5
- package/runtime/storage/StoredString.ts +12 -6
- package/runtime/storage/StoredU256.ts +51 -15
- 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.0",
|
|
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
|
|
|
@@ -3,7 +3,6 @@ import { MemorySlotPointer } from '../memory/MemorySlotPointer';
|
|
|
3
3
|
import { MemorySlotData } from '../memory/MemorySlot';
|
|
4
4
|
import { u256 } from 'as-bignum/assembly';
|
|
5
5
|
import { BytesReader } from '../buffer/BytesReader';
|
|
6
|
-
import { encodePointerHash } from '../math/abi';
|
|
7
6
|
import { BytesWriter } from '../buffer/BytesWriter';
|
|
8
7
|
import { NetEvent } from '../events/NetEvent';
|
|
9
8
|
import { Potential } from '../lang/Definitions';
|
|
@@ -34,7 +33,7 @@ export function runtimeError(msg: string): Error {
|
|
|
34
33
|
export class BlockchainEnvironment {
|
|
35
34
|
private static readonly MAX_U16: u16 = 65535;
|
|
36
35
|
|
|
37
|
-
public readonly DEAD_ADDRESS: Address =
|
|
36
|
+
public readonly DEAD_ADDRESS: Address = Address.dead();
|
|
38
37
|
|
|
39
38
|
private storage: PointerStorage = new MapU256();
|
|
40
39
|
private _selfContract: Potential<OP_NET> = null;
|
|
@@ -128,7 +127,7 @@ export class BlockchainEnvironment {
|
|
|
128
127
|
}
|
|
129
128
|
|
|
130
129
|
public call(destinationContract: Address, calldata: BytesWriter): BytesReader {
|
|
131
|
-
if (destinationContract === this.
|
|
130
|
+
if (destinationContract === this.contractAddress) {
|
|
132
131
|
throw this.error('Cannot call self');
|
|
133
132
|
}
|
|
134
133
|
|
|
@@ -163,9 +162,10 @@ export class BlockchainEnvironment {
|
|
|
163
162
|
emit(buffer.getBuffer());
|
|
164
163
|
}
|
|
165
164
|
|
|
166
|
-
public encodeVirtualAddress(virtualAddress:
|
|
167
|
-
const writer: BytesWriter = new BytesWriter(virtualAddress.
|
|
168
|
-
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);
|
|
169
169
|
|
|
170
170
|
const buffer: Uint8Array = writer.getBuffer();
|
|
171
171
|
const cb: Potential<Uint8Array> = encodeAddress(buffer);
|
|
@@ -210,11 +210,9 @@ export class BlockchainEnvironment {
|
|
|
210
210
|
}
|
|
211
211
|
|
|
212
212
|
public getStorageAt(
|
|
213
|
-
|
|
214
|
-
subPointer: MemorySlotPointer,
|
|
213
|
+
pointerHash: MemorySlotPointer,
|
|
215
214
|
defaultValue: MemorySlotData<u256>,
|
|
216
215
|
): MemorySlotData<u256> {
|
|
217
|
-
const pointerHash: MemorySlotPointer = encodePointerHash(pointer, subPointer);
|
|
218
216
|
this.ensureStorageAtPointer(pointerHash, defaultValue);
|
|
219
217
|
|
|
220
218
|
if (this.storage.has(pointerHash)) {
|
|
@@ -224,20 +222,14 @@ export class BlockchainEnvironment {
|
|
|
224
222
|
return defaultValue;
|
|
225
223
|
}
|
|
226
224
|
|
|
227
|
-
public hasStorageAt(
|
|
225
|
+
public hasStorageAt(pointerHash: MemorySlotPointer): bool {
|
|
228
226
|
// We mark zero as the default value for the storage, if something is 0, the storage slot get deleted or is non-existent
|
|
229
|
-
const val: u256 = this.getStorageAt(
|
|
227
|
+
const val: u256 = this.getStorageAt(pointerHash, u256.Zero);
|
|
230
228
|
|
|
231
229
|
return u256.ne(val, u256.Zero);
|
|
232
230
|
}
|
|
233
231
|
|
|
234
|
-
public setStorageAt(
|
|
235
|
-
pointer: u16,
|
|
236
|
-
keyPointer: MemorySlotPointer,
|
|
237
|
-
value: MemorySlotData<u256>,
|
|
238
|
-
): void {
|
|
239
|
-
const pointerHash: u256 = encodePointerHash(pointer, keyPointer);
|
|
240
|
-
|
|
232
|
+
public setStorageAt(pointerHash: MemorySlotPointer, value: MemorySlotData<u256>): void {
|
|
241
233
|
this._internalSetStorageAt(pointerHash, value);
|
|
242
234
|
}
|
|
243
235
|
|
|
@@ -288,12 +280,14 @@ export class BlockchainEnvironment {
|
|
|
288
280
|
pointerHash: MemorySlotPointer,
|
|
289
281
|
defaultValue: MemorySlotData<u256>,
|
|
290
282
|
): void {
|
|
291
|
-
if (
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
}
|
|
283
|
+
if (this.hasPointerStorageHash(pointerHash)) {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
295
286
|
|
|
296
|
-
|
|
287
|
+
if (u256.eq(defaultValue, u256.Zero)) {
|
|
288
|
+
return;
|
|
297
289
|
}
|
|
290
|
+
|
|
291
|
+
this._internalSetStorageAt(pointerHash, defaultValue);
|
|
298
292
|
}
|
|
299
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';
|
package/runtime/math/abi.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// SO IN TYPESCRIPT, WE CAN NOT USE TWO METHOD WITH THE SAME NAME. SO NOT ADDING THE TYPE TO THE HASH IS A DESIGN CHOICE.
|
|
2
2
|
import { bytes32, bytes4 } from './bytes';
|
|
3
3
|
import { MemorySlotPointer } from '../memory/MemorySlotPointer';
|
|
4
|
-
import { u256 } from 'as-bignum/assembly';
|
|
5
4
|
import { Sha256 } from './sha256';
|
|
6
5
|
|
|
7
6
|
export type Selector = u32;
|
|
@@ -13,25 +12,8 @@ export function encodeSelector(name: string): Selector {
|
|
|
13
12
|
return bytes4(hash);
|
|
14
13
|
}
|
|
15
14
|
|
|
16
|
-
export function encodePointer(
|
|
17
|
-
const typed = Uint8Array.wrap(String.UTF8.encode(str));
|
|
15
|
+
export function encodePointer(typed: Uint8Array): MemorySlotPointer {
|
|
18
16
|
const hash = Sha256.hash(typed);
|
|
19
17
|
|
|
20
18
|
return bytes32(hash);
|
|
21
19
|
}
|
|
22
|
-
|
|
23
|
-
export function encodePointerHash(pointer: u16, sub: u256): MemorySlotPointer {
|
|
24
|
-
const finalBuffer: Uint8Array = new Uint8Array(34);
|
|
25
|
-
const mergedKey: u8[] = [u8(pointer & u16(0xff)), u8((pointer >> u16(8)) & u16(0xff))];
|
|
26
|
-
|
|
27
|
-
for (let i: i32 = 0; i < mergedKey.length; i++) {
|
|
28
|
-
finalBuffer[i] = mergedKey[i];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const subKey = sub.toUint8Array();
|
|
32
|
-
for (let i: i32 = 0; i < subKey.length; i++) {
|
|
33
|
-
finalBuffer[mergedKey.length + i] = subKey[i];
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return bytes32(Sha256.hash(finalBuffer));
|
|
37
|
-
}
|