@shapeshiftoss/hdwallet-gridplus 1.62.8 → 1.62.10-alpha.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/dist/adapter.d.ts +4 -9
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +28 -98
- package/dist/adapter.js.map +1 -1
- package/dist/bitcoin.d.ts +1 -2
- package/dist/bitcoin.d.ts.map +1 -1
- package/dist/bitcoin.js +134 -552
- package/dist/bitcoin.js.map +1 -1
- package/dist/ethereum.d.ts.map +1 -1
- package/dist/ethereum.js +18 -34
- package/dist/ethereum.js.map +1 -1
- package/dist/gridplus.d.ts +17 -22
- package/dist/gridplus.d.ts.map +1 -1
- package/dist/gridplus.js +68 -100
- package/dist/gridplus.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/utils.d.ts +0 -16
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +2 -87
- package/dist/utils.js.map +1 -1
- package/package.json +6 -5
- package/src/adapter.ts +30 -81
- package/src/bitcoin.ts +134 -649
- package/src/ethereum.ts +11 -47
- package/src/gridplus.ts +79 -131
- package/src/index.ts +0 -1
- package/src/utils.ts +2 -100
- package/tsconfig.json +2 -1
- package/tsconfig.tsbuildinfo +1 -1
package/src/ethereum.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Common, Hardfork } from "@ethereumjs/common";
|
|
2
|
-
import {
|
|
2
|
+
import { RLP } from "@ethereumjs/rlp";
|
|
3
|
+
import { TransactionFactory, TransactionType, TypedTxData } from "@ethereumjs/tx";
|
|
3
4
|
import * as core from "@shapeshiftoss/hdwallet-core";
|
|
4
5
|
import { Client, Constants, Utils } from "gridplus-sdk";
|
|
5
|
-
import { encode } from "rlp";
|
|
6
6
|
|
|
7
7
|
export async function ethGetAddress(client: Client, msg: core.ETHGetAddress): Promise<core.Address | null> {
|
|
8
8
|
const address = (await client.getAddresses({ startPath: msg.addressNList, n: 1 }))[0];
|
|
@@ -23,37 +23,23 @@ export async function ethSignTx(client: Client, msg: core.ETHSignTx): Promise<co
|
|
|
23
23
|
nonce: msg.nonce,
|
|
24
24
|
gasLimit: msg.gasLimit,
|
|
25
25
|
chainId: msg.chainId,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
maxFeePerGas: msg.maxFeePerGas,
|
|
31
|
-
maxPriorityFeePerGas: msg.maxPriorityFeePerGas,
|
|
32
|
-
}
|
|
33
|
-
: {
|
|
34
|
-
gasPrice: msg.gasPrice,
|
|
35
|
-
}),
|
|
26
|
+
type: isEIP1559 ? TransactionType.FeeMarketEIP1559 : TransactionType.Legacy,
|
|
27
|
+
maxFeePerGas: msg.maxFeePerGas,
|
|
28
|
+
maxPriorityFeePerGas: msg.maxPriorityFeePerGas,
|
|
29
|
+
gasPrice: msg.gasPrice,
|
|
36
30
|
};
|
|
37
31
|
|
|
38
|
-
const common =
|
|
39
|
-
? Common.custom({ chainId: msg.chainId }, { hardfork: Hardfork.London })
|
|
40
|
-
: Common.custom({ chainId: msg.chainId });
|
|
41
|
-
|
|
42
|
-
// Use TransactionFactory with explicit type field (Kevin's approach)
|
|
32
|
+
const common = Common.custom({ chainId: msg.chainId }, { hardfork: Hardfork.London });
|
|
43
33
|
const unsignedTx = TransactionFactory.fromTxData(txData, { common });
|
|
44
34
|
|
|
45
|
-
|
|
46
|
-
// Legacy transactions return an array that needs RLP encoding
|
|
47
|
-
// EIP-1559 transactions return a pre-encoded buffer
|
|
48
|
-
const rawPayload = unsignedTx.getMessageToSign();
|
|
49
|
-
const payload = Array.isArray(rawPayload) ? encode(rawPayload) : rawPayload;
|
|
35
|
+
const payload = isEIP1559 ? unsignedTx.getMessageToSign() : RLP.encode(unsignedTx.getMessageToSign());
|
|
50
36
|
|
|
51
37
|
const fwVersion = client.getFwVersion();
|
|
52
38
|
const supportsDecoderRecursion = fwVersion.major > 0 || fwVersion.minor >= 16;
|
|
53
39
|
|
|
54
40
|
const decoderResult = await (() => {
|
|
55
41
|
if (!msg.data || (msg.data.startsWith("0x") && Buffer.from(msg.data.slice(2), "hex").length < 4)) {
|
|
56
|
-
return { def:
|
|
42
|
+
return { def: undefined };
|
|
57
43
|
}
|
|
58
44
|
return Utils.fetchCalldataDecoder(msg.data, msg.to, msg.chainId, supportsDecoderRecursion);
|
|
59
45
|
})();
|
|
@@ -67,7 +53,7 @@ export async function ethSignTx(client: Client, msg: core.ETHSignTx): Promise<co
|
|
|
67
53
|
hashType: Constants.SIGNING.HASHES.KECCAK256,
|
|
68
54
|
encodingType: Constants.SIGNING.ENCODINGS.EVM,
|
|
69
55
|
signerPath: msg.addressNList,
|
|
70
|
-
decoder: def,
|
|
56
|
+
decoder: def ? Buffer.from(def) : undefined,
|
|
71
57
|
},
|
|
72
58
|
});
|
|
73
59
|
|
|
@@ -79,29 +65,7 @@ export async function ethSignTx(client: Client, msg: core.ETHSignTx): Promise<co
|
|
|
79
65
|
if (!Buffer.isBuffer(s)) throw new Error("Invalid signature (s)");
|
|
80
66
|
if (!Buffer.isBuffer(v)) throw new Error("Invalid signature (v)");
|
|
81
67
|
|
|
82
|
-
|
|
83
|
-
const signedTxData = {
|
|
84
|
-
to: msg.to,
|
|
85
|
-
value: msg.value,
|
|
86
|
-
data: msg.data,
|
|
87
|
-
nonce: msg.nonce,
|
|
88
|
-
gasLimit: msg.gasLimit,
|
|
89
|
-
chainId: msg.chainId,
|
|
90
|
-
type: isEIP1559 ? 2 : 0,
|
|
91
|
-
r,
|
|
92
|
-
s,
|
|
93
|
-
v,
|
|
94
|
-
...(isEIP1559
|
|
95
|
-
? {
|
|
96
|
-
maxFeePerGas: msg.maxFeePerGas,
|
|
97
|
-
maxPriorityFeePerGas: msg.maxPriorityFeePerGas,
|
|
98
|
-
}
|
|
99
|
-
: {
|
|
100
|
-
gasPrice: msg.gasPrice,
|
|
101
|
-
}),
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const signedTx = TransactionFactory.fromTxData(signedTxData, { common });
|
|
68
|
+
const signedTx = TransactionFactory.fromTxData({ ...txData, r, s, v }, { common });
|
|
105
69
|
const serialized = `0x${Buffer.from(signedTx.serialize()).toString("hex")}`;
|
|
106
70
|
|
|
107
71
|
return { r: `0x${r.toString("hex")}`, s: `0x${s.toString("hex")}`, v: v.readUIntBE(0, v.length), serialized };
|
package/src/gridplus.ts
CHANGED
|
@@ -8,8 +8,6 @@ import * as eth from "./ethereum";
|
|
|
8
8
|
import * as mayachain from "./mayachain";
|
|
9
9
|
import * as solana from "./solana";
|
|
10
10
|
import * as thorchain from "./thorchain";
|
|
11
|
-
import { GridPlusTransport } from "./transport";
|
|
12
|
-
import { convertXpubVersion, scriptTypeToAccountType } from "./utils";
|
|
13
11
|
|
|
14
12
|
export function isGridPlus(wallet: core.HDWallet): wallet is GridPlusHDWallet {
|
|
15
13
|
return isObject(wallet) && (wallet as any)._isGridPlus;
|
|
@@ -273,12 +271,12 @@ export class GridPlusHDWallet
|
|
|
273
271
|
extends GridPlusWalletInfo
|
|
274
272
|
implements
|
|
275
273
|
core.HDWallet,
|
|
276
|
-
core.ETHWallet,
|
|
277
|
-
core.SolanaWallet,
|
|
278
274
|
core.BTCWallet,
|
|
279
275
|
core.CosmosWallet,
|
|
280
|
-
core.
|
|
281
|
-
core.MayachainWallet
|
|
276
|
+
core.ETHWallet,
|
|
277
|
+
core.MayachainWallet,
|
|
278
|
+
core.SolanaWallet,
|
|
279
|
+
core.ThorchainWallet
|
|
282
280
|
{
|
|
283
281
|
readonly _supportsArbitrum = true;
|
|
284
282
|
readonly _supportsArbitrumNova = false;
|
|
@@ -298,117 +296,77 @@ export class GridPlusHDWallet
|
|
|
298
296
|
|
|
299
297
|
readonly _isGridPlus = true;
|
|
300
298
|
|
|
301
|
-
|
|
299
|
+
client: Client | undefined;
|
|
302
300
|
|
|
303
|
-
|
|
304
|
-
client?: Client;
|
|
305
|
-
|
|
306
|
-
constructor(transport: GridPlusTransport) {
|
|
301
|
+
constructor(client: Client) {
|
|
307
302
|
super();
|
|
308
|
-
this.
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
303
|
+
this.client = client;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
async cancel(): Promise<void> {}
|
|
307
|
+
async clearSession(): Promise<void> {}
|
|
308
|
+
async initialize(): Promise<void> {}
|
|
309
|
+
async loadDevice(): Promise<void> {}
|
|
310
|
+
async recover(): Promise<void> {}
|
|
311
|
+
async reset(): Promise<void> {}
|
|
312
|
+
async sendCharacter(): Promise<void> {}
|
|
313
|
+
async sendPassphrase(): Promise<void> {}
|
|
314
|
+
async sendPin(): Promise<void> {}
|
|
315
|
+
async sendWord(): Promise<void> {}
|
|
316
|
+
async wipe(): Promise<void> {}
|
|
317
|
+
|
|
318
|
+
async getDeviceID(): Promise<string> {
|
|
319
|
+
if (!this.client) throw new Error("Device not connected");
|
|
320
|
+
return this.client.getDeviceId();
|
|
313
321
|
}
|
|
314
322
|
|
|
315
323
|
async getFeatures(): Promise<Record<string, any>> {
|
|
324
|
+
if (!this.client) throw new Error("Device not connected");
|
|
325
|
+
|
|
316
326
|
return {
|
|
317
327
|
vendor: "GridPlus",
|
|
318
|
-
deviceId: this.
|
|
328
|
+
deviceId: this.client.getDeviceId(),
|
|
319
329
|
model: "Lattice1",
|
|
320
330
|
};
|
|
321
331
|
}
|
|
322
332
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
public async clearSession(): Promise<void> {
|
|
328
|
-
if (!this.client) return;
|
|
329
|
-
await this.transport.disconnect();
|
|
330
|
-
this.client = undefined;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
public async isInitialized(): Promise<boolean> {
|
|
334
|
-
return !!this.client;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
public async initialize(): Promise<void> {
|
|
338
|
-
// Get the GridPlus client from transport after successful pairing
|
|
339
|
-
this.client = this.transport.getClient();
|
|
340
|
-
|
|
341
|
-
if (!this.client) {
|
|
342
|
-
throw new Error("GridPlus client not available - device may not be paired");
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// Validate that the client has the expected methods
|
|
346
|
-
if (typeof this.client.getAddresses !== "function") {
|
|
347
|
-
throw new Error("GridPlus client missing required getAddresses method");
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
public async ping(msg: core.Ping): Promise<core.Pong> {
|
|
352
|
-
return { msg: msg.msg };
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
public async sendPin(): Promise<void> {}
|
|
356
|
-
|
|
357
|
-
public async sendPassphrase(): Promise<void> {}
|
|
358
|
-
|
|
359
|
-
public async sendCharacter(): Promise<void> {}
|
|
360
|
-
|
|
361
|
-
public async sendWord(): Promise<void> {}
|
|
362
|
-
|
|
363
|
-
public async cancel(): Promise<void> {
|
|
364
|
-
// GridPlus has no pending device interactions to cancel
|
|
365
|
-
// Wallet persists in keyring - do not disconnect
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
public async wipe(): Promise<void> {
|
|
369
|
-
throw new Error("GridPlus does not support wiping");
|
|
333
|
+
async getFirmwareVersion(): Promise<string> {
|
|
334
|
+
if (!this.client) throw new Error("Device not connected");
|
|
335
|
+
const { major, minor, fix } = this.client.getFwVersion();
|
|
336
|
+
return `${major}.${minor}.${fix}`;
|
|
370
337
|
}
|
|
371
338
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
await this.initialize();
|
|
339
|
+
async getModel(): Promise<string> {
|
|
340
|
+
return "Lattice1";
|
|
375
341
|
}
|
|
376
342
|
|
|
377
|
-
|
|
378
|
-
|
|
343
|
+
async getLabel(): Promise<string> {
|
|
344
|
+
return "GridPlus Lattice1";
|
|
379
345
|
}
|
|
380
346
|
|
|
381
|
-
|
|
382
|
-
|
|
347
|
+
async isInitialized(): Promise<boolean> {
|
|
348
|
+
return Boolean(this.client);
|
|
383
349
|
}
|
|
384
350
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
coin: "Unknown",
|
|
389
|
-
isKnown: false,
|
|
390
|
-
};
|
|
351
|
+
async isLocked(): Promise<boolean> {
|
|
352
|
+
if (!this.client) throw new Error("Device not connected");
|
|
353
|
+
return false;
|
|
391
354
|
}
|
|
392
355
|
|
|
393
|
-
|
|
394
|
-
return
|
|
356
|
+
async ping(msg: core.Ping): Promise<core.Pong> {
|
|
357
|
+
return { msg: msg.msg };
|
|
395
358
|
}
|
|
396
359
|
|
|
397
|
-
|
|
398
|
-
|
|
360
|
+
async disconnect(): Promise<void> {
|
|
361
|
+
this.client = undefined;
|
|
399
362
|
}
|
|
400
363
|
|
|
401
|
-
|
|
364
|
+
getSessionId(): string | undefined {
|
|
402
365
|
if (!this.client) throw new Error("Device not connected");
|
|
403
|
-
|
|
404
|
-
return `${major}.${minor}.${fix}`;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
public async getDeviceID(): Promise<string> {
|
|
408
|
-
return this.activeWalletId || (await this.transport.getDeviceID());
|
|
366
|
+
return JSON.parse(this.client.getStateData())["privKey"];
|
|
409
367
|
}
|
|
410
368
|
|
|
411
|
-
|
|
369
|
+
async getPublicKeys(msg: Array<core.GetPublicKey>): Promise<Array<core.PublicKey | null>> {
|
|
412
370
|
if (!this.client) throw new Error("Device not connected");
|
|
413
371
|
|
|
414
372
|
const publicKeys: Array<core.PublicKey | null> = [];
|
|
@@ -417,18 +375,18 @@ export class GridPlusHDWallet
|
|
|
417
375
|
const { addressNList, curve, coin, scriptType } = getPublicKey;
|
|
418
376
|
|
|
419
377
|
try {
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
}
|
|
378
|
+
const flag = (() => {
|
|
379
|
+
switch (curve) {
|
|
380
|
+
// For UTXO chains (Bitcoin, Dogecoin), we need the xpub
|
|
381
|
+
case "secp256k1":
|
|
382
|
+
return Constants.GET_ADDR_FLAGS.SECP256K1_XPUB;
|
|
383
|
+
// For Solana/ed25519 chains, we need the public key
|
|
384
|
+
case "ed25519":
|
|
385
|
+
return Constants.GET_ADDR_FLAGS.ED25519_PUB;
|
|
386
|
+
default:
|
|
387
|
+
throw new Error(`Unsupported curve: ${curve}`);
|
|
388
|
+
}
|
|
389
|
+
})();
|
|
432
390
|
|
|
433
391
|
const addresses = await this.client!.getAddresses({
|
|
434
392
|
startPath: addressNList,
|
|
@@ -436,17 +394,15 @@ export class GridPlusHDWallet
|
|
|
436
394
|
flag,
|
|
437
395
|
});
|
|
438
396
|
|
|
439
|
-
if (!addresses.length)
|
|
440
|
-
throw new Error("No public key returned from device");
|
|
441
|
-
}
|
|
397
|
+
if (!addresses.length) throw new Error("No public key returned from device");
|
|
442
398
|
|
|
443
399
|
// addresses[0] contains either xpub string (for SECP256K1_XPUB) or pubkey hex (for ED25519_PUB)
|
|
444
400
|
let xpub = typeof addresses[0] === "string" ? addresses[0] : Buffer.from(addresses[0]).toString("hex");
|
|
445
401
|
|
|
446
402
|
// Convert xpub format for Dogecoin/Litecoin (GridPlus returns Bitcoin xpub format)
|
|
447
403
|
if (coin && curve === "secp256k1") {
|
|
448
|
-
const accountType = scriptTypeToAccountType
|
|
449
|
-
xpub = convertXpubVersion(xpub, accountType, coin);
|
|
404
|
+
const accountType = scriptType ? core.scriptTypeToAccountType[scriptType] : undefined;
|
|
405
|
+
xpub = core.convertXpubVersion(xpub, accountType, coin);
|
|
450
406
|
}
|
|
451
407
|
|
|
452
408
|
publicKeys.push({ xpub });
|
|
@@ -458,53 +414,45 @@ export class GridPlusHDWallet
|
|
|
458
414
|
return publicKeys;
|
|
459
415
|
}
|
|
460
416
|
|
|
461
|
-
|
|
462
|
-
return this.transport.getSessionId();
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
public async disconnect(): Promise<void> {
|
|
466
|
-
await this.clearSession();
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
public async btcGetAddress(msg: core.BTCGetAddress): Promise<string | null> {
|
|
417
|
+
async btcGetAddress(msg: core.BTCGetAddress): Promise<string | null> {
|
|
470
418
|
if (!this.client) throw new Error("Device not connected");
|
|
471
419
|
return btc.btcGetAddress(this.client!, msg);
|
|
472
420
|
}
|
|
473
421
|
|
|
474
|
-
|
|
422
|
+
async btcSignTx(msg: core.BTCSignTx): Promise<core.BTCSignedTx | null> {
|
|
475
423
|
if (!this.client) throw new Error("Device not connected");
|
|
476
424
|
return btc.btcSignTx(this.client, msg);
|
|
477
425
|
}
|
|
478
426
|
|
|
479
|
-
|
|
427
|
+
async btcSignMessage(): Promise<core.BTCSignedMessage | null> {
|
|
480
428
|
throw new Error("GridPlus BTC message signing not yet implemented");
|
|
481
429
|
}
|
|
482
430
|
|
|
483
|
-
|
|
431
|
+
async btcVerifyMessage(): Promise<boolean | null> {
|
|
484
432
|
throw new Error("GridPlus BTC message verification not yet implemented");
|
|
485
433
|
}
|
|
486
434
|
|
|
487
|
-
|
|
435
|
+
async ethGetAddress(msg: core.ETHGetAddress): Promise<core.Address | null> {
|
|
488
436
|
if (!this.client) throw new Error("Device not connected");
|
|
489
437
|
return eth.ethGetAddress(this.client, msg);
|
|
490
438
|
}
|
|
491
439
|
|
|
492
|
-
|
|
440
|
+
async ethSignTx(msg: core.ETHSignTx): Promise<core.ETHSignedTx> {
|
|
493
441
|
if (!this.client) throw new Error("Device not connected");
|
|
494
442
|
return eth.ethSignTx(this.client, msg);
|
|
495
443
|
}
|
|
496
444
|
|
|
497
|
-
|
|
445
|
+
async ethSignTypedData(msg: core.ETHSignTypedData): Promise<core.ETHSignedTypedData> {
|
|
498
446
|
if (!this.client) throw new Error("Device not connected");
|
|
499
447
|
return eth.ethSignTypedData(this.client, msg);
|
|
500
448
|
}
|
|
501
449
|
|
|
502
|
-
|
|
450
|
+
async ethSignMessage(msg: core.ETHSignMessage): Promise<core.ETHSignedMessage> {
|
|
503
451
|
if (!this.client) throw new Error("Device not connected");
|
|
504
452
|
return eth.ethSignMessage(this.client, msg);
|
|
505
453
|
}
|
|
506
454
|
|
|
507
|
-
|
|
455
|
+
async ethVerifyMessage(): Promise<boolean> {
|
|
508
456
|
throw new Error("GridPlus ETH message verification not implemented yet");
|
|
509
457
|
}
|
|
510
458
|
|
|
@@ -520,42 +468,42 @@ export class GridPlusHDWallet
|
|
|
520
468
|
}
|
|
521
469
|
}
|
|
522
470
|
|
|
523
|
-
|
|
471
|
+
async solanaGetAddress(msg: core.SolanaGetAddress): Promise<string | null> {
|
|
524
472
|
this.assertSolanaFwSupport();
|
|
525
473
|
return solana.solanaGetAddress(this.client, msg);
|
|
526
474
|
}
|
|
527
475
|
|
|
528
|
-
|
|
476
|
+
async solanaSignTx(msg: core.SolanaSignTx): Promise<core.SolanaSignedTx | null> {
|
|
529
477
|
this.assertSolanaFwSupport();
|
|
530
478
|
return solana.solanaSignTx(this.client, msg);
|
|
531
479
|
}
|
|
532
480
|
|
|
533
|
-
|
|
481
|
+
async cosmosGetAddress(msg: core.CosmosGetAddress): Promise<string | null> {
|
|
534
482
|
if (!this.client) throw new Error("Device not connected");
|
|
535
483
|
return cosmos.cosmosGetAddress(this.client, msg);
|
|
536
484
|
}
|
|
537
485
|
|
|
538
|
-
|
|
486
|
+
async cosmosSignTx(msg: core.CosmosSignTx): Promise<core.CosmosSignedTx | null> {
|
|
539
487
|
if (!this.client) throw new Error("Device not connected");
|
|
540
488
|
return cosmos.cosmosSignTx(this.client, msg);
|
|
541
489
|
}
|
|
542
490
|
|
|
543
|
-
|
|
491
|
+
async thorchainGetAddress(msg: core.ThorchainGetAddress): Promise<string | null> {
|
|
544
492
|
if (!this.client) throw new Error("Device not connected");
|
|
545
493
|
return thorchain.thorchainGetAddress(this.client, msg);
|
|
546
494
|
}
|
|
547
495
|
|
|
548
|
-
|
|
496
|
+
async thorchainSignTx(msg: core.ThorchainSignTx): Promise<core.ThorchainSignedTx | null> {
|
|
549
497
|
if (!this.client) throw new Error("Device not connected");
|
|
550
498
|
return thorchain.thorchainSignTx(this.client, msg);
|
|
551
499
|
}
|
|
552
500
|
|
|
553
|
-
|
|
501
|
+
async mayachainGetAddress(msg: core.MayachainGetAddress): Promise<string | null> {
|
|
554
502
|
if (!this.client) throw new Error("Device not connected");
|
|
555
503
|
return mayachain.mayachainGetAddress(this.client, msg);
|
|
556
504
|
}
|
|
557
505
|
|
|
558
|
-
|
|
506
|
+
async mayachainSignTx(msg: core.MayachainSignTx): Promise<core.MayachainSignedTx | null> {
|
|
559
507
|
if (!this.client) throw new Error("Device not connected");
|
|
560
508
|
return mayachain.mayachainSignTx(this.client, msg);
|
|
561
509
|
}
|
package/src/index.ts
CHANGED
package/src/utils.ts
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { pointCompress } from "@bitcoinerlab/secp256k1";
|
|
2
|
-
import * as core from "@shapeshiftoss/hdwallet-core";
|
|
3
2
|
import * as bech32 from "bech32";
|
|
4
|
-
import
|
|
3
|
+
import * as bs58 from "bs58check";
|
|
5
4
|
import CryptoJS from "crypto-js";
|
|
6
5
|
|
|
7
|
-
import { accountTypeToVersion, convertVersions, UTXO_NETWORK_PARAMS, UtxoAccountType } from "./constants";
|
|
8
|
-
|
|
9
6
|
export const getCompressedPubkey = (pubkey: string | Buffer): Buffer => {
|
|
10
7
|
// Extended public key (xpub/ypub/zpub)
|
|
11
|
-
if (typeof pubkey === "string") return
|
|
8
|
+
if (typeof pubkey === "string") return bs58.decode(pubkey).subarray(45, 78);
|
|
12
9
|
|
|
13
10
|
// Already compressed public key (33 bytes)
|
|
14
11
|
if (pubkey.length === 33) return pubkey;
|
|
@@ -27,98 +24,3 @@ export const createBech32Address = (pubkey: string | Buffer, prefix: string): st
|
|
|
27
24
|
const words = bech32.toWords(address);
|
|
28
25
|
return bech32.encode(prefix, words);
|
|
29
26
|
};
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Convert xpub version bytes for different coins (e.g., xpub → dgub for Dogecoin)
|
|
33
|
-
* GridPlus returns Bitcoin-format xpubs, but some coins like Dogecoin need different prefixes
|
|
34
|
-
*/
|
|
35
|
-
export function convertXpubVersion(xpub: string, accountType: UtxoAccountType | undefined, coin: string): string {
|
|
36
|
-
if (!accountType) return xpub;
|
|
37
|
-
if (!convertVersions.includes(xpub.substring(0, 4))) {
|
|
38
|
-
return xpub;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const payload = bs58Decode(xpub);
|
|
42
|
-
const version = payload.slice(0, 4);
|
|
43
|
-
const desiredVersion = accountTypeToVersion(coin, accountType);
|
|
44
|
-
if (version.compare(desiredVersion) !== 0) {
|
|
45
|
-
const key = payload.slice(4);
|
|
46
|
-
return bs58Encode(Buffer.concat([desiredVersion, key]));
|
|
47
|
-
}
|
|
48
|
-
return xpub;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function scriptTypeToAccountType(scriptType: core.BTCInputScriptType | undefined): UtxoAccountType | undefined {
|
|
52
|
-
switch (scriptType) {
|
|
53
|
-
case core.BTCInputScriptType.SpendAddress:
|
|
54
|
-
return UtxoAccountType.P2pkh;
|
|
55
|
-
case core.BTCInputScriptType.SpendWitness:
|
|
56
|
-
return UtxoAccountType.SegwitNative;
|
|
57
|
-
case core.BTCInputScriptType.SpendP2SHWitness:
|
|
58
|
-
return UtxoAccountType.SegwitP2sh;
|
|
59
|
-
default:
|
|
60
|
-
return undefined;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Derive a UTXO address from a compressed public key
|
|
66
|
-
* @param pubkeyHex - Compressed public key as hex string (33 bytes, starting with 02 or 03)
|
|
67
|
-
* @param coin - Coin name (Bitcoin, Dogecoin, Litecoin, etc.)
|
|
68
|
-
* @param scriptType - Script type (p2pkh, p2wpkh, p2sh-p2wpkh)
|
|
69
|
-
* @returns The derived address
|
|
70
|
-
*/
|
|
71
|
-
export function deriveAddressFromPubkey(
|
|
72
|
-
pubkeyHex: string,
|
|
73
|
-
coin: string,
|
|
74
|
-
scriptType: core.BTCInputScriptType = core.BTCInputScriptType.SpendAddress
|
|
75
|
-
): string {
|
|
76
|
-
const network = UTXO_NETWORK_PARAMS[coin] || UTXO_NETWORK_PARAMS.Bitcoin;
|
|
77
|
-
const pubkeyBuffer = Buffer.from(pubkeyHex, "hex");
|
|
78
|
-
|
|
79
|
-
if (pubkeyBuffer.length !== 33) {
|
|
80
|
-
throw new Error(`Invalid compressed public key length: ${pubkeyBuffer.length} bytes`);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Hash160 = RIPEMD160(SHA256(pubkey))
|
|
84
|
-
const sha256Hash = CryptoJS.SHA256(CryptoJS.enc.Hex.parse(pubkeyHex));
|
|
85
|
-
const hash160 = CryptoJS.RIPEMD160(sha256Hash).toString();
|
|
86
|
-
const hash160Buffer = Buffer.from(hash160, "hex");
|
|
87
|
-
|
|
88
|
-
switch (scriptType) {
|
|
89
|
-
case core.BTCInputScriptType.SpendAddress: {
|
|
90
|
-
// P2PKH: <pubKeyHash version byte> + hash160 + checksum
|
|
91
|
-
const payload = Buffer.concat([Buffer.from([network.pubKeyHash]), hash160Buffer]);
|
|
92
|
-
return bs58Encode(payload);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
case core.BTCInputScriptType.SpendWitness: {
|
|
96
|
-
// P2WPKH (bech32): witness version 0 + hash160
|
|
97
|
-
if (!network.bech32) {
|
|
98
|
-
throw new Error(`Bech32 not supported for ${coin}`);
|
|
99
|
-
}
|
|
100
|
-
const words = bech32.toWords(hash160Buffer);
|
|
101
|
-
words.unshift(0); // witness version 0
|
|
102
|
-
return bech32.encode(network.bech32, words);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
case core.BTCInputScriptType.SpendP2SHWitness: {
|
|
106
|
-
// P2SH-P2WPKH: scriptHash of witness program
|
|
107
|
-
// Witness program: OP_0 (0x00) + length (0x14) + hash160
|
|
108
|
-
const witnessProgram = Buffer.concat([Buffer.from([0x00, 0x14]), hash160Buffer]);
|
|
109
|
-
|
|
110
|
-
// Hash160 of witness program
|
|
111
|
-
const wpHex = witnessProgram.toString("hex");
|
|
112
|
-
const wpSha256 = CryptoJS.SHA256(CryptoJS.enc.Hex.parse(wpHex));
|
|
113
|
-
const wpHash160 = CryptoJS.RIPEMD160(wpSha256).toString();
|
|
114
|
-
const wpHash160Buffer = Buffer.from(wpHash160, "hex");
|
|
115
|
-
|
|
116
|
-
// Encode with scriptHash version byte
|
|
117
|
-
const payload = Buffer.concat([Buffer.from([network.scriptHash]), wpHash160Buffer]);
|
|
118
|
-
return bs58Encode(payload);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
default:
|
|
122
|
-
throw new Error(`Unsupported script type: ${scriptType}`);
|
|
123
|
-
}
|
|
124
|
-
}
|
package/tsconfig.json
CHANGED