1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 | import { splitPath, foreach } from "./utils";
|
21 | import { EthAppPleaseEnableContractData } from "@ledgerhq/errors";
|
22 | import type Transport from "@ledgerhq/hw-transport";
|
23 | import { BigNumber } from "bignumber.js";
|
24 | import { encode, decode } from "rlp";
|
25 |
|
26 | export type StarkQuantizationType =
|
27 | | "eth"
|
28 | | "erc20"
|
29 | | "erc721"
|
30 | | "erc20mintable"
|
31 | | "erc721mintable";
|
32 |
|
33 | const starkQuantizationTypeMap = {
|
34 | eth: 1,
|
35 | erc20: 2,
|
36 | erc721: 3,
|
37 | erc20mintable: 4,
|
38 | erc721mintable: 5,
|
39 | };
|
40 |
|
41 | function hexBuffer(str: string): Buffer {
|
42 | return Buffer.from(str.startsWith("0x") ? str.slice(2) : str, "hex");
|
43 | }
|
44 |
|
45 | function maybeHexBuffer(str: ?string): ?Buffer {
|
46 | if (!str) return null;
|
47 | return hexBuffer(str);
|
48 | }
|
49 |
|
50 | const remapTransactionRelatedErrors = (e) => {
|
51 | if (e && e.statusCode === 0x6a80) {
|
52 | return new EthAppPleaseEnableContractData(
|
53 | "Please enable Contract data on the Ethereum app Settings"
|
54 | );
|
55 | }
|
56 | return e;
|
57 | };
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 | export default class Eth {
|
67 | transport: Transport<*>;
|
68 |
|
69 | constructor(transport: Transport<*>, scrambleKey: string = "w0w") {
|
70 | this.transport = transport;
|
71 | transport.decorateAppAPIMethods(
|
72 | this,
|
73 | [
|
74 | "getAddress",
|
75 | "provideERC20TokenInformation",
|
76 | "signTransaction",
|
77 | "signPersonalMessage",
|
78 | "getAppConfiguration",
|
79 | "signEIP712HashedMessage",
|
80 | "starkGetPublicKey",
|
81 | "starkSignOrder",
|
82 | "starkSignOrder_v2",
|
83 | "starkSignTransfer",
|
84 | "starkSignTransfer_v2",
|
85 | "starkProvideQuantum",
|
86 | "starkProvideQuantum_v2",
|
87 | "starkUnsafeSign",
|
88 | "eth2GetPublicKey",
|
89 | "eth2SetWithdrawalIndex",
|
90 | ],
|
91 | scrambleKey
|
92 | );
|
93 | }
|
94 |
|
95 | |
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 | getAddress(
|
105 | path: string,
|
106 | boolDisplay?: boolean,
|
107 | boolChaincode?: boolean
|
108 | ): Promise<{
|
109 | publicKey: string,
|
110 | address: string,
|
111 | chainCode?: string,
|
112 | }> {
|
113 | let paths = splitPath(path);
|
114 | let buffer = Buffer.alloc(1 + paths.length * 4);
|
115 | buffer[0] = paths.length;
|
116 | paths.forEach((element, index) => {
|
117 | buffer.writeUInt32BE(element, 1 + 4 * index);
|
118 | });
|
119 | return this.transport
|
120 | .send(
|
121 | 0xe0,
|
122 | 0x02,
|
123 | boolDisplay ? 0x01 : 0x00,
|
124 | boolChaincode ? 0x01 : 0x00,
|
125 | buffer
|
126 | )
|
127 | .then((response) => {
|
128 | let result = {};
|
129 | let publicKeyLength = response[0];
|
130 | let addressLength = response[1 + publicKeyLength];
|
131 | result.publicKey = response
|
132 | .slice(1, 1 + publicKeyLength)
|
133 | .toString("hex");
|
134 | result.address =
|
135 | "0x" +
|
136 | response
|
137 | .slice(
|
138 | 1 + publicKeyLength + 1,
|
139 | 1 + publicKeyLength + 1 + addressLength
|
140 | )
|
141 | .toString("ascii");
|
142 | if (boolChaincode) {
|
143 | result.chainCode = response
|
144 | .slice(
|
145 | 1 + publicKeyLength + 1 + addressLength,
|
146 | 1 + publicKeyLength + 1 + addressLength + 32
|
147 | )
|
148 | .toString("hex");
|
149 | }
|
150 | return result;
|
151 | });
|
152 | }
|
153 |
|
154 | |
155 |
|
156 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 |
|
169 | provideERC20TokenInformation({ data }: { data: Buffer }): Promise<boolean> {
|
170 | return this.transport.send(0xe0, 0x0a, 0x00, 0x00, data).then(
|
171 | () => true,
|
172 | (e) => {
|
173 | if (e && e.statusCode === 0x6d00) {
|
174 |
|
175 |
|
176 | return false;
|
177 | }
|
178 | throw e;
|
179 | }
|
180 | );
|
181 | }
|
182 |
|
183 | |
184 |
|
185 |
|
186 |
|
187 |
|
188 | signTransaction(
|
189 | path: string,
|
190 | rawTxHex: string
|
191 | ): Promise<{
|
192 | s: string,
|
193 | v: string,
|
194 | r: string,
|
195 | }> {
|
196 | let paths = splitPath(path);
|
197 | let offset = 0;
|
198 | let rawTx = Buffer.from(rawTxHex, "hex");
|
199 | let toSend = [];
|
200 | let response;
|
201 |
|
202 | let rlpTx = decode(rawTx);
|
203 | let rlpOffset = 0;
|
204 | let chainIdPrefix = "";
|
205 | if (rlpTx.length > 6) {
|
206 | let rlpVrs = encode(rlpTx.slice(-3));
|
207 | rlpOffset = rawTx.length - (rlpVrs.length - 1);
|
208 | const chainIdSrc = rlpTx[6];
|
209 | const chainIdBuf = Buffer.alloc(4);
|
210 | chainIdSrc.copy(chainIdBuf, 4 - chainIdSrc.length);
|
211 | chainIdPrefix = (chainIdBuf.readUInt32BE(0) * 2)
|
212 | .toString(16)
|
213 | .slice(0, -2);
|
214 | }
|
215 | while (offset !== rawTx.length) {
|
216 | let maxChunkSize = offset === 0 ? 150 - 1 - paths.length * 4 : 150;
|
217 | let chunkSize =
|
218 | offset + maxChunkSize > rawTx.length
|
219 | ? rawTx.length - offset
|
220 | : maxChunkSize;
|
221 | if (rlpOffset != 0 && offset + chunkSize == rlpOffset) {
|
222 |
|
223 | chunkSize--;
|
224 | }
|
225 | let buffer = Buffer.alloc(
|
226 | offset === 0 ? 1 + paths.length * 4 + chunkSize : chunkSize
|
227 | );
|
228 | if (offset === 0) {
|
229 | buffer[0] = paths.length;
|
230 | paths.forEach((element, index) => {
|
231 | buffer.writeUInt32BE(element, 1 + 4 * index);
|
232 | });
|
233 | rawTx.copy(buffer, 1 + 4 * paths.length, offset, offset + chunkSize);
|
234 | } else {
|
235 | rawTx.copy(buffer, 0, offset, offset + chunkSize);
|
236 | }
|
237 | toSend.push(buffer);
|
238 | offset += chunkSize;
|
239 | }
|
240 | return foreach(toSend, (data, i) =>
|
241 | this.transport
|
242 | .send(0xe0, 0x04, i === 0 ? 0x00 : 0x80, 0x00, data)
|
243 | .then((apduResponse) => {
|
244 | response = apduResponse;
|
245 | })
|
246 | ).then(
|
247 | () => {
|
248 | const v = chainIdPrefix + response.slice(0, 1).toString("hex");
|
249 | const r = response.slice(1, 1 + 32).toString("hex");
|
250 | const s = response.slice(1 + 32, 1 + 32 + 32).toString("hex");
|
251 | return { v, r, s };
|
252 | },
|
253 | (e) => {
|
254 | throw remapTransactionRelatedErrors(e);
|
255 | }
|
256 | );
|
257 | }
|
258 |
|
259 | |
260 |
|
261 | getAppConfiguration(): Promise<{
|
262 | arbitraryDataEnabled: number,
|
263 | erc20ProvisioningNecessary: number,
|
264 | starkEnabled: number,
|
265 | starkv2Supported: number,
|
266 | version: string,
|
267 | }> {
|
268 | return this.transport.send(0xe0, 0x06, 0x00, 0x00).then((response) => {
|
269 | let result = {};
|
270 | result.arbitraryDataEnabled = response[0] & 0x01;
|
271 | result.erc20ProvisioningNecessary = response[0] & 0x02;
|
272 | result.starkEnabled = response[0] & 0x04;
|
273 | result.starkv2Supported = response[0] & 0x08;
|
274 | result.version = "" + response[1] + "." + response[2] + "." + response[3];
|
275 | return result;
|
276 | });
|
277 | }
|
278 |
|
279 | |
280 |
|
281 |
|
282 |
|
283 |
|
284 |
|
285 |
|
286 |
|
287 |
|
288 |
|
289 |
|
290 |
|
291 | signPersonalMessage(
|
292 | path: string,
|
293 | messageHex: string
|
294 | ): Promise<{
|
295 | v: number,
|
296 | s: string,
|
297 | r: string,
|
298 | }> {
|
299 | let paths = splitPath(path);
|
300 | let offset = 0;
|
301 | let message = Buffer.from(messageHex, "hex");
|
302 | let toSend = [];
|
303 | let response;
|
304 | while (offset !== message.length) {
|
305 | let maxChunkSize = offset === 0 ? 150 - 1 - paths.length * 4 - 4 : 150;
|
306 | let chunkSize =
|
307 | offset + maxChunkSize > message.length
|
308 | ? message.length - offset
|
309 | : maxChunkSize;
|
310 | let buffer = Buffer.alloc(
|
311 | offset === 0 ? 1 + paths.length * 4 + 4 + chunkSize : chunkSize
|
312 | );
|
313 | if (offset === 0) {
|
314 | buffer[0] = paths.length;
|
315 | paths.forEach((element, index) => {
|
316 | buffer.writeUInt32BE(element, 1 + 4 * index);
|
317 | });
|
318 | buffer.writeUInt32BE(message.length, 1 + 4 * paths.length);
|
319 | message.copy(
|
320 | buffer,
|
321 | 1 + 4 * paths.length + 4,
|
322 | offset,
|
323 | offset + chunkSize
|
324 | );
|
325 | } else {
|
326 | message.copy(buffer, 0, offset, offset + chunkSize);
|
327 | }
|
328 | toSend.push(buffer);
|
329 | offset += chunkSize;
|
330 | }
|
331 | return foreach(toSend, (data, i) =>
|
332 | this.transport
|
333 | .send(0xe0, 0x08, i === 0 ? 0x00 : 0x80, 0x00, data)
|
334 | .then((apduResponse) => {
|
335 | response = apduResponse;
|
336 | })
|
337 | ).then(() => {
|
338 | const v = response[0];
|
339 | const r = response.slice(1, 1 + 32).toString("hex");
|
340 | const s = response.slice(1 + 32, 1 + 32 + 32).toString("hex");
|
341 | return { v, r, s };
|
342 | });
|
343 | }
|
344 |
|
345 | |
346 |
|
347 |
|
348 |
|
349 |
|
350 |
|
351 |
|
352 |
|
353 |
|
354 |
|
355 |
|
356 |
|
357 | signEIP712HashedMessage(
|
358 | path: string,
|
359 | domainSeparatorHex: string,
|
360 | hashStructMessageHex: string
|
361 | ): Promise<{
|
362 | v: number,
|
363 | s: string,
|
364 | r: string,
|
365 | }> {
|
366 | const domainSeparator = hexBuffer(domainSeparatorHex);
|
367 | const hashStruct = hexBuffer(hashStructMessageHex);
|
368 | let paths = splitPath(path);
|
369 | let buffer = Buffer.alloc(1 + paths.length * 4 + 32 + 32, 0);
|
370 | let offset = 0;
|
371 | buffer[0] = paths.length;
|
372 | paths.forEach((element, index) => {
|
373 | buffer.writeUInt32BE(element, 1 + 4 * index);
|
374 | });
|
375 | offset = 1 + 4 * paths.length;
|
376 | domainSeparator.copy(buffer, offset);
|
377 | offset += 32;
|
378 | hashStruct.copy(buffer, offset);
|
379 | return this.transport
|
380 | .send(0xe0, 0x0c, 0x00, 0x00, buffer)
|
381 | .then((response) => {
|
382 | const v = response[0];
|
383 | const r = response.slice(1, 1 + 32).toString("hex");
|
384 | const s = response.slice(1 + 32, 1 + 32 + 32).toString("hex");
|
385 | return { v, r, s };
|
386 | });
|
387 | }
|
388 |
|
389 | |
390 |
|
391 |
|
392 |
|
393 |
|
394 |
|
395 | starkGetPublicKey(path: string, boolDisplay?: boolean): Promise<Buffer> {
|
396 | let paths = splitPath(path);
|
397 | let buffer = Buffer.alloc(1 + paths.length * 4);
|
398 | buffer[0] = paths.length;
|
399 | paths.forEach((element, index) => {
|
400 | buffer.writeUInt32BE(element, 1 + 4 * index);
|
401 | });
|
402 | return this.transport
|
403 | .send(0xf0, 0x02, boolDisplay ? 0x01 : 0x00, 0x00, buffer)
|
404 | .then((response) => {
|
405 | return response.slice(0, response.length - 2);
|
406 | });
|
407 | }
|
408 |
|
409 | |
410 |
|
411 |
|
412 |
|
413 |
|
414 |
|
415 |
|
416 |
|
417 |
|
418 |
|
419 |
|
420 |
|
421 |
|
422 |
|
423 |
|
424 | starkSignOrder(
|
425 | path: string,
|
426 | sourceTokenAddress?: string,
|
427 | sourceQuantization: BigNumber,
|
428 | destinationTokenAddress?: string,
|
429 | destinationQuantization: BigNumber,
|
430 | sourceVault: number,
|
431 | destinationVault: number,
|
432 | amountSell: BigNumber,
|
433 | amountBuy: BigNumber,
|
434 | nonce: number,
|
435 | timestamp: number
|
436 | ): Promise<Buffer> {
|
437 | const sourceTokenAddressHex = maybeHexBuffer(sourceTokenAddress);
|
438 | const destinationTokenAddressHex = maybeHexBuffer(destinationTokenAddress);
|
439 | let paths = splitPath(path);
|
440 | let buffer = Buffer.alloc(
|
441 | 1 + paths.length * 4 + 20 + 32 + 20 + 32 + 4 + 4 + 8 + 8 + 4 + 4,
|
442 | 0
|
443 | );
|
444 | let offset = 0;
|
445 | buffer[0] = paths.length;
|
446 | paths.forEach((element, index) => {
|
447 | buffer.writeUInt32BE(element, 1 + 4 * index);
|
448 | });
|
449 | offset = 1 + 4 * paths.length;
|
450 | if (sourceTokenAddressHex) {
|
451 | sourceTokenAddressHex.copy(buffer, offset);
|
452 | }
|
453 | offset += 20;
|
454 | Buffer.from(sourceQuantization.toString(16).padStart(64, "0"), "hex").copy(
|
455 | buffer,
|
456 | offset
|
457 | );
|
458 | offset += 32;
|
459 | if (destinationTokenAddressHex) {
|
460 | destinationTokenAddressHex.copy(buffer, offset);
|
461 | }
|
462 | offset += 20;
|
463 | Buffer.from(
|
464 | destinationQuantization.toString(16).padStart(64, "0"),
|
465 | "hex"
|
466 | ).copy(buffer, offset);
|
467 | offset += 32;
|
468 | buffer.writeUInt32BE(sourceVault, offset);
|
469 | offset += 4;
|
470 | buffer.writeUInt32BE(destinationVault, offset);
|
471 | offset += 4;
|
472 | Buffer.from(amountSell.toString(16).padStart(16, "0"), "hex").copy(
|
473 | buffer,
|
474 | offset
|
475 | );
|
476 | offset += 8;
|
477 | Buffer.from(amountBuy.toString(16).padStart(16, "0"), "hex").copy(
|
478 | buffer,
|
479 | offset
|
480 | );
|
481 | offset += 8;
|
482 | buffer.writeUInt32BE(nonce, offset);
|
483 | offset += 4;
|
484 | buffer.writeUInt32BE(timestamp, offset);
|
485 | return this.transport
|
486 | .send(0xf0, 0x04, 0x01, 0x00, buffer)
|
487 | .then((response) => {
|
488 | const r = response.slice(1, 1 + 32).toString("hex");
|
489 | const s = response.slice(1 + 32, 1 + 32 + 32).toString("hex");
|
490 | return { r, s };
|
491 | });
|
492 | }
|
493 |
|
494 | |
495 |
|
496 |
|
497 |
|
498 |
|
499 |
|
500 |
|
501 |
|
502 |
|
503 |
|
504 |
|
505 |
|
506 |
|
507 |
|
508 |
|
509 |
|
510 |
|
511 |
|
512 |
|
513 | starkSignOrder_v2(
|
514 | path: string,
|
515 | sourceTokenAddress?: string,
|
516 | sourceQuantizationType: StarkQuantizationType,
|
517 | sourceQuantization?: BigNumber,
|
518 | sourceMintableBlobOrTokenId?: BigNumber,
|
519 | destinationTokenAddress?: string,
|
520 | destinationQuantizationType: StarkQuantizationType,
|
521 | destinationQuantization?: BigNumber,
|
522 | destinationMintableBlobOrTokenId?: BigNumber,
|
523 | sourceVault: number,
|
524 | destinationVault: number,
|
525 | amountSell: BigNumber,
|
526 | amountBuy: BigNumber,
|
527 | nonce: number,
|
528 | timestamp: number
|
529 | ): Promise<Buffer> {
|
530 | const sourceTokenAddressHex = maybeHexBuffer(sourceTokenAddress);
|
531 | const destinationTokenAddressHex = maybeHexBuffer(destinationTokenAddress);
|
532 | if (!(sourceQuantizationType in starkQuantizationTypeMap)) {
|
533 | throw new Error(
|
534 | "eth.starkSignOrderv2 invalid source quantization type=" +
|
535 | sourceQuantizationType
|
536 | );
|
537 | }
|
538 | if (!(destinationQuantizationType in starkQuantizationTypeMap)) {
|
539 | throw new Error(
|
540 | "eth.starkSignOrderv2 invalid destination quantization type=" +
|
541 | destinationQuantizationType
|
542 | );
|
543 | }
|
544 | let paths = splitPath(path);
|
545 | let buffer = Buffer.alloc(
|
546 | 1 +
|
547 | paths.length * 4 +
|
548 | 1 +
|
549 | 20 +
|
550 | 32 +
|
551 | 32 +
|
552 | 1 +
|
553 | 20 +
|
554 | 32 +
|
555 | 32 +
|
556 | 4 +
|
557 | 4 +
|
558 | 8 +
|
559 | 8 +
|
560 | 4 +
|
561 | 4,
|
562 | 0
|
563 | );
|
564 | let offset = 0;
|
565 | buffer[0] = paths.length;
|
566 | paths.forEach((element, index) => {
|
567 | buffer.writeUInt32BE(element, 1 + 4 * index);
|
568 | });
|
569 | offset = 1 + 4 * paths.length;
|
570 | buffer[offset] = starkQuantizationTypeMap[sourceQuantizationType];
|
571 | offset++;
|
572 | if (sourceTokenAddressHex) {
|
573 | sourceTokenAddressHex.copy(buffer, offset);
|
574 | }
|
575 | offset += 20;
|
576 | if (sourceQuantization) {
|
577 | Buffer.from(
|
578 | sourceQuantization.toString(16).padStart(64, "0"),
|
579 | "hex"
|
580 | ).copy(buffer, offset);
|
581 | }
|
582 | offset += 32;
|
583 | if (sourceMintableBlobOrTokenId) {
|
584 | Buffer.from(
|
585 | sourceMintableBlobOrTokenId.toString(16).padStart(64, "0"),
|
586 | "hex"
|
587 | ).copy(buffer, offset);
|
588 | }
|
589 | offset += 32;
|
590 | buffer[offset] = starkQuantizationTypeMap[destinationQuantizationType];
|
591 | offset++;
|
592 | if (destinationTokenAddressHex) {
|
593 | destinationTokenAddressHex.copy(buffer, offset);
|
594 | }
|
595 | offset += 20;
|
596 | if (destinationQuantization) {
|
597 | Buffer.from(
|
598 | destinationQuantization.toString(16).padStart(64, "0"),
|
599 | "hex"
|
600 | ).copy(buffer, offset);
|
601 | }
|
602 | offset += 32;
|
603 | if (destinationMintableBlobOrTokenId) {
|
604 | Buffer.from(
|
605 | destinationMintableBlobOrTokenId.toString(16).padStart(64, "0"),
|
606 | "hex"
|
607 | ).copy(buffer, offset);
|
608 | }
|
609 | offset += 32;
|
610 | buffer.writeUInt32BE(sourceVault, offset);
|
611 | offset += 4;
|
612 | buffer.writeUInt32BE(destinationVault, offset);
|
613 | offset += 4;
|
614 | Buffer.from(amountSell.toString(16).padStart(16, "0"), "hex").copy(
|
615 | buffer,
|
616 | offset
|
617 | );
|
618 | offset += 8;
|
619 | Buffer.from(amountBuy.toString(16).padStart(16, "0"), "hex").copy(
|
620 | buffer,
|
621 | offset
|
622 | );
|
623 | offset += 8;
|
624 | buffer.writeUInt32BE(nonce, offset);
|
625 | offset += 4;
|
626 | buffer.writeUInt32BE(timestamp, offset);
|
627 | return this.transport
|
628 | .send(0xf0, 0x04, 0x03, 0x00, buffer)
|
629 | .then((response) => {
|
630 | const r = response.slice(1, 1 + 32).toString("hex");
|
631 | const s = response.slice(1 + 32, 1 + 32 + 32).toString("hex");
|
632 | return { r, s };
|
633 | });
|
634 | }
|
635 |
|
636 | |
637 |
|
638 |
|
639 |
|
640 |
|
641 |
|
642 |
|
643 |
|
644 |
|
645 |
|
646 |
|
647 |
|
648 |
|
649 | starkSignTransfer(
|
650 | path: string,
|
651 | transferTokenAddress?: string,
|
652 | transferQuantization: BigNumber,
|
653 | targetPublicKey: string,
|
654 | sourceVault: number,
|
655 | destinationVault: number,
|
656 | amountTransfer: BigNumber,
|
657 | nonce: number,
|
658 | timestamp: number
|
659 | ): Promise<Buffer> {
|
660 | const transferTokenAddressHex = maybeHexBuffer(transferTokenAddress);
|
661 | const targetPublicKeyHex = hexBuffer(targetPublicKey);
|
662 | let paths = splitPath(path);
|
663 | let buffer = Buffer.alloc(
|
664 | 1 + paths.length * 4 + 20 + 32 + 32 + 4 + 4 + 8 + 4 + 4,
|
665 | 0
|
666 | );
|
667 | let offset = 0;
|
668 | buffer[0] = paths.length;
|
669 | paths.forEach((element, index) => {
|
670 | buffer.writeUInt32BE(element, 1 + 4 * index);
|
671 | });
|
672 | offset = 1 + 4 * paths.length;
|
673 | if (transferTokenAddressHex) {
|
674 | transferTokenAddressHex.copy(buffer, offset);
|
675 | }
|
676 | offset += 20;
|
677 | Buffer.from(
|
678 | transferQuantization.toString(16).padStart(64, "0"),
|
679 | "hex"
|
680 | ).copy(buffer, offset);
|
681 | offset += 32;
|
682 | targetPublicKeyHex.copy(buffer, offset);
|
683 | offset += 32;
|
684 | buffer.writeUInt32BE(sourceVault, offset);
|
685 | offset += 4;
|
686 | buffer.writeUInt32BE(destinationVault, offset);
|
687 | offset += 4;
|
688 | Buffer.from(amountTransfer.toString(16).padStart(16, "0"), "hex").copy(
|
689 | buffer,
|
690 | offset
|
691 | );
|
692 | offset += 8;
|
693 | buffer.writeUInt32BE(nonce, offset);
|
694 | offset += 4;
|
695 | buffer.writeUInt32BE(timestamp, offset);
|
696 | return this.transport
|
697 | .send(0xf0, 0x04, 0x02, 0x00, buffer)
|
698 | .then((response) => {
|
699 | const r = response.slice(1, 1 + 32).toString("hex");
|
700 | const s = response.slice(1 + 32, 1 + 32 + 32).toString("hex");
|
701 | return { r, s };
|
702 | });
|
703 | }
|
704 |
|
705 | |
706 |
|
707 |
|
708 |
|
709 |
|
710 |
|
711 |
|
712 |
|
713 |
|
714 |
|
715 |
|
716 |
|
717 |
|
718 |
|
719 |
|
720 |
|
721 |
|
722 | starkSignTransfer_v2(
|
723 | path: string,
|
724 | transferTokenAddress?: string,
|
725 | transferQuantizationType: StarkQuantizationType,
|
726 | transferQuantization?: BigNumber,
|
727 | transferMintableBlobOrTokenId?: BigNumber,
|
728 | targetPublicKey: string,
|
729 | sourceVault: number,
|
730 | destinationVault: number,
|
731 | amountTransfer: BigNumber,
|
732 | nonce: number,
|
733 | timestamp: number,
|
734 | conditionalTransferAddress?: string,
|
735 | conditionalTransferFact?: BigNumber
|
736 | ): Promise<Buffer> {
|
737 | const transferTokenAddressHex = maybeHexBuffer(transferTokenAddress);
|
738 | const targetPublicKeyHex = hexBuffer(targetPublicKey);
|
739 | const conditionalTransferAddressHex = maybeHexBuffer(
|
740 | conditionalTransferAddress
|
741 | );
|
742 | if (!(transferQuantizationType in starkQuantizationTypeMap)) {
|
743 | throw new Error(
|
744 | "eth.starkSignTransferv2 invalid quantization type=" +
|
745 | transferQuantizationType
|
746 | );
|
747 | }
|
748 | let paths = splitPath(path);
|
749 | let buffer = Buffer.alloc(
|
750 | 1 +
|
751 | paths.length * 4 +
|
752 | 1 +
|
753 | 20 +
|
754 | 32 +
|
755 | 32 +
|
756 | 32 +
|
757 | 4 +
|
758 | 4 +
|
759 | 8 +
|
760 | 4 +
|
761 | 4 +
|
762 | (conditionalTransferAddressHex ? 32 + 20 : 0),
|
763 | 0
|
764 | );
|
765 | let offset = 0;
|
766 | buffer[0] = paths.length;
|
767 | paths.forEach((element, index) => {
|
768 | buffer.writeUInt32BE(element, 1 + 4 * index);
|
769 | });
|
770 | offset = 1 + 4 * paths.length;
|
771 | buffer[offset] = starkQuantizationTypeMap[transferQuantizationType];
|
772 | offset++;
|
773 | if (transferTokenAddressHex) {
|
774 | transferTokenAddressHex.copy(buffer, offset);
|
775 | }
|
776 | offset += 20;
|
777 | if (transferQuantization) {
|
778 | Buffer.from(
|
779 | transferQuantization.toString(16).padStart(64, "0"),
|
780 | "hex"
|
781 | ).copy(buffer, offset);
|
782 | }
|
783 | offset += 32;
|
784 | if (transferMintableBlobOrTokenId) {
|
785 | Buffer.from(
|
786 | transferMintableBlobOrTokenId.toString(16).padStart(64, "0"),
|
787 | "hex"
|
788 | ).copy(buffer, offset);
|
789 | }
|
790 | offset += 32;
|
791 | targetPublicKeyHex.copy(buffer, offset);
|
792 | offset += 32;
|
793 | buffer.writeUInt32BE(sourceVault, offset);
|
794 | offset += 4;
|
795 | buffer.writeUInt32BE(destinationVault, offset);
|
796 | offset += 4;
|
797 | Buffer.from(amountTransfer.toString(16).padStart(16, "0"), "hex").copy(
|
798 | buffer,
|
799 | offset
|
800 | );
|
801 | offset += 8;
|
802 | buffer.writeUInt32BE(nonce, offset);
|
803 | offset += 4;
|
804 | buffer.writeUInt32BE(timestamp, offset);
|
805 | if (conditionalTransferAddressHex && conditionalTransferFact) {
|
806 | offset += 4;
|
807 | Buffer.from(
|
808 | conditionalTransferFact.toString(16).padStart(64, "0"),
|
809 | "hex"
|
810 | ).copy(buffer, offset);
|
811 | offset += 32;
|
812 | conditionalTransferAddressHex.copy(buffer, offset);
|
813 | }
|
814 | return this.transport
|
815 | .send(
|
816 | 0xf0,
|
817 | 0x04,
|
818 | conditionalTransferAddressHex ? 0x05 : 0x04,
|
819 | 0x00,
|
820 | buffer
|
821 | )
|
822 | .then((response) => {
|
823 | const r = response.slice(1, 1 + 32).toString("hex");
|
824 | const s = response.slice(1 + 32, 1 + 32 + 32).toString("hex");
|
825 | return { r, s };
|
826 | });
|
827 | }
|
828 |
|
829 | |
830 |
|
831 |
|
832 |
|
833 |
|
834 |
|
835 |
|
836 |
|
837 | starkProvideQuantum(
|
838 | operationContract?: string,
|
839 | operationQuantization: BigNumber
|
840 | ): Promise<boolean> {
|
841 | const operationContractHex = maybeHexBuffer(operationContract);
|
842 | let buffer = Buffer.alloc(20 + 32, 0);
|
843 | if (operationContractHex) {
|
844 | operationContractHex.copy(buffer, 0);
|
845 | }
|
846 | Buffer.from(
|
847 | operationQuantization.toString(16).padStart(64, "0"),
|
848 | "hex"
|
849 | ).copy(buffer, 20);
|
850 | return this.transport.send(0xf0, 0x08, 0x00, 0x00, buffer).then(
|
851 | () => true,
|
852 | (e) => {
|
853 | if (e && e.statusCode === 0x6d00) {
|
854 |
|
855 | return false;
|
856 | }
|
857 | throw e;
|
858 | }
|
859 | );
|
860 | }
|
861 |
|
862 | |
863 |
|
864 |
|
865 |
|
866 |
|
867 |
|
868 |
|
869 |
|
870 |
|
871 |
|
872 | starkProvideQuantum_v2(
|
873 | operationContract?: string,
|
874 | operationQuantizationType: StarkQuantizationType,
|
875 | operationQuantization?: BigNumber,
|
876 | operationMintableBlobOrTokenId?: BigNumber
|
877 | ): Promise<boolean> {
|
878 | const operationContractHex = maybeHexBuffer(operationContract);
|
879 | if (!(operationQuantizationType in starkQuantizationTypeMap)) {
|
880 | throw new Error(
|
881 | "eth.starkProvideQuantumV2 invalid quantization type=" +
|
882 | operationQuantizationType
|
883 | );
|
884 | }
|
885 | let buffer = Buffer.alloc(20 + 32 + 32, 0);
|
886 | let offset = 0;
|
887 | if (operationContractHex) {
|
888 | operationContractHex.copy(buffer, offset);
|
889 | }
|
890 | offset += 20;
|
891 | if (operationQuantization) {
|
892 | Buffer.from(
|
893 | operationQuantization.toString(16).padStart(64, "0"),
|
894 | "hex"
|
895 | ).copy(buffer, offset);
|
896 | }
|
897 | offset += 32;
|
898 | if (operationMintableBlobOrTokenId) {
|
899 | Buffer.from(
|
900 | operationMintableBlobOrTokenId.toString(16).padStart(64, "0"),
|
901 | "hex"
|
902 | ).copy(buffer, offset);
|
903 | }
|
904 | return this.transport
|
905 | .send(
|
906 | 0xf0,
|
907 | 0x08,
|
908 | starkQuantizationTypeMap[operationQuantizationType],
|
909 | 0x00,
|
910 | buffer
|
911 | )
|
912 | .then(
|
913 | () => true,
|
914 | (e) => {
|
915 | if (e && e.statusCode === 0x6d00) {
|
916 |
|
917 | return false;
|
918 | }
|
919 | throw e;
|
920 | }
|
921 | );
|
922 | }
|
923 |
|
924 | |
925 |
|
926 |
|
927 |
|
928 |
|
929 |
|
930 |
|
931 | starkUnsafeSign(path: string, hash: string): Promise<Buffer> {
|
932 | const hashHex = hexBuffer(hash);
|
933 | let paths = splitPath(path);
|
934 | let buffer = Buffer.alloc(1 + paths.length * 4 + 32);
|
935 | let offset = 0;
|
936 | buffer[0] = paths.length;
|
937 | paths.forEach((element, index) => {
|
938 | buffer.writeUInt32BE(element, 1 + 4 * index);
|
939 | });
|
940 | offset = 1 + 4 * paths.length;
|
941 | hashHex.copy(buffer, offset);
|
942 | return this.transport
|
943 | .send(0xf0, 0x0a, 0x00, 0x00, buffer)
|
944 | .then((response) => {
|
945 | const r = response.slice(1, 1 + 32).toString("hex");
|
946 | const s = response.slice(1 + 32, 1 + 32 + 32).toString("hex");
|
947 | return { r, s };
|
948 | });
|
949 | }
|
950 |
|
951 | |
952 |
|
953 |
|
954 |
|
955 |
|
956 |
|
957 |
|
958 |
|
959 | eth2GetPublicKey(
|
960 | path: string,
|
961 | boolDisplay?: boolean
|
962 | ): Promise<{
|
963 | publicKey: string,
|
964 | }> {
|
965 | let paths = splitPath(path);
|
966 | let buffer = Buffer.alloc(1 + paths.length * 4);
|
967 | buffer[0] = paths.length;
|
968 | paths.forEach((element, index) => {
|
969 | buffer.writeUInt32BE(element, 1 + 4 * index);
|
970 | });
|
971 | return this.transport
|
972 | .send(0xe0, 0x0e, boolDisplay ? 0x01 : 0x00, 0x00, buffer)
|
973 | .then((response) => {
|
974 | let result = {};
|
975 | result.publicKey = response.slice(0, -2).toString("hex");
|
976 | return result;
|
977 | });
|
978 | }
|
979 |
|
980 | |
981 |
|
982 |
|
983 |
|
984 |
|
985 |
|
986 |
|
987 |
|
988 | eth2SetWithdrawalIndex(withdrawalIndex: number): Promise<boolean> {
|
989 | let buffer = Buffer.alloc(4, 0);
|
990 | buffer.writeUInt32BE(withdrawalIndex, 0);
|
991 | return this.transport.send(0xe0, 0x10, 0x00, 0x00, buffer).then(
|
992 | () => true,
|
993 | (e) => {
|
994 | if (e && e.statusCode === 0x6d00) {
|
995 |
|
996 | return false;
|
997 | }
|
998 | throw e;
|
999 | }
|
1000 | );
|
1001 | }
|
1002 | }
|