UNPKG

29.5 kBJavaScriptView Raw
1/********************************************************************************
2 * Ledger Node JS API
3 * (c) 2016-2017 Ledger
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 ********************************************************************************/
17// FIXME drop:
18import { splitPath, foreach } from "./utils";
19import { EthAppPleaseEnableContractData } from "@ledgerhq/errors";
20import { BigNumber } from "bignumber.js";
21import { encode, decode } from "rlp";
22const starkQuantizationTypeMap = {
23 eth: 1,
24 erc20: 2,
25 erc721: 3,
26 erc20mintable: 4,
27 erc721mintable: 5
28};
29
30function hexBuffer(str) {
31 return Buffer.from(str.startsWith("0x") ? str.slice(2) : str, "hex");
32}
33
34function maybeHexBuffer(str) {
35 if (!str) return null;
36 return hexBuffer(str);
37}
38
39const remapTransactionRelatedErrors = e => {
40 if (e && e.statusCode === 0x6a80) {
41 return new EthAppPleaseEnableContractData("Please enable Contract data on the Ethereum app Settings");
42 }
43
44 return e;
45};
46/**
47 * Ethereum API
48 *
49 * @example
50 * import Eth from "@ledgerhq/hw-app-eth";
51 * const eth = new Eth(transport)
52 */
53
54
55export default class Eth {
56 constructor(transport, scrambleKey = "w0w") {
57 this.transport = void 0;
58 this.transport = transport;
59 transport.decorateAppAPIMethods(this, ["getAddress", "provideERC20TokenInformation", "signTransaction", "signPersonalMessage", "getAppConfiguration", "signEIP712HashedMessage", "starkGetPublicKey", "starkSignOrder", "starkSignOrder_v2", "starkSignTransfer", "starkSignTransfer_v2", "starkProvideQuantum", "starkProvideQuantum_v2", "starkUnsafeSign", "eth2GetPublicKey", "eth2SetWithdrawalIndex"], scrambleKey);
60 }
61 /**
62 * get Ethereum address for a given BIP 32 path.
63 * @param path a path in BIP 32 format
64 * @option boolDisplay optionally enable or not the display
65 * @option boolChaincode optionally enable or not the chaincode request
66 * @return an object with a publicKey, address and (optionally) chainCode
67 * @example
68 * eth.getAddress("44'/60'/0'/0/0").then(o => o.address)
69 */
70
71
72 getAddress(path, boolDisplay, boolChaincode) {
73 let paths = splitPath(path);
74 let buffer = Buffer.alloc(1 + paths.length * 4);
75 buffer[0] = paths.length;
76 paths.forEach((element, index) => {
77 buffer.writeUInt32BE(element, 1 + 4 * index);
78 });
79 return this.transport.send(0xe0, 0x02, boolDisplay ? 0x01 : 0x00, boolChaincode ? 0x01 : 0x00, buffer).then(response => {
80 let result = {};
81 let publicKeyLength = response[0];
82 let addressLength = response[1 + publicKeyLength];
83 result.publicKey = response.slice(1, 1 + publicKeyLength).toString("hex");
84 result.address = "0x" + response.slice(1 + publicKeyLength + 1, 1 + publicKeyLength + 1 + addressLength).toString("ascii");
85
86 if (boolChaincode) {
87 result.chainCode = response.slice(1 + publicKeyLength + 1 + addressLength, 1 + publicKeyLength + 1 + addressLength + 32).toString("hex");
88 }
89
90 return result;
91 });
92 }
93 /**
94 * This commands provides a trusted description of an ERC 20 token
95 * to associate a contract address with a ticker and number of decimals.
96 *
97 * It shall be run immediately before performing a transaction involving a contract
98 * calling this contract address to display the proper token information to the user if necessary.
99 *
100 * @param {*} info: a blob from "erc20.js" utilities that contains all token information.
101 *
102 * @example
103 * import { byContractAddress } from "@ledgerhq/hw-app-eth/erc20"
104 * const zrxInfo = byContractAddress("0xe41d2489571d322189246dafa5ebde1f4699f498")
105 * if (zrxInfo) await appEth.provideERC20TokenInformation(zrxInfo)
106 * const signed = await appEth.signTransaction(path, rawTxHex)
107 */
108
109
110 provideERC20TokenInformation({
111 data
112 }) {
113 return this.transport.send(0xe0, 0x0a, 0x00, 0x00, data).then(() => true, e => {
114 if (e && e.statusCode === 0x6d00) {
115 // this case happen for older version of ETH app, since older app version had the ERC20 data hardcoded, it's fine to assume it worked.
116 // we return a flag to know if the call was effective or not
117 return false;
118 }
119
120 throw e;
121 });
122 }
123 /**
124 * You can sign a transaction and retrieve v, r, s given the raw transaction and the BIP 32 path of the account to sign
125 * @example
126 eth.signTransaction("44'/60'/0'/0/0", "e8018504e3b292008252089428ee52a8f3d6e5d15f8b131996950d7f296c7952872bd72a2487400080").then(result => ...)
127 */
128
129
130 signTransaction(path, rawTxHex) {
131 let paths = splitPath(path);
132 let offset = 0;
133 let rawTx = Buffer.from(rawTxHex, "hex");
134 let toSend = [];
135 let response; // Check if the TX is encoded following EIP 155
136
137 let rlpTx = decode(rawTx);
138 let rlpOffset = 0;
139 let chainIdPrefix = "";
140
141 if (rlpTx.length > 6) {
142 let rlpVrs = encode(rlpTx.slice(-3));
143 rlpOffset = rawTx.length - (rlpVrs.length - 1);
144 const chainIdSrc = rlpTx[6];
145 const chainIdBuf = Buffer.alloc(4);
146 chainIdSrc.copy(chainIdBuf, 4 - chainIdSrc.length);
147 chainIdPrefix = (chainIdBuf.readUInt32BE(0) * 2).toString(16).slice(0, -2); // Drop the low byte, that comes from the ledger.
148 }
149
150 while (offset !== rawTx.length) {
151 let maxChunkSize = offset === 0 ? 150 - 1 - paths.length * 4 : 150;
152 let chunkSize = offset + maxChunkSize > rawTx.length ? rawTx.length - offset : maxChunkSize;
153
154 if (rlpOffset != 0 && offset + chunkSize == rlpOffset) {
155 // Make sure that the chunk doesn't end right on the EIP 155 marker if set
156 chunkSize--;
157 }
158
159 let buffer = Buffer.alloc(offset === 0 ? 1 + paths.length * 4 + chunkSize : chunkSize);
160
161 if (offset === 0) {
162 buffer[0] = paths.length;
163 paths.forEach((element, index) => {
164 buffer.writeUInt32BE(element, 1 + 4 * index);
165 });
166 rawTx.copy(buffer, 1 + 4 * paths.length, offset, offset + chunkSize);
167 } else {
168 rawTx.copy(buffer, 0, offset, offset + chunkSize);
169 }
170
171 toSend.push(buffer);
172 offset += chunkSize;
173 }
174
175 return foreach(toSend, (data, i) => this.transport.send(0xe0, 0x04, i === 0 ? 0x00 : 0x80, 0x00, data).then(apduResponse => {
176 response = apduResponse;
177 })).then(() => {
178 const v = chainIdPrefix + response.slice(0, 1).toString("hex");
179 const r = response.slice(1, 1 + 32).toString("hex");
180 const s = response.slice(1 + 32, 1 + 32 + 32).toString("hex");
181 return {
182 v,
183 r,
184 s
185 };
186 }, e => {
187 throw remapTransactionRelatedErrors(e);
188 });
189 }
190 /**
191 */
192
193
194 getAppConfiguration() {
195 return this.transport.send(0xe0, 0x06, 0x00, 0x00).then(response => {
196 let result = {};
197 result.arbitraryDataEnabled = response[0] & 0x01;
198 result.erc20ProvisioningNecessary = response[0] & 0x02;
199 result.starkEnabled = response[0] & 0x04;
200 result.starkv2Supported = response[0] & 0x08;
201 result.version = "" + response[1] + "." + response[2] + "." + response[3];
202 return result;
203 });
204 }
205 /**
206 * You can sign a message according to eth_sign RPC call and retrieve v, r, s given the message and the BIP 32 path of the account to sign.
207 * @example
208 eth.signPersonalMessage("44'/60'/0'/0/0", Buffer.from("test").toString("hex")).then(result => {
209 var v = result['v'] - 27;
210 v = v.toString(16);
211 if (v.length < 2) {
212 v = "0" + v;
213 }
214 console.log("Signature 0x" + result['r'] + result['s'] + v);
215 })
216 */
217
218
219 signPersonalMessage(path, messageHex) {
220 let paths = splitPath(path);
221 let offset = 0;
222 let message = Buffer.from(messageHex, "hex");
223 let toSend = [];
224 let response;
225
226 while (offset !== message.length) {
227 let maxChunkSize = offset === 0 ? 150 - 1 - paths.length * 4 - 4 : 150;
228 let chunkSize = offset + maxChunkSize > message.length ? message.length - offset : maxChunkSize;
229 let buffer = Buffer.alloc(offset === 0 ? 1 + paths.length * 4 + 4 + chunkSize : chunkSize);
230
231 if (offset === 0) {
232 buffer[0] = paths.length;
233 paths.forEach((element, index) => {
234 buffer.writeUInt32BE(element, 1 + 4 * index);
235 });
236 buffer.writeUInt32BE(message.length, 1 + 4 * paths.length);
237 message.copy(buffer, 1 + 4 * paths.length + 4, offset, offset + chunkSize);
238 } else {
239 message.copy(buffer, 0, offset, offset + chunkSize);
240 }
241
242 toSend.push(buffer);
243 offset += chunkSize;
244 }
245
246 return foreach(toSend, (data, i) => this.transport.send(0xe0, 0x08, i === 0 ? 0x00 : 0x80, 0x00, data).then(apduResponse => {
247 response = apduResponse;
248 })).then(() => {
249 const v = response[0];
250 const r = response.slice(1, 1 + 32).toString("hex");
251 const s = response.slice(1 + 32, 1 + 32 + 32).toString("hex");
252 return {
253 v,
254 r,
255 s
256 };
257 });
258 }
259 /**
260 * Sign a prepared message following web3.eth.signTypedData specification. The host computes the domain separator and hashStruct(message)
261 * @example
262 eth.signEIP712HashedMessage("44'/60'/0'/0/0", Buffer.from("0101010101010101010101010101010101010101010101010101010101010101").toString("hex"), Buffer.from("0202020202020202020202020202020202020202020202020202020202020202").toString("hex")).then(result => {
263 var v = result['v'] - 27;
264 v = v.toString(16);
265 if (v.length < 2) {
266 v = "0" + v;
267 }
268 console.log("Signature 0x" + result['r'] + result['s'] + v);
269 })
270 */
271
272
273 signEIP712HashedMessage(path, domainSeparatorHex, hashStructMessageHex) {
274 const domainSeparator = hexBuffer(domainSeparatorHex);
275 const hashStruct = hexBuffer(hashStructMessageHex);
276 let paths = splitPath(path);
277 let buffer = Buffer.alloc(1 + paths.length * 4 + 32 + 32, 0);
278 let offset = 0;
279 buffer[0] = paths.length;
280 paths.forEach((element, index) => {
281 buffer.writeUInt32BE(element, 1 + 4 * index);
282 });
283 offset = 1 + 4 * paths.length;
284 domainSeparator.copy(buffer, offset);
285 offset += 32;
286 hashStruct.copy(buffer, offset);
287 return this.transport.send(0xe0, 0x0c, 0x00, 0x00, buffer).then(response => {
288 const v = response[0];
289 const r = response.slice(1, 1 + 32).toString("hex");
290 const s = response.slice(1 + 32, 1 + 32 + 32).toString("hex");
291 return {
292 v,
293 r,
294 s
295 };
296 });
297 }
298 /**
299 * get Stark public key for a given BIP 32 path.
300 * @param path a path in BIP 32 format
301 * @option boolDisplay optionally enable or not the display
302 * @return the Stark public key
303 */
304
305
306 starkGetPublicKey(path, boolDisplay) {
307 let paths = splitPath(path);
308 let buffer = Buffer.alloc(1 + paths.length * 4);
309 buffer[0] = paths.length;
310 paths.forEach((element, index) => {
311 buffer.writeUInt32BE(element, 1 + 4 * index);
312 });
313 return this.transport.send(0xf0, 0x02, boolDisplay ? 0x01 : 0x00, 0x00, buffer).then(response => {
314 return response.slice(0, response.length - 2);
315 });
316 }
317 /**
318 * sign a Stark order
319 * @param path a path in BIP 32 format
320 * @option sourceTokenAddress contract address of the source token (not present for ETH)
321 * @param sourceQuantization quantization used for the source token
322 * @option destinationTokenAddress contract address of the destination token (not present for ETH)
323 * @param destinationQuantization quantization used for the destination token
324 * @param sourceVault ID of the source vault
325 * @param destinationVault ID of the destination vault
326 * @param amountSell amount to sell
327 * @param amountBuy amount to buy
328 * @param nonce transaction nonce
329 * @param timestamp transaction validity timestamp
330 * @return the signature
331 */
332
333
334 starkSignOrder(path, sourceTokenAddress, sourceQuantization, destinationTokenAddress, destinationQuantization, sourceVault, destinationVault, amountSell, amountBuy, nonce, timestamp) {
335 const sourceTokenAddressHex = maybeHexBuffer(sourceTokenAddress);
336 const destinationTokenAddressHex = maybeHexBuffer(destinationTokenAddress);
337 let paths = splitPath(path);
338 let buffer = Buffer.alloc(1 + paths.length * 4 + 20 + 32 + 20 + 32 + 4 + 4 + 8 + 8 + 4 + 4, 0);
339 let offset = 0;
340 buffer[0] = paths.length;
341 paths.forEach((element, index) => {
342 buffer.writeUInt32BE(element, 1 + 4 * index);
343 });
344 offset = 1 + 4 * paths.length;
345
346 if (sourceTokenAddressHex) {
347 sourceTokenAddressHex.copy(buffer, offset);
348 }
349
350 offset += 20;
351 Buffer.from(sourceQuantization.toString(16).padStart(64, "0"), "hex").copy(buffer, offset);
352 offset += 32;
353
354 if (destinationTokenAddressHex) {
355 destinationTokenAddressHex.copy(buffer, offset);
356 }
357
358 offset += 20;
359 Buffer.from(destinationQuantization.toString(16).padStart(64, "0"), "hex").copy(buffer, offset);
360 offset += 32;
361 buffer.writeUInt32BE(sourceVault, offset);
362 offset += 4;
363 buffer.writeUInt32BE(destinationVault, offset);
364 offset += 4;
365 Buffer.from(amountSell.toString(16).padStart(16, "0"), "hex").copy(buffer, offset);
366 offset += 8;
367 Buffer.from(amountBuy.toString(16).padStart(16, "0"), "hex").copy(buffer, offset);
368 offset += 8;
369 buffer.writeUInt32BE(nonce, offset);
370 offset += 4;
371 buffer.writeUInt32BE(timestamp, offset);
372 return this.transport.send(0xf0, 0x04, 0x01, 0x00, buffer).then(response => {
373 const r = response.slice(1, 1 + 32).toString("hex");
374 const s = response.slice(1 + 32, 1 + 32 + 32).toString("hex");
375 return {
376 r,
377 s
378 };
379 });
380 }
381 /**
382 * sign a Stark order using the Starkex V2 protocol
383 * @param path a path in BIP 32 format
384 * @option sourceTokenAddress contract address of the source token (not present for ETH)
385 * @param sourceQuantizationType quantization type used for the source token
386 * @option sourceQuantization quantization used for the source token (not present for erc 721 or mintable erc 721)
387 * @option sourceMintableBlobOrTokenId mintable blob (mintable erc 20 / mintable erc 721) or token id (erc 721) associated to the source token
388 * @option destinationTokenAddress contract address of the destination token (not present for ETH)
389 * @param destinationQuantizationType quantization type used for the destination token
390 * @option destinationQuantization quantization used for the destination token (not present for erc 721 or mintable erc 721)
391 * @option destinationMintableBlobOrTokenId mintable blob (mintable erc 20 / mintable erc 721) or token id (erc 721) associated to the destination token
392 * @param sourceVault ID of the source vault
393 * @param destinationVault ID of the destination vault
394 * @param amountSell amount to sell
395 * @param amountBuy amount to buy
396 * @param nonce transaction nonce
397 * @param timestamp transaction validity timestamp
398 * @return the signature
399 */
400
401
402 starkSignOrder_v2(path, sourceTokenAddress, sourceQuantizationType, sourceQuantization, sourceMintableBlobOrTokenId, destinationTokenAddress, destinationQuantizationType, destinationQuantization, destinationMintableBlobOrTokenId, sourceVault, destinationVault, amountSell, amountBuy, nonce, timestamp) {
403 const sourceTokenAddressHex = maybeHexBuffer(sourceTokenAddress);
404 const destinationTokenAddressHex = maybeHexBuffer(destinationTokenAddress);
405
406 if (!(sourceQuantizationType in starkQuantizationTypeMap)) {
407 throw new Error("eth.starkSignOrderv2 invalid source quantization type=" + sourceQuantizationType);
408 }
409
410 if (!(destinationQuantizationType in starkQuantizationTypeMap)) {
411 throw new Error("eth.starkSignOrderv2 invalid destination quantization type=" + destinationQuantizationType);
412 }
413
414 let paths = splitPath(path);
415 let buffer = Buffer.alloc(1 + paths.length * 4 + 1 + 20 + 32 + 32 + 1 + 20 + 32 + 32 + 4 + 4 + 8 + 8 + 4 + 4, 0);
416 let offset = 0;
417 buffer[0] = paths.length;
418 paths.forEach((element, index) => {
419 buffer.writeUInt32BE(element, 1 + 4 * index);
420 });
421 offset = 1 + 4 * paths.length;
422 buffer[offset] = starkQuantizationTypeMap[sourceQuantizationType];
423 offset++;
424
425 if (sourceTokenAddressHex) {
426 sourceTokenAddressHex.copy(buffer, offset);
427 }
428
429 offset += 20;
430
431 if (sourceQuantization) {
432 Buffer.from(sourceQuantization.toString(16).padStart(64, "0"), "hex").copy(buffer, offset);
433 }
434
435 offset += 32;
436
437 if (sourceMintableBlobOrTokenId) {
438 Buffer.from(sourceMintableBlobOrTokenId.toString(16).padStart(64, "0"), "hex").copy(buffer, offset);
439 }
440
441 offset += 32;
442 buffer[offset] = starkQuantizationTypeMap[destinationQuantizationType];
443 offset++;
444
445 if (destinationTokenAddressHex) {
446 destinationTokenAddressHex.copy(buffer, offset);
447 }
448
449 offset += 20;
450
451 if (destinationQuantization) {
452 Buffer.from(destinationQuantization.toString(16).padStart(64, "0"), "hex").copy(buffer, offset);
453 }
454
455 offset += 32;
456
457 if (destinationMintableBlobOrTokenId) {
458 Buffer.from(destinationMintableBlobOrTokenId.toString(16).padStart(64, "0"), "hex").copy(buffer, offset);
459 }
460
461 offset += 32;
462 buffer.writeUInt32BE(sourceVault, offset);
463 offset += 4;
464 buffer.writeUInt32BE(destinationVault, offset);
465 offset += 4;
466 Buffer.from(amountSell.toString(16).padStart(16, "0"), "hex").copy(buffer, offset);
467 offset += 8;
468 Buffer.from(amountBuy.toString(16).padStart(16, "0"), "hex").copy(buffer, offset);
469 offset += 8;
470 buffer.writeUInt32BE(nonce, offset);
471 offset += 4;
472 buffer.writeUInt32BE(timestamp, offset);
473 return this.transport.send(0xf0, 0x04, 0x03, 0x00, buffer).then(response => {
474 const r = response.slice(1, 1 + 32).toString("hex");
475 const s = response.slice(1 + 32, 1 + 32 + 32).toString("hex");
476 return {
477 r,
478 s
479 };
480 });
481 }
482 /**
483 * sign a Stark transfer
484 * @param path a path in BIP 32 format
485 * @option transferTokenAddress contract address of the token to be transferred (not present for ETH)
486 * @param transferQuantization quantization used for the token to be transferred
487 * @param targetPublicKey target Stark public key
488 * @param sourceVault ID of the source vault
489 * @param destinationVault ID of the destination vault
490 * @param amountTransfer amount to transfer
491 * @param nonce transaction nonce
492 * @param timestamp transaction validity timestamp
493 * @return the signature
494 */
495
496
497 starkSignTransfer(path, transferTokenAddress, transferQuantization, targetPublicKey, sourceVault, destinationVault, amountTransfer, nonce, timestamp) {
498 const transferTokenAddressHex = maybeHexBuffer(transferTokenAddress);
499 const targetPublicKeyHex = hexBuffer(targetPublicKey);
500 let paths = splitPath(path);
501 let buffer = Buffer.alloc(1 + paths.length * 4 + 20 + 32 + 32 + 4 + 4 + 8 + 4 + 4, 0);
502 let offset = 0;
503 buffer[0] = paths.length;
504 paths.forEach((element, index) => {
505 buffer.writeUInt32BE(element, 1 + 4 * index);
506 });
507 offset = 1 + 4 * paths.length;
508
509 if (transferTokenAddressHex) {
510 transferTokenAddressHex.copy(buffer, offset);
511 }
512
513 offset += 20;
514 Buffer.from(transferQuantization.toString(16).padStart(64, "0"), "hex").copy(buffer, offset);
515 offset += 32;
516 targetPublicKeyHex.copy(buffer, offset);
517 offset += 32;
518 buffer.writeUInt32BE(sourceVault, offset);
519 offset += 4;
520 buffer.writeUInt32BE(destinationVault, offset);
521 offset += 4;
522 Buffer.from(amountTransfer.toString(16).padStart(16, "0"), "hex").copy(buffer, offset);
523 offset += 8;
524 buffer.writeUInt32BE(nonce, offset);
525 offset += 4;
526 buffer.writeUInt32BE(timestamp, offset);
527 return this.transport.send(0xf0, 0x04, 0x02, 0x00, buffer).then(response => {
528 const r = response.slice(1, 1 + 32).toString("hex");
529 const s = response.slice(1 + 32, 1 + 32 + 32).toString("hex");
530 return {
531 r,
532 s
533 };
534 });
535 }
536 /**
537 * sign a Stark transfer or conditional transfer using the Starkex V2 protocol
538 * @param path a path in BIP 32 format
539 * @option transferTokenAddress contract address of the token to be transferred (not present for ETH)
540 * @param transferQuantizationType quantization type used for the token to be transferred
541 * @option transferQuantization quantization used for the token to be transferred (not present for erc 721 or mintable erc 721)
542 * @option transferMintableBlobOrTokenId mintable blob (mintable erc 20 / mintable erc 721) or token id (erc 721) associated to the token to be transferred
543 * @param targetPublicKey target Stark public key
544 * @param sourceVault ID of the source vault
545 * @param destinationVault ID of the destination vault
546 * @param amountTransfer amount to transfer
547 * @param nonce transaction nonce
548 * @param timestamp transaction validity timestamp
549 * @option conditionalTransferAddress onchain address of the condition for a conditional transfer
550 * @option conditionalTransferFact fact associated to the condition for a conditional transfer
551 * @return the signature
552 */
553
554
555 starkSignTransfer_v2(path, transferTokenAddress, transferQuantizationType, transferQuantization, transferMintableBlobOrTokenId, targetPublicKey, sourceVault, destinationVault, amountTransfer, nonce, timestamp, conditionalTransferAddress, conditionalTransferFact) {
556 const transferTokenAddressHex = maybeHexBuffer(transferTokenAddress);
557 const targetPublicKeyHex = hexBuffer(targetPublicKey);
558 const conditionalTransferAddressHex = maybeHexBuffer(conditionalTransferAddress);
559
560 if (!(transferQuantizationType in starkQuantizationTypeMap)) {
561 throw new Error("eth.starkSignTransferv2 invalid quantization type=" + transferQuantizationType);
562 }
563
564 let paths = splitPath(path);
565 let buffer = Buffer.alloc(1 + paths.length * 4 + 1 + 20 + 32 + 32 + 32 + 4 + 4 + 8 + 4 + 4 + (conditionalTransferAddressHex ? 32 + 20 : 0), 0);
566 let offset = 0;
567 buffer[0] = paths.length;
568 paths.forEach((element, index) => {
569 buffer.writeUInt32BE(element, 1 + 4 * index);
570 });
571 offset = 1 + 4 * paths.length;
572 buffer[offset] = starkQuantizationTypeMap[transferQuantizationType];
573 offset++;
574
575 if (transferTokenAddressHex) {
576 transferTokenAddressHex.copy(buffer, offset);
577 }
578
579 offset += 20;
580
581 if (transferQuantization) {
582 Buffer.from(transferQuantization.toString(16).padStart(64, "0"), "hex").copy(buffer, offset);
583 }
584
585 offset += 32;
586
587 if (transferMintableBlobOrTokenId) {
588 Buffer.from(transferMintableBlobOrTokenId.toString(16).padStart(64, "0"), "hex").copy(buffer, offset);
589 }
590
591 offset += 32;
592 targetPublicKeyHex.copy(buffer, offset);
593 offset += 32;
594 buffer.writeUInt32BE(sourceVault, offset);
595 offset += 4;
596 buffer.writeUInt32BE(destinationVault, offset);
597 offset += 4;
598 Buffer.from(amountTransfer.toString(16).padStart(16, "0"), "hex").copy(buffer, offset);
599 offset += 8;
600 buffer.writeUInt32BE(nonce, offset);
601 offset += 4;
602 buffer.writeUInt32BE(timestamp, offset);
603
604 if (conditionalTransferAddressHex && conditionalTransferFact) {
605 offset += 4;
606 Buffer.from(conditionalTransferFact.toString(16).padStart(64, "0"), "hex").copy(buffer, offset);
607 offset += 32;
608 conditionalTransferAddressHex.copy(buffer, offset);
609 }
610
611 return this.transport.send(0xf0, 0x04, conditionalTransferAddressHex ? 0x05 : 0x04, 0x00, buffer).then(response => {
612 const r = response.slice(1, 1 + 32).toString("hex");
613 const s = response.slice(1 + 32, 1 + 32 + 32).toString("hex");
614 return {
615 r,
616 s
617 };
618 });
619 }
620 /**
621 * provide quantization information before singing a deposit or withdrawal Stark powered contract call
622 *
623 * It shall be run following a provideERC20TokenInformation call for the given contract
624 *
625 * @param operationContract contract address of the token to be transferred (not present for ETH)
626 * @param operationQuantization quantization used for the token to be transferred
627 */
628
629
630 starkProvideQuantum(operationContract, operationQuantization) {
631 const operationContractHex = maybeHexBuffer(operationContract);
632 let buffer = Buffer.alloc(20 + 32, 0);
633
634 if (operationContractHex) {
635 operationContractHex.copy(buffer, 0);
636 }
637
638 Buffer.from(operationQuantization.toString(16).padStart(64, "0"), "hex").copy(buffer, 20);
639 return this.transport.send(0xf0, 0x08, 0x00, 0x00, buffer).then(() => true, e => {
640 if (e && e.statusCode === 0x6d00) {
641 // this case happen for ETH application versions not supporting Stark extensions
642 return false;
643 }
644
645 throw e;
646 });
647 }
648 /**
649 * provide quantization information before singing a deposit or withdrawal Stark powered contract call using the Starkex V2 protocol
650 *
651 * It shall be run following a provideERC20TokenInformation call for the given contract
652 *
653 * @param operationContract contract address of the token to be transferred (not present for ETH)
654 * @param operationQuantizationType quantization type of the token to be transferred
655 * @option operationQuantization quantization used for the token to be transferred (not present for erc 721 or mintable erc 721)
656 * @option operationMintableBlobOrTokenId mintable blob (mintable erc 20 / mintable erc 721) or token id (erc 721) of the token to be transferred
657 */
658
659
660 starkProvideQuantum_v2(operationContract, operationQuantizationType, operationQuantization, operationMintableBlobOrTokenId) {
661 const operationContractHex = maybeHexBuffer(operationContract);
662
663 if (!(operationQuantizationType in starkQuantizationTypeMap)) {
664 throw new Error("eth.starkProvideQuantumV2 invalid quantization type=" + operationQuantizationType);
665 }
666
667 let buffer = Buffer.alloc(20 + 32 + 32, 0);
668 let offset = 0;
669
670 if (operationContractHex) {
671 operationContractHex.copy(buffer, offset);
672 }
673
674 offset += 20;
675
676 if (operationQuantization) {
677 Buffer.from(operationQuantization.toString(16).padStart(64, "0"), "hex").copy(buffer, offset);
678 }
679
680 offset += 32;
681
682 if (operationMintableBlobOrTokenId) {
683 Buffer.from(operationMintableBlobOrTokenId.toString(16).padStart(64, "0"), "hex").copy(buffer, offset);
684 }
685
686 return this.transport.send(0xf0, 0x08, starkQuantizationTypeMap[operationQuantizationType], 0x00, buffer).then(() => true, e => {
687 if (e && e.statusCode === 0x6d00) {
688 // this case happen for ETH application versions not supporting Stark extensions
689 return false;
690 }
691
692 throw e;
693 });
694 }
695 /**
696 * sign the given hash over the Stark curve
697 * It is intended for speed of execution in case an unknown Stark model is pushed and should be avoided as much as possible.
698 * @param path a path in BIP 32 format
699 * @param hash hexadecimal hash to sign
700 * @return the signature
701 */
702
703
704 starkUnsafeSign(path, hash) {
705 const hashHex = hexBuffer(hash);
706 let paths = splitPath(path);
707 let buffer = Buffer.alloc(1 + paths.length * 4 + 32);
708 let offset = 0;
709 buffer[0] = paths.length;
710 paths.forEach((element, index) => {
711 buffer.writeUInt32BE(element, 1 + 4 * index);
712 });
713 offset = 1 + 4 * paths.length;
714 hashHex.copy(buffer, offset);
715 return this.transport.send(0xf0, 0x0a, 0x00, 0x00, buffer).then(response => {
716 const r = response.slice(1, 1 + 32).toString("hex");
717 const s = response.slice(1 + 32, 1 + 32 + 32).toString("hex");
718 return {
719 r,
720 s
721 };
722 });
723 }
724 /**
725 * get an Ethereum 2 BLS-12 381 public key for a given BIP 32 path.
726 * @param path a path in BIP 32 format
727 * @option boolDisplay optionally enable or not the display
728 * @return an object with a publicKey
729 * @example
730 * eth.eth2GetPublicKey("12381/3600/0/0").then(o => o.publicKey)
731 */
732
733
734 eth2GetPublicKey(path, boolDisplay) {
735 let paths = splitPath(path);
736 let buffer = Buffer.alloc(1 + paths.length * 4);
737 buffer[0] = paths.length;
738 paths.forEach((element, index) => {
739 buffer.writeUInt32BE(element, 1 + 4 * index);
740 });
741 return this.transport.send(0xe0, 0x0e, boolDisplay ? 0x01 : 0x00, 0x00, buffer).then(response => {
742 let result = {};
743 result.publicKey = response.slice(0, -2).toString("hex");
744 return result;
745 });
746 }
747 /**
748 * Set the index of a Withdrawal key used as withdrawal credentials in an ETH 2 deposit contract call signature
749 *
750 * It shall be run before the ETH 2 deposit transaction is signed. If not called, the index is set to 0
751 *
752 * @param withdrawalIndex index path in the EIP 2334 path m/12381/3600/withdrawalIndex/0
753 * @return True if the method was executed successfully
754 */
755
756
757 eth2SetWithdrawalIndex(withdrawalIndex) {
758 let buffer = Buffer.alloc(4, 0);
759 buffer.writeUInt32BE(withdrawalIndex, 0);
760 return this.transport.send(0xe0, 0x10, 0x00, 0x00, buffer).then(() => true, e => {
761 if (e && e.statusCode === 0x6d00) {
762 // this case happen for ETH application versions not supporting ETH 2
763 return false;
764 }
765
766 throw e;
767 });
768 }
769
770}
771//# sourceMappingURL=Eth.js.map
\No newline at end of file