@swapkit/wallet-hardware 4.9.9 → 4.9.10
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/chunk-bwf31zxr.js +4 -0
- package/dist/chunk-bwf31zxr.js.map +10 -0
- package/dist/chunk-hegt01q0.js +4 -0
- package/dist/chunk-hegt01q0.js.map +10 -0
- package/dist/ledger/index.cjs +3 -3
- package/dist/ledger/index.cjs.map +4 -4
- package/dist/ledger/index.js +3 -3
- package/dist/ledger/index.js.map +4 -4
- package/dist/trezor/index.cjs +2 -2
- package/dist/trezor/index.cjs.map +3 -3
- package/dist/trezor/index.js +2 -2
- package/dist/trezor/index.js.map +3 -3
- package/dist/types/keepkey/index.d.ts +1 -0
- package/dist/types/keepkey/index.d.ts.map +1 -1
- package/dist/types/ledger/clients/thorchain/index.d.ts +1 -0
- package/dist/types/ledger/clients/thorchain/index.d.ts.map +1 -1
- package/dist/types/ledger/clients/utxo-legacy-adapter.d.ts +17 -9
- package/dist/types/ledger/clients/utxo-legacy-adapter.d.ts.map +1 -1
- package/dist/types/ledger/index.d.ts +7 -1
- package/dist/types/ledger/index.d.ts.map +1 -1
- package/dist/types/trezor/index.d.ts +13 -2
- package/dist/types/trezor/index.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/ledger/clients/thorchain/index.ts +41 -1
- package/src/ledger/clients/utxo-legacy-adapter.ts +40 -19
- package/src/ledger/index.ts +55 -18
- package/src/trezor/index.ts +278 -70
- package/dist/chunk-mrsfcest.js +0 -4
- package/dist/chunk-mrsfcest.js.map +0 -10
- package/dist/chunk-px09mwnt.js +0 -4
- package/dist/chunk-px09mwnt.js.map +0 -10
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { hex } from "@scure/base";
|
|
2
|
-
import type
|
|
2
|
+
import { SwapKitError, type UTXOChain } from "@swapkit/helpers";
|
|
3
3
|
import type { UTXOType } from "@swapkit/toolboxes/utxo";
|
|
4
4
|
import type { Transaction } from "@swapkit/utxo-signer";
|
|
5
5
|
|
|
@@ -7,15 +7,15 @@ import type { Transaction } from "@swapkit/utxo-signer";
|
|
|
7
7
|
* Extract per-input metadata from a V3 PSBT in the shape the legacy
|
|
8
8
|
* `@ledgerhq/hw-app-btc.createPaymentTransaction` adapter expects.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
* (
|
|
12
|
-
*
|
|
13
|
-
* `btcApp.splitTransaction(hex)` can consume it.
|
|
10
|
+
* The legacy Ledger signer still needs the full previous tx hex for
|
|
11
|
+
* `btcApp.splitTransaction(hex)`. Some V3 PSBTs only include `witnessUtxo`,
|
|
12
|
+
* so we fetch the previous raw tx when `nonWitnessUtxo` is absent.
|
|
14
13
|
*
|
|
15
14
|
* Single-address account assumption: all inputs share our derivation path.
|
|
16
15
|
*/
|
|
17
|
-
export async function extractInputsFromPsbt(tx: Transaction): Promise<UTXOType[]> {
|
|
16
|
+
export async function extractInputsFromPsbt(tx: Transaction, chain: UTXOChain): Promise<UTXOType[]> {
|
|
18
17
|
const { RawTx } = await import("@swapkit/utxo-signer");
|
|
18
|
+
const { getUtxoApi } = await import("@swapkit/toolboxes/utxo");
|
|
19
19
|
const inputs: UTXOType[] = [];
|
|
20
20
|
|
|
21
21
|
for (let i = 0; i < tx.inputsLength; i++) {
|
|
@@ -25,31 +25,53 @@ export async function extractInputsFromPsbt(tx: Transaction): Promise<UTXOType[]
|
|
|
25
25
|
throw new Error(`PSBT input ${i} is missing txid/index`);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
const
|
|
28
|
+
const txid = hex.encode(input.txid);
|
|
29
|
+
const txHex = input.nonWitnessUtxo
|
|
30
|
+
? hex.encode(RawTx.encode(input.nonWitnessUtxo))
|
|
31
|
+
: await getUtxoApi(chain).getRawTx(txid);
|
|
32
|
+
if (!txHex) {
|
|
33
|
+
throw new SwapKitError("wallet_ledger_invalid_params", {
|
|
34
|
+
chain,
|
|
35
|
+
inputIndex: i,
|
|
36
|
+
reason: "Unable to resolve previous transaction hex for Ledger signing",
|
|
37
|
+
txid,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
29
40
|
const witnessUtxo = input.witnessUtxo
|
|
30
41
|
? { script: input.witnessUtxo.script, value: Number(input.witnessUtxo.amount) }
|
|
31
42
|
: undefined;
|
|
43
|
+
const nonWitnessPrevout = input.index !== undefined ? input.nonWitnessUtxo?.outputs?.[input.index] : undefined;
|
|
44
|
+
const value = witnessUtxo?.value ?? (nonWitnessPrevout ? Number(nonWitnessPrevout.amount) : 0);
|
|
32
45
|
|
|
33
|
-
inputs.push({
|
|
34
|
-
hash: hex.encode(input.txid),
|
|
35
|
-
index: input.index,
|
|
36
|
-
txHex,
|
|
37
|
-
value: witnessUtxo?.value ?? 0,
|
|
38
|
-
witnessUtxo,
|
|
39
|
-
} as UTXOType);
|
|
46
|
+
inputs.push({ hash: txid, index: input.index, txHex, value, witnessUtxo } as UTXOType);
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
return inputs;
|
|
43
50
|
}
|
|
44
51
|
|
|
52
|
+
export async function signLegacyPsbtTransaction({
|
|
53
|
+
legacyClient,
|
|
54
|
+
chain,
|
|
55
|
+
tx,
|
|
56
|
+
}: {
|
|
57
|
+
legacyClient: { signTransaction: (tx: Transaction, inputUtxos: UTXOType[]) => Promise<string> };
|
|
58
|
+
chain: UTXOChain;
|
|
59
|
+
tx: Transaction;
|
|
60
|
+
}): Promise<string> {
|
|
61
|
+
const inputUtxos = await extractInputsFromPsbt(tx, chain);
|
|
62
|
+
return legacyClient.signTransaction(tx, inputUtxos);
|
|
63
|
+
}
|
|
64
|
+
|
|
45
65
|
/**
|
|
46
66
|
* Build a toolbox-compatible signer from the existing legacy Ledger UTXO
|
|
47
|
-
* client.
|
|
48
|
-
* `
|
|
67
|
+
* client. Callers that need sign-and-broadcast should broadcast the raw hex
|
|
68
|
+
* from `signLegacyPsbtTransaction` directly; the legacy Ledger app returns an
|
|
69
|
+
* already-finalized transaction that should not be passed back to toolbox
|
|
70
|
+
* finalization.
|
|
49
71
|
*/
|
|
50
72
|
export function createLegacyPsbtSigner({
|
|
51
73
|
legacyClient,
|
|
52
|
-
chain
|
|
74
|
+
chain,
|
|
53
75
|
address,
|
|
54
76
|
}: {
|
|
55
77
|
legacyClient: { signTransaction: (tx: Transaction, inputUtxos: UTXOType[]) => Promise<string> };
|
|
@@ -59,8 +81,7 @@ export function createLegacyPsbtSigner({
|
|
|
59
81
|
return {
|
|
60
82
|
getAddress: async () => address,
|
|
61
83
|
signTransaction: async (tx: Transaction): Promise<Transaction> => {
|
|
62
|
-
const
|
|
63
|
-
const signedTxHex = await legacyClient.signTransaction(tx, inputUtxos);
|
|
84
|
+
const signedTxHex = await signLegacyPsbtTransaction({ chain, legacyClient, tx });
|
|
64
85
|
|
|
65
86
|
const { Transaction: TxClass } = await import("@swapkit/utxo-signer");
|
|
66
87
|
// `Transaction.fromRaw` parses a serialised tx (no PSBT envelope) — exactly
|
package/src/ledger/index.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
assertDerivationIndex,
|
|
19
19
|
compileMemo,
|
|
20
20
|
createHDWalletHelpers,
|
|
21
|
+
getNetworkForChain,
|
|
21
22
|
getUTXOAccountIndexFromPath,
|
|
22
23
|
getUTXOAccountPath,
|
|
23
24
|
getUTXOAddressPath,
|
|
@@ -26,7 +27,7 @@ import {
|
|
|
26
27
|
type UTXOForMultiAddressTransfer,
|
|
27
28
|
} from "@swapkit/toolboxes/utxo";
|
|
28
29
|
import type { Transaction } from "@swapkit/utxo-signer";
|
|
29
|
-
import { createWallet, getWalletSupportedChains } from "@swapkit/wallet-core";
|
|
30
|
+
import { createWallet, getWalletSupportedChains, type HardwareExtendedPublicKeyInfo } from "@swapkit/wallet-core";
|
|
30
31
|
import { getLedgerAddress, getLedgerClient } from "./helpers";
|
|
31
32
|
|
|
32
33
|
/**
|
|
@@ -37,21 +38,21 @@ import { getLedgerAddress, getLedgerClient } from "./helpers";
|
|
|
37
38
|
* client and will NOT recreate it on `forceReconnect`. When omitted, the
|
|
38
39
|
* default browser flow (WebHID / WebUSB via `navigator.usb`) is used.
|
|
39
40
|
*/
|
|
40
|
-
export type ConnectLedgerOptions = { transport?: Transport };
|
|
41
|
+
export type ConnectLedgerOptions = { address?: string; transport?: Transport };
|
|
41
42
|
|
|
42
43
|
export const ledgerWallet = createWallet({
|
|
43
44
|
connect: ({ addChain, supportedChains, walletType }) =>
|
|
44
45
|
async function connectLedger(
|
|
45
46
|
chains: Chain[],
|
|
46
47
|
derivationPath?: DerivationPathArray,
|
|
47
|
-
{ transport }: ConnectLedgerOptions = {},
|
|
48
|
+
{ address, transport }: ConnectLedgerOptions = {},
|
|
48
49
|
) {
|
|
49
50
|
const [chain] = filterSupportedChains({ chains, supportedChains, walletType });
|
|
50
51
|
|
|
51
52
|
if (!chain) return false;
|
|
52
53
|
|
|
53
54
|
const resolvedPath = derivationPath ?? (NetworkDerivationPath[chain] as DerivationPathArray | undefined);
|
|
54
|
-
const walletMethods = await getWalletMethods({ chain, derivationPath: resolvedPath, transport });
|
|
55
|
+
const walletMethods = await getWalletMethods({ address, chain, derivationPath: resolvedPath, transport });
|
|
55
56
|
|
|
56
57
|
addChain({ ...walletMethods, chain, walletType: WalletOption.LEDGER });
|
|
57
58
|
|
|
@@ -83,6 +84,7 @@ export const ledgerWallet = createWallet({
|
|
|
83
84
|
[Chain.XLayer]: true,
|
|
84
85
|
// ZEC: still on bespoke signPCZT path
|
|
85
86
|
},
|
|
87
|
+
getExtendedPublicKey: getLedgerExtendedPublicKey,
|
|
86
88
|
name: "connectLedger",
|
|
87
89
|
supportedChains: [
|
|
88
90
|
Chain.Arbitrum,
|
|
@@ -124,7 +126,30 @@ function reduceMemo(memo?: string, affiliateAddress = "t") {
|
|
|
124
126
|
return removedAffiliate?.substring(0, removedAffiliate.lastIndexOf(":"));
|
|
125
127
|
}
|
|
126
128
|
|
|
129
|
+
export async function getLedgerExtendedPublicKey(
|
|
130
|
+
chain: Chain,
|
|
131
|
+
derivationPath?: DerivationPathArray,
|
|
132
|
+
{ accountIndex }: { accountIndex?: number } = {},
|
|
133
|
+
): Promise<HardwareExtendedPublicKeyInfo | undefined> {
|
|
134
|
+
if (![Chain.BitcoinCash, Chain.Bitcoin, Chain.Dash, Chain.Dogecoin, Chain.Litecoin, Chain.Zcash].includes(chain)) {
|
|
135
|
+
throw new SwapKitError("wallet_chain_not_supported", { chain, wallet: WalletOption.LEDGER });
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const utxoChain = chain as UTXOChain;
|
|
139
|
+
const signer = await getLedgerClient({ chain: utxoChain, derivationPath });
|
|
140
|
+
if (!signer.getExtendedPublicKey) return undefined;
|
|
141
|
+
|
|
142
|
+
const accountPath = getUTXOAccountPath({ accountIndex, chain: utxoChain, derivationPath });
|
|
143
|
+
const path = derivationPathToString(accountPath);
|
|
144
|
+
const ledgerPath = chain === Chain.Bitcoin || chain === Chain.Litecoin ? path : path.replace(/^m\//, "");
|
|
145
|
+
const xpubVersion = getNetworkForChain(utxoChain).bip32.public;
|
|
146
|
+
const xpub = await signer.getExtendedPublicKey(ledgerPath, xpubVersion);
|
|
147
|
+
|
|
148
|
+
return { accountIndex: getUTXOAccountIndexFromPath(accountPath), path, xpub };
|
|
149
|
+
}
|
|
150
|
+
|
|
127
151
|
async function getWalletMethods({
|
|
152
|
+
address: providedAddress,
|
|
128
153
|
chain,
|
|
129
154
|
derivationPath,
|
|
130
155
|
transport,
|
|
@@ -141,31 +166,41 @@ async function getWalletMethods({
|
|
|
141
166
|
|
|
142
167
|
const signer = await getLedgerClient({ chain, derivationPath, transport });
|
|
143
168
|
|
|
144
|
-
const address = await getLedgerAddress({ chain, ledgerClient: signer });
|
|
169
|
+
const address = providedAddress ?? (await getLedgerAddress({ chain, ledgerClient: signer }));
|
|
145
170
|
|
|
146
171
|
// V3 toolbox signer:
|
|
147
|
-
// - BTC
|
|
148
|
-
// - BCH/DOGE/DASH use the legacy `hw-app-btc` adapter that pulls
|
|
172
|
+
// - BTC uses the modern `ledger-bitcoin` AppClient with native PSBT signing.
|
|
173
|
+
// - LTC/BCH/DOGE/DASH use the legacy `hw-app-btc` adapter that pulls
|
|
149
174
|
// `nonWitnessUtxo` (full prev-tx hex) out of the API PSBT.
|
|
175
|
+
// The Litecoin Ledger app does not support the `ledger-bitcoin`
|
|
176
|
+
// policy APDUs and returns CLA_NOT_SUPPORTED.
|
|
150
177
|
// - ZEC stays on the bespoke `signPCZT` flow for now.
|
|
151
178
|
let toolboxSigner:
|
|
152
179
|
| { getAddress: () => Promise<string>; signTransaction: (tx: Transaction) => Promise<Transaction> }
|
|
153
180
|
| undefined;
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
? BitcoinPsbtLedger(derivationPath, transport)
|
|
159
|
-
: LitecoinPsbtLedger(derivationPath, transport);
|
|
181
|
+
let signAndBroadcastLegacyPsbtTransaction: ((tx: Transaction) => Promise<string>) | undefined;
|
|
182
|
+
if (chain === Chain.Bitcoin) {
|
|
183
|
+
const { BitcoinPsbtLedger } = await import("./clients/utxo-psbt");
|
|
184
|
+
const psbtClient = BitcoinPsbtLedger(derivationPath, transport);
|
|
160
185
|
toolboxSigner = { getAddress: psbtClient.getAddress, signTransaction: psbtClient.signTransaction };
|
|
161
|
-
} else if (
|
|
162
|
-
|
|
186
|
+
} else if (
|
|
187
|
+
chain === Chain.BitcoinCash ||
|
|
188
|
+
chain === Chain.Dogecoin ||
|
|
189
|
+
chain === Chain.Dash ||
|
|
190
|
+
chain === Chain.Litecoin
|
|
191
|
+
) {
|
|
192
|
+
const { createLegacyPsbtSigner, signLegacyPsbtTransaction } = await import("./clients/utxo-legacy-adapter");
|
|
163
193
|
toolboxSigner = createLegacyPsbtSigner({ address, chain: utxoChain, legacyClient: signer });
|
|
194
|
+
signAndBroadcastLegacyPsbtTransaction = async (tx) => {
|
|
195
|
+
const signedTxHex = await signLegacyPsbtTransaction({ chain: utxoChain, legacyClient: signer, tx });
|
|
196
|
+
return toolbox.broadcastTx(signedTxHex);
|
|
197
|
+
};
|
|
164
198
|
}
|
|
165
199
|
|
|
166
200
|
const toolbox = toolboxSigner
|
|
167
201
|
? await getUtxoToolbox(utxoChain, { signer: toolboxSigner })
|
|
168
202
|
: getUtxoToolbox(utxoChain);
|
|
203
|
+
const signAndBroadcastTransaction = signAndBroadcastLegacyPsbtTransaction ?? toolbox.signAndBroadcastTransaction;
|
|
169
204
|
|
|
170
205
|
const transfer = async (params: UTXOBuildTxParams) => {
|
|
171
206
|
const feeRate = params.feeRate || (await toolbox.getFeeRates())[FeeOption.Average];
|
|
@@ -179,8 +214,8 @@ async function getWalletMethods({
|
|
|
179
214
|
sender: address,
|
|
180
215
|
});
|
|
181
216
|
|
|
182
|
-
//
|
|
183
|
-
//
|
|
217
|
+
// Legacy Ledger UTXO signing returns finalized raw tx hex, so transfer
|
|
218
|
+
// broadcasts directly instead of routing through toolbox finalization.
|
|
184
219
|
const txHex = await signer.signTransaction(tx as Transaction, inputs);
|
|
185
220
|
const txHash = await toolbox.broadcastTx(txHex);
|
|
186
221
|
|
|
@@ -193,7 +228,8 @@ async function getWalletMethods({
|
|
|
193
228
|
const accountPath = getUTXOAccountPath({ accountIndex, chain: utxoChain, derivationPath });
|
|
194
229
|
const path = derivationPathToString(accountPath);
|
|
195
230
|
const ledgerPath = chain === Chain.Bitcoin || chain === Chain.Litecoin ? path : path.replace(/^m\//, "");
|
|
196
|
-
const
|
|
231
|
+
const xpubVersion = getNetworkForChain(utxoChain).bip32.public;
|
|
232
|
+
const xpub = await signer.getExtendedPublicKey(ledgerPath, xpubVersion);
|
|
197
233
|
|
|
198
234
|
return { accountIndex: getUTXOAccountIndexFromPath(accountPath), path, xpub };
|
|
199
235
|
}
|
|
@@ -352,6 +388,7 @@ async function getWalletMethods({
|
|
|
352
388
|
deriveAddresses,
|
|
353
389
|
getExtendedPublicKey,
|
|
354
390
|
getExtendedPublicKeyInfo,
|
|
391
|
+
signAndBroadcastTransaction,
|
|
355
392
|
transfer,
|
|
356
393
|
transferFromMultipleAddresses,
|
|
357
394
|
};
|