@btc-vision/btc-runtime 1.7.0 → 1.7.2

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.7.0",
3
+ "version": "1.7.2",
4
4
  "description": "Bitcoin Smart Contract Runtime",
5
5
  "main": "btc/index.ts",
6
6
  "scripts": {},
@@ -17,8 +17,8 @@
17
17
  "author": "BlobMaster41",
18
18
  "license": "MIT",
19
19
  "devDependencies": {
20
- "@types/node": "^22.14.0",
21
- "assemblyscript": "^0.27.35"
20
+ "@types/node": "^22.15.26",
21
+ "assemblyscript": "^0.28.1"
22
22
  },
23
23
  "repository": {
24
24
  "type": "git",
@@ -35,12 +35,13 @@
35
35
  "test/*.ts"
36
36
  ],
37
37
  "dependencies": {
38
- "@assemblyscript/loader": "^0.27.35",
38
+ "@assemblyscript/loader": "^0.28.1",
39
39
  "@btc-vision/as-bignum": "^0.0.5",
40
- "@eslint/js": "^9.24.0",
40
+ "@btc-vision/opnet-transform": "^0.1.3",
41
+ "@eslint/js": "^9.27.0",
41
42
  "gulplog": "^2.2.0",
42
43
  "ts-node": "^10.9.2",
43
44
  "typescript": "^5.8.3",
44
- "typescript-eslint": "^8.29.1"
45
+ "typescript-eslint": "^8.33.0"
45
46
  }
46
47
  }
@@ -7,19 +7,17 @@ export function revertOnError(message: string, fileName: string, line: u32, colu
7
7
 
8
8
  const revertMessage = `${message} at ${fileName}:${line}:${column}`;
9
9
 
10
- // THIS MIGHT OVERFLOW, IT'S OK.
11
- const length = u16(revertMessage.length);
12
- const _length_i32 = i32(length);
10
+ const length = revertMessage.length;
13
11
 
14
- const arrayBuffer = new ArrayBuffer(4 + _length_i32 + 2);
12
+ const arrayBuffer = new ArrayBuffer(4 + length + 4);
15
13
  const writer = new DataView(arrayBuffer);
16
14
 
17
15
  writer.setUint32(0, selector, false);
18
- writer.setUint16(4, length, false);
16
+ writer.setUint32(4, length, false);
19
17
 
20
- for (let i = 0; i < _length_i32; i++) {
21
- writer.setUint8(6 + i, <u8>revertMessage.charCodeAt(i));
18
+ for (let i = 0; i < length; i++) {
19
+ writer.setUint8(8 + i, <u8>revertMessage.charCodeAt(i));
22
20
  }
23
21
 
24
22
  env_exit(1, arrayBuffer, arrayBuffer.byteLength);
25
- }
23
+ }
@@ -1,19 +1,17 @@
1
1
  import { u256 } from '@btc-vision/as-bignum/assembly';
2
+
2
3
  import { BytesWriter } from '../buffer/BytesWriter';
3
4
  import { Blockchain } from '../env';
4
5
  import { ApproveEvent, BurnEvent, MintEvent, TransferEvent } from '../events/predefined';
5
- import { encodeSelector, Selector } from '../math/abi';
6
6
  import { StoredString } from '../storage/StoredString';
7
7
  import { StoredU256 } from '../storage/StoredU256';
8
8
  import { Address } from '../types/Address';
9
9
  import { Revert } from '../types/Revert';
10
10
  import { SafeMath } from '../types/SafeMath';
11
-
12
11
  import { sha256 } from '../env/global';
13
12
  import { EMPTY_POINTER } from '../math/bytes';
14
13
  import { AddressMemoryMap } from '../memory/AddressMemoryMap';
15
14
  import { MapOfMap } from '../memory/MapOfMap';
16
- import { ApproveStr, TransferFromStr, TransferStr } from '../shared-libraries/TransferHelper';
17
15
  import { Calldata } from '../types';
18
16
  import { ADDRESS_BYTE_LENGTH, BOOLEAN_BYTE_LENGTH, U256_BYTE_LENGTH } from '../utils';
19
17
  import { IOP_20 } from './interfaces/IOP_20';
@@ -36,14 +34,12 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
36
34
  protected readonly _decimals: StoredU256;
37
35
  protected readonly _name: StoredString;
