@btc-vision/btc-runtime 1.1.5 → 1.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -7
- package/runtime/contracts/DeployableOP_20.ts +402 -402
package/package.json
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@btc-vision/btc-runtime",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.6",
|
|
4
4
|
"description": "Bitcoin Smart Contract Runtime",
|
|
5
5
|
"main": "btc/index.ts",
|
|
6
6
|
"types": "btc/index.ts",
|
|
7
|
-
"scripts": {
|
|
8
|
-
"test": "jest",
|
|
9
|
-
"build": "asc runtime/index.ts --target release --measure -Ospeed --noAssert --optimizeLevel 3 --shrinkLevel 2 --converge --disable mutable-globals,sign-extension,nontrapping-f2i,bulk-memory --runtime stub --memoryBase 0 --lowMemoryLimit --uncheckedBehavior never --initialMemory 16 --maximumMemory 16 --exportRuntime"
|
|
10
|
-
},
|
|
7
|
+
"scripts": {},
|
|
11
8
|
"keywords": [
|
|
12
9
|
"bitcoin",
|
|
13
10
|
"smart",
|
|
@@ -20,7 +17,7 @@
|
|
|
20
17
|
"author": "BlobMaster41",
|
|
21
18
|
"license": "LICENSE.MD",
|
|
22
19
|
"devDependencies": {
|
|
23
|
-
"@types/node": "^
|
|
20
|
+
"@types/node": "^22.5.5",
|
|
24
21
|
"assemblyscript": "^0.27.25",
|
|
25
22
|
"prettier": "^3.2.5"
|
|
26
23
|
},
|
|
@@ -41,6 +38,7 @@
|
|
|
41
38
|
"@assemblyscript/loader": "^0.27.25",
|
|
42
39
|
"as-bignum": "^0.3.0",
|
|
43
40
|
"gulplog": "^2.2.0",
|
|
44
|
-
"ts-node": "^10.9.2"
|
|
41
|
+
"ts-node": "^10.9.2",
|
|
42
|
+
"typescript": "^5.6.2"
|
|
45
43
|
}
|
|
46
44
|
}
|
|
@@ -1,402 +1,402 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
const maxSupplyPointer: u16 = Blockchain.nextPointer;
|
|
20
|
-
const decimalsPointer: u16 = Blockchain.nextPointer;
|
|
21
|
-
const namePointer: u16 = Blockchain.nextPointer;
|
|
22
|
-
const symbolPointer: u16 = Blockchain.nextPointer;
|
|
23
|
-
const totalSupplyPointer: u16 = Blockchain.nextPointer;
|
|
24
|
-
const allowanceMapPointer: u16 = Blockchain.nextPointer;
|
|
25
|
-
const balanceOfMapPointer: u16 = Blockchain.nextPointer;
|
|
26
|
-
|
|
27
|
-
export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
|
|
28
|
-
protected readonly allowanceMap: MultiAddressMemoryMap<Address, Address, MemorySlotData<u256>>;
|
|
29
|
-
protected readonly balanceOfMap: AddressMemoryMap<Address, MemorySlotData<u256>>;
|
|
30
|
-
|
|
31
|
-
protected readonly _maxSupply: StoredU256;
|
|
32
|
-
protected readonly _decimals: StoredU256;
|
|
33
|
-
protected readonly _name: StoredString;
|
|
34
|
-
protected readonly _symbol: StoredString;
|
|
35
|
-
|
|
36
|
-
protected constructor(params: OP20InitParameters | null = null) {
|
|
37
|
-
super();
|
|
38
|
-
|
|
39
|
-
this.allowanceMap = new MultiAddressMemoryMap<Address, Address, MemorySlotData<u256>>(
|
|
40
|
-
allowanceMapPointer,
|
|
41
|
-
u256.Zero,
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
this.balanceOfMap = new AddressMemoryMap<Address, MemorySlotData<u256>>(
|
|
45
|
-
balanceOfMapPointer,
|
|
46
|
-
u256.Zero,
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
this._totalSupply = new StoredU256(totalSupplyPointer, u256.Zero, u256.Zero);
|
|
50
|
-
|
|
51
|
-
this._maxSupply = new StoredU256(maxSupplyPointer, u256.Zero, u256.Zero);
|
|
52
|
-
this._decimals = new StoredU256(decimalsPointer, u256.Zero, u256.Zero);
|
|
53
|
-
|
|
54
|
-
this._name = new StoredString(namePointer, '');
|
|
55
|
-
this._symbol = new StoredString(symbolPointer, '');
|
|
56
|
-
|
|
57
|
-
if (params && this._maxSupply.value.isZero()) {
|
|
58
|
-
this.instantiate(params, true);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
public _totalSupply: StoredU256;
|
|
63
|
-
|
|
64
|
-
public get totalSupply(): u256 {
|
|
65
|
-
return this._totalSupply.value;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
public get maxSupply(): u256 {
|
|
69
|
-
if (!this._maxSupply) throw new Revert('Max supply not set');
|
|
70
|
-
|
|
71
|
-
return this._maxSupply.value;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
public get decimals(): u8 {
|
|
75
|
-
if (!this._decimals) throw new Revert('Decimals not set');
|
|
76
|
-
|
|
77
|
-
return u8(this._decimals.value.toU32());
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
public get name(): string {
|
|
81
|
-
if (!this._name) throw new Revert('Name not set');
|
|
82
|
-
|
|
83
|
-
return this._name.value;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
public get symbol(): string {
|
|
87
|
-
if (!this._symbol) throw new Revert('Symbol not set');
|
|
88
|
-
|
|
89
|
-
return this._symbol.value;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
public instantiate(params: OP20InitParameters, skipOwnerVerification: boolean = false): void {
|
|
93
|
-
if (!this._maxSupply.value.isZero()) {
|
|
94
|
-
throw new Revert('Already initialized');
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (!skipOwnerVerification) this.onlyOwner(Blockchain.
|
|
98
|
-
|
|
99
|
-
if (params.decimals > 32) {
|
|
100
|
-
throw new Revert('Decimals can not be more than 32');
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
this._maxSupply.value = params.maxSupply;
|
|
104
|
-
this._decimals.value = u256.fromU32(u32(params.decimals));
|
|
105
|
-
this._name.value = params.name;
|
|
106
|
-
this._symbol.value = params.symbol;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/** METHODS */
|
|
110
|
-
public allowance(callData: Calldata): BytesWriter {
|
|
111
|
-
const response = new BytesWriter();
|
|
112
|
-
|
|
113
|
-
const resp = this._allowance(callData.readAddress(), callData.readAddress());
|
|
114
|
-
response.writeU256(resp);
|
|
115
|
-
|
|
116
|
-
return response;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
public approve(callData: Calldata): BytesWriter {
|
|
120
|
-
// Define the owner and spender
|
|
121
|
-
const owner = Blockchain.msgSender;
|
|
122
|
-
const spender: Address = callData.readAddress();
|
|
123
|
-
const value = callData.readU256();
|
|
124
|
-
|
|
125
|
-
// Response buffer
|
|
126
|
-
const response = new BytesWriter();
|
|
127
|
-
|
|
128
|
-
const resp = this._approve(owner, spender, value);
|
|
129
|
-
response.writeBoolean(resp);
|
|
130
|
-
|
|
131
|
-
return response;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
public balanceOf(callData: Calldata): BytesWriter {
|
|
135
|
-
const response = new BytesWriter();
|
|
136
|
-
const address: Address = callData.readAddress();
|
|
137
|
-
const resp = this._balanceOf(address);
|
|
138
|
-
|
|
139
|
-
response.writeU256(resp);
|
|
140
|
-
|
|
141
|
-
return response;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
public burn(callData: Calldata): BytesWriter {
|
|
145
|
-
const response = new BytesWriter();
|
|
146
|
-
const resp = this._burn(callData.readU256());
|
|
147
|
-
response.writeBoolean(resp);
|
|
148
|
-
|
|
149
|
-
return response;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
public mint(callData: Calldata): BytesWriter {
|
|
153
|
-
const response = new BytesWriter();
|
|
154
|
-
const resp = this._mint(callData.readAddress(), callData.readU256());
|
|
155
|
-
|
|
156
|
-
response.writeBoolean(resp);
|
|
157
|
-
|
|
158
|
-
return response;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
public transfer(callData: Calldata): BytesWriter {
|
|
162
|
-
const response = new BytesWriter();
|
|
163
|
-
const resp = this._transfer(callData.readAddress(), callData.readU256());
|
|
164
|
-
|
|
165
|
-
response.writeBoolean(resp);
|
|
166
|
-
|
|
167
|
-
return response;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
public transferFrom(callData: Calldata): BytesWriter {
|
|
171
|
-
const response = new BytesWriter();
|
|
172
|
-
const resp = this._transferFrom(
|
|
173
|
-
callData.readAddress(),
|
|
174
|
-
callData.readAddress(),
|
|
175
|
-
callData.readU256(),
|
|
176
|
-
);
|
|
177
|
-
|
|
178
|
-
response.writeBoolean(resp);
|
|
179
|
-
|
|
180
|
-
return response;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
public callMethod(method: Selector, calldata: Calldata): BytesWriter {
|
|
184
|
-
switch (method) {
|
|
185
|
-
case encodeSelector('allowance'):
|
|
186
|
-
return this.allowance(calldata);
|
|
187
|
-
case encodeSelector('approve'):
|
|
188
|
-
return this.approve(calldata);
|
|
189
|
-
case encodeSelector('balanceOf'):
|
|
190
|
-
return this.balanceOf(calldata);
|
|
191
|
-
case encodeSelector('burn'):
|
|
192
|
-
return this.burn(calldata);
|
|
193
|
-
case encodeSelector('mint'):
|
|
194
|
-
return this.mint(calldata);
|
|
195
|
-
case encodeSelector('transfer'):
|
|
196
|
-
return this.transfer(calldata);
|
|
197
|
-
case encodeSelector('transferFrom'):
|
|
198
|
-
return this.transferFrom(calldata);
|
|
199
|
-
default:
|
|
200
|
-
return super.callMethod(method, calldata);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
public callView(method: Selector): BytesWriter {
|
|
205
|
-
const response = new BytesWriter();
|
|
206
|
-
|
|
207
|
-
switch (method) {
|
|
208
|
-
case encodeSelector('decimals'):
|
|
209
|
-
response.writeU8(this.decimals);
|
|
210
|
-
break;
|
|
211
|
-
case encodeSelector('name'):
|
|
212
|
-
response.writeStringWithLength(this.name);
|
|
213
|
-
break;
|
|
214
|
-
case encodeSelector('symbol'):
|
|
215
|
-
response.writeStringWithLength(this.symbol);
|
|
216
|
-
break;
|
|
217
|
-
case encodeSelector('totalSupply'):
|
|
218
|
-
response.writeU256(this.totalSupply);
|
|
219
|
-
break;
|
|
220
|
-
case encodeSelector('maximumSupply'):
|
|
221
|
-
response.writeU256(this.maxSupply);
|
|
222
|
-
break;
|
|
223
|
-
default:
|
|
224
|
-
return super.callView(method);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
return response;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/** REDEFINED METHODS */
|
|
231
|
-
protected _allowance(owner: Address, spender: Address): u256 {
|
|
232
|
-
const senderMap = this.allowanceMap.get(owner);
|
|
233
|
-
|
|
234
|
-
return senderMap.get(spender);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
protected _approve(owner: Address, spender: Address, value: u256): boolean {
|
|
238
|
-
if (owner === Blockchain.DEAD_ADDRESS || spender === Blockchain.DEAD_ADDRESS) {
|
|
239
|
-
throw new Revert('Cannot approve from or to dead address');
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
const senderMap = this.allowanceMap.get(owner);
|
|
243
|
-
senderMap.set(spender, value);
|
|
244
|
-
|
|
245
|
-
this.createApproveEvent(owner, spender, value);
|
|
246
|
-
|
|
247
|
-
return true;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
protected _balanceOf(owner: Address): u256 {
|
|
251
|
-
const hasAddress = this.balanceOfMap.has(owner);
|
|
252
|
-
if (!hasAddress) return u256.Zero;
|
|
253
|
-
|
|
254
|
-
return this.balanceOfMap.get(owner);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
protected _burn(value: u256, onlyOwner: boolean = true): boolean {
|
|
258
|
-
if (u256.eq(value, u256.Zero)) {
|
|
259
|
-
throw new Revert(`No tokens`);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
if (onlyOwner) this.onlyOwner(Blockchain.
|
|
263
|
-
|
|
264
|
-
if (this._totalSupply.value < value) throw new Revert(`Insufficient total supply.`);
|
|
265
|
-
if (!this.balanceOfMap.has(Blockchain.msgSender)) throw new Revert('No balance');
|
|
266
|
-
|
|
267
|
-
const balance: u256 = this.balanceOfMap.get(Blockchain.msgSender);
|
|
268
|
-
if (balance < value) throw new Revert(`Insufficient balance`);
|
|
269
|
-
|
|
270
|
-
const newBalance: u256 = SafeMath.sub(balance, value);
|
|
271
|
-
this.balanceOfMap.set(Blockchain.msgSender, newBalance);
|
|
272
|
-
|
|
273
|
-
// @ts-ignore
|
|
274
|
-
this._totalSupply -= value;
|
|
275
|
-
|
|
276
|
-
this.createBurnEvent(value);
|
|
277
|
-
return true;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
protected _mint(to: Address, value: u256, onlyOwner: boolean = true): boolean {
|
|
281
|
-
if (onlyOwner) this.onlyOwner(Blockchain.
|
|
282
|
-
|
|
283
|
-
if (!this.balanceOfMap.has(to)) {
|
|
284
|
-
this.balanceOfMap.set(to, value);
|
|
285
|
-
} else {
|
|
286
|
-
const toBalance: u256 = this.balanceOfMap.get(to);
|
|
287
|
-
const newToBalance: u256 = SafeMath.add(toBalance, value);
|
|
288
|
-
|
|
289
|
-
this.balanceOfMap.set(to, newToBalance);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// @ts-ignore
|
|
293
|
-
this._totalSupply += value;
|
|
294
|
-
|
|
295
|
-
if (this._totalSupply.value > this.maxSupply) throw new Revert('Max supply reached');
|
|
296
|
-
|
|
297
|
-
this.createMintEvent(to, value);
|
|
298
|
-
return true;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
protected _transfer(to: string, value: u256): boolean {
|
|
302
|
-
const sender = Blockchain.msgSender;
|
|
303
|
-
|
|
304
|
-
if (!this.balanceOfMap.has(sender)) throw new Revert();
|
|
305
|
-
if (this.isSelf(sender)) throw new Revert('Can not transfer from self account');
|
|
306
|
-
|
|
307
|
-
if (u256.eq(value, u256.Zero)) {
|
|
308
|
-
throw new Revert(`Cannot transfer 0 tokens`);
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
const balance: u256 = this.balanceOfMap.get(sender);
|
|
312
|
-
if (balance < value) throw new Revert(`Insufficient balance`);
|
|
313
|
-
|
|
314
|
-
const newBalance: u256 = SafeMath.sub(balance, value);
|
|
315
|
-
this.balanceOfMap.set(sender, newBalance);
|
|
316
|
-
|
|
317
|
-
const toBalance: u256 = this.balanceOfMap.get(to);
|
|
318
|
-
const newToBalance: u256 = SafeMath.add(toBalance, value);
|
|
319
|
-
|
|
320
|
-
this.balanceOfMap.set(to, newToBalance);
|
|
321
|
-
|
|
322
|
-
this.createTransferEvent(sender, to, value);
|
|
323
|
-
|
|
324
|
-
return true;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
@unsafe
|
|
328
|
-
protected _unsafeTransferFrom(from: Address, to: Address, value: u256): boolean {
|
|
329
|
-
const balance: u256 = this.balanceOfMap.get(from);
|
|
330
|
-
if (balance < value)
|
|
331
|
-
throw new Revert(
|
|
332
|
-
`TransferFrom insufficient balance of ${from} is ${balance} and value is ${value}`,
|
|
333
|
-
);
|
|
334
|
-
|
|
335
|
-
const newBalance: u256 = SafeMath.sub(balance, value);
|
|
336
|
-
this.balanceOfMap.set(from, newBalance);
|
|
337
|
-
|
|
338
|
-
if (!this.balanceOfMap.has(to)) {
|
|
339
|
-
this.balanceOfMap.set(to, value);
|
|
340
|
-
} else {
|
|
341
|
-
const toBalance: u256 = this.balanceOfMap.get(to);
|
|
342
|
-
const newToBalance: u256 = SafeMath.add(toBalance, value);
|
|
343
|
-
|
|
344
|
-
this.balanceOfMap.set(to, newToBalance);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
this.createTransferEvent(from, to, value);
|
|
348
|
-
|
|
349
|
-
return true;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
protected _transferFrom(from: Address, to: Address, value: u256): boolean {
|
|
353
|
-
if (to === Blockchain.DEAD_ADDRESS || from === Blockchain.DEAD_ADDRESS) {
|
|
354
|
-
throw new Revert('Cannot transfer to or from dead address');
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
this._spendAllowance(from, Blockchain.msgSender, value);
|
|
358
|
-
this._unsafeTransferFrom(from, to, value);
|
|
359
|
-
|
|
360
|
-
return true;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
protected _spendAllowance(owner: Address, spender: Address, value: u256): void {
|
|
364
|
-
const ownerAllowanceMap = this.allowanceMap.get(owner);
|
|
365
|
-
const allowed: u256 = ownerAllowanceMap.get(spender);
|
|
366
|
-
|
|
367
|
-
if (allowed < value) {
|
|
368
|
-
throw new Revert(
|
|
369
|
-
`Insufficient allowance ${allowed} < ${value}. Spender: ${spender} - Owner: ${owner}`,
|
|
370
|
-
);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
const newAllowance: u256 = SafeMath.sub(allowed, value);
|
|
374
|
-
ownerAllowanceMap.set(spender, newAllowance);
|
|
375
|
-
|
|
376
|
-
this.allowanceMap.set(owner, ownerAllowanceMap);
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
protected createBurnEvent(value: u256): void {
|
|
380
|
-
const burnEvent = new BurnEvent(value);
|
|
381
|
-
|
|
382
|
-
this.emitEvent(burnEvent);
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
protected createApproveEvent(owner: Address, spender: Address, value: u256): void {
|
|
386
|
-
const approveEvent = new ApproveEvent(owner, spender, value);
|
|
387
|
-
|
|
388
|
-
this.emitEvent(approveEvent);
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
protected createMintEvent(owner: Address, value: u256): void {
|
|
392
|
-
const mintEvent = new MintEvent(owner, value);
|
|
393
|
-
|
|
394
|
-
this.emitEvent(mintEvent);
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
protected createTransferEvent(from: Address, to: Address, value: u256): void {
|
|
398
|
-
const transferEvent = new TransferEvent(from, to, value);
|
|
399
|
-
|
|
400
|
-
this.emitEvent(transferEvent);
|
|
401
|
-
}
|
|
402
|
-
}
|
|
1
|
+
import { u256 } from 'as-bignum/assembly';
|
|
2
|
+
import { BytesWriter } from '../buffer/BytesWriter';
|
|
3
|
+
import { Blockchain } from '../env';
|
|
4
|
+
import { ApproveEvent, BurnEvent, MintEvent, TransferEvent } from '../events/predefined';
|
|
5
|
+
import { encodeSelector, Selector } from '../math/abi';
|
|
6
|
+
import { AddressMemoryMap } from '../memory/AddressMemoryMap';
|
|
7
|
+
import { MemorySlotData } from '../memory/MemorySlot';
|
|
8
|
+
import { MultiAddressMemoryMap } from '../memory/MultiAddressMemoryMap';
|
|
9
|
+
import { StoredString } from '../storage/StoredString';
|
|
10
|
+
import { StoredU256 } from '../storage/StoredU256';
|
|
11
|
+
import { Address } from '../types/Address';
|
|
12
|
+
import { Revert } from '../types/Revert';
|
|
13
|
+
import { SafeMath } from '../types/SafeMath';
|
|
14
|
+
import { Calldata } from '../universal/ABIRegistry';
|
|
15
|
+
import { IOP_20 } from './interfaces/IOP_20';
|
|
16
|
+
import { OP20InitParameters } from './interfaces/OP20InitParameters';
|
|
17
|
+
import { OP_NET } from './OP_NET';
|
|
18
|
+
|
|
19
|
+
const maxSupplyPointer: u16 = Blockchain.nextPointer;
|
|
20
|
+
const decimalsPointer: u16 = Blockchain.nextPointer;
|
|
21
|
+
const namePointer: u16 = Blockchain.nextPointer;
|
|
22
|
+
const symbolPointer: u16 = Blockchain.nextPointer;
|
|
23
|
+
const totalSupplyPointer: u16 = Blockchain.nextPointer;
|
|
24
|
+
const allowanceMapPointer: u16 = Blockchain.nextPointer;
|
|
25
|
+
const balanceOfMapPointer: u16 = Blockchain.nextPointer;
|
|
26
|
+
|
|
27
|
+
export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
|
|
28
|
+
protected readonly allowanceMap: MultiAddressMemoryMap<Address, Address, MemorySlotData<u256>>;
|
|
29
|
+
protected readonly balanceOfMap: AddressMemoryMap<Address, MemorySlotData<u256>>;
|
|
30
|
+
|
|
31
|
+
protected readonly _maxSupply: StoredU256;
|
|
32
|
+
protected readonly _decimals: StoredU256;
|
|
33
|
+
protected readonly _name: StoredString;
|
|
34
|
+
protected readonly _symbol: StoredString;
|
|
35
|
+
|
|
36
|
+
protected constructor(params: OP20InitParameters | null = null) {
|
|
37
|
+
super();
|
|
38
|
+
|
|
39
|
+
this.allowanceMap = new MultiAddressMemoryMap<Address, Address, MemorySlotData<u256>>(
|
|
40
|
+
allowanceMapPointer,
|
|
41
|
+
u256.Zero,
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
this.balanceOfMap = new AddressMemoryMap<Address, MemorySlotData<u256>>(
|
|
45
|
+
balanceOfMapPointer,
|
|
46
|
+
u256.Zero,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
this._totalSupply = new StoredU256(totalSupplyPointer, u256.Zero, u256.Zero);
|
|
50
|
+
|
|
51
|
+
this._maxSupply = new StoredU256(maxSupplyPointer, u256.Zero, u256.Zero);
|
|
52
|
+
this._decimals = new StoredU256(decimalsPointer, u256.Zero, u256.Zero);
|
|
53
|
+
|
|
54
|
+
this._name = new StoredString(namePointer, '');
|
|
55
|
+
this._symbol = new StoredString(symbolPointer, '');
|
|
56
|
+
|
|
57
|
+
if (params && this._maxSupply.value.isZero()) {
|
|
58
|
+
this.instantiate(params, true);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public _totalSupply: StoredU256;
|
|
63
|
+
|
|
64
|
+
public get totalSupply(): u256 {
|
|
65
|
+
return this._totalSupply.value;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public get maxSupply(): u256 {
|
|
69
|
+
if (!this._maxSupply) throw new Revert('Max supply not set');
|
|
70
|
+
|
|
71
|
+
return this._maxSupply.value;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public get decimals(): u8 {
|
|
75
|
+
if (!this._decimals) throw new Revert('Decimals not set');
|
|
76
|
+
|
|
77
|
+
return u8(this._decimals.value.toU32());
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public get name(): string {
|
|
81
|
+
if (!this._name) throw new Revert('Name not set');
|
|
82
|
+
|
|
83
|
+
return this._name.value;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public get symbol(): string {
|
|
87
|
+
if (!this._symbol) throw new Revert('Symbol not set');
|
|
88
|
+
|
|
89
|
+
return this._symbol.value;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
public instantiate(params: OP20InitParameters, skipOwnerVerification: boolean = false): void {
|
|
93
|
+
if (!this._maxSupply.value.isZero()) {
|
|
94
|
+
throw new Revert('Already initialized');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (!skipOwnerVerification) this.onlyOwner(Blockchain.msgSender);
|
|
98
|
+
|
|
99
|
+
if (params.decimals > 32) {
|
|
100
|
+
throw new Revert('Decimals can not be more than 32');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
this._maxSupply.value = params.maxSupply;
|
|
104
|
+
this._decimals.value = u256.fromU32(u32(params.decimals));
|
|
105
|
+
this._name.value = params.name;
|
|
106
|
+
this._symbol.value = params.symbol;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** METHODS */
|
|
110
|
+
public allowance(callData: Calldata): BytesWriter {
|
|
111
|
+
const response = new BytesWriter();
|
|
112
|
+
|
|
113
|
+
const resp = this._allowance(callData.readAddress(), callData.readAddress());
|
|
114
|
+
response.writeU256(resp);
|
|
115
|
+
|
|
116
|
+
return response;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public approve(callData: Calldata): BytesWriter {
|
|
120
|
+
// Define the owner and spender
|
|
121
|
+
const owner = Blockchain.msgSender;
|
|
122
|
+
const spender: Address = callData.readAddress();
|
|
123
|
+
const value = callData.readU256();
|
|
124
|
+
|
|
125
|
+
// Response buffer
|
|
126
|
+
const response = new BytesWriter();
|
|
127
|
+
|
|
128
|
+
const resp = this._approve(owner, spender, value);
|
|
129
|
+
response.writeBoolean(resp);
|
|
130
|
+
|
|
131
|
+
return response;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
public balanceOf(callData: Calldata): BytesWriter {
|
|
135
|
+
const response = new BytesWriter();
|
|
136
|
+
const address: Address = callData.readAddress();
|
|
137
|
+
const resp = this._balanceOf(address);
|
|
138
|
+
|
|
139
|
+
response.writeU256(resp);
|
|
140
|
+
|
|
141
|
+
return response;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
public burn(callData: Calldata): BytesWriter {
|
|
145
|
+
const response = new BytesWriter();
|
|
146
|
+
const resp = this._burn(callData.readU256());
|
|
147
|
+
response.writeBoolean(resp);
|
|
148
|
+
|
|
149
|
+
return response;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
public mint(callData: Calldata): BytesWriter {
|
|
153
|
+
const response = new BytesWriter();
|
|
154
|
+
const resp = this._mint(callData.readAddress(), callData.readU256());
|
|
155
|
+
|
|
156
|
+
response.writeBoolean(resp);
|
|
157
|
+
|
|
158
|
+
return response;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
public transfer(callData: Calldata): BytesWriter {
|
|
162
|
+
const response = new BytesWriter();
|
|
163
|
+
const resp = this._transfer(callData.readAddress(), callData.readU256());
|
|
164
|
+
|
|
165
|
+
response.writeBoolean(resp);
|
|
166
|
+
|
|
167
|
+
return response;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
public transferFrom(callData: Calldata): BytesWriter {
|
|
171
|
+
const response = new BytesWriter();
|
|
172
|
+
const resp = this._transferFrom(
|
|
173
|
+
callData.readAddress(),
|
|
174
|
+
callData.readAddress(),
|
|
175
|
+
callData.readU256(),
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
response.writeBoolean(resp);
|
|
179
|
+
|
|
180
|
+
return response;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
public callMethod(method: Selector, calldata: Calldata): BytesWriter {
|
|
184
|
+
switch (method) {
|
|
185
|
+
case encodeSelector('allowance'):
|
|
186
|
+
return this.allowance(calldata);
|
|
187
|
+
case encodeSelector('approve'):
|
|
188
|
+
return this.approve(calldata);
|
|
189
|
+
case encodeSelector('balanceOf'):
|
|
190
|
+
return this.balanceOf(calldata);
|
|
191
|
+
case encodeSelector('burn'):
|
|
192
|
+
return this.burn(calldata);
|
|
193
|
+
case encodeSelector('mint'):
|
|
194
|
+
return this.mint(calldata);
|
|
195
|
+
case encodeSelector('transfer'):
|
|
196
|
+
return this.transfer(calldata);
|
|
197
|
+
case encodeSelector('transferFrom'):
|
|
198
|
+
return this.transferFrom(calldata);
|
|
199
|
+
default:
|
|
200
|
+
return super.callMethod(method, calldata);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
public callView(method: Selector): BytesWriter {
|
|
205
|
+
const response = new BytesWriter();
|
|
206
|
+
|
|
207
|
+
switch (method) {
|
|
208
|
+
case encodeSelector('decimals'):
|
|
209
|
+
response.writeU8(this.decimals);
|
|
210
|
+
break;
|
|
211
|
+
case encodeSelector('name'):
|
|
212
|
+
response.writeStringWithLength(this.name);
|
|
213
|
+
break;
|
|
214
|
+
case encodeSelector('symbol'):
|
|
215
|
+
response.writeStringWithLength(this.symbol);
|
|
216
|
+
break;
|
|
217
|
+
case encodeSelector('totalSupply'):
|
|
218
|
+
response.writeU256(this.totalSupply);
|
|
219
|
+
break;
|
|
220
|
+
case encodeSelector('maximumSupply'):
|
|
221
|
+
response.writeU256(this.maxSupply);
|
|
222
|
+
break;
|
|
223
|
+
default:
|
|
224
|
+
return super.callView(method);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return response;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** REDEFINED METHODS */
|
|
231
|
+
protected _allowance(owner: Address, spender: Address): u256 {
|
|
232
|
+
const senderMap = this.allowanceMap.get(owner);
|
|
233
|
+
|
|
234
|
+
return senderMap.get(spender);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
protected _approve(owner: Address, spender: Address, value: u256): boolean {
|
|
238
|
+
if (owner === Blockchain.DEAD_ADDRESS || spender === Blockchain.DEAD_ADDRESS) {
|
|
239
|
+
throw new Revert('Cannot approve from or to dead address');
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const senderMap = this.allowanceMap.get(owner);
|
|
243
|
+
senderMap.set(spender, value);
|
|
244
|
+
|
|
245
|
+
this.createApproveEvent(owner, spender, value);
|
|
246
|
+
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
protected _balanceOf(owner: Address): u256 {
|
|
251
|
+
const hasAddress = this.balanceOfMap.has(owner);
|
|
252
|
+
if (!hasAddress) return u256.Zero;
|
|
253
|
+
|
|
254
|
+
return this.balanceOfMap.get(owner);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
protected _burn(value: u256, onlyOwner: boolean = true): boolean {
|
|
258
|
+
if (u256.eq(value, u256.Zero)) {
|
|
259
|
+
throw new Revert(`No tokens`);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (onlyOwner) this.onlyOwner(Blockchain.msgSender);
|
|
263
|
+
|
|
264
|
+
if (this._totalSupply.value < value) throw new Revert(`Insufficient total supply.`);
|
|
265
|
+
if (!this.balanceOfMap.has(Blockchain.msgSender)) throw new Revert('No balance');
|
|
266
|
+
|
|
267
|
+
const balance: u256 = this.balanceOfMap.get(Blockchain.msgSender);
|
|
268
|
+
if (balance < value) throw new Revert(`Insufficient balance`);
|
|
269
|
+
|
|
270
|
+
const newBalance: u256 = SafeMath.sub(balance, value);
|
|
271
|
+
this.balanceOfMap.set(Blockchain.msgSender, newBalance);
|
|
272
|
+
|
|
273
|
+
// @ts-ignore
|
|
274
|
+
this._totalSupply -= value;
|
|
275
|
+
|
|
276
|
+
this.createBurnEvent(value);
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
protected _mint(to: Address, value: u256, onlyOwner: boolean = true): boolean {
|
|
281
|
+
if (onlyOwner) this.onlyOwner(Blockchain.msgSender);
|
|
282
|
+
|
|
283
|
+
if (!this.balanceOfMap.has(to)) {
|
|
284
|
+
this.balanceOfMap.set(to, value);
|
|
285
|
+
} else {
|
|
286
|
+
const toBalance: u256 = this.balanceOfMap.get(to);
|
|
287
|
+
const newToBalance: u256 = SafeMath.add(toBalance, value);
|
|
288
|
+
|
|
289
|
+
this.balanceOfMap.set(to, newToBalance);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// @ts-ignore
|
|
293
|
+
this._totalSupply += value;
|
|
294
|
+
|
|
295
|
+
if (this._totalSupply.value > this.maxSupply) throw new Revert('Max supply reached');
|
|
296
|
+
|
|
297
|
+
this.createMintEvent(to, value);
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
protected _transfer(to: string, value: u256): boolean {
|
|
302
|
+
const sender = Blockchain.msgSender;
|
|
303
|
+
|
|
304
|
+
if (!this.balanceOfMap.has(sender)) throw new Revert();
|
|
305
|
+
if (this.isSelf(sender)) throw new Revert('Can not transfer from self account');
|
|
306
|
+
|
|
307
|
+
if (u256.eq(value, u256.Zero)) {
|
|
308
|
+
throw new Revert(`Cannot transfer 0 tokens`);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const balance: u256 = this.balanceOfMap.get(sender);
|
|
312
|
+
if (balance < value) throw new Revert(`Insufficient balance`);
|
|
313
|
+
|
|
314
|
+
const newBalance: u256 = SafeMath.sub(balance, value);
|
|
315
|
+
this.balanceOfMap.set(sender, newBalance);
|
|
316
|
+
|
|
317
|
+
const toBalance: u256 = this.balanceOfMap.get(to);
|
|
318
|
+
const newToBalance: u256 = SafeMath.add(toBalance, value);
|
|
319
|
+
|
|
320
|
+
this.balanceOfMap.set(to, newToBalance);
|
|
321
|
+
|
|
322
|
+
this.createTransferEvent(sender, to, value);
|
|
323
|
+
|
|
324
|
+
return true;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
@unsafe
|
|
328
|
+
protected _unsafeTransferFrom(from: Address, to: Address, value: u256): boolean {
|
|
329
|
+
const balance: u256 = this.balanceOfMap.get(from);
|
|
330
|
+
if (balance < value)
|
|
331
|
+
throw new Revert(
|
|
332
|
+
`TransferFrom insufficient balance of ${from} is ${balance} and value is ${value}`,
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
const newBalance: u256 = SafeMath.sub(balance, value);
|
|
336
|
+
this.balanceOfMap.set(from, newBalance);
|
|
337
|
+
|
|
338
|
+
if (!this.balanceOfMap.has(to)) {
|
|
339
|
+
this.balanceOfMap.set(to, value);
|
|
340
|
+
} else {
|
|
341
|
+
const toBalance: u256 = this.balanceOfMap.get(to);
|
|
342
|
+
const newToBalance: u256 = SafeMath.add(toBalance, value);
|
|
343
|
+
|
|
344
|
+
this.balanceOfMap.set(to, newToBalance);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
this.createTransferEvent(from, to, value);
|
|
348
|
+
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
protected _transferFrom(from: Address, to: Address, value: u256): boolean {
|
|
353
|
+
if (to === Blockchain.DEAD_ADDRESS || from === Blockchain.DEAD_ADDRESS) {
|
|
354
|
+
throw new Revert('Cannot transfer to or from dead address');
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
this._spendAllowance(from, Blockchain.msgSender, value);
|
|
358
|
+
this._unsafeTransferFrom(from, to, value);
|
|
359
|
+
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
protected _spendAllowance(owner: Address, spender: Address, value: u256): void {
|
|
364
|
+
const ownerAllowanceMap = this.allowanceMap.get(owner);
|
|
365
|
+
const allowed: u256 = ownerAllowanceMap.get(spender);
|
|
366
|
+
|
|
367
|
+
if (allowed < value) {
|
|
368
|
+
throw new Revert(
|
|
369
|
+
`Insufficient allowance ${allowed} < ${value}. Spender: ${spender} - Owner: ${owner}`,
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const newAllowance: u256 = SafeMath.sub(allowed, value);
|
|
374
|
+
ownerAllowanceMap.set(spender, newAllowance);
|
|
375
|
+
|
|
376
|
+
this.allowanceMap.set(owner, ownerAllowanceMap);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
protected createBurnEvent(value: u256): void {
|
|
380
|
+
const burnEvent = new BurnEvent(value);
|
|
381
|
+
|
|
382
|
+
this.emitEvent(burnEvent);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
protected createApproveEvent(owner: Address, spender: Address, value: u256): void {
|
|
386
|
+
const approveEvent = new ApproveEvent(owner, spender, value);
|
|
387
|
+
|
|
388
|
+
this.emitEvent(approveEvent);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
protected createMintEvent(owner: Address, value: u256): void {
|
|
392
|
+
const mintEvent = new MintEvent(owner, value);
|
|
393
|
+
|
|
394
|
+
this.emitEvent(mintEvent);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
protected createTransferEvent(from: Address, to: Address, value: u256): void {
|
|
398
|
+
const transferEvent = new TransferEvent(from, to, value);
|
|
399
|
+
|
|
400
|
+
this.emitEvent(transferEvent);
|
|
401
|
+
}
|
|
402
|
+
}
|