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