38
36
  protected readonly _symbol: StoredString;
39
-
40
37
  protected readonly _nonceMap: AddressMemoryMap;
41
38
 
42
39
  public constructor(params: OP20InitParameters | null = null) {
43
40
  super();
44
41
 
45
- // Initialize main storage structures
46
- this.allowanceMap = new MapOfMap(allowanceMapPointer);
42
+ this.allowanceMap = new MapOfMap<u256>(allowanceMapPointer);
47
43
  this.balanceOfMap = new AddressMemoryMap(balanceOfMapPointer);
48
44
  this._nonceMap = new AddressMemoryMap(nonceMapPointer);
49
45
 
@@ -58,6 +54,7 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
58
54
  }
59
55
  }
60
56
 
57
+ /** Intentionally public for inherited classes */
61
58
  public _totalSupply: StoredU256;
62
59
 
63
60
  public get totalSupply(): u256 {
@@ -88,15 +85,9 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
88
85
  params: OP20InitParameters,
89
86
  skipDeployerVerification: boolean = false,
90
87
  ): void {
91
- if (!this._maxSupply.value.isZero()) {
92
- throw new Revert('Already initialized');
93
- }
94
-
88
+ if (!this._maxSupply.value.isZero()) throw new Revert('Already initialized');
95
89
  if (!skipDeployerVerification) this.onlyDeployer(Blockchain.tx.sender);
96
-
97
- if (params.decimals > 32) {
98
- throw new Revert('Decimals can not be more than 32');
99
- }
90
+ if (params.decimals > 32) throw new Revert('Decimals > 32');
100
91
 
101
92
  this._maxSupply.value = params.maxSupply;
102
93
  this._decimals.value = u256.fromU32(u32(params.decimals));
@@ -104,153 +95,156 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
104
95
  this._symbol.value = params.symbol;
105
96
  }
106
97
 
