@ledgerhq/hw-app-eth 6.26.1 → 6.27.1
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/lib-es/Eth.js.flow +1120 -0
- package/lib-es/contracts.d.ts +7 -10
- package/lib-es/contracts.d.ts.map +1 -1
- package/lib-es/contracts.js +16 -91
- package/lib-es/contracts.js.flow +24 -0
- package/lib-es/contracts.js.map +1 -1
- package/lib-es/erc20.d.ts +2 -2
- package/lib-es/erc20.d.ts.map +1 -1
- package/lib-es/erc20.js +5 -7
- package/lib-es/erc20.js.flow +80 -0
- package/lib-es/erc20.js.map +1 -1
- package/lib-es/utils.js.flow +100 -0
- package/package.json +5 -5
- package/lib-es/loadConfig.d.ts +0 -7
- package/lib-es/loadConfig.d.ts.map +0 -1
- package/lib-es/loadConfig.js +0 -20
- package/lib-es/loadConfig.js.map +0 -1
- package/lib-es/nfts.d.ts +0 -11
- package/lib-es/nfts.d.ts.map +0 -1
- package/lib-es/nfts.js +0 -94
- package/lib-es/nfts.js.map +0 -1
|
@@ -0,0 +1,1120 @@
|
|
|
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
|
+
//@flow
|
|
18
|
+
|
|
19
|
+
// FIXME drop:
|
|
20
|
+
import { splitPath, foreach } from "./utils";
|
|
21
|
+
import { log } from "@ledgerhq/logs";
|
|
22
|
+
import { EthAppPleaseEnableContractData } from "@ledgerhq/errors";
|
|
23
|
+
import type Transport from "@ledgerhq/hw-transport";
|
|
24
|
+
import { BigNumber } from "bignumber.js";
|
|
25
|
+
import { ethers } from "ethers";
|
|
26
|
+
import { byContractAddress } from "./erc20";
|
|
27
|
+
import { getInfosForContractMethod } from "./contracts";
|
|
28
|
+
|
|
29
|
+
export type StarkQuantizationType =
|
|
30
|
+
| "eth"
|
|
31
|
+
| "erc20"
|
|
32
|
+
| "erc721"
|
|
33
|
+
| "erc20mintable"
|
|
34
|
+
| "erc721mintable";
|
|
35
|
+
|
|
36
|
+
const starkQuantizationTypeMap = {
|
|
37
|
+
eth: 1,
|
|
38
|
+
erc20: 2,
|
|
39
|
+
erc721: 3,
|
|
40
|
+
erc20mintable: 4,
|
|
41
|
+
erc721mintable: 5,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
function hexBuffer(str: string): Buffer {
|
|
45
|
+
return Buffer.from(str.startsWith("0x") ? str.slice(2) : str, "hex");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function maybeHexBuffer(str: ?string): ?Buffer {
|
|
49
|
+
if (!str) return null;
|
|
50
|
+
return hexBuffer(str);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const remapTransactionRelatedErrors = (e) => {
|
|
54
|
+
if (e && e.statusCode === 0x6a80) {
|
|
55
|
+
return new EthAppPleaseEnableContractData(
|
|
56
|
+
"Please enable Contract data on the Ethereum app Settings"
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
return e;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Ethereum API
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* import Eth from "@ledgerhq/hw-app-eth";
|
|
67
|
+
* const eth = new Eth(transport)
|
|
68
|
+
*/
|
|
69
|
+
export default class Eth {
|
|
70
|
+
transport: Transport<*>;
|
|
71
|
+
|
|
72
|
+
constructor(transport: Transport<*>, scrambleKey: string = "w0w") {
|
|
73
|
+
this.transport = transport;
|
|
74
|
+
transport.decorateAppAPIMethods(
|
|
75
|
+
this,
|
|
76
|
+
[
|
|
77
|
+
"getAddress",
|
|
78
|
+
"provideERC20TokenInformation",
|
|
79
|
+
"signTransaction",
|
|
80
|
+
"signPersonalMessage",
|
|
81
|
+
"getAppConfiguration",
|
|
82
|
+
"signEIP712HashedMessage",
|
|
83
|
+
"starkGetPublicKey",
|
|
84
|
+
"starkSignOrder",
|
|
85
|
+
"starkSignOrder_v2",
|
|
86
|
+
"starkSignTransfer",
|
|
87
|
+
"starkSignTransfer_v2",
|
|
88
|
+
"starkProvideQuantum",
|
|
89
|
+
"starkProvideQuantum_v2",
|
|
90
|
+
"starkUnsafeSign",
|
|
91
|
+
"eth2GetPublicKey",
|
|
92
|
+
"eth2SetWithdrawalIndex",
|
|
93
|
+
"setExternalPlugin",
|
|
94
|
+
],
|
|
95
|
+
scrambleKey
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* get Ethereum address for a given BIP 32 path.
|
|
101
|
+
* @param path a path in BIP 32 format
|
|
102
|
+
* @option boolDisplay optionally enable or not the display
|
|
103
|
+
* @option boolChaincode optionally enable or not the chaincode request
|
|
104
|
+
* @return an object with a publicKey, address and (optionally) chainCode
|
|
105
|
+
* @example
|
|
106
|
+
* eth.getAddress("44'/60'/0'/0/0").then(o => o.address)
|
|
107
|
+
*/
|
|
108
|
+
getAddress(
|
|
109
|
+
path: string,
|
|
110
|
+
boolDisplay?: boolean,
|
|
111
|
+
boolChaincode?: boolean
|
|
112
|
+
): Promise<{
|
|
113
|
+
publicKey: string,
|
|
114
|
+
address: string,
|
|
115
|
+
chainCode?: string,
|
|
116
|
+
}> {
|
|
117
|
+
let paths = splitPath(path);
|
|
118
|
+
let buffer = Buffer.alloc(1 + paths.length * 4);
|
|
119
|
+
buffer[0] = paths.length;
|
|
120
|
+
paths.forEach((element, index) => {
|
|
121
|
+
buffer.writeUInt32BE(element, 1 + 4 * index);
|
|
122
|
+
});
|
|
123
|
+
return this.transport
|
|
124
|
+
.send(
|
|
125
|
+
0xe0,
|
|
126
|
+
0x02,
|
|
127
|
+
boolDisplay ? 0x01 : 0x00,
|
|
128
|
+
boolChaincode ? 0x01 : 0x00,
|
|
129
|
+
buffer
|
|
130
|
+
)
|
|
131
|
+
.then((response) => {
|
|
132
|
+
let result = {};
|
|
133
|
+
let publicKeyLength = response[0];
|
|
134
|
+
let addressLength = response[1 + publicKeyLength];
|
|
135
|
+
result.publicKey = response
|
|
136
|
+
.slice(1, 1 + publicKeyLength)
|
|
137
|
+
.toString("hex");
|
|
138
|
+
result.address =
|
|
139
|
+
"0x" +
|
|
140
|
+
response
|
|
141
|
+
.slice(
|
|
142
|
+
1 + publicKeyLength + 1,
|
|
143
|
+
1 + publicKeyLength + 1 + addressLength
|
|
144
|
+
)
|
|
145
|
+
.toString("ascii");
|
|
146
|
+
if (boolChaincode) {
|
|
147
|
+
result.chainCode = response
|
|
148
|
+
.slice(
|
|
149
|
+
1 + publicKeyLength + 1 + addressLength,
|
|
150
|
+
1 + publicKeyLength + 1 + addressLength + 32
|
|
151
|
+
)
|
|
152
|
+
.toString("hex");
|
|
153
|
+
}
|
|
154
|
+
return result;
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* This commands provides a trusted description of an ERC 20 token
|
|
160
|
+
* to associate a contract address with a ticker and number of decimals.
|
|
161
|
+
*
|
|
162
|
+
* It shall be run immediately before performing a transaction involving a contract
|
|
163
|
+
* calling this contract address to display the proper token information to the user if necessary.
|
|
164
|
+
*
|
|
165
|
+
* @param {*} info: a blob from "erc20.js" utilities that contains all token information.
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* import { byContractAddress } from "@ledgerhq/hw-app-eth/erc20"
|
|
169
|
+
* const zrxInfo = byContractAddress("0xe41d2489571d322189246dafa5ebde1f4699f498")
|
|
170
|
+
* if (zrxInfo) await appEth.provideERC20TokenInformation(zrxInfo)
|
|
171
|
+
* const signed = await appEth.signTransaction(path, rawTxHex)
|
|
172
|
+
*/
|
|
173
|
+
provideERC20TokenInformation({ data }: { data: Buffer }): Promise<boolean> {
|
|
174
|
+
return provideERC20TokenInformation(this.transport, data);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* You can sign a transaction and retrieve v, r, s given the raw transaction and the BIP 32 path of the account to sign
|
|
179
|
+
* @example
|
|
180
|
+
eth.signTransaction("44'/60'/0'/0/0", "e8018504e3b292008252089428ee52a8f3d6e5d15f8b131996950d7f296c7952872bd72a2487400080").then(result => ...)
|
|
181
|
+
*/
|
|
182
|
+
async signTransaction(
|
|
183
|
+
path: string,
|
|
184
|
+
rawTxHex: string
|
|
185
|
+
): Promise<{
|
|
186
|
+
s: string,
|
|
187
|
+
v: string,
|
|
188
|
+
r: string,
|
|
189
|
+
}> {
|
|
190
|
+
let paths = splitPath(path);
|
|
191
|
+
let offset = 0;
|
|
192
|
+
let rawTx = Buffer.from(rawTxHex, "hex");
|
|
193
|
+
let toSend = [];
|
|
194
|
+
let response;
|
|
195
|
+
// Check if the TX is encoded following EIP 155
|
|
196
|
+
let rlpTx = ethers.utils.RLP.decode("0x" + rawTxHex).map((hex) =>
|
|
197
|
+
Buffer.from(hex.slice(2), "hex")
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
let rlpOffset = 0;
|
|
201
|
+
let chainIdPrefix = "";
|
|
202
|
+
if (rlpTx.length > 6) {
|
|
203
|
+
let rlpVrs = Buffer.from(
|
|
204
|
+
ethers.utils.RLP.encode(rlpTx.slice(-3)).slice(2),
|
|
205
|
+
"hex"
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
rlpOffset = rawTx.length - (rlpVrs.length - 1);
|
|
209
|
+
const chainIdSrc = rlpTx[6];
|
|
210
|
+
const chainIdBuf = Buffer.alloc(4);
|
|
211
|
+
chainIdSrc.copy(chainIdBuf, 4 - chainIdSrc.length);
|
|
212
|
+
chainIdPrefix = (chainIdBuf.readUInt32BE(0) * 2 + 35)
|
|
213
|
+
.toString(16)
|
|
214
|
+
.slice(0, -2); // Drop the low byte, that comes from the ledger.
|
|
215
|
+
if (chainIdPrefix.length % 2 === 1) {
|
|
216
|
+
chainIdPrefix = "0" + chainIdPrefix;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
while (offset !== rawTx.length) {
|
|
220
|
+
let maxChunkSize = offset === 0 ? 150 - 1 - paths.length * 4 : 150;
|
|
221
|
+
let chunkSize =
|
|
222
|
+
offset + maxChunkSize > rawTx.length
|
|
223
|
+
? rawTx.length - offset
|
|
224
|
+
: maxChunkSize;
|
|
225
|
+
if (rlpOffset != 0 && offset + chunkSize == rlpOffset) {
|
|
226
|
+
// Make sure that the chunk doesn't end right on the EIP 155 marker if set
|
|
227
|
+
chunkSize--;
|
|
228
|
+
}
|
|
229
|
+
let buffer = Buffer.alloc(
|
|
230
|
+
offset === 0 ? 1 + paths.length * 4 + chunkSize : chunkSize
|
|
231
|
+
);
|
|
232
|
+
if (offset === 0) {
|
|
233
|
+
buffer[0] = paths.length;
|
|
234
|
+
paths.forEach((element, index) => {
|
|
235
|
+
buffer.writeUInt32BE(element, 1 + 4 * index);
|
|
236
|
+
});
|
|
237
|
+
rawTx.copy(buffer, 1 + 4 * paths.length, offset, offset + chunkSize);
|
|
238
|
+
} else {
|
|
239
|
+
rawTx.copy(buffer, 0, offset, offset + chunkSize);
|
|
240
|
+
}
|
|
241
|
+
toSend.push(buffer);
|
|
242
|
+
offset += chunkSize;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
rlpTx = ethers.utils.RLP.decode("0x" + rawTxHex);
|
|
246
|
+
|
|
247
|
+
const decodedTx = {
|
|
248
|
+
data: rlpTx[5],
|
|
249
|
+
to: rlpTx[3],
|
|
250
|
+
};
|
|
251
|
+
const provideForContract = async (address) => {
|
|
252
|
+
const erc20Info = byContractAddress(address);
|
|
253
|
+
if (erc20Info) {
|
|
254
|
+
log(
|
|
255
|
+
"ethereum",
|
|
256
|
+
"loading erc20token info for " +
|
|
257
|
+
erc20Info.contractAddress +
|
|
258
|
+
" (" +
|
|
259
|
+
erc20Info.ticker +
|
|
260
|
+
")"
|
|
261
|
+
);
|
|
262
|
+
await provideERC20TokenInformation(this.transport, erc20Info.data);
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
if (decodedTx.data.length >= 10) {
|
|
267
|
+
const selector = decodedTx.data.substring(0, 10);
|
|
268
|
+
const infos = getInfosForContractMethod(decodedTx.to, selector);
|
|
269
|
+
|
|
270
|
+
if (infos) {
|
|
271
|
+
let { plugin, payload, signature, erc20OfInterest, abi } = infos;
|
|
272
|
+
|
|
273
|
+
if (plugin) {
|
|
274
|
+
log("ethereum", "loading plugin for " + selector);
|
|
275
|
+
await setExternalPlugin(this.transport, payload, signature);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (erc20OfInterest && erc20OfInterest.length && abi) {
|
|
279
|
+
const contract = new ethers.utils.Interface(abi);
|
|
280
|
+
const args = contract.parseTransaction(decodedTx).args;
|
|
281
|
+
|
|
282
|
+
for (path of erc20OfInterest) {
|
|
283
|
+
const address = path.split(".").reduce((value, seg) => {
|
|
284
|
+
if (seg === "-1" && Array.isArray(value)) {
|
|
285
|
+
return value[value.length - 1];
|
|
286
|
+
}
|
|
287
|
+
return value[seg];
|
|
288
|
+
}, args);
|
|
289
|
+
await provideForContract(address);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
} else {
|
|
293
|
+
log("ethereum", "no infos for selector " + selector);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
await provideForContract(decodedTx.to);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return foreach(toSend, (data, i) =>
|
|
300
|
+
this.transport
|
|
301
|
+
.send(0xe0, 0x04, i === 0 ? 0x00 : 0x80, 0x00, data)
|
|
302
|
+
.then((apduResponse) => {
|
|
303
|
+
response = apduResponse;
|
|
304
|
+
})
|
|
305
|
+
).then(
|
|
306
|
+
() => {
|
|
307
|
+
const v = chainIdPrefix + response.slice(0, 1).toString("hex");
|
|
308
|
+
const r = response.slice(1, 1 + 32).toString("hex");
|
|
309
|
+
const s = response.slice(1 + 32, 1 + 32 + 32).toString("hex");
|
|
310
|
+
return { v, r, s };
|
|
311
|
+
},
|
|
312
|
+
(e) => {
|
|
313
|
+
throw remapTransactionRelatedErrors(e);
|
|
314
|
+
}
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
*/
|
|
320
|
+
getAppConfiguration(): Promise<{
|
|
321
|
+
arbitraryDataEnabled: number,
|
|
322
|
+
erc20ProvisioningNecessary: number,
|
|
323
|
+
starkEnabled: number,
|
|
324
|
+
starkv2Supported: number,
|
|
325
|
+
version: string,
|
|
326
|
+
}> {
|
|
327
|
+
return this.transport.send(0xe0, 0x06, 0x00, 0x00).then((response) => {
|
|
328
|
+
let result = {};
|
|
329
|
+
result.arbitraryDataEnabled = response[0] & 0x01;
|
|
330
|
+
result.erc20ProvisioningNecessary = response[0] & 0x02;
|
|
331
|
+
result.starkEnabled = response[0] & 0x04;
|
|
332
|
+
result.starkv2Supported = response[0] & 0x08;
|
|
333
|
+
result.version = "" + response[1] + "." + response[2] + "." + response[3];
|
|
334
|
+
return result;
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* 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.
|
|
340
|
+
* @example
|
|
341
|
+
eth.signPersonalMessage("44'/60'/0'/0/0", Buffer.from("test").toString("hex")).then(result => {
|
|
342
|
+
var v = result['v'] - 27;
|
|
343
|
+
v = v.toString(16);
|
|
344
|
+
if (v.length < 2) {
|
|
345
|
+
v = "0" + v;
|
|
346
|
+
}
|
|
347
|
+
console.log("Signature 0x" + result['r'] + result['s'] + v);
|
|
348
|
+
})
|
|
349
|
+
*/
|
|
350
|
+
signPersonalMessage(
|
|
351
|
+
path: string,
|
|
352
|
+
messageHex: string
|
|
353
|
+
): Promise<{
|
|
354
|
+
v: number,
|
|
355
|
+
s: string,
|
|
356
|
+
r: string,
|
|
357
|
+
}> {
|
|
358
|
+
let paths = splitPath(path);
|
|
359
|
+
let offset = 0;
|
|
360
|
+
let message = Buffer.from(messageHex, "hex");
|
|
361
|
+
let toSend = [];
|
|
362
|
+
let response;
|
|
363
|
+
while (offset !== message.length) {
|
|
364
|
+
let maxChunkSize = offset === 0 ? 150 - 1 - paths.length * 4 - 4 : 150;
|
|
365
|
+
let chunkSize =
|
|
366
|
+
offset + maxChunkSize > message.length
|
|
367
|
+
? message.length - offset
|
|
368
|
+
: maxChunkSize;
|
|
369
|
+
let buffer = Buffer.alloc(
|
|
370
|
+
offset === 0 ? 1 + paths.length * 4 + 4 + chunkSize : chunkSize
|
|
371
|
+
);
|
|
372
|
+
if (offset === 0) {
|
|
373
|
+
buffer[0] = paths.length;
|
|
374
|
+
paths.forEach((element, index) => {
|
|
375
|
+
buffer.writeUInt32BE(element, 1 + 4 * index);
|
|
376
|
+
});
|
|
377
|
+
buffer.writeUInt32BE(message.length, 1 + 4 * paths.length);
|
|
378
|
+
message.copy(
|
|
379
|
+
buffer,
|
|
380
|
+
1 + 4 * paths.length + 4,
|
|
381
|
+
offset,
|
|
382
|
+
offset + chunkSize
|
|
383
|
+
);
|
|
384
|
+
} else {
|
|
385
|
+
message.copy(buffer, 0, offset, offset + chunkSize);
|
|
386
|
+
}
|
|
387
|
+
toSend.push(buffer);
|
|
388
|
+
offset += chunkSize;
|
|
389
|
+
}
|
|
390
|
+
return foreach(toSend, (data, i) =>
|
|
391
|
+
this.transport
|
|
392
|
+
.send(0xe0, 0x08, i === 0 ? 0x00 : 0x80, 0x00, data)
|
|
393
|
+
.then((apduResponse) => {
|
|
394
|
+
response = apduResponse;
|
|
395
|
+
})
|
|
396
|
+
).then(() => {
|
|
397
|
+
const v = response[0];
|
|
398
|
+
const r = response.slice(1, 1 + 32).toString("hex");
|
|
399
|
+
const s = response.slice(1 + 32, 1 + 32 + 32).toString("hex");
|
|
400
|
+
return { v, r, s };
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Sign a prepared message following web3.eth.signTypedData specification. The host computes the domain separator and hashStruct(message)
|
|
406
|
+
* @example
|
|
407
|
+
eth.signEIP712HashedMessage("44'/60'/0'/0/0", Buffer.from("0101010101010101010101010101010101010101010101010101010101010101").toString("hex"), Buffer.from("0202020202020202020202020202020202020202020202020202020202020202").toString("hex")).then(result => {
|
|
408
|
+
var v = result['v'] - 27;
|
|
409
|
+
v = v.toString(16);
|
|
410
|
+
if (v.length < 2) {
|
|
411
|
+
v = "0" + v;
|
|
412
|
+
}
|
|
413
|
+
console.log("Signature 0x" + result['r'] + result['s'] + v);
|
|
414
|
+
})
|
|
415
|
+
*/
|
|
416
|
+
signEIP712HashedMessage(
|
|
417
|
+
path: string,
|
|
418
|
+
domainSeparatorHex: string,
|
|
419
|
+
hashStructMessageHex: string
|
|
420
|
+
): Promise<{
|
|
421
|
+
v: number,
|
|
422
|
+
s: string,
|
|
423
|
+
r: string,
|
|
424
|
+
}> {
|
|
425
|
+
const domainSeparator = hexBuffer(domainSeparatorHex);
|
|
426
|
+
const hashStruct = hexBuffer(hashStructMessageHex);
|
|
427
|
+
let paths = splitPath(path);
|
|
428
|
+
let buffer = Buffer.alloc(1 + paths.length * 4 + 32 + 32, 0);
|
|
429
|
+
let offset = 0;
|
|
430
|
+
buffer[0] = paths.length;
|
|
431
|
+
paths.forEach((element, index) => {
|
|
432
|
+
buffer.writeUInt32BE(element, 1 + 4 * index);
|
|
433
|
+
});
|
|
434
|
+
offset = 1 + 4 * paths.length;
|
|
435
|
+
domainSeparator.copy(buffer, offset);
|
|
436
|
+
offset += 32;
|
|
437
|
+
hashStruct.copy(buffer, offset);
|
|
438
|
+
return this.transport
|
|
439
|
+
.send(0xe0, 0x0c, 0x00, 0x00, buffer)
|
|
440
|
+
.then((response) => {
|
|
441
|
+
const v = response[0];
|
|
442
|
+
const r = response.slice(1, 1 + 32).toString("hex");
|
|
443
|
+
const s = response.slice(1 + 32, 1 + 32 + 32).toString("hex");
|
|
444
|
+
return { v, r, s };
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* get Stark public key for a given BIP 32 path.
|
|
450
|
+
* @param path a path in BIP 32 format
|
|
451
|
+
* @option boolDisplay optionally enable or not the display
|
|
452
|
+
* @return the Stark public key
|
|
453
|
+
*/
|
|
454
|
+
starkGetPublicKey(path: string, boolDisplay?: boolean): Promise<Buffer> {
|
|
455
|
+
let paths = splitPath(path);
|
|
456
|
+
let buffer = Buffer.alloc(1 + paths.length * 4);
|
|
457
|
+
buffer[0] = paths.length;
|
|
458
|
+
paths.forEach((element, index) => {
|
|
459
|
+
buffer.writeUInt32BE(element, 1 + 4 * index);
|
|
460
|
+
});
|
|
461
|
+
return this.transport
|
|
462
|
+
.send(0xf0, 0x02, boolDisplay ? 0x01 : 0x00, 0x00, buffer)
|
|
463
|
+
.then((response) => {
|
|
464
|
+
return response.slice(0, response.length - 2);
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* sign a Stark order
|
|
470
|
+
* @param path a path in BIP 32 format
|
|
471
|
+
* @option sourceTokenAddress contract address of the source token (not present for ETH)
|
|
472
|
+
* @param sourceQuantization quantization used for the source token
|
|
473
|
+
* @option destinationTokenAddress contract address of the destination token (not present for ETH)
|
|
474
|
+
* @param destinationQuantization quantization used for the destination token
|
|
475
|
+
* @param sourceVault ID of the source vault
|
|
476
|
+
* @param destinationVault ID of the destination vault
|
|
477
|
+
* @param amountSell amount to sell
|
|
478
|
+
* @param amountBuy amount to buy
|
|
479
|
+
* @param nonce transaction nonce
|
|
480
|
+
* @param timestamp transaction validity timestamp
|
|
481
|
+
* @return the signature
|
|
482
|
+
*/
|
|
483
|
+
starkSignOrder(
|
|
484
|
+
path: string,
|
|
485
|
+
sourceTokenAddress?: string,
|
|
486
|
+
sourceQuantization: BigNumber,
|
|
487
|
+
destinationTokenAddress?: string,
|
|
488
|
+
destinationQuantization: BigNumber,
|
|
489
|
+
sourceVault: number,
|
|
490
|
+
destinationVault: number,
|
|
491
|
+
amountSell: BigNumber,
|
|
492
|
+
amountBuy: BigNumber,
|
|
493
|
+
nonce: number,
|
|
494
|
+
timestamp: number
|
|
495
|
+
): Promise<Buffer> {
|
|
496
|
+
const sourceTokenAddressHex = maybeHexBuffer(sourceTokenAddress);
|
|
497
|
+
const destinationTokenAddressHex = maybeHexBuffer(destinationTokenAddress);
|
|
498
|
+
let paths = splitPath(path);
|
|
499
|
+
let buffer = Buffer.alloc(
|
|
500
|
+
1 + paths.length * 4 + 20 + 32 + 20 + 32 + 4 + 4 + 8 + 8 + 4 + 4,
|
|
501
|
+
0
|
|
502
|
+
);
|
|
503
|
+
let offset = 0;
|
|
504
|
+
buffer[0] = paths.length;
|
|
505
|
+
paths.forEach((element, index) => {
|
|
506
|
+
buffer.writeUInt32BE(element, 1 + 4 * index);
|
|
507
|
+
});
|
|
508
|
+
offset = 1 + 4 * paths.length;
|
|
509
|
+
if (sourceTokenAddressHex) {
|
|
510
|
+
sourceTokenAddressHex.copy(buffer, offset);
|
|
511
|
+
}
|
|
512
|
+
offset += 20;
|
|
513
|
+
Buffer.from(sourceQuantization.toString(16).padStart(64, "0"), "hex").copy(
|
|
514
|
+
buffer,
|
|
515
|
+
offset
|
|
516
|
+
);
|
|
517
|
+
offset += 32;
|
|
518
|
+
if (destinationTokenAddressHex) {
|
|
519
|
+
destinationTokenAddressHex.copy(buffer, offset);
|
|
520
|
+
}
|
|
521
|
+
offset += 20;
|
|
522
|
+
Buffer.from(
|
|
523
|
+
destinationQuantization.toString(16).padStart(64, "0"),
|
|
524
|
+
"hex"
|
|
525
|
+
).copy(buffer, offset);
|
|
526
|
+
offset += 32;
|
|
527
|
+
buffer.writeUInt32BE(sourceVault, offset);
|
|
528
|
+
offset += 4;
|
|
529
|
+
buffer.writeUInt32BE(destinationVault, offset);
|
|
530
|
+
offset += 4;
|
|
531
|
+
Buffer.from(amountSell.toString(16).padStart(16, "0"), "hex").copy(
|
|
532
|
+
buffer,
|
|
533
|
+
offset
|
|
534
|
+
);
|
|
535
|
+
offset += 8;
|
|
536
|
+
Buffer.from(amountBuy.toString(16).padStart(16, "0"), "hex").copy(
|
|
537
|
+
buffer,
|
|
538
|
+
offset
|
|
539
|
+
);
|
|
540
|
+
offset += 8;
|
|
541
|
+
buffer.writeUInt32BE(nonce, offset);
|
|
542
|
+
offset += 4;
|
|
543
|
+
buffer.writeUInt32BE(timestamp, offset);
|
|
544
|
+
return this.transport
|
|
545
|
+
.send(0xf0, 0x04, 0x01, 0x00, buffer)
|
|
546
|
+
.then((response) => {
|
|
547
|
+
const r = response.slice(1, 1 + 32).toString("hex");
|
|
548
|
+
const s = response.slice(1 + 32, 1 + 32 + 32).toString("hex");
|
|
549
|
+
return { r, s };
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* sign a Stark order using the Starkex V2 protocol
|
|
555
|
+
* @param path a path in BIP 32 format
|
|
556
|
+
* @option sourceTokenAddress contract address of the source token (not present for ETH)
|
|
557
|
+
* @param sourceQuantizationType quantization type used for the source token
|
|
558
|
+
* @option sourceQuantization quantization used for the source token (not present for erc 721 or mintable erc 721)
|
|
559
|
+
* @option sourceMintableBlobOrTokenId mintable blob (mintable erc 20 / mintable erc 721) or token id (erc 721) associated to the source token
|
|
560
|
+
* @option destinationTokenAddress contract address of the destination token (not present for ETH)
|
|
561
|
+
* @param destinationQuantizationType quantization type used for the destination token
|
|
562
|
+
* @option destinationQuantization quantization used for the destination token (not present for erc 721 or mintable erc 721)
|
|
563
|
+
* @option destinationMintableBlobOrTokenId mintable blob (mintable erc 20 / mintable erc 721) or token id (erc 721) associated to the destination token
|
|
564
|
+
* @param sourceVault ID of the source vault
|
|
565
|
+
* @param destinationVault ID of the destination vault
|
|
566
|
+
* @param amountSell amount to sell
|
|
567
|
+
* @param amountBuy amount to buy
|
|
568
|
+
* @param nonce transaction nonce
|
|
569
|
+
* @param timestamp transaction validity timestamp
|
|
570
|
+
* @return the signature
|
|
571
|
+
*/
|
|
572
|
+
starkSignOrder_v2(
|
|
573
|
+
path: string,
|
|
574
|
+
sourceTokenAddress?: string,
|
|
575
|
+
sourceQuantizationType: StarkQuantizationType,
|
|
576
|
+
sourceQuantization?: BigNumber,
|
|
577
|
+
sourceMintableBlobOrTokenId?: BigNumber,
|
|
578
|
+
destinationTokenAddress?: string,
|
|
579
|
+
destinationQuantizationType: StarkQuantizationType,
|
|
580
|
+
destinationQuantization?: BigNumber,
|
|
581
|
+
destinationMintableBlobOrTokenId?: BigNumber,
|
|
582
|
+
sourceVault: number,
|
|
583
|
+
destinationVault: number,
|
|
584
|
+
amountSell: BigNumber,
|
|
585
|
+
amountBuy: BigNumber,
|
|
586
|
+
nonce: number,
|
|
587
|
+
timestamp: number
|
|
588
|
+
): Promise<Buffer> {
|
|
589
|
+
const sourceTokenAddressHex = maybeHexBuffer(sourceTokenAddress);
|
|
590
|
+
const destinationTokenAddressHex = maybeHexBuffer(destinationTokenAddress);
|
|
591
|
+
if (!(sourceQuantizationType in starkQuantizationTypeMap)) {
|
|
592
|
+
throw new Error(
|
|
593
|
+
"eth.starkSignOrderv2 invalid source quantization type=" +
|
|
594
|
+
sourceQuantizationType
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
if (!(destinationQuantizationType in starkQuantizationTypeMap)) {
|
|
598
|
+
throw new Error(
|
|
599
|
+
"eth.starkSignOrderv2 invalid destination quantization type=" +
|
|
600
|
+
destinationQuantizationType
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
let paths = splitPath(path);
|
|
604
|
+
let buffer = Buffer.alloc(
|
|
605
|
+
1 +
|
|
606
|
+
paths.length * 4 +
|
|
607
|
+
1 +
|
|
608
|
+
20 +
|
|
609
|
+
32 +
|
|
610
|
+
32 +
|
|
611
|
+
1 +
|
|
612
|
+
20 +
|
|
613
|
+
32 +
|
|
614
|
+
32 +
|
|
615
|
+
4 +
|
|
616
|
+
4 +
|
|
617
|
+
8 +
|
|
618
|
+
8 +
|
|
619
|
+
4 +
|
|
620
|
+
4,
|
|
621
|
+
0
|
|
622
|
+
);
|
|
623
|
+
let offset = 0;
|
|
624
|
+
buffer[0] = paths.length;
|
|
625
|
+
paths.forEach((element, index) => {
|
|
626
|
+
buffer.writeUInt32BE(element, 1 + 4 * index);
|
|
627
|
+
});
|
|
628
|
+
offset = 1 + 4 * paths.length;
|
|
629
|
+
buffer[offset] = starkQuantizationTypeMap[sourceQuantizationType];
|
|
630
|
+
offset++;
|
|
631
|
+
if (sourceTokenAddressHex) {
|
|
632
|
+
sourceTokenAddressHex.copy(buffer, offset);
|
|
633
|
+
}
|
|
634
|
+
offset += 20;
|
|
635
|
+
if (sourceQuantization) {
|
|
636
|
+
Buffer.from(
|
|
637
|
+
sourceQuantization.toString(16).padStart(64, "0"),
|
|
638
|
+
"hex"
|
|
639
|
+
).copy(buffer, offset);
|
|
640
|
+
}
|
|
641
|
+
offset += 32;
|
|
642
|
+
if (sourceMintableBlobOrTokenId) {
|
|
643
|
+
Buffer.from(
|
|
644
|
+
sourceMintableBlobOrTokenId.toString(16).padStart(64, "0"),
|
|
645
|
+
"hex"
|
|
646
|
+
).copy(buffer, offset);
|
|
647
|
+
}
|
|
648
|
+
offset += 32;
|
|
649
|
+
buffer[offset] = starkQuantizationTypeMap[destinationQuantizationType];
|
|
650
|
+
offset++;
|
|
651
|
+
if (destinationTokenAddressHex) {
|
|
652
|
+
destinationTokenAddressHex.copy(buffer, offset);
|
|
653
|
+
}
|
|
654
|
+
offset += 20;
|
|
655
|
+
if (destinationQuantization) {
|
|
656
|
+
Buffer.from(
|
|
657
|
+
destinationQuantization.toString(16).padStart(64, "0"),
|
|
658
|
+
"hex"
|
|
659
|
+
).copy(buffer, offset);
|
|
660
|
+
}
|
|
661
|
+
offset += 32;
|
|
662
|
+
if (destinationMintableBlobOrTokenId) {
|
|
663
|
+
Buffer.from(
|
|
664
|
+
destinationMintableBlobOrTokenId.toString(16).padStart(64, "0"),
|
|
665
|
+
"hex"
|
|
666
|
+
).copy(buffer, offset);
|
|
667
|
+
}
|
|
668
|
+
offset += 32;
|
|
669
|
+
buffer.writeUInt32BE(sourceVault, offset);
|
|
670
|
+
offset += 4;
|
|
671
|
+
buffer.writeUInt32BE(destinationVault, offset);
|
|
672
|
+
offset += 4;
|
|
673
|
+
Buffer.from(amountSell.toString(16).padStart(16, "0"), "hex").copy(
|
|
674
|
+
buffer,
|
|
675
|
+
offset
|
|
676
|
+
);
|
|
677
|
+
offset += 8;
|
|
678
|
+
Buffer.from(amountBuy.toString(16).padStart(16, "0"), "hex").copy(
|
|
679
|
+
buffer,
|
|
680
|
+
offset
|
|
681
|
+
);
|
|
682
|
+
offset += 8;
|
|
683
|
+
buffer.writeUInt32BE(nonce, offset);
|
|
684
|
+
offset += 4;
|
|
685
|
+
buffer.writeUInt32BE(timestamp, offset);
|
|
686
|
+
return this.transport
|
|
687
|
+
.send(0xf0, 0x04, 0x03, 0x00, buffer)
|
|
688
|
+
.then((response) => {
|
|
689
|
+
const r = response.slice(1, 1 + 32).toString("hex");
|
|
690
|
+
const s = response.slice(1 + 32, 1 + 32 + 32).toString("hex");
|
|
691
|
+
return { r, s };
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* sign a Stark transfer
|
|
697
|
+
* @param path a path in BIP 32 format
|
|
698
|
+
* @option transferTokenAddress contract address of the token to be transferred (not present for ETH)
|
|
699
|
+
* @param transferQuantization quantization used for the token to be transferred
|
|
700
|
+
* @param targetPublicKey target Stark public key
|
|
701
|
+
* @param sourceVault ID of the source vault
|
|
702
|
+
* @param destinationVault ID of the destination vault
|
|
703
|
+
* @param amountTransfer amount to transfer
|
|
704
|
+
* @param nonce transaction nonce
|
|
705
|
+
* @param timestamp transaction validity timestamp
|
|
706
|
+
* @return the signature
|
|
707
|
+
*/
|
|
708
|
+
starkSignTransfer(
|
|
709
|
+
path: string,
|
|
710
|
+
transferTokenAddress?: string,
|
|
711
|
+
transferQuantization: BigNumber,
|
|
712
|
+
targetPublicKey: string,
|
|
713
|
+
sourceVault: number,
|
|
714
|
+
destinationVault: number,
|
|
715
|
+
amountTransfer: BigNumber,
|
|
716
|
+
nonce: number,
|
|
717
|
+
timestamp: number
|
|
718
|
+
): Promise<Buffer> {
|
|
719
|
+
const transferTokenAddressHex = maybeHexBuffer(transferTokenAddress);
|
|
720
|
+
const targetPublicKeyHex = hexBuffer(targetPublicKey);
|
|
721
|
+
let paths = splitPath(path);
|
|
722
|
+
let buffer = Buffer.alloc(
|
|
723
|
+
1 + paths.length * 4 + 20 + 32 + 32 + 4 + 4 + 8 + 4 + 4,
|
|
724
|
+
0
|
|
725
|
+
);
|
|
726
|
+
let offset = 0;
|
|
727
|
+
buffer[0] = paths.length;
|
|
728
|
+
paths.forEach((element, index) => {
|
|
729
|
+
buffer.writeUInt32BE(element, 1 + 4 * index);
|
|
730
|
+
});
|
|
731
|
+
offset = 1 + 4 * paths.length;
|
|
732
|
+
if (transferTokenAddressHex) {
|
|
733
|
+
transferTokenAddressHex.copy(buffer, offset);
|
|
734
|
+
}
|
|
735
|
+
offset += 20;
|
|
736
|
+
Buffer.from(
|
|
737
|
+
transferQuantization.toString(16).padStart(64, "0"),
|
|
738
|
+
"hex"
|
|
739
|
+
).copy(buffer, offset);
|
|
740
|
+
offset += 32;
|
|
741
|
+
targetPublicKeyHex.copy(buffer, offset);
|
|
742
|
+
offset += 32;
|
|
743
|
+
buffer.writeUInt32BE(sourceVault, offset);
|
|
744
|
+
offset += 4;
|
|
745
|
+
buffer.writeUInt32BE(destinationVault, offset);
|
|
746
|
+
offset += 4;
|
|
747
|
+
Buffer.from(amountTransfer.toString(16).padStart(16, "0"), "hex").copy(
|
|
748
|
+
buffer,
|
|
749
|
+
offset
|
|
750
|
+
);
|
|
751
|
+
offset += 8;
|
|
752
|
+
buffer.writeUInt32BE(nonce, offset);
|
|
753
|
+
offset += 4;
|
|
754
|
+
buffer.writeUInt32BE(timestamp, offset);
|
|
755
|
+
return this.transport
|
|
756
|
+
.send(0xf0, 0x04, 0x02, 0x00, buffer)
|
|
757
|
+
.then((response) => {
|
|
758
|
+
const r = response.slice(1, 1 + 32).toString("hex");
|
|
759
|
+
const s = response.slice(1 + 32, 1 + 32 + 32).toString("hex");
|
|
760
|
+
return { r, s };
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* sign a Stark transfer or conditional transfer using the Starkex V2 protocol
|
|
766
|
+
* @param path a path in BIP 32 format
|
|
767
|
+
* @option transferTokenAddress contract address of the token to be transferred (not present for ETH)
|
|
768
|
+
* @param transferQuantizationType quantization type used for the token to be transferred
|
|
769
|
+
* @option transferQuantization quantization used for the token to be transferred (not present for erc 721 or mintable erc 721)
|
|
770
|
+
* @option transferMintableBlobOrTokenId mintable blob (mintable erc 20 / mintable erc 721) or token id (erc 721) associated to the token to be transferred
|
|
771
|
+
* @param targetPublicKey target Stark public key
|
|
772
|
+
* @param sourceVault ID of the source vault
|
|
773
|
+
* @param destinationVault ID of the destination vault
|
|
774
|
+
* @param amountTransfer amount to transfer
|
|
775
|
+
* @param nonce transaction nonce
|
|
776
|
+
* @param timestamp transaction validity timestamp
|
|
777
|
+
* @option conditionalTransferAddress onchain address of the condition for a conditional transfer
|
|
778
|
+
* @option conditionalTransferFact fact associated to the condition for a conditional transfer
|
|
779
|
+
* @return the signature
|
|
780
|
+
*/
|
|
781
|
+
starkSignTransfer_v2(
|
|
782
|
+
path: string,
|
|
783
|
+
transferTokenAddress?: string,
|
|
784
|
+
transferQuantizationType: StarkQuantizationType,
|
|
785
|
+
transferQuantization?: BigNumber,
|
|
786
|
+
transferMintableBlobOrTokenId?: BigNumber,
|
|
787
|
+
targetPublicKey: string,
|
|
788
|
+
sourceVault: number,
|
|
789
|
+
destinationVault: number,
|
|
790
|
+
amountTransfer: BigNumber,
|
|
791
|
+
nonce: number,
|
|
792
|
+
timestamp: number,
|
|
793
|
+
conditionalTransferAddress?: string,
|
|
794
|
+
conditionalTransferFact?: BigNumber
|
|
795
|
+
): Promise<Buffer> {
|
|
796
|
+
const transferTokenAddressHex = maybeHexBuffer(transferTokenAddress);
|
|
797
|
+
const targetPublicKeyHex = hexBuffer(targetPublicKey);
|
|
798
|
+
const conditionalTransferAddressHex = maybeHexBuffer(
|
|
799
|
+
conditionalTransferAddress
|
|
800
|
+
);
|
|
801
|
+
if (!(transferQuantizationType in starkQuantizationTypeMap)) {
|
|
802
|
+
throw new Error(
|
|
803
|
+
"eth.starkSignTransferv2 invalid quantization type=" +
|
|
804
|
+
transferQuantizationType
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
let paths = splitPath(path);
|
|
808
|
+
let buffer = Buffer.alloc(
|
|
809
|
+
1 +
|
|
810
|
+
paths.length * 4 +
|
|
811
|
+
1 +
|
|
812
|
+
20 +
|
|
813
|
+
32 +
|
|
814
|
+
32 +
|
|
815
|
+
32 +
|
|
816
|
+
4 +
|
|
817
|
+
4 +
|
|
818
|
+
8 +
|
|
819
|
+
4 +
|
|
820
|
+
4 +
|
|
821
|
+
(conditionalTransferAddressHex ? 32 + 20 : 0),
|
|
822
|
+
0
|
|
823
|
+
);
|
|
824
|
+
let offset = 0;
|
|
825
|
+
buffer[0] = paths.length;
|
|
826
|
+
paths.forEach((element, index) => {
|
|
827
|
+
buffer.writeUInt32BE(element, 1 + 4 * index);
|
|
828
|
+
});
|
|
829
|
+
offset = 1 + 4 * paths.length;
|
|
830
|
+
buffer[offset] = starkQuantizationTypeMap[transferQuantizationType];
|
|
831
|
+
offset++;
|
|
832
|
+
if (transferTokenAddressHex) {
|
|
833
|
+
transferTokenAddressHex.copy(buffer, offset);
|
|
834
|
+
}
|
|
835
|
+
offset += 20;
|
|
836
|
+
if (transferQuantization) {
|
|
837
|
+
Buffer.from(
|
|
838
|
+
transferQuantization.toString(16).padStart(64, "0"),
|
|
839
|
+
"hex"
|
|
840
|
+
).copy(buffer, offset);
|
|
841
|
+
}
|
|
842
|
+
offset += 32;
|
|
843
|
+
if (transferMintableBlobOrTokenId) {
|
|
844
|
+
Buffer.from(
|
|
845
|
+
transferMintableBlobOrTokenId.toString(16).padStart(64, "0"),
|
|
846
|
+
"hex"
|
|
847
|
+
).copy(buffer, offset);
|
|
848
|
+
}
|
|
849
|
+
offset += 32;
|
|
850
|
+
targetPublicKeyHex.copy(buffer, offset);
|
|
851
|
+
offset += 32;
|
|
852
|
+
buffer.writeUInt32BE(sourceVault, offset);
|
|
853
|
+
offset += 4;
|
|
854
|
+
buffer.writeUInt32BE(destinationVault, offset);
|
|
855
|
+
offset += 4;
|
|
856
|
+
Buffer.from(amountTransfer.toString(16).padStart(16, "0"), "hex").copy(
|
|
857
|
+
buffer,
|
|
858
|
+
offset
|
|
859
|
+
);
|
|
860
|
+
offset += 8;
|
|
861
|
+
buffer.writeUInt32BE(nonce, offset);
|
|
862
|
+
offset += 4;
|
|
863
|
+
buffer.writeUInt32BE(timestamp, offset);
|
|
864
|
+
if (conditionalTransferAddressHex && conditionalTransferFact) {
|
|
865
|
+
offset += 4;
|
|
866
|
+
Buffer.from(
|
|
867
|
+
conditionalTransferFact.toString(16).padStart(64, "0"),
|
|
868
|
+
"hex"
|
|
869
|
+
).copy(buffer, offset);
|
|
870
|
+
offset += 32;
|
|
871
|
+
conditionalTransferAddressHex.copy(buffer, offset);
|
|
872
|
+
}
|
|
873
|
+
return this.transport
|
|
874
|
+
.send(
|
|
875
|
+
0xf0,
|
|
876
|
+
0x04,
|
|
877
|
+
conditionalTransferAddressHex ? 0x05 : 0x04,
|
|
878
|
+
0x00,
|
|
879
|
+
buffer
|
|
880
|
+
)
|
|
881
|
+
.then((response) => {
|
|
882
|
+
const r = response.slice(1, 1 + 32).toString("hex");
|
|
883
|
+
const s = response.slice(1 + 32, 1 + 32 + 32).toString("hex");
|
|
884
|
+
return { r, s };
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
/**
|
|
889
|
+
* provide quantization information before singing a deposit or withdrawal Stark powered contract call
|
|
890
|
+
*
|
|
891
|
+
* It shall be run following a provideERC20TokenInformation call for the given contract
|
|
892
|
+
*
|
|
893
|
+
* @param operationContract contract address of the token to be transferred (not present for ETH)
|
|
894
|
+
* @param operationQuantization quantization used for the token to be transferred
|
|
895
|
+
*/
|
|
896
|
+
starkProvideQuantum(
|
|
897
|
+
operationContract?: string,
|
|
898
|
+
operationQuantization: BigNumber
|
|
899
|
+
): Promise<boolean> {
|
|
900
|
+
const operationContractHex = maybeHexBuffer(operationContract);
|
|
901
|
+
let buffer = Buffer.alloc(20 + 32, 0);
|
|
902
|
+
if (operationContractHex) {
|
|
903
|
+
operationContractHex.copy(buffer, 0);
|
|
904
|
+
}
|
|
905
|
+
Buffer.from(
|
|
906
|
+
operationQuantization.toString(16).padStart(64, "0"),
|
|
907
|
+
"hex"
|
|
908
|
+
).copy(buffer, 20);
|
|
909
|
+
return this.transport.send(0xf0, 0x08, 0x00, 0x00, buffer).then(
|
|
910
|
+
() => true,
|
|
911
|
+
(e) => {
|
|
912
|
+
if (e && e.statusCode === 0x6d00) {
|
|
913
|
+
// this case happen for ETH application versions not supporting Stark extensions
|
|
914
|
+
return false;
|
|
915
|
+
}
|
|
916
|
+
throw e;
|
|
917
|
+
}
|
|
918
|
+
);
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
/**
|
|
922
|
+
* provide quantization information before singing a deposit or withdrawal Stark powered contract call using the Starkex V2 protocol
|
|
923
|
+
*
|
|
924
|
+
* It shall be run following a provideERC20TokenInformation call for the given contract
|
|
925
|
+
*
|
|
926
|
+
* @param operationContract contract address of the token to be transferred (not present for ETH)
|
|
927
|
+
* @param operationQuantizationType quantization type of the token to be transferred
|
|
928
|
+
* @option operationQuantization quantization used for the token to be transferred (not present for erc 721 or mintable erc 721)
|
|
929
|
+
* @option operationMintableBlobOrTokenId mintable blob (mintable erc 20 / mintable erc 721) or token id (erc 721) of the token to be transferred
|
|
930
|
+
*/
|
|
931
|
+
starkProvideQuantum_v2(
|
|
932
|
+
operationContract?: string,
|
|
933
|
+
operationQuantizationType: StarkQuantizationType,
|
|
934
|
+
operationQuantization?: BigNumber,
|
|
935
|
+
operationMintableBlobOrTokenId?: BigNumber
|
|
936
|
+
): Promise<boolean> {
|
|
937
|
+
const operationContractHex = maybeHexBuffer(operationContract);
|
|
938
|
+
if (!(operationQuantizationType in starkQuantizationTypeMap)) {
|
|
939
|
+
throw new Error(
|
|
940
|
+
"eth.starkProvideQuantumV2 invalid quantization type=" +
|
|
941
|
+
operationQuantizationType
|
|
942
|
+
);
|
|
943
|
+
}
|
|
944
|
+
let buffer = Buffer.alloc(20 + 32 + 32, 0);
|
|
945
|
+
let offset = 0;
|
|
946
|
+
if (operationContractHex) {
|
|
947
|
+
operationContractHex.copy(buffer, offset);
|
|
948
|
+
}
|
|
949
|
+
offset += 20;
|
|
950
|
+
if (operationQuantization) {
|
|
951
|
+
Buffer.from(
|
|
952
|
+
operationQuantization.toString(16).padStart(64, "0"),
|
|
953
|
+
"hex"
|
|
954
|
+
).copy(buffer, offset);
|
|
955
|
+
}
|
|
956
|
+
offset += 32;
|
|
957
|
+
if (operationMintableBlobOrTokenId) {
|
|
958
|
+
Buffer.from(
|
|
959
|
+
operationMintableBlobOrTokenId.toString(16).padStart(64, "0"),
|
|
960
|
+
"hex"
|
|
961
|
+
).copy(buffer, offset);
|
|
962
|
+
}
|
|
963
|
+
return this.transport
|
|
964
|
+
.send(
|
|
965
|
+
0xf0,
|
|
966
|
+
0x08,
|
|
967
|
+
starkQuantizationTypeMap[operationQuantizationType],
|
|
968
|
+
0x00,
|
|
969
|
+
buffer
|
|
970
|
+
)
|
|
971
|
+
.then(
|
|
972
|
+
() => true,
|
|
973
|
+
(e) => {
|
|
974
|
+
if (e && e.statusCode === 0x6d00) {
|
|
975
|
+
// this case happen for ETH application versions not supporting Stark extensions
|
|
976
|
+
return false;
|
|
977
|
+
}
|
|
978
|
+
throw e;
|
|
979
|
+
}
|
|
980
|
+
);
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
/**
|
|
984
|
+
* sign the given hash over the Stark curve
|
|
985
|
+
* It is intended for speed of execution in case an unknown Stark model is pushed and should be avoided as much as possible.
|
|
986
|
+
* @param path a path in BIP 32 format
|
|
987
|
+
* @param hash hexadecimal hash to sign
|
|
988
|
+
* @return the signature
|
|
989
|
+
*/
|
|
990
|
+
starkUnsafeSign(path: string, hash: string): Promise<Buffer> {
|
|
991
|
+
const hashHex = hexBuffer(hash);
|
|
992
|
+
let paths = splitPath(path);
|
|
993
|
+
let buffer = Buffer.alloc(1 + paths.length * 4 + 32);
|
|
994
|
+
let offset = 0;
|
|
995
|
+
buffer[0] = paths.length;
|
|
996
|
+
paths.forEach((element, index) => {
|
|
997
|
+
buffer.writeUInt32BE(element, 1 + 4 * index);
|
|
998
|
+
});
|
|
999
|
+
offset = 1 + 4 * paths.length;
|
|
1000
|
+
hashHex.copy(buffer, offset);
|
|
1001
|
+
return this.transport
|
|
1002
|
+
.send(0xf0, 0x0a, 0x00, 0x00, buffer)
|
|
1003
|
+
.then((response) => {
|
|
1004
|
+
const r = response.slice(1, 1 + 32).toString("hex");
|
|
1005
|
+
const s = response.slice(1 + 32, 1 + 32 + 32).toString("hex");
|
|
1006
|
+
return { r, s };
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
/**
|
|
1011
|
+
* get an Ethereum 2 BLS-12 381 public key for a given BIP 32 path.
|
|
1012
|
+
* @param path a path in BIP 32 format
|
|
1013
|
+
* @option boolDisplay optionally enable or not the display
|
|
1014
|
+
* @return an object with a publicKey
|
|
1015
|
+
* @example
|
|
1016
|
+
* eth.eth2GetPublicKey("12381/3600/0/0").then(o => o.publicKey)
|
|
1017
|
+
*/
|
|
1018
|
+
eth2GetPublicKey(
|
|
1019
|
+
path: string,
|
|
1020
|
+
boolDisplay?: boolean
|
|
1021
|
+
): Promise<{
|
|
1022
|
+
publicKey: string,
|
|
1023
|
+
}> {
|
|
1024
|
+
let paths = splitPath(path);
|
|
1025
|
+
let buffer = Buffer.alloc(1 + paths.length * 4);
|
|
1026
|
+
buffer[0] = paths.length;
|
|
1027
|
+
paths.forEach((element, index) => {
|
|
1028
|
+
buffer.writeUInt32BE(element, 1 + 4 * index);
|
|
1029
|
+
});
|
|
1030
|
+
return this.transport
|
|
1031
|
+
.send(0xe0, 0x0e, boolDisplay ? 0x01 : 0x00, 0x00, buffer)
|
|
1032
|
+
.then((response) => {
|
|
1033
|
+
let result = {};
|
|
1034
|
+
result.publicKey = response.slice(0, -2).toString("hex");
|
|
1035
|
+
return result;
|
|
1036
|
+
});
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
/**
|
|
1040
|
+
* Set the index of a Withdrawal key used as withdrawal credentials in an ETH 2 deposit contract call signature
|
|
1041
|
+
*
|
|
1042
|
+
* It shall be run before the ETH 2 deposit transaction is signed. If not called, the index is set to 0
|
|
1043
|
+
*
|
|
1044
|
+
* @param withdrawalIndex index path in the EIP 2334 path m/12381/3600/withdrawalIndex/0
|
|
1045
|
+
* @return True if the method was executed successfully
|
|
1046
|
+
*/
|
|
1047
|
+
eth2SetWithdrawalIndex(withdrawalIndex: number): Promise<boolean> {
|
|
1048
|
+
let buffer = Buffer.alloc(4, 0);
|
|
1049
|
+
buffer.writeUInt32BE(withdrawalIndex, 0);
|
|
1050
|
+
return this.transport.send(0xe0, 0x10, 0x00, 0x00, buffer).then(
|
|
1051
|
+
() => true,
|
|
1052
|
+
(e) => {
|
|
1053
|
+
if (e && e.statusCode === 0x6d00) {
|
|
1054
|
+
// this case happen for ETH application versions not supporting ETH 2
|
|
1055
|
+
return false;
|
|
1056
|
+
}
|
|
1057
|
+
throw e;
|
|
1058
|
+
}
|
|
1059
|
+
);
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
/**
|
|
1063
|
+
* Set the name of the plugin that should be used to parse the next transaction
|
|
1064
|
+
*
|
|
1065
|
+
* @param pluginName string containing the name of the plugin, must have length between 1 and 30 bytes
|
|
1066
|
+
* @return True if the method was executed successfully
|
|
1067
|
+
*/
|
|
1068
|
+
setExternalPlugin(
|
|
1069
|
+
pluginName: string,
|
|
1070
|
+
contractAddress: string,
|
|
1071
|
+
selector: string
|
|
1072
|
+
): Promise<boolean> {
|
|
1073
|
+
return setExternalPlugin(this.transport, pluginName, selector);
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
// internal helpers
|
|
1078
|
+
|
|
1079
|
+
function provideERC20TokenInformation(
|
|
1080
|
+
transport: Transport<*>,
|
|
1081
|
+
data: Buffer
|
|
1082
|
+
): Promise<boolean> {
|
|
1083
|
+
return transport.send(0xe0, 0x0a, 0x00, 0x00, data).then(
|
|
1084
|
+
() => true,
|
|
1085
|
+
(e) => {
|
|
1086
|
+
if (e && e.statusCode === 0x6d00) {
|
|
1087
|
+
// 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.
|
|
1088
|
+
// we return a flag to know if the call was effective or not
|
|
1089
|
+
return false;
|
|
1090
|
+
}
|
|
1091
|
+
throw e;
|
|
1092
|
+
}
|
|
1093
|
+
);
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
function setExternalPlugin(
|
|
1097
|
+
transport: Transport<*>,
|
|
1098
|
+
payload: string,
|
|
1099
|
+
signature: string
|
|
1100
|
+
): Promise<boolean> {
|
|
1101
|
+
let payloadBuffer = Buffer.from(payload, "hex");
|
|
1102
|
+
let signatureBuffer = Buffer.from(signature, "hex");
|
|
1103
|
+
let buffer = Buffer.concat([payloadBuffer, signatureBuffer]);
|
|
1104
|
+
return transport.send(0xe0, 0x12, 0x00, 0x00, buffer).then(
|
|
1105
|
+
() => true,
|
|
1106
|
+
(e) => {
|
|
1107
|
+
if (e && e.statusCode === 0x6a80) {
|
|
1108
|
+
// this case happen when the plugin name is too short or too long
|
|
1109
|
+
return false;
|
|
1110
|
+
} else if (e && e.statusCode === 0x6984) {
|
|
1111
|
+
// this case happen when the plugin requested is not installed on the device
|
|
1112
|
+
return false;
|
|
1113
|
+
} else if (e && e.statusCode === 0x6d00) {
|
|
1114
|
+
// this case happen for older version of ETH app
|
|
1115
|
+
return false;
|
|
1116
|
+
}
|
|
1117
|
+
throw e;
|
|
1118
|
+
}
|
|
1119
|
+
);
|
|
1120
|
+
}
|