@btc-vision/btc-runtime 1.8.1 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -79
- package/package.json +8 -8
- package/runtime/buffer/BytesReader.ts +36 -45
- package/runtime/buffer/BytesWriter.ts +60 -20
- package/runtime/contracts/OP20.ts +636 -0
- package/runtime/contracts/OP_NET.ts +3 -3
- package/runtime/contracts/interfaces/IOP20.ts +15 -0
- package/runtime/contracts/interfaces/OP20InitParameters.ts +3 -1
- package/runtime/env/BlockchainEnvironment.ts +28 -7
- package/runtime/env/classes/Transaction.ts +1 -2
- package/runtime/env/global.ts +171 -23
- package/runtime/events/NetEvent.ts +1 -1
- package/runtime/events/predefined/{ApproveEvent.ts → ApprovedEvent.ts} +3 -3
- package/runtime/events/predefined/{MintEvent.ts → BurnedEvent.ts} +5 -6
- package/runtime/events/predefined/{TransferEvent.ts → MintedEvent.ts} +5 -6
- package/runtime/events/predefined/TransferredEvent.ts +18 -0
- package/runtime/events/predefined/index.ts +4 -4
- package/runtime/exports/index.ts +4 -4
- package/runtime/index.ts +13 -3
- package/runtime/math/abi.ts +10 -6
- package/runtime/math/bytes.ts +5 -17
- package/runtime/memory/AddressMemoryMap.ts +4 -5
- package/runtime/memory/KeyMerger.ts +7 -7
- package/runtime/memory/MapOfMap.ts +2 -7
- package/runtime/memory/Nested.ts +6 -8
- package/runtime/nested/PointerManager.ts +1 -1
- package/runtime/nested/codecs/AddressCodec.ts +1 -1
- package/runtime/nested/codecs/BooleanCodec.ts +1 -1
- package/runtime/nested/codecs/Ids.ts +1 -1
- package/runtime/nested/codecs/NumericCodec.ts +1 -1
- package/runtime/nested/codecs/StringCodec.ts +1 -1
- package/runtime/nested/codecs/VariableBytesCodec.ts +3 -3
- package/runtime/nested/storage/StorageMap.ts +3 -3
- package/runtime/nested/storage/StorageSet.ts +2 -2
- package/runtime/plugins/Plugin.ts +5 -7
- package/runtime/script/Bech32.ts +369 -0
- package/runtime/script/BitcoinAddresses.ts +208 -0
- package/runtime/script/BitcoinCodec.ts +395 -0
- package/runtime/script/Networks.ts +94 -0
- package/runtime/script/Opcodes.ts +155 -0
- package/runtime/script/Script.ts +463 -0
- package/runtime/script/ScriptUtils.ts +101 -0
- package/runtime/script/Segwit.ts +185 -0
- package/runtime/script/reader/ScriptReader.ts +247 -0
- package/runtime/secp256k1/ECPoint.ts +6 -12
- package/runtime/shared-libraries/TransferHelper.ts +72 -31
- package/runtime/storage/AdvancedStoredString.ts +1 -1
- package/runtime/storage/StoredAddress.ts +1 -1
- package/runtime/storage/StoredBoolean.ts +2 -4
- package/runtime/storage/StoredString.ts +6 -3
- package/runtime/storage/StoredU256.ts +1 -3
- package/runtime/storage/StoredU32.ts +1 -6
- package/runtime/storage/StoredU64.ts +1 -4
- package/runtime/storage/arrays/StoredBooleanArray.ts +7 -12
- package/runtime/storage/arrays/StoredPackedArray.ts +3 -13
- package/runtime/storage/maps/StoredMapU256.ts +5 -5
- package/runtime/types/Address.ts +49 -39
- package/runtime/types/SafeMath.ts +19 -18
- package/runtime/types/SafeMathI128.ts +14 -13
- package/runtime/utils/hex.ts +41 -19
- package/runtime/utils/index.ts +0 -2
- package/runtime/contracts/DeployableOP_20.ts +0 -415
- package/runtime/contracts/OP_20.ts +0 -9
- package/runtime/contracts/interfaces/IOP_20.ts +0 -19
- package/runtime/events/predefined/BurnEvent.ts +0 -14
- package/runtime/utils/b32.ts +0 -243
- package/runtime/utils/box.ts +0 -134
- package/runtime/utils/encodings.ts +0 -45
- /package/{LICENSE → LICENSE.md} +0 -0
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
import { u256 } from '@btc-vision/as-bignum/assembly';
|
|
2
|
+
|
|
3
|
+
import { BytesWriter } from '../buffer/BytesWriter';
|
|
4
|
+
import { Blockchain } from '../env';
|
|
5
|
+
import { sha256, sha256String } from '../env/global';
|
|
6
|
+
import { ApprovedEvent, BurnedEvent, MintedEvent, TransferredEvent } from '../events/predefined';
|
|
7
|
+
import { EMPTY_POINTER } from '../math/bytes';
|
|
8
|
+
import { AddressMemoryMap } from '../memory/AddressMemoryMap';
|
|
9
|
+
import { MapOfMap } from '../memory/MapOfMap';
|
|
10
|
+
import { StoredString } from '../storage/StoredString';
|
|
11
|
+
import { StoredU256 } from '../storage/StoredU256';
|
|
12
|
+
import { Calldata } from '../types';
|
|
13
|
+
import { Address } from '../types/Address';
|
|
14
|
+
import { Revert } from '../types/Revert';
|
|
15
|
+
import { SafeMath } from '../types/SafeMath';
|
|
16
|
+
import {
|
|
17
|
+
ADDRESS_BYTE_LENGTH,
|
|
18
|
+
SELECTOR_BYTE_LENGTH,
|
|
19
|
+
U256_BYTE_LENGTH,
|
|
20
|
+
U32_BYTE_LENGTH,
|
|
21
|
+
U64_BYTE_LENGTH,
|
|
22
|
+
} from '../utils';
|
|
23
|
+
import { IOP20 } from './interfaces/IOP20';
|
|
24
|
+
import { OP20InitParameters } from './interfaces/OP20InitParameters';
|
|
25
|
+
import { OP_NET } from './OP_NET';
|
|
26
|
+
|
|
27
|
+
// onOP20Received(address,address,uint256,bytes)
|
|
28
|
+
const ON_OP20_RECEIVED_SELECTOR: u32 = 0xd83e7dbc;
|
|
29
|
+
|
|
30
|
+
// sha256("OP712Domain(string name,string version,bytes32 chainId,bytes32 protocolId,address verifyingContract)")
|
|
31
|
+
const OP712_DOMAIN_TYPE_HASH: u8[] = [
|
|
32
|
+
0xfe, 0xe8, 0x22, 0x92, 0x35, 0x1d, 0x1a, 0x8b, 0xab, 0x21, 0xc4, 0xef, 0xdd, 0x15, 0x7e, 0x31,
|
|
33
|
+
0x68, 0xe8, 0xf6, 0x32, 0x3a, 0xd0, 0x4c, 0xba, 0x12, 0xf7, 0x7c, 0x0b, 0xdc, 0x46, 0x22, 0x58,
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
// sha256("1")
|
|
37
|
+
const OP712_VERSION_HASH: u8[] = [
|
|
38
|
+
0x6b, 0x86, 0xb2, 0x73, 0xff, 0x34, 0xfc, 0xe1, 0x9d, 0x6b, 0x80, 0x4e, 0xff, 0x5a, 0x3f, 0x57,
|
|
39
|
+
0x47, 0xad, 0xa4, 0xea, 0xa2, 0x2f, 0x1d, 0x49, 0xc0, 0x1e, 0x52, 0xdd, 0xb7, 0x87, 0x5b, 0x4b,
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
// sha256("OP20AllowanceIncrease(address owner,address spender,uint256 amount,uint256 nonce,uint64 deadline)")
|
|
43
|
+
const ALLOWANCE_INCREASE_TYPE_HASH: u8[] = [
|
|
44
|
+
0x7e, 0x88, 0x02, 0xf1, 0xfd, 0x23, 0xe1, 0x0e, 0x0d, 0xde, 0x3f, 0x00, 0xc0, 0xaa, 0x48, 0x15,
|
|
45
|
+
0xd8, 0x85, 0xec, 0xd9, 0xcd, 0xa0, 0xdf, 0x56, 0xff, 0xa2, 0x5e, 0xcc, 0x70, 0x2d, 0x45, 0x8e,
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
// sha256("OP20AllowanceDecrease(address owner,address spender,uint256 amount,uint256 nonce,uint64 deadline)")
|
|
49
|
+
const ALLOWANCE_DECREASE_TYPE_HASH: u8[] = [
|
|
50
|
+
0x70, 0x87, 0x99, 0x34, 0x92, 0x1c, 0x2f, 0x48, 0x17, 0x78, 0x87, 0x89, 0x77, 0xd5, 0xb4, 0x5e,
|
|
51
|
+
0x2a, 0x59, 0xda, 0x1d, 0x28, 0x22, 0x41, 0xc9, 0x3f, 0xf1, 0xba, 0x6a, 0xf0, 0x98, 0xfc, 0xd0,
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
const nonceMapPointer: u16 = Blockchain.nextPointer;
|
|
55
|
+
const maxSupplyPointer: u16 = Blockchain.nextPointer;
|
|
56
|
+
const decimalsPointer: u16 = Blockchain.nextPointer;
|
|
57
|
+
const stringPointer: u16 = Blockchain.nextPointer;
|
|
58
|
+
const totalSupplyPointer: u16 = Blockchain.nextPointer;
|
|
59
|
+
const allowanceMapPointer: u16 = Blockchain.nextPointer;
|
|
60
|
+
|
|
61
|
+
const balanceOfMapPointer: u16 = Blockchain.nextPointer;
|
|
62
|
+
|
|
63
|
+
export abstract class OP20 extends OP_NET implements IOP20 {
|
|
64
|
+
protected readonly allowanceMap: MapOfMap<u256>;
|
|
65
|
+
protected readonly balanceOfMap: AddressMemoryMap;
|
|
66
|
+
|
|
67
|
+
protected readonly _maxSupply: StoredU256;
|
|
68
|
+
protected readonly _decimals: StoredU256;
|
|
69
|
+
protected readonly _name: StoredString;
|
|
70
|
+
protected readonly _icon: StoredString;
|
|
71
|
+
protected readonly _symbol: StoredString;
|
|
72
|
+
protected readonly _nonceMap: AddressMemoryMap;
|
|
73
|
+
|
|
74
|
+
public constructor() {
|
|
75
|
+
super();
|
|
76
|
+
|
|
77
|
+
this.allowanceMap = new MapOfMap<u256>(allowanceMapPointer);
|
|
78
|
+
this.balanceOfMap = new AddressMemoryMap(balanceOfMapPointer);
|
|
79
|
+
this._nonceMap = new AddressMemoryMap(nonceMapPointer);
|
|
80
|
+
|
|
81
|
+
this._totalSupply = new StoredU256(totalSupplyPointer, EMPTY_POINTER);
|
|
82
|
+
this._maxSupply = new StoredU256(maxSupplyPointer, EMPTY_POINTER);
|
|
83
|
+
this._decimals = new StoredU256(decimalsPointer, EMPTY_POINTER);
|
|
84
|
+
this._name = new StoredString(stringPointer, 0);
|
|
85
|
+
this._symbol = new StoredString(stringPointer, 1);
|
|
86
|
+
this._icon = new StoredString(stringPointer, 2);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Intentionally public for inherited classes */
|
|
90
|
+
public _totalSupply: StoredU256;
|
|
91
|
+
|
|
92
|
+
public get totalSupply(): u256 {
|
|
93
|
+
return this._totalSupply.value;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public get name(): string {
|
|
97
|
+
if (!this._name) throw new Revert('Name not set');
|
|
98
|
+
return this._name.value;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
public get symbol(): string {
|
|
102
|
+
if (!this._symbol) throw new Revert('Symbol not set');
|
|
103
|
+
return this._symbol.value;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
public get icon(): string {
|
|
107
|
+
if (!this._icon) throw new Revert('Icon not set');
|
|
108
|
+
return this._icon.value;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
public get decimals(): u8 {
|
|
112
|
+
if (!this._decimals) throw new Revert('Decimals not set');
|
|
113
|
+
return u8(this._decimals.value.toU32());
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
public get maxSupply(): u256 {
|
|
117
|
+
if (!this._maxSupply) throw new Revert('Max supply not set');
|
|
118
|
+
return this._maxSupply.value;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
public instantiate(
|
|
122
|
+
params: OP20InitParameters,
|
|
123
|
+
skipDeployerVerification: boolean = false,
|
|
124
|
+
): void {
|
|
125
|
+
if (!this._maxSupply.value.isZero()) throw new Revert('Already initialized');
|
|
126
|
+
if (!skipDeployerVerification) this.onlyDeployer(Blockchain.tx.sender);
|
|
127
|
+
if (params.decimals > 32) throw new Revert('Decimals > 32');
|
|
128
|
+
|
|
129
|
+
this._maxSupply.value = params.maxSupply;
|
|
130
|
+
this._decimals.value = u256.fromU32(u32(params.decimals));
|
|
131
|
+
this._name.value = params.name;
|
|
132
|
+
this._symbol.value = params.symbol;
|
|
133
|
+
this._icon.value = params.icon;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@method('name')
|
|
137
|
+
@returns({ name: 'name', type: ABIDataTypes.STRING })
|
|
138
|
+
public fn_name(_: Calldata): BytesWriter {
|
|
139
|
+
const w = new BytesWriter(String.UTF8.byteLength(this.name) + 4);
|
|
140
|
+
w.writeStringWithLength(this.name);
|
|
141
|
+
return w;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
@method('symbol')
|
|
145
|
+
@returns({ name: 'symbol', type: ABIDataTypes.STRING })
|
|
146
|
+
public fn_symbol(_: Calldata): BytesWriter {
|
|
147
|
+
const w = new BytesWriter(String.UTF8.byteLength(this.symbol) + 4);
|
|
148
|
+
w.writeStringWithLength(this.symbol);
|
|
149
|
+
return w;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@method('icon')
|
|
153
|
+
@returns({ name: 'icon', type: ABIDataTypes.STRING })
|
|
154
|
+
public fn_icon(_: Calldata): BytesWriter {
|
|
155
|
+
const w = new BytesWriter(String.UTF8.byteLength(this.icon) + 4);
|
|
156
|
+
w.writeStringWithLength(this.icon);
|
|
157
|
+
return w;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
@method('decimals')
|
|
161
|
+
@returns({ name: 'decimals', type: ABIDataTypes.UINT8 })
|
|
162
|
+
public fn_decimals(_: Calldata): BytesWriter {
|
|
163
|
+
const w = new BytesWriter(1);
|
|
164
|
+
w.writeU8(this.decimals);
|
|
165
|
+
return w;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
@method('totalSupply')
|
|
169
|
+
@returns({ name: 'totalSupply', type: ABIDataTypes.UINT256 })
|
|
170
|
+
public fn_totalSupply(_: Calldata): BytesWriter {
|
|
171
|
+
const w = new BytesWriter(U256_BYTE_LENGTH);
|
|
172
|
+
w.writeU256(this.totalSupply);
|
|
173
|
+
return w;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
@method('maximumSupply')
|
|
177
|
+
@returns({ name: 'maximumSupply', type: ABIDataTypes.UINT256 })
|
|
178
|
+
public fn_maximumSupply(_: Calldata): BytesWriter {
|
|
179
|
+
const w = new BytesWriter(U256_BYTE_LENGTH);
|
|
180
|
+
w.writeU256(this.maxSupply);
|
|
181
|
+
return w;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
@method()
|
|
185
|
+
@returns({ name: 'domainSeparator', type: ABIDataTypes.BYTES32 })
|
|
186
|
+
public domainSeparator(_: Calldata): BytesWriter {
|
|
187
|
+
const w = new BytesWriter(32);
|
|
188
|
+
w.writeBytes(this._buildDomainSeparator());
|
|
189
|
+
return w;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
@method({ name: 'owner', type: ABIDataTypes.ADDRESS })
|
|
193
|
+
@returns({ name: 'balance', type: ABIDataTypes.UINT256 })
|
|
194
|
+
public balanceOf(calldata: Calldata): BytesWriter {
|
|
195
|
+
const bal = this._balanceOf(calldata.readAddress());
|
|
196
|
+
const w = new BytesWriter(U256_BYTE_LENGTH);
|
|
197
|
+
w.writeU256(bal);
|
|
198
|
+
return w;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
@method({ name: 'owner', type: ABIDataTypes.ADDRESS })
|
|
202
|
+
@returns({ name: 'nonce', type: ABIDataTypes.UINT256 })
|
|
203
|
+
public nonceOf(calldata: Calldata): BytesWriter {
|
|
204
|
+
const current = this._nonceMap.get(calldata.readAddress());
|
|
205
|
+
const w = new BytesWriter(U256_BYTE_LENGTH);
|
|
206
|
+
w.writeU256(current);
|
|
207
|
+
return w;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
@method(
|
|
211
|
+
{ name: 'owner', type: ABIDataTypes.ADDRESS },
|
|
212
|
+
{ name: 'spender', type: ABIDataTypes.ADDRESS },
|
|
213
|
+
)
|
|
214
|
+
@returns({ name: 'remaining', type: ABIDataTypes.UINT256 })
|
|
215
|
+
public allowance(calldata: Calldata): BytesWriter {
|
|
216
|
+
const w = new BytesWriter(U256_BYTE_LENGTH);
|
|
217
|
+
const rem = this._allowance(calldata.readAddress(), calldata.readAddress());
|
|
218
|
+
w.writeU256(rem);
|
|
219
|
+
return w;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
@method(
|
|
223
|
+
{ name: 'to', type: ABIDataTypes.ADDRESS },
|
|
224
|
+
{ name: 'amount', type: ABIDataTypes.UINT256 },
|
|
225
|
+
{ name: 'data', type: ABIDataTypes.BYTES },
|
|
226
|
+
)
|
|
227
|
+
@emit('Transferred')
|
|
228
|
+
public safeTransfer(calldata: Calldata): BytesWriter {
|
|
229
|
+
this._transfer(
|
|
230
|
+
Blockchain.tx.sender,
|
|
231
|
+
calldata.readAddress(),
|
|
232
|
+
calldata.readU256(),
|
|
233
|
+
calldata.readBytesWithLength(),
|
|
234
|
+
);
|
|
235
|
+
return new BytesWriter(0);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
@method(
|
|
239
|
+
{ name: 'from', type: ABIDataTypes.ADDRESS },
|
|
240
|
+
{ name: 'to', type: ABIDataTypes.ADDRESS },
|
|
241
|
+
{ name: 'amount', type: ABIDataTypes.UINT256 },
|
|
242
|
+
{ name: 'data', type: ABIDataTypes.BYTES },
|
|
243
|
+
)
|
|
244
|
+
@emit('Transferred')
|
|
245
|
+
public safeTransferFrom(calldata: Calldata): BytesWriter {
|
|
246
|
+
const from = calldata.readAddress();
|
|
247
|
+
const to = calldata.readAddress();
|
|
248
|
+
const amount = calldata.readU256();
|
|
249
|
+
const data = calldata.readBytesWithLength();
|
|
250
|
+
|
|
251
|
+
this._spendAllowance(from, Blockchain.tx.sender, amount);
|
|
252
|
+
this._transfer(from, to, amount, data);
|
|
253
|
+
|
|
254
|
+
return new BytesWriter(0);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
@method(
|
|
258
|
+
{ name: 'spender', type: ABIDataTypes.ADDRESS },
|
|
259
|
+
{ name: 'amount', type: ABIDataTypes.UINT256 },
|
|
260
|
+
)
|
|
261
|
+
@emit('Approved')
|
|
262
|
+
public increaseAllowance(calldata: Calldata): BytesWriter {
|
|
263
|
+
const owner: Address = Blockchain.tx.sender;
|
|
264
|
+
const spender: Address = calldata.readAddress();
|
|
265
|
+
const amount: u256 = calldata.readU256();
|
|
266
|
+
|
|
267
|
+
this._increaseAllowance(owner, spender, amount);
|
|
268
|
+
return new BytesWriter(0);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
@method(
|
|
272
|
+
{ name: 'spender', type: ABIDataTypes.ADDRESS },
|
|
273
|
+
{ name: 'amount', type: ABIDataTypes.UINT256 },
|
|
274
|
+
)
|
|
275
|
+
@emit('Approved')
|
|
276
|
+
public decreaseAllowance(calldata: Calldata): BytesWriter {
|
|
277
|
+
const owner: Address = Blockchain.tx.sender;
|
|
278
|
+
const spender: Address = calldata.readAddress();
|
|
279
|
+
const amount: u256 = calldata.readU256();
|
|
280
|
+
|
|
281
|
+
this._decreaseAllowance(owner, spender, amount);
|
|
282
|
+
return new BytesWriter(0);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
@method(
|
|
286
|
+
{ name: 'owner', type: ABIDataTypes.ADDRESS },
|
|
287
|
+
{ name: 'spender', type: ABIDataTypes.ADDRESS },
|
|
288
|
+
{ name: 'amount', type: ABIDataTypes.UINT256 },
|
|
289
|
+
{ name: 'deadline', type: ABIDataTypes.UINT64 },
|
|
290
|
+
{ name: 'signature', type: ABIDataTypes.BYTES },
|
|
291
|
+
)
|
|
292
|
+
@emit('Approved')
|
|
293
|
+
public increaseAllowanceBySignature(calldata: Calldata): BytesWriter {
|
|
294
|
+
const owner: Address = calldata.readAddress();
|
|
295
|
+
const spender: Address = calldata.readAddress();
|
|
296
|
+
const amount: u256 = calldata.readU256();
|
|
297
|
+
const deadline: u64 = calldata.readU64();
|
|
298
|
+
const signature = calldata.readBytesWithLength();
|
|
299
|
+
|
|
300
|
+
this._increaseAllowanceBySignature(owner, spender, amount, deadline, signature);
|
|
301
|
+
return new BytesWriter(0);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
@method(
|
|
305
|
+
{ name: 'owner', type: ABIDataTypes.ADDRESS },
|
|
306
|
+
{ name: 'spender', type: ABIDataTypes.ADDRESS },
|
|
307
|
+
{ name: 'amount', type: ABIDataTypes.UINT256 },
|
|
308
|
+
{ name: 'deadline', type: ABIDataTypes.UINT64 },
|
|
309
|
+
{ name: 'signature', type: ABIDataTypes.BYTES },
|
|
310
|
+
)
|
|
311
|
+
@emit('Approved')
|
|
312
|
+
public decreaseAllowanceBySignature(calldata: Calldata): BytesWriter {
|
|
313
|
+
const owner: Address = calldata.readAddress();
|
|
314
|
+
const spender: Address = calldata.readAddress();
|
|
315
|
+
const amount: u256 = calldata.readU256();
|
|
316
|
+
const deadline: u64 = calldata.readU64();
|
|
317
|
+
const signature = calldata.readBytesWithLength();
|
|
318
|
+
|
|
319
|
+
this._decreaseAllowanceBySignature(owner, spender, amount, deadline, signature);
|
|
320
|
+
return new BytesWriter(0);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
@method({ name: 'amount', type: ABIDataTypes.UINT256 })
|
|
324
|
+
@emit('Burned')
|
|
325
|
+
public burn(calldata: Calldata): BytesWriter {
|
|
326
|
+
this._burn(Blockchain.tx.sender, calldata.readU256());
|
|
327
|
+
return new BytesWriter(0);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
@method()
|
|
331
|
+
@returns(
|
|
332
|
+
{ name: 'name', type: ABIDataTypes.STRING },
|
|
333
|
+
{ name: 'symbol', type: ABIDataTypes.STRING },
|
|
334
|
+
{ name: 'decimals', type: ABIDataTypes.UINT8 },
|
|
335
|
+
{ name: 'totalSupply', type: ABIDataTypes.UINT256 },
|
|
336
|
+
{ name: 'maximumSupply', type: ABIDataTypes.UINT256 },
|
|
337
|
+
{ name: 'icon', type: ABIDataTypes.STRING },
|
|
338
|
+
{ name: 'domainSeparator', type: ABIDataTypes.BYTES32 },
|
|
339
|
+
)
|
|
340
|
+
public metadata(_: Calldata): BytesWriter {
|
|
341
|
+
const name = this.name;
|
|
342
|
+
const symbol = this.symbol;
|
|
343
|
+
const icon = this.icon;
|
|
344
|
+
const domainSeparator = this._buildDomainSeparator();
|
|
345
|
+
|
|
346
|
+
const nameLength = String.UTF8.byteLength(name);
|
|
347
|
+
const symbolLength = String.UTF8.byteLength(symbol);
|
|
348
|
+
const iconLength = String.UTF8.byteLength(icon);
|
|
349
|
+
|
|
350
|
+
const totalSize =
|
|
351
|
+
4 + nameLength + 4 + symbolLength + 1 + U256_BYTE_LENGTH + 4 + iconLength + 32;
|
|
352
|
+
|
|
353
|
+
const w = new BytesWriter(totalSize);
|
|
354
|
+
w.writeStringWithLength(name);
|
|
355
|
+
w.writeStringWithLength(symbol);
|
|
356
|
+
w.writeU8(this.decimals);
|
|
357
|
+
w.writeU256(this.totalSupply);
|
|
358
|
+
w.writeU256(this.maxSupply);
|
|
359
|
+
w.writeStringWithLength(icon);
|
|
360
|
+
w.writeBytesWithLength(domainSeparator);
|
|
361
|
+
|
|
362
|
+
return w;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
protected _balanceOf(owner: Address): u256 {
|
|
366
|
+
if (!this.balanceOfMap.has(owner)) return u256.Zero;
|
|
367
|
+
return this.balanceOfMap.get(owner);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
protected _allowance(owner: Address, spender: Address): u256 {
|
|
371
|
+
const senderMap = this.allowanceMap.get(owner);
|
|
372
|
+
return senderMap.get(spender);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
protected _transfer(from: Address, to: Address, amount: u256, data: Uint8Array): void {
|
|
376
|
+
if (from === Address.zero() || from === Address.dead()) {
|
|
377
|
+
throw new Revert('Invalid sender');
|
|
378
|
+
}
|
|
379
|
+
if (to === Address.zero() || to === Address.dead()) {
|
|
380
|
+
throw new Revert('Invalid receiver');
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const balance: u256 = this.balanceOfMap.get(from);
|
|
384
|
+
|
|
385
|
+
if (balance < amount) {
|
|
386
|
+
throw new Revert('Insufficient balance');
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
this.balanceOfMap.set(from, SafeMath.sub(balance, amount));
|
|
390
|
+
|
|
391
|
+
const toBal: u256 = this.balanceOfMap.get(to);
|
|
392
|
+
this.balanceOfMap.set(to, SafeMath.add(toBal, amount));
|
|
393
|
+
|
|
394
|
+
if (Blockchain.isContract(to)) {
|
|
395
|
+
this._callOnOP20Received(from, to, amount, data);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
this.createTransferredEvent(Blockchain.tx.sender, from, to, amount);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
protected _spendAllowance(owner: Address, spender: Address, amount: u256): void {
|
|
402
|
+
if (owner.equals(spender)) return;
|
|
403
|
+
|
|
404
|
+
const ownerMap = this.allowanceMap.get(owner);
|
|
405
|
+
const allowed: u256 = ownerMap.get(spender);
|
|
406
|
+
|
|
407
|
+
if (allowed === u256.Max) return;
|
|
408
|
+
|
|
409
|
+
if (allowed < amount) {
|
|
410
|
+
throw new Revert('Insufficient allowance');
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
ownerMap.set(spender, SafeMath.sub(allowed, amount));
|
|
414
|
+
this.allowanceMap.set(owner, ownerMap);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
protected _callOnOP20Received(
|
|
418
|
+
from: Address,
|
|
419
|
+
to: Address,
|
|
420
|
+
amount: u256,
|
|
421
|
+
data: Uint8Array,
|
|
422
|
+
): void {
|
|
423
|
+
const operator = Blockchain.tx.sender;
|
|
424
|
+
const calldata = new BytesWriter(
|
|
425
|
+
SELECTOR_BYTE_LENGTH +
|
|
426
|
+
ADDRESS_BYTE_LENGTH * 2 +
|
|
427
|
+
U256_BYTE_LENGTH +
|
|
428
|
+
U32_BYTE_LENGTH +
|
|
429
|
+
data.length,
|
|
430
|
+
);
|
|
431
|
+
calldata.writeSelector(ON_OP20_RECEIVED_SELECTOR);
|
|
432
|
+
calldata.writeAddress(operator);
|
|
433
|
+
calldata.writeAddress(from);
|
|
434
|
+
calldata.writeU256(amount);
|
|
435
|
+
calldata.writeBytesWithLength(data);
|
|
436
|
+
|
|
437
|
+
const response = Blockchain.call(to, calldata);
|
|
438
|
+
|
|
439
|
+
if (response.byteLength < SELECTOR_BYTE_LENGTH) {
|
|
440
|
+
throw new Revert('Transfer rejected by recipient');
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const retval = response.readSelector();
|
|
444
|
+
|
|
445
|
+
if (retval !== ON_OP20_RECEIVED_SELECTOR) {
|
|
446
|
+
throw new Revert('Transfer rejected by recipient');
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
protected _increaseAllowanceBySignature(
|
|
451
|
+
owner: Address,
|
|
452
|
+
spender: Address,
|
|
453
|
+
amount: u256,
|
|
454
|
+
deadline: u64,
|
|
455
|
+
signature: Uint8Array,
|
|
456
|
+
): void {
|
|
457
|
+
this._verifySignature(
|
|
458
|
+
ALLOWANCE_INCREASE_TYPE_HASH,
|
|
459
|
+
owner,
|
|
460
|
+
spender,
|
|
461
|
+
amount,
|
|
462
|
+
deadline,
|
|
463
|
+
signature,
|
|
464
|
+
);
|
|
465
|
+
this._increaseAllowance(owner, spender, amount);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
protected _decreaseAllowanceBySignature(
|
|
469
|
+
owner: Address,
|
|
470
|
+
spender: Address,
|
|
471
|
+
amount: u256,
|
|
472
|
+
deadline: u64,
|
|
473
|
+
signature: Uint8Array,
|
|
474
|
+
): void {
|
|
475
|
+
this._verifySignature(
|
|
476
|
+
ALLOWANCE_DECREASE_TYPE_HASH,
|
|
477
|
+
owner,
|
|
478
|
+
spender,
|
|
479
|
+
amount,
|
|
480
|
+
deadline,
|
|
481
|
+
signature,
|
|
482
|
+
);
|
|
483
|
+
this._decreaseAllowance(owner, spender, amount);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
protected _verifySignature(
|
|
487
|
+
typeHash: u8[],
|
|
488
|
+
owner: Address,
|
|
489
|
+
spender: Address,
|
|
490
|
+
amount: u256,
|
|
491
|
+
deadline: u64,
|
|
492
|
+
signature: Uint8Array,
|
|
493
|
+
): void {
|
|
494
|
+
if (signature.length !== 64) {
|
|
495
|
+
throw new Revert('Invalid signature length');
|
|
496
|
+
}
|
|
497
|
+
if (Blockchain.block.number > deadline) {
|
|
498
|
+
throw new Revert('Signature expired');
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
const nonce = this._nonceMap.get(owner);
|
|
502
|
+
|
|
503
|
+
const structWriter = new BytesWriter(
|
|
504
|
+
32 + ADDRESS_BYTE_LENGTH * 2 + U256_BYTE_LENGTH * 2 + U64_BYTE_LENGTH,
|
|
505
|
+
);
|
|
506
|
+
structWriter.writeBytesU8Array(typeHash);
|
|
507
|
+
structWriter.writeAddress(owner);
|
|
508
|
+
structWriter.writeAddress(spender);
|
|
509
|
+
structWriter.writeU256(amount);
|
|
510
|
+
structWriter.writeU256(nonce);
|
|
511
|
+
structWriter.writeU64(deadline);
|
|
512
|
+
|
|
513
|
+
const structHash = sha256(structWriter.getBuffer());
|
|
514
|
+
|
|
515
|
+
const messageWriter = new BytesWriter(2 + 32 + 32);
|
|
516
|
+
messageWriter.writeU16(0x1901);
|
|
517
|
+
messageWriter.writeBytes(this._buildDomainSeparator());
|
|
518
|
+
messageWriter.writeBytes(structHash);
|
|
519
|
+
|
|
520
|
+
const hash = sha256(messageWriter.getBuffer());
|
|
521
|
+
|
|
522
|
+
if (!Blockchain.verifySchnorrSignature(owner, signature, hash)) {
|
|
523
|
+
throw new Revert('Invalid signature');
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
this._nonceMap.set(owner, SafeMath.add(nonce, u256.One));
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
protected _buildDomainSeparator(): Uint8Array {
|
|
530
|
+
const writer = new BytesWriter(32 * 5 + ADDRESS_BYTE_LENGTH);
|
|
531
|
+
writer.writeBytesU8Array(OP712_DOMAIN_TYPE_HASH);
|
|
532
|
+
writer.writeBytes(sha256String(this.name));
|
|
533
|
+
writer.writeBytesU8Array(OP712_VERSION_HASH);
|
|
534
|
+
writer.writeBytes(Blockchain.chainId);
|
|
535
|
+
writer.writeBytes(Blockchain.protocolId);
|
|
536
|
+
writer.writeAddress(this.address);
|
|
537
|
+
|
|
538
|
+
return sha256(writer.getBuffer());
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
protected _increaseAllowance(owner: Address, spender: Address, amount: u256): void {
|
|
542
|
+
if (owner === Address.zero() || owner === Address.dead()) {
|
|
543
|
+
throw new Revert('Invalid approver');
|
|
544
|
+
}
|
|
545
|
+
if (spender === Address.zero() || spender === Address.dead()) {
|
|
546
|
+
throw new Revert('Invalid spender');
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const senderMap = this.allowanceMap.get(owner);
|
|
550
|
+
const previousAllowance = senderMap.get(spender);
|
|
551
|
+
let newAllowance: u256 = u256.add(previousAllowance, amount);
|
|
552
|
+
// If it overflows, set to max
|
|
553
|
+
if (newAllowance < previousAllowance) {
|
|
554
|
+
newAllowance = u256.Max;
|
|
555
|
+
}
|
|
556
|
+
senderMap.set(spender, newAllowance);
|
|
557
|
+
|
|
558
|
+
this.createApprovedEvent(owner, spender, newAllowance);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
protected _decreaseAllowance(owner: Address, spender: Address, amount: u256): void {
|
|
562
|
+
if (owner === Address.zero() || owner === Address.dead()) {
|
|
563
|
+
throw new Revert('Invalid approver');
|
|
564
|
+
}
|
|
565
|
+
if (spender === Address.zero() || spender === Address.dead()) {
|
|
566
|
+
throw new Revert('Invalid spender');
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
const senderMap = this.allowanceMap.get(owner);
|
|
570
|
+
const previousAllowance = senderMap.get(spender);
|
|
571
|
+
let newAllowance: u256;
|
|
572
|
+
// If it underflows, set to zero
|
|
573
|
+
if (amount > previousAllowance) {
|
|
574
|
+
newAllowance = u256.Zero;
|
|
575
|
+
} else {
|
|
576
|
+
newAllowance = SafeMath.sub(previousAllowance, amount);
|
|
577
|
+
}
|
|
578
|
+
senderMap.set(spender, newAllowance);
|
|
579
|
+
|
|
580
|
+
this.createApprovedEvent(owner, spender, newAllowance);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
protected _mint(to: Address, amount: u256): void {
|
|
584
|
+
if (to === Address.zero() || to === Address.dead()) {
|
|
585
|
+
throw new Revert('Invalid receiver');
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
const toBal: u256 = this.balanceOfMap.get(to);
|
|
589
|
+
this.balanceOfMap.set(to, SafeMath.add(toBal, amount));
|
|
590
|
+
|
|
591
|
+
// @ts-expect-error AssemblyScript valid
|
|
592
|
+
this._totalSupply += amount;
|
|
593
|
+
|
|
594
|
+
if (this._totalSupply.value > this.maxSupply) {
|
|
595
|
+
throw new Revert('Max supply reached');
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
this.createMintedEvent(to, amount);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
protected _burn(from: Address, amount: u256): void {
|
|
602
|
+
if (from === Address.zero() || from === Address.dead()) {
|
|
603
|
+
throw new Revert('Invalid sender');
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
const balance: u256 = this.balanceOfMap.get(from);
|
|
607
|
+
const newBalance: u256 = SafeMath.sub(balance, amount);
|
|
608
|
+
this.balanceOfMap.set(from, newBalance);
|
|
609
|
+
|
|
610
|
+
// @ts-expect-error AssemblyScript valid
|
|
611
|
+
this._totalSupply -= amount;
|
|
612
|
+
|
|
613
|
+
this.createBurnedEvent(from, amount);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
protected createBurnedEvent(from: Address, amount: u256): void {
|
|
617
|
+
this.emitEvent(new BurnedEvent(from, amount));
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
protected createApprovedEvent(owner: Address, spender: Address, amount: u256): void {
|
|
621
|
+
this.emitEvent(new ApprovedEvent(owner, spender, amount));
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
protected createMintedEvent(to: Address, amount: u256): void {
|
|
625
|
+
this.emitEvent(new MintedEvent(to, amount));
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
protected createTransferredEvent(
|
|
629
|
+
operator: Address,
|
|
630
|
+
from: Address,
|
|
631
|
+
to: Address,
|
|
632
|
+
amount: u256,
|
|
633
|
+
): void {
|
|
634
|
+
this.emitEvent(new TransferredEvent(operator, from, to, amount));
|
|
635
|
+
}
|
|
636
|
+
}
|
|
@@ -19,9 +19,9 @@ export class OP_NET implements IBTC {
|
|
|
19
19
|
|
|
20
20
|
public onDeployment(_calldata: Calldata): void {}
|
|
21
21
|
|
|
22
|
-
public onExecutionStarted(): void {}
|
|
22
|
+
public onExecutionStarted(_selector: Selector, _calldata: Calldata): void {}
|
|
23
23
|
|
|
24
|
-
public onExecutionCompleted(): void {}
|
|
24
|
+
public onExecutionCompleted(_selector: Selector, _calldata: Calldata): void {}
|
|
25
25
|
|
|
26
26
|
public execute(method: Selector, _calldata: Calldata): BytesWriter {
|
|
27
27
|
let response: BytesWriter;
|
|
@@ -40,7 +40,7 @@ export class OP_NET implements IBTC {
|
|
|
40
40
|
|
|
41
41
|
protected emitEvent(event: NetEvent): void {
|
|
42
42
|
if (event.length > MAX_EVENT_DATA_SIZE) {
|
|
43
|
-
throw new
|
|
43
|
+
throw new Revert('Event data length exceeds maximum length.');
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
Blockchain.emit(event);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { BytesWriter } from '../../buffer/BytesWriter';
|
|
2
|
+
import { Calldata } from '../../types';
|
|
3
|
+
|
|
4
|
+
export interface IOP20 {
|
|
5
|
+
domainSeparator(callData: Calldata): BytesWriter;
|
|
6
|
+
balanceOf(callData: Calldata): BytesWriter;
|
|
7
|
+
nonceOf(callData: Calldata): BytesWriter;
|
|
8
|
+
allowance(callData: Calldata): BytesWriter;
|
|
9
|
+
safeTransfer(callData: Calldata): BytesWriter;
|
|
10
|
+
safeTransferFrom(callData: Calldata): BytesWriter;
|
|
11
|
+
increaseAllowance(callData: Calldata): BytesWriter;
|
|
12
|
+
decreaseAllowance(callData: Calldata): BytesWriter;
|
|
13
|
+
increaseAllowanceBySignature(callData: Calldata): BytesWriter;
|
|
14
|
+
decreaseAllowanceBySignature(callData: Calldata): BytesWriter;
|
|
15
|
+
}
|
|
@@ -5,11 +5,13 @@ export class OP20InitParameters {
|
|
|
5
5
|
readonly decimals: u8;
|
|
6
6
|
readonly name: string;
|
|
7
7
|
readonly symbol: string;
|
|
8
|
+
readonly icon: string;
|
|
8
9
|
|
|
9
|
-
constructor(maxSupply: u256, decimals: u8, name: string, symbol: string) {
|
|
10
|
+
constructor(maxSupply: u256, decimals: u8, name: string, symbol: string, icon: string = '') {
|
|
10
11
|
this.maxSupply = maxSupply;
|
|
11
12
|
this.decimals = decimals;
|
|
12
13
|
this.name = name;
|
|
13
14
|
this.symbol = symbol;
|
|
15
|
+
this.icon = icon;
|
|
14
16
|
}
|
|
15
17
|
}
|