@btc-vision/btc-runtime 1.0.15 → 1.0.16

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.15",
3
+ "version": "1.0.16",
4
4
  "description": "Bitcoin Smart Contract Runtime",
5
5
  "main": "btc/index.ts",
6
6
  "types": "btc/index.ts",
@@ -176,6 +176,17 @@ export class BytesReader {
176
176
  }
177
177
  }
178
178
 
179
+ public readAddressArray(): Address[] {
180
+ const length = this.readU16();
181
+ const result = new Array<Address>(length);
182
+
183
+ for (let i: u16 = 0; i < length; i++) {
184
+ result[i] = this.readAddress();
185
+ }
186
+
187
+ return result;
188
+ }
189
+
179
190
  private verifyChecksum(): void {
180
191
  const writtenChecksum = this.readU32();
181
192
 
@@ -70,6 +70,16 @@ export class BytesWriter {
70
70
  this.currentOffset += 8;
71
71
  }
72
72
 
73
+ public writeAddressArray(value: Address[]): void {
74
+ if (value.length > 65535) throw new Revert('Array size is too large');
75
+
76
+ this.writeU16(value.length);
77
+
78
+ for (let i: i32 = 0; i < value.length; i++) {
79
+ this.writeAddress(value[i]);
80
+ }
81
+ }
82
+
73
83
  public writeStorage(storage: BlockchainStorage): void {
74
84
  this.writeU32(storage.size);
75
85
 
@@ -14,14 +14,6 @@ import { MultiAddressMemoryMap } from '../memory/MultiAddressMemoryMap';
14
14
  import { StoredU256 } from '../storage/StoredU256';
15
15
  import { ApproveEvent, BurnEvent, MintEvent, TransferEvent } from '../events/predefined';
16
16
 
17
- const allowanceSelector = encodeSelector('allowance');
18
- const approveSelector = encodeSelector('approve');
19
- const balanceOfSelector = encodeSelector('balanceOf');
20
- const burnSelector = encodeSelector('burn');
21
- const mintSelector = encodeSelector('mint');
22
- const transferSelector = encodeSelector('transfer');
23
- const transferFromSelector = encodeSelector('transferFrom');
24
-
25
17
  export abstract class OP_20 extends OP_NET implements IOP_20 {
26
18
  protected readonly allowanceMap: MultiAddressMemoryMap<Address, Address, MemorySlotData<u256>>;
27
19
  protected readonly balanceOfMap: AddressMemoryMap<Address, MemorySlotData<u256>>;
@@ -128,19 +120,19 @@ export abstract class OP_20 extends OP_NET implements IOP_20 {
128
120
 
129
121
  public callMethod(method: Selector, calldata: Calldata): BytesWriter {
130
122
  switch (method) {
131
- case allowanceSelector:
123
+ case encodeSelector('allowance'):
132
124
  return this.allowance(calldata);
133
- case approveSelector:
125
+ case encodeSelector('approve'):
134
126
  return this.approve(calldata);
135
- case balanceOfSelector:
127
+ case encodeSelector('balanceOf'):
136
128
  return this.balanceOf(calldata);
137
- case burnSelector:
129
+ case encodeSelector('burn'):
138
130
  return this.burn(calldata);
139
- case mintSelector:
131
+ case encodeSelector('mint'):
140
132
  return this.mint(calldata);
141
- case transferSelector:
133
+ case encodeSelector('transfer'):
142
134
  return this.transfer(calldata);
143
- case transferFromSelector:
135
+ case encodeSelector('transferFrom'):
144
136
  return this.transferFrom(calldata);
145
137
  default:
146
138
  return super.callMethod(method, calldata);
@@ -174,14 +166,13 @@ export abstract class OP_20 extends OP_NET implements IOP_20 {
174
166
  }
175
167
 
176
168
  /** REDEFINED METHODS */
177
- protected _allowance(owner: string, spender: string): u256 {
169
+ protected _allowance(owner: Address, spender: Address): u256 {
178
170
  const senderMap = this.allowanceMap.get(owner);
179
- if (!senderMap.has(spender)) throw new Revert();
180
171
 
181
172
  return senderMap.get(spender);
182
173
  }
183
174
 
184
- protected _approve(spender: string, value: u256): boolean {
175
+ protected _approve(spender: Address, value: u256): boolean {
185
176
  const callee = Blockchain.callee();
186
177
 
187
178
  const senderMap = this.allowanceMap.get(callee);
@@ -225,10 +216,8 @@ export abstract class OP_20 extends OP_NET implements IOP_20 {
225
216
 
226
217
  protected _mint(to: Address, value: u256, onlyOwner: boolean = true): boolean {
227
218
  const callee = Blockchain.callee();
228
- const caller = Blockchain.caller();
229
219
 
230
220
  if (onlyOwner) this.onlyOwner(callee);
231
- if (caller !== callee) throw new Revert(`callee != caller`);
232
221
 
233
222
  if (!this.balanceOfMap.has(to)) {
234
223
  this.balanceOfMap.set(to, value);
@@ -305,27 +294,19 @@ export abstract class OP_20 extends OP_NET implements IOP_20 {
305
294
  }
306
295
 
307
296
  protected _transferFrom(from: Address, to: Address, value: u256): boolean {
308
- if (!this.allowanceMap.has(from)) throw new Revert();
309
-
310
297
  const spender = Blockchain.callee();
311
298
  if (Blockchain.caller() !== from) {
312
299
  throw new Revert('Not caller.');
313
300
  }
314
301
 
315
- const fromAllowanceMap = this.allowanceMap.get(from);
316
- const allowed: u256 = fromAllowanceMap.get(spender);
317
- if (allowed < value) throw new Revert(`Insufficient allowance`);
318
-
319
302
  if (this.isSelf(spender)) throw new Revert('Can not transfer from self account');
320
303
 
321
- const senderMap = this.allowanceMap.get(from);
322
- if (!senderMap.has(spender)) throw new Revert();
323
-
324
- const allowance: u256 = senderMap.get(spender);
325
- if (allowance < value) throw new Revert(`Insufficient allowance`);
304
+ const fromAllowanceMap = this.allowanceMap.get(from);
305
+ const allowed: u256 = fromAllowanceMap.get(spender);
306
+ if (allowed < value) throw new Revert(`Insufficient allowance ${allowed} < ${value}`);
326
307
 
327
- const newAllowance: u256 = SafeMath.sub(allowance, value);
328
- senderMap.set(spender, newAllowance);
308
+ const newAllowance: u256 = SafeMath.sub(allowed, value);
309
+ fromAllowanceMap.set(from, newAllowance);
329
310
 
330
311
  this._unsafeTransferFrom(from, to, value);
331
312
 
@@ -7,10 +7,6 @@ import { encodeSelector, Selector } from '../math/abi';
7
7
  import { Revert } from '../types/Revert';
8
8
  import { MAX_EVENT_DATA_SIZE, NetEvent } from '../events/NetEvent';
9
9
 
10
- const isAddressOwnerSelector = encodeSelector('isAddressOwner');
11
- const addressSelector = encodeSelector('address');
12
- const ownerSelector = encodeSelector('owner');
13
-
14
10
  export class OP_NET implements IBTC {
15
11
  constructor() {}
16
12
 
@@ -24,7 +20,7 @@ export class OP_NET implements IBTC {
24
20
 
25
21
  public callMethod(method: Selector, calldata: Calldata): BytesWriter {
26
22
  switch (method) {
27
- case isAddressOwnerSelector:
23
+ case encodeSelector('isAddressOwner'):
28
24
  return this.isAddressOwner(calldata);
29
25
  default:
30
26
  throw new Revert('Method not found');
@@ -35,10 +31,10 @@ export class OP_NET implements IBTC {
35
31
  const response = new BytesWriter();
36
32
 
37
33
  switch (method) {
38
- case addressSelector:
34
+ case encodeSelector('address'):
39
35
  response.writeAddress(this.address);
40
36
  break;
41
- case ownerSelector:
37
+ case encodeSelector('owner'):
42
38
  response.writeAddress(this.owner);
43
39
  break;
44
40
  default:
@@ -11,7 +11,15 @@ import { Potential } from '../lang/Definitions';
11
11
  import { Map } from '../generic/Map';
12
12
  import { OP_NET } from '../contracts/OP_NET';
13
13
  import { PointerStorage } from '../types';
14
- import { callContract, deploy, deployFromAddress, loadPointer, log, storePointer } from './global';
14
+ import {
15
+ callContract,
16
+ deploy,
17
+ deployFromAddress,
18
+ encodeAddress,
19
+ loadPointer,
20
+ log,
21
+ storePointer,
22
+ } from './global';
15
23
  import { DeployContractResponse } from '../interfaces/DeployContractResponse';
16
24
 
17
25
  export * from '../env/global';
@@ -119,6 +127,10 @@ export class BlockchainEnvironment {
119
127
  throw this.error('Cannot call self');
120
128
  }
121
129
 
130
+ if (!destinationContract) {
131
+ throw this.error('Destination contract is required');
132
+ }
133
+
122
134
  const call = new BytesWriter();
123
135
  call.writeAddress(destinationContract);
124
136
  call.writeBytesWithLength(calldata.getBuffer());
@@ -164,6 +176,18 @@ export class BlockchainEnvironment {
164
176
  return buffer.getBuffer();
165
177
  }
166
178
 
179
+ public encodeVirtualAddress(virtualAddress: Uint8Array): Address {
180
+ const writer: BytesWriter = new BytesWriter();
181
+ writer.writeBytesWithLength(virtualAddress);
182
+
183
+ const buffer: Uint8Array = writer.getBuffer();
184
+ const cb: Potential<Uint8Array> = encodeAddress(buffer);
185
+ if (!cb) throw this.error('Failed to encode virtual address');
186
+
187
+ const reader: BytesReader = new BytesReader(cb as Uint8Array);
188
+ return reader.readAddress();
189
+ }
190
+
167
191
  public deployContract(hash: u256, bytecode: Uint8Array): BytesReader {
168
192
  const writer = new BytesWriter();
169
193
  writer.writeU256(hash);
@@ -21,3 +21,12 @@ export declare function callContract(data: Uint8Array): Uint8Array;
21
21
  // @ts-ignore
22
22
  @external('env', 'log')
23
23
  export declare function log(data: Uint8Array): void;
24
+
25
+ // @ts-ignore
26
+ @external('env', 'encodeAddress')
27
+ export declare function encodeAddress(data: Uint8Array): Uint8Array;
28
+
29
+
30
+ // @ts-ignore
31
+ @external('env', 'sha256')
32
+ export declare function sha256(data: Uint8Array): Uint8Array;
package/runtime/index.ts CHANGED
@@ -47,3 +47,6 @@ export * from './storage/StoredString';
47
47
 
48
48
  /** Universal */
49
49
  export * from './universal/ABIRegistry';
50
+
51
+ /** Shared libraries */
52
+ export * from './shared-libraries/TransferHelper';
@@ -1,8 +1,8 @@
1
1
  // SO IN TYPESCRIPT, WE CAN NOT USE TWO METHOD WITH THE SAME NAME. SO NOT ADDING THE TYPE TO THE HASH IS A DESIGN CHOICE.
2
2
  import { bytes32, bytes4 } from './bytes';
3
- import { Sha256 } from './sha256';
4
3
  import { MemorySlotPointer } from '../memory/MemorySlotPointer';
5
4
  import { u256 } from 'as-bignum/assembly';
5
+ import { Sha256 } from './sha256';
6
6
 
7
7
  export type Selector = u32;
8
8
 
@@ -1,278 +1,12 @@
1
- type aisize = i32;
2
-
3
- function setU8(t: Uint8Array, s: Uint8Array, o: isize = 0): void {
4
- memory.copy(t.dataStart + o, s.dataStart, s.length);
5
- }
6
-
7
- function store64_be(x: Uint8Array, offset: isize, u: u64): void {
8
- store<u64>(changetype<usize>(x.buffer) + offset, bswap(u));
9
- }
10
-
11
- function load32_be(x: Uint8Array, offset: isize): u32 {
12
- return bswap(load<u32>(changetype<usize>(x.buffer) + offset));
13
- }
14
-
15
- function store32_be(x: Uint8Array, offset: isize, u: u32): void {
16
- store<u32>(changetype<usize>(x.buffer) + offset, bswap(u));
17
- }
18
-
19
- class Internal {
20
- static K: u32[] = [
21
- 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4,
22
- 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe,
23
- 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f,
24
- 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
25
- 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
26
- 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
27
- 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116,
28
- 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
29
- 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7,
30
- 0xc67178f2,
31
- ];
32
- static iv: u8[] = [
33
- 0x6a, 0x09, 0xe6, 0x67, 0xbb, 0x67, 0xae, 0x85, 0x3c, 0x6e, 0xf3, 0x72, 0xa5, 0x4f, 0xf5,
34
- 0x3a, 0x51, 0x0e, 0x52, 0x7f, 0x9b, 0x05, 0x68, 0x8c, 0x1f, 0x83, 0xd9, 0xab, 0x5b, 0xe0,
35
- 0xcd, 0x19,
36
- ];
37
-
38
- @inline
39
- static Sigma0(x: u32): u32 {
40
- return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22);
41
- }
42
-
43
- @inline
44
- static Sigma1(x: u32): u32 {
45
- return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25);
46
- }
47
-
48
- @inline
49
- static sigma0(x: u32): u32 {
50
- return rotr(x, 7) ^ rotr(x, 18) ^ (x >> 3);
51
- }
52
-
53
- @inline
54
- static sigma1(x: u32): u32 {
55
- return rotr(x, 17) ^ rotr(x, 19) ^ (x >> 10);
56
- }
57
-
58
- @inline
59
- static Ch(x: u32, y: u32, z: u32): u32 {
60
- return z ^ (x & (y ^ z));
61
- }
62
-
63
- @inline
64
- static Maj(x: u32, y: u32, z: u32): u32 {
65
- return (x & (y ^ z)) ^ (y & z);
66
- }
67
-
68
- static expand(w: StaticArray<u32>): void {
69
- for (let i = 0; i < 16; i++) {
70
- unchecked(
71
- (w[i] +=
72
- w[(i + 9) & 15] +
73
- Internal.sigma1(w[(i + 14) & 15]) +
74
- Internal.sigma0(w[(i + 1) & 15])),
75
- );
76
- }
77
- }
78
-
79
- static handle(r: StaticArray<u32>, w: StaticArray<u32>, c: u32[]): void {
80
- for (let i = 0; i < 16; i++) {
81
- var x = r[7 & (7 - i)] + w[i] + c[i];
82
- x += unchecked(Internal.Sigma1(r[7 & (4 - i)]));
83
- x += unchecked(Internal.Ch(r[7 & (4 - i)], r[7 & (5 - i)], r[7 & (6 - i)]));
84
- unchecked((r[7 & (3 - i)] += x));
85
- x += unchecked(Internal.Sigma0(r[7 & (0 - i)]));
86
- x += unchecked(Internal.Maj(r[7 & (0 - i)], r[7 & (1 - i)], r[7 & (2 - i)]));
87
- unchecked((r[7 & (7 - i)] = x));
88
- }
89
- }
90
-
91
- static _hashblocks(st: Uint8Array, m: Uint8Array, n_: isize): isize {
92
- let z = new StaticArray<u32>(8),
93
- r = new StaticArray<u32>(8),
94
- w = new StaticArray<u32>(16);
95
- for (let i = 0; i < 8; ++i) {
96
- unchecked((z[i] = r[i] = load32_be(st, i << 2)));
97
- }
98
- let pos = 0,
99
- n = n_;
100
- while (n >= 64) {
101
- for (let i = 0; i < 16; ++i) {
102
- w[i] = load32_be(m, (i << 2) + pos);
103
- }
104
- Internal.handle(r, w, Internal.K.slice(0));
105
- Internal.expand(w);
106
- Internal.handle(r, w, Internal.K.slice(16));
107
- Internal.expand(w);
108
- Internal.handle(r, w, Internal.K.slice(32));
109
- Internal.expand(w);
110
- Internal.handle(r, w, Internal.K.slice(48));
111
- for (let i = 0; i < 8; ++i) {
112
- let x = unchecked(r[i] + z[i]);
113
- unchecked((z[i] = x));
114
- unchecked((r[i] = x));
115
- }
116
- pos += 64;
117
- n -= 64;
118
- }
119
- for (let i = 0; i < 8; ++i) {
120
- store32_be(st, i << 2, unchecked(z[i]));
121
- }
122
- return n;
123
- }
124
-
125
- static _hashInit(): Uint8Array {
126
- let st = new Uint8Array(32 + 64);
127
-
128
- for (let i = 0; i < 32; ++i) {
129
- st[i] = Internal.iv[i];
130
- }
131
- return st;
132
- }
133
-
134
- static _hashUpdate(st: Uint8Array, m: Uint8Array, n: isize, r: isize): isize {
135
- let obuffered = st.subarray(32);
136
- let buffered = new Uint8Array(64);
137
- setU8(buffered, obuffered.subarray(0, 64));
138
-
139
- let still_available_in_buffer = <isize>64 - r;
140
- let copiable_to_buffer = min(n, still_available_in_buffer);
141
- setU8(buffered, m.subarray(0, <aisize>copiable_to_buffer), r);
142
- r += copiable_to_buffer;
143
- n -= copiable_to_buffer;
144
- let pos: isize = 0;
145
- if (r === 64) {
146
- Internal._hashblocks(st, buffered, 64);
147
- r = 0;
148
- pos = copiable_to_buffer;
149
- }
150
- if (n == 0) {
151
- setU8(obuffered, buffered);
152
- return r;
153
- }
154
- let left = m.subarray(<aisize>pos);
155
- r = Internal._hashblocks(st, left, left.length);
156
- if (r > 0) {
157
- setU8(obuffered, left.subarray(left.length - <aisize>r));
158
- }
159
- return r;
160
- }
161
-
162
- static _hashFinal(st: Uint8Array, out: Uint8Array, t: isize, r: isize): void {
163
- let buffered = st.subarray(32);
164
- let padded = new Uint8Array(128);
165
- setU8(padded, buffered.subarray(0, <aisize>r));
166
- padded[<aisize>r] = 0x80;
167
- if (r < 56) {
168
- store64_be(padded, 64 - 8, t << 3);
169
- Internal._hashblocks(st, padded, 64);
170
- } else {
171
- store64_be(padded, 128 - 8, t << 3);
172
- Internal._hashblocks(st, padded, 128);
173
- }
174
- for (let i = 0; i < 32; ++i) {
175
- out[i] = st[i];
176
- }
177
- }
178
-
179
- static _hash(out: Uint8Array, m: Uint8Array, n: isize): void {
180
- let st = Internal._hashInit();
181
- let r = Internal._hashUpdate(st, m, n, 0);
182
-
183
- Internal._hashFinal(st, out, n, r);
184
- }
185
-
186
- static _hmac(m: Uint8Array, k: Uint8Array): Uint8Array {
187
- if (k.length > 64) {
188
- k = Sha256.hash(k);
189
- }
190
- let b = new Uint8Array(64);
191
- setU8(b, k);
192
- for (let i = 0; i < b.length; ++i) {
193
- b[i] ^= 0x36;
194
- }
195
- let out = new Uint8Array(32);
196
- let st = Internal._hashInit();
197
- let r = Internal._hashUpdate(st, b, b.length, 0);
198
- r = Internal._hashUpdate(st, m, m.length, r);
199
- Internal._hashFinal(st, out, b.length + m.length, r);
200
- for (let i = 0; i < b.length; ++i) {
201
- b[i] ^= 0x6a;
202
- }
203
- st = Internal._hashInit();
204
- r = Internal._hashUpdate(st, b, b.length, 0);
205
- r = Internal._hashUpdate(st, out, out.length, r);
206
- Internal._hashFinal(st, out, b.length + out.length, r);
207
- return out;
208
- }
209
- }
210
-
211
- /**
212
- * Hash function output size, in bytes
213
- */
214
- export const SHA256_HASH_BYTES: isize = 32;
1
+ import { sha256 } from '../env/global';
215
2
 
216
3
  export class Sha256 {
217
- r: u64 = 0;
218
- t: u64 = 0;
219
- st: Uint8Array;
220
-
221
- /**
222
- * Initialize a multipart hash computation
223
- * @returns A hash function state
224
- */
225
- constructor() {
226
- this.st = Internal._hashInit();
227
- }
228
-
229
- /**
230
- * Compute a hash for a single-part message
231
- * @param m Message
232
- * @returns Hash
233
- */
234
- static hash(m: Uint8Array): Uint8Array {
235
- let h = new Uint8Array(<aisize>SHA256_HASH_BYTES);
236
- Internal._hash(h, m, m.length);
237
- return h;
238
- }
239
-
240
- /**
241
- * HMAC-SHA-256
242
- * @param m Message
243
- * @param k Key
244
- * @returns `HMAC-SHA-256(m, k)`
245
- */
246
- static hmac(m: Uint8Array, k: Uint8Array): Uint8Array {
247
- return Internal._hmac(m, k);
248
- }
249
-
250
- /**
251
- * Hash256
252
- * @param {Uint8Array} m Message
253
- * @returns {Uint8Array} `SHA-256(SHA-256(m))`
254
- */
255
- static hash256(m: Uint8Array): Uint8Array {
256
- return Sha256.hash(Sha256.hash(m));
257
- }
258
-
259
- /**
260
- * Absorb data to be hashed
261
- * @param m (partial) message
262
- */
263
- update(m: Uint8Array): void {
264
- let n = m.length;
265
- this.t += n;
266
- this.r = Internal._hashUpdate(this.st, m, n, this.r as isize);
4
+ static hash(buffer: Uint8Array): Uint8Array {
5
+ return sha256(buffer);
267
6
  }
268
7
 
269
- /**
270
- * Finalize a hash computation
271
- * @returns Hash
272
- */
273
- final(): Uint8Array {
274
- let h = new Uint8Array(<aisize>SHA256_HASH_BYTES);
275
- Internal._hashFinal(this.st, h, this.t as isize, this.r as isize);
276
- return h;
8
+ static hash256(buffer: Uint8Array): Uint8Array {
9
+ const hash = sha256(buffer);
10
+ return sha256(hash);
277
11
  }
278
12
  }
@@ -0,0 +1,64 @@
1
+ import { u256 } from 'as-bignum/assembly/integer/u256';
2
+ import { encodeSelector, Selector } from '../math/abi';
3
+ import { Address } from '../types/Address';
4
+ import { BytesWriter } from '../buffer/BytesWriter';
5
+ import { Blockchain } from '../env';
6
+ import { Revert } from '../types/Revert';
7
+
8
+ export class TransferHelper {
9
+ public static get APPROVE_SELECTOR(): Selector {
10
+ return encodeSelector('approve');
11
+ }
12
+
13
+ public static get TRANSFER_SELECTOR(): Selector {
14
+ return encodeSelector('transfer');
15
+ }
16
+
17
+ public static get TRANSFER_FROM_SELECTOR(): Selector {
18
+ return encodeSelector('transferFrom');
19
+ }
20
+
21
+ public static safeApprove(token: Address, spender: Address, amount: u256): void {
22
+ const calldata = new BytesWriter();
23
+ calldata.writeSelector(this.APPROVE_SELECTOR);
24
+ calldata.writeAddress(spender);
25
+ calldata.writeU256(amount);
26
+
27
+ const response = Blockchain.call(token, calldata);
28
+ const isOk = response.readBoolean();
29
+
30
+ if (!isOk) {
31
+ throw new Revert(`TransferHelper: APPROVE_FAILED`);
32
+ }
33
+ }
34
+
35
+ public static safeTransfer(token: Address, to: Address, amount: u256): void {
36
+ const calldata = new BytesWriter();
37
+ calldata.writeSelector(this.TRANSFER_SELECTOR);
38
+ calldata.writeAddress(to);
39
+ calldata.writeU256(amount);
40
+
41
+ const response = Blockchain.call(token, calldata);
42
+ const isOk = response.readBoolean();
43
+
44
+ if (!isOk) {
45
+ throw new Revert(`TransferHelper: TRANSFER_FAILED`);
46
+ }
47
+ }
48
+
49
+ public static safeTransferFrom(token: Address, from: Address, to: Address, amount: u256): void {
50
+ const calldata = new BytesWriter();
51
+ calldata.writeSelector(this.TRANSFER_FROM_SELECTOR);
52
+
53
+ calldata.writeAddress(from);
54
+ calldata.writeAddress(to);
55
+ calldata.writeU256(amount);
56
+
57
+ const response = Blockchain.call(token, calldata);
58
+ const isOk = response.readBoolean();
59
+
60
+ if (!isOk) {
61
+ throw new Revert(`TransferHelper: TRANSFER_FROM_FAILED`);
62
+ }
63
+ }
64
+ }