107
- /** METHODS */
108
- public allowance(callData: Calldata): BytesWriter {
109
- const response = new BytesWriter(U256_BYTE_LENGTH);
110
- const resp = this._allowance(callData.readAddress(), callData.readAddress());
111
- response.writeU256(resp);
112
- return response;
98
+ @method('decimals')
99
+ @returns({ name: 'decimals', type: ABIDataTypes.UINT8 })
100
+ public fn_decimals(_: Calldata): BytesWriter {
101
+ const w = new BytesWriter(1);
102
+ w.writeU8(this.decimals);
103
+ return w;
113
104
  }
114
105
 
115
- public approve(callData: Calldata): BytesWriter {
116
- const owner = Blockchain.tx.sender;
117
- const spender: Address = callData.readAddress();
118
- const value = callData.readU256();
106
+ @method('name')
107
+ @returns({ name: 'name', type: ABIDataTypes.STRING })
108
+ public fn_name(_: Calldata): BytesWriter {
109
+ const w = new BytesWriter(this.name.length + 4);
110
+ w.writeStringWithLength(this.name);
111
+ return w;
112
+ }
119
113
 
120
- const resp = this._approve(owner, spender, value);
114
+ @method('symbol')
115
+ @returns({ name: 'symbol', type: ABIDataTypes.STRING })
116
+ public fn_symbol(_: Calldata): BytesWriter {
117
+ const w = new BytesWriter(this.symbol.length + 4);
118
+ w.writeStringWithLength(this.symbol);
119
+ return w;
120
+ }
121
121
 
122
- const response = new BytesWriter(BOOLEAN_BYTE_LENGTH);
123
- response.writeBoolean(resp);
124
- return response;
122
+ @method('totalSupply')
123
+ @returns({ name: 'totalSupply', type: ABIDataTypes.UINT256 })
124
+ public fn_totalSupply(_: Calldata): BytesWriter {
125
+ const w = new BytesWriter(U256_BYTE_LENGTH);
126
+ w.writeU256(this.totalSupply);
127
+ return w;
125
128
  }
126
129
 
127
- public approveFrom(callData: Calldata): BytesWriter {
128
- // If the transaction is initiated directly by the owner, there is no need for an off-chain signature.
129
- if (Blockchain.tx.origin == Blockchain.tx.sender) {
130
- throw new Revert(
131
- 'Direct owner approval detected. Use approve function instead of approveFrom.',
132
- );
133
- }
130
+ @method('maximumSupply')
131
+ @returns({ name: 'maximumSupply', type: ABIDataTypes.UINT256 })
132
+ public fn_maximumSupply(_: Calldata): BytesWriter {
133
+ const w = new BytesWriter(U256_BYTE_LENGTH);
134
+ w.writeU256(this.maxSupply);
135
+ return w;
136
+ }
134
137
 
135
- const response = new BytesWriter(BOOLEAN_BYTE_LENGTH);
138
+ @method(
139
+ { name: 'owner', type: ABIDataTypes.ADDRESS },
140
+ { name: 'spender', type: ABIDataTypes.ADDRESS },
141
+ )
142
+ @returns({ name: 'remaining', type: ABIDataTypes.UINT256 })
143
+ public allowance(calldata: Calldata): BytesWriter {
144
+ const w = new BytesWriter(U256_BYTE_LENGTH);
145
+ const rem = this._allowance(calldata.readAddress(), calldata.readAddress());
146
+ w.writeU256(rem);
147
+ return w;
148
+ }
136
149
 
137
- const owner: Address = Blockchain.tx.origin;
138
- const spender: Address = callData.readAddress();
139
- const value: u256 = callData.readU256();
140
- const nonce: u256 = callData.readU256();
150
+ @method(
151
+ { name: 'spender', type: ABIDataTypes.ADDRESS },
152
+ { name: 'amount', type: ABIDataTypes.UINT256 },
153
+ )
154
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
155
+ @emit('Approve')
156
+ public approve(calldata: Calldata): BytesWriter {
157
+ const ok = this._approve(Blockchain.tx.sender, calldata.readAddress(), calldata.readU256());
158
+ const w = new BytesWriter(BOOLEAN_BYTE_LENGTH);
159
+ w.writeBoolean(ok);
160
+ return w;
161
+ }
141
162
 
142
- const signature = callData.readBytesWithLength();
143
- if (signature.length !== 64) {
144
- throw new Revert('Invalid signature length');
163
+ @method(
164
+ { name: 'spender', type: ABIDataTypes.ADDRESS },
165
+ { name: 'amount', type: ABIDataTypes.UINT256 },
166
+ { name: 'nonce', type: ABIDataTypes.UINT256 },
167
+ { name: 'sig', type: ABIDataTypes.BYTES },
168
+ )
169
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
170
+ @emit('Approve')
171
+ public approveFrom(calldata: Calldata): BytesWriter {
172
+ if (Blockchain.tx.origin == Blockchain.tx.sender) {
173
+ throw new Revert('Direct owner approval – use approve()');
145
174
  }
146
175
 
147
- const resp = this._approveFrom(owner, spender, value, nonce, signature);
148
- response.writeBoolean(resp);
149
-
150
- return response;
176
+ const owner: Address = Blockchain.tx.origin;
177
+ const spender: Address = calldata.readAddress();
178
+ const value: u256 = calldata.readU256();
179
+ const nonce: u256 = calldata.readU256();
180
+ const sig = calldata.readBytesWithLength();
181
+ if (sig.length !== 64) throw new Revert('Invalid signature length');
182
+
183
+ const ok = this._approveFrom(owner, spender, value, nonce, sig);
184
+ const w = new BytesWriter(BOOLEAN_BYTE_LENGTH);
185
+ w.writeBoolean(ok);
186
+ return w;
151
187
  }
152
188
 
153
- /**
154
- * Returns the current nonce for a given owner.
155
- */
156
- public nonceOf(callData: Calldata): BytesWriter {
157
- const owner = callData.readAddress();
158
- const currentNonce = this._nonceMap.get(owner);
159
-
160
- const response = new BytesWriter(32);
161
- response.writeU256(currentNonce);
162
-
163
- return response;
189
+ @method({ name: 'owner', type: ABIDataTypes.ADDRESS })
190
+ @returns({ name: 'nonce', type: ABIDataTypes.UINT256 })
191
+ public nonceOf(calldata: Calldata): BytesWriter {
192
+ const current = this._nonceMap.get(calldata.readAddress());
193
+ const w = new BytesWriter(U256_BYTE_LENGTH);
194
+ w.writeU256(current);
195
+ return w;
164
196
  }
165
197
 
166
- public balanceOf(callData: Calldata): BytesWriter {
167
- const response = new BytesWriter(U256_BYTE_LENGTH);
168
- const address: Address = callData.readAddress();
169
-
170
- const resp = this._balanceOf(address);
171
- response.writeU256(resp);
172
-
173
- return response;
198
+ @method({ name: 'owner', type: ABIDataTypes.ADDRESS })
199
+ @returns({ name: 'balance', type: ABIDataTypes.UINT256 })
200
+ public balanceOf(calldata: Calldata): BytesWriter {
201
+ const bal = this._balanceOf(calldata.readAddress());
202
+ const w = new BytesWriter(U256_BYTE_LENGTH);
203
+ w.writeU256(bal);
204
+ return w;
174
205
  }
175
206
 
176
- public burn(callData: Calldata): BytesWriter {
177
- const response = new BytesWriter(BOOLEAN_BYTE_LENGTH);
178
- const resp = this._burn(callData.readU256());
179
- response.writeBoolean(resp);
180
-
181
- return response;
207
+ @method({ name: 'amount', type: ABIDataTypes.UINT256 })
208
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
209
+ @emit('Burn')
210
+ public burn(calldata: Calldata): BytesWriter {
211
+ const ok = this._burn(calldata.readU256());
212
+ const w = new BytesWriter(BOOLEAN_BYTE_LENGTH);
213
+ w.writeBoolean(ok);
214
+ return w;
182
215
  }
183
216
 
184
- public transfer(callData: Calldata): BytesWriter {
185
- const response = new BytesWriter(BOOLEAN_BYTE_LENGTH);
186
- const resp = this._transfer(callData.readAddress(), callData.readU256());
187
- response.writeBoolean(resp);
188
-
189
- return response;
217
+ @method(
218
+ { name: 'to', type: ABIDataTypes.ADDRESS },
219
+ { name: 'amount', type: ABIDataTypes.UINT256 },
220
+ )
221
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
222
+ @emit('Transfer')
223
+ public transfer(calldata: Calldata): BytesWriter {
224
+ const ok = this._transfer(calldata.readAddress(), calldata.readU256());
225
+ const w = new BytesWriter(BOOLEAN_BYTE_LENGTH);
226
+ w.writeBoolean(ok);
227
+ return w;
190
228
  }
191
229
 
192
- public transferFrom(callData: Calldata): BytesWriter {
193
- const response = new BytesWriter(BOOLEAN_BYTE_LENGTH);
194
- const resp = this._transferFrom(
195
- callData.readAddress(),
196
- callData.readAddress(),
197
- callData.readU256(),
230
+ @method(
231
+ { name: 'from', type: ABIDataTypes.ADDRESS },
232
+ { name: 'to', type: ABIDataTypes.ADDRESS },
233
+ { name: 'amount', type: ABIDataTypes.UINT256 },
234
+ )
235
+ @returns({ name: 'success', type: ABIDataTypes.BOOL })
236
+ @emit('Transfer')
237
+ public transferFrom(calldata: Calldata): BytesWriter {
238
+ const ok = this._transferFrom(
239
+ calldata.readAddress(),
240
+ calldata.readAddress(),
241
+ calldata.readU256(),
198
242
  );
199
-
200
- response.writeBoolean(resp);
201
- return response;
202
- }
203
-
204
- public execute(method: Selector, calldata: Calldata): BytesWriter {
205
- let response: BytesWriter;
206
-
207
- switch (method) {
208
- case encodeSelector('decimals()'):
209
- response = new BytesWriter(BOOLEAN_BYTE_LENGTH);
210
- response.writeU8(this.decimals);
211
- break;
212
- case encodeSelector('name()'):
213
- response = new BytesWriter(this.name.length + 2);
214
- response.writeStringWithLength(this.name);
215
- break;
216
- case encodeSelector('symbol()'):
217
- response = new BytesWriter(this.symbol.length + 2);
218
- response.writeStringWithLength(this.symbol);
219
- break;
220
- case encodeSelector('totalSupply()'):
221
- response = new BytesWriter(U256_BYTE_LENGTH);
222
- response.writeU256(this.totalSupply);
223
- break;
224
- case encodeSelector('maximumSupply()'):
225
- response = new BytesWriter(U256_BYTE_LENGTH);
226
- response.writeU256(this.maxSupply);
227
- break;
228
- case encodeSelector('allowance(address,address)'):
229
- return this.allowance(calldata);
230
- case encodeSelector(ApproveStr):
231
- return this.approve(calldata);
232
- case encodeSelector('approveFrom(address,uint256,uint256,bytes)'):
233
- return this.approveFrom(calldata);
234
- case encodeSelector('balanceOf(address)'):
235
- return this.balanceOf(calldata);
236
- case encodeSelector('burn(uint256)'):
237
- return this.burn(calldata);
238
- case encodeSelector(TransferStr):
239
- return this.transfer(calldata);
240
- case encodeSelector(TransferFromStr):
241
- return this.transferFrom(calldata);
242
-
243
- case encodeSelector('nonceOf(address)'):
244
- return this.nonceOf(calldata);
245
-
246
- default:
247
- return super.execute(method, calldata);
248
- }
249
-
250
- return response;
243
+ const w = new BytesWriter(BOOLEAN_BYTE_LENGTH);
244
+ w.writeBoolean(ok);
245
+ return w;
251
246
  }
252
247
 
253
- /** REDEFINED METHODS */
254
248
  protected _allowance(owner: Address, spender: Address): u256 {
255
249
  const senderMap = this.allowanceMap.get(owner);
256
250
  return senderMap.get(spender);
@@ -263,25 +257,15 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
263
257
  nonce: u256,
264
258
  signature: Uint8Array,
265
259
  ): boolean {
266
- if (owner === Blockchain.DEAD_ADDRESS) {
267
- throw new Revert('Address can not be dead address');
268
- }
260
+ if (owner === Blockchain.DEAD_ADDRESS) throw new Revert('Address can not be dead');
261
+ if (spender === Blockchain.DEAD_ADDRESS) throw new Revert('Spender can not be dead');
269
262
 
270
- if (spender === Blockchain.DEAD_ADDRESS) {
271
- throw new Revert('Spender cannot be dead address');
272
- }
273
-
274
- // Ensure the nonce matches what we have stored on-chain
275
263
  const storedNonce = this._nonceMap.get(owner);
276
- if (!u256.eq(storedNonce, nonce)) {
277
- throw new Revert('Invalid nonce (possible replay or out-of-sync)');
278
- }
264
+ if (!u256.eq(storedNonce, nonce)) throw new Revert('Invalid nonce');
279
265
 
280
- // Build the hash to match exactly what the user signed, including the nonce
281
266
  const writer = new BytesWriter(
282
267
  ADDRESS_BYTE_LENGTH * 3 + U256_BYTE_LENGTH + U256_BYTE_LENGTH,
283
268
  );
284
-
285
269
  writer.writeAddress(owner);
286
270
  writer.writeAddress(spender);
287
271
  writer.writeU256(value);
@@ -290,29 +274,21 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
290
274
 
291
275
  const hash = sha256(writer.getBuffer());
292
276
  if (!Blockchain.verifySchnorrSignature(owner, signature, hash)) {
293
- throw new Revert('ApproveFrom: Invalid signature');
277
+ throw new Revert('Invalid signature');
294
278
  }
295
279
 
296
- // If valid, increment the nonce so this signature can't be reused
297
280
  this._nonceMap.set(owner, SafeMath.add(storedNonce, u256.One));
298
281
 
299
- // Update allowance
300
282
  const senderMap = this.allowanceMap.get(owner);
301
283
  senderMap.set(spender, value);
302
284
 
303
- // Emit event
304
285
  this.createApproveEvent(owner, spender, value);
305
286
  return true;
306
287
  }
307
288
 
308
289
  protected _approve(owner: Address, spender: Address, value: u256): boolean {
309
- if (owner === Blockchain.DEAD_ADDRESS) {
310
- throw new Revert('Address can not be dead address');
311
- }
312
-
313
- if (spender === Blockchain.DEAD_ADDRESS) {
314
- throw new Revert('Spender cannot be dead address');
315
- }
290
+ if (owner === Blockchain.DEAD_ADDRESS) throw new Revert('Address can not be dead');
291
+ if (spender === Blockchain.DEAD_ADDRESS) throw new Revert('Spender can not be dead');
316
292
 
317
293
  const senderMap = this.allowanceMap.get(owner);
318
294
  senderMap.set(spender, value);
@@ -323,26 +299,23 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
323
299
 
324
300
  protected _balanceOf(owner: Address): u256 {
325
301
  if (!this.balanceOfMap.has(owner)) return u256.Zero;
326
-
327
302
  return this.balanceOfMap.get(owner);
328
303
  }
329
304
 
330
305
  protected _burn(value: u256, onlyDeployer: boolean = true): boolean {
331
- if (u256.eq(value, u256.Zero)) {
332
- throw new Revert(`No tokens`);
333
- }
306
+ if (u256.eq(value, u256.Zero)) throw new Revert('No tokens');
334
307
 
335
308
  if (onlyDeployer) this.onlyDeployer(Blockchain.tx.sender);
336
- if (this._totalSupply.value < value) throw new Revert(`Insufficient total supply.`);
309
+ if (this._totalSupply.value < value) throw new Revert('Insufficient supply');
337
310
  if (!this.balanceOfMap.has(Blockchain.tx.sender)) throw new Revert('No balance');
338
311
 
339
312
  const balance: u256 = this.balanceOfMap.get(Blockchain.tx.sender);
340
- if (balance < value) throw new Revert(`Insufficient balance`);
313
+ if (balance < value) throw new Revert('Insufficient balance');
341
314
 
342
315
  const newBalance: u256 = SafeMath.sub(balance, value);
343
316
  this.balanceOfMap.set(Blockchain.tx.sender, newBalance);
344
317
 
345
- // @ts-expect-error This is valid AssemblyScript but can trip TS
318
+ // @ts-expect-error AssemblyScript valid
346
319
  this._totalSupply -= value;
347
320
 
348
321
  this.createBurnEvent(value);
@@ -355,12 +328,11 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
355
328
  if (!this.balanceOfMap.has(to)) {
356
329
  this.balanceOfMap.set(to, value);
357
330
  } else {
358
- const toBalance: u256 = this.balanceOfMap.get(to);
359
- const newToBalance: u256 = SafeMath.add(toBalance, value);
360
- this.balanceOfMap.set(to, newToBalance);
331
+ const toBal: u256 = this.balanceOfMap.get(to);
332
+ this.balanceOfMap.set(to, SafeMath.add(toBal, value));
361
333
  }
362
334
 
363
- // @ts-expect-error This is valid AssemblyScript but can trip TS
335
+ // @ts-expect-error AssemblyScript valid
364
336
  this._totalSupply += value;
365
337
 
366
338
  if (this._totalSupply.value > this.maxSupply) throw new Revert('Max supply reached');
@@ -370,20 +342,16 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
370
342
 
371
343
  protected _transfer(to: Address, value: u256): boolean {
372
344
  const sender = Blockchain.tx.sender;
373
- if (this.isSelf(sender)) throw new Revert('Can not transfer from self account');
374
- if (u256.eq(value, u256.Zero)) {
375
- throw new Revert(`Cannot transfer 0 tokens`);
376
- }
345
+ if (this.isSelf(sender)) throw new Revert('Cannot transfer from self');
346
+ if (u256.eq(value, u256.Zero)) throw new Revert('Cannot transfer 0');
377
347
 
378
348
  const balance: u256 = this.balanceOfMap.get(sender);
379
- if (balance < value) throw new Revert(`Insufficient balance`);
349
+ if (balance < value) throw new Revert('Insufficient balance');
380
350
 
381
- const newBalance: u256 = SafeMath.sub(balance, value);
382
- this.balanceOfMap.set(sender, newBalance);
351
+ this.balanceOfMap.set(sender, SafeMath.sub(balance, value));
383
352
 
384
- const toBalance: u256 = this.balanceOfMap.get(to);
385
- const newToBalance: u256 = SafeMath.add(toBalance, value);
386
- this.balanceOfMap.set(to, newToBalance);
353
+ const toBal: u256 = this.balanceOfMap.get(to);
354
+ this.balanceOfMap.set(to, SafeMath.add(toBal, value));
387
355
 
388
356
  this.createTransferEvent(sender, to, value);
389
357
  return true;
@@ -393,20 +361,16 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
393
361
  protected _unsafeTransferFrom(from: Address, to: Address, value: u256): boolean {
394
362
  const balance: u256 = this.balanceOfMap.get(from);
395
363
  if (balance < value) {
396
- throw new Revert(
397
- `TransferFrom insufficient balance of ${from.toHex()} is ${balance} and value is ${value}`,
398
- );
364
+ throw new Revert(`TransferFrom insufficient balance`);
399
365
  }
400
366
 
401
- const newBalance: u256 = SafeMath.sub(balance, value);
402
- this.balanceOfMap.set(from, newBalance);
367
+ this.balanceOfMap.set(from, SafeMath.sub(balance, value));
403
368
 
404
369
  if (!this.balanceOfMap.has(to)) {
405
370
  this.balanceOfMap.set(to, value);
406
371
  } else {
407
- const toBalance: u256 = this.balanceOfMap.get(to);
408
- const newToBalance: u256 = SafeMath.add(toBalance, value);
409
- this.balanceOfMap.set(to, newToBalance);
372
+ const toBal: u256 = this.balanceOfMap.get(to);
373
+ this.balanceOfMap.set(to, SafeMath.add(toBal, value));
410
374
  }
411
375
 
412
376
  this.createTransferEvent(from, to, value);
@@ -414,49 +378,38 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
414
378
  }
415
379
 
416
380
  protected _transferFrom(from: Address, to: Address, value: u256): boolean {
417
- if (from === Blockchain.DEAD_ADDRESS) {
418
- throw new Revert('Cannot transfer to or from dead address');
419
- }
381
+ if (from === Blockchain.DEAD_ADDRESS) throw new Revert('Cannot transfer from dead address');
420
382
 
421
383
  this._spendAllowance(from, Blockchain.tx.sender, value);
422
384
  this._unsafeTransferFrom(from, to, value);
423
-
424
385
  return true;
425
386
  }
426
387
 
427
388
  protected _spendAllowance(owner: Address, spender: Address, value: u256): void {
428
- const ownerAllowanceMap = this.allowanceMap.get(owner);
429
- const allowed: u256 = ownerAllowanceMap.get(spender);
389
+ const ownerMap = this.allowanceMap.get(owner);
390
+ const allowed: u256 = ownerMap.get(spender);
430
391
 
431
392
  if (allowed < value) {
432
- throw new Revert(
433
- `Insufficient allowance ${allowed} < ${value}. Spender: ${spender} - Owner: ${owner}`,
434
- );
393
+ throw new Revert('Insufficient allowance');
435
394
  }
436
395
 
437
- const newAllowance: u256 = SafeMath.sub(allowed, value);
438
- ownerAllowanceMap.set(spender, newAllowance);
439
-
440
- this.allowanceMap.set(owner, ownerAllowanceMap);
396
+ ownerMap.set(spender, SafeMath.sub(allowed, value));
397
+ this.allowanceMap.set(owner, ownerMap);
441
398
  }
442
399
 
443
400
  protected createBurnEvent(value: u256): void {
444
- const burnEvent = new BurnEvent(value);
445
- this.emitEvent(burnEvent);
401
+ this.emitEvent(new BurnEvent(value));
446
402
  }
447
403
 
448
404
  protected createApproveEvent(owner: Address, spender: Address, value: u256): void {
449
- const approveEvent = new ApproveEvent(owner, spender, value);
450
- this.emitEvent(approveEvent);
405
+ this.emitEvent(new ApproveEvent(owner, spender, value));
451
406
  }
452
407
 
453
408
  protected createMintEvent(recipient: Address, value: u256): void {
454
- const mintEvent = new MintEvent(recipient, value);
455
- this.emitEvent(mintEvent);
409
+ this.emitEvent(new MintEvent(recipient, value));
456
410
  }
457
411
 
458
412
  protected createTransferEvent(from: Address, to: Address, value: u256): void {
459
- const transferEvent = new TransferEvent(from, to, value);
460
- this.emitEvent(transferEvent);
413
+ this.emitEvent(new TransferEvent(from, to, value));
461
414
  }
462
415
  }
@@ -200,7 +200,7 @@ export class BlockchainEnvironment {
200
200
 
201
201
  public emit(event: NetEvent): void {
202
202
  const data = event.getEventData();
203
- const writer = new BytesWriter(event.eventType.length + 6 + data.byteLength);
203
+ const writer = new BytesWriter(event.eventType.length + 8 + data.byteLength);
204
204
 
205
205
  writer.writeStringWithLength(event.eventType);
206
206
  writer.writeBytesWithLength(data);