@swapkit/wallet-hardware 5.0.0 → 6.0.0

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.
Files changed (167) hide show
  1. package/dist/THIRD_PARTY_LICENSES.md +2344 -0
  2. package/dist/chunk-09e51wma.js +4 -0
  3. package/dist/chunk-09e51wma.js.map +167 -0
  4. package/dist/chunk-0arpsm5f.js +4 -0
  5. package/dist/chunk-0arpsm5f.js.map +10 -0
  6. package/dist/chunk-1amf58aq.js +4 -0
  7. package/dist/{chunk-vwv6z2mk.js.map → chunk-1amf58aq.js.map} +2 -2
  8. package/dist/chunk-3t17wpjg.js +17 -0
  9. package/dist/chunk-3t17wpjg.js.map +29 -0
  10. package/dist/chunk-5c7b7z2d.js +4 -0
  11. package/dist/{chunk-9gy0xve4.js.map → chunk-5c7b7z2d.js.map} +2 -2
  12. package/dist/chunk-5p3ttw6y.js +4 -0
  13. package/dist/{chunk-n05bv2n5.js.map → chunk-5p3ttw6y.js.map} +1 -1
  14. package/dist/chunk-dptbnkzm.js +64 -0
  15. package/dist/chunk-dptbnkzm.js.map +260 -0
  16. package/dist/chunk-ds2m83jp.js +5 -0
  17. package/dist/chunk-ds2m83jp.js.map +11 -0
  18. package/dist/chunk-eenx3cv6.js +4 -0
  19. package/dist/chunk-eenx3cv6.js.map +33 -0
  20. package/dist/chunk-gr3eswf1.js +4 -0
  21. package/dist/chunk-gr3eswf1.js.map +73 -0
  22. package/dist/{chunk-7fsaymh4.js → chunk-hg71hp4m.js} +3 -3
  23. package/dist/{chunk-7fsaymh4.js.map → chunk-hg71hp4m.js.map} +1 -1
  24. package/dist/chunk-jewm2ag3.js +5 -0
  25. package/dist/chunk-jewm2ag3.js.map +10 -0
  26. package/dist/chunk-jzrytcrf.js +4 -0
  27. package/dist/chunk-jzrytcrf.js.map +12 -0
  28. package/dist/chunk-k3thje6a.js +4 -0
  29. package/dist/chunk-k3thje6a.js.map +9 -0
  30. package/dist/chunk-nhva847d.js +5 -0
  31. package/dist/chunk-nhva847d.js.map +9 -0
  32. package/dist/chunk-tm47brht.js +4 -0
  33. package/dist/chunk-tm47brht.js.map +140 -0
  34. package/dist/chunk-xmvg4cds.js +4 -0
  35. package/dist/chunk-xmvg4cds.js.map +127 -0
  36. package/dist/chunk-xx7cwpzq.js +4 -0
  37. package/dist/chunk-xx7cwpzq.js.map +9 -0
  38. package/dist/chunk-yrxe491r.js +20 -0
  39. package/dist/chunk-yrxe491r.js.map +17 -0
  40. package/dist/index.cjs +2 -2
  41. package/dist/index.cjs.map +1 -1
  42. package/dist/keepkey/index.cjs +2 -2
  43. package/dist/keepkey/index.cjs.map +2 -2
  44. package/dist/keepkey/index.js +2 -2
  45. package/dist/keepkey/index.js.map +2 -2
  46. package/dist/ledger/index.cjs +90 -2
  47. package/dist/ledger/index.cjs.map +830 -23
  48. package/dist/ledger/index.js +2 -2
  49. package/dist/ledger/index.js.map +21 -19
  50. package/dist/trezor/index.cjs +2 -2
  51. package/dist/trezor/index.cjs.map +5 -4
  52. package/dist/trezor/index.js +2 -2
  53. package/dist/trezor/index.js.map +3 -3
  54. package/dist/types/helpers/psbt.d.ts +9 -0
  55. package/dist/types/helpers/psbt.d.ts.map +1 -0
  56. package/dist/types/ledger/clients/bitcoin.d.ts +36 -0
  57. package/dist/types/ledger/clients/bitcoin.d.ts.map +1 -0
  58. package/dist/types/ledger/clients/cosmos.d.ts +38 -10
  59. package/dist/types/ledger/clients/cosmos.d.ts.map +1 -1
  60. package/dist/types/ledger/clients/evm.d.ts +37 -46
  61. package/dist/types/ledger/clients/evm.d.ts.map +1 -1
  62. package/dist/types/ledger/clients/near.d.ts +5 -2
  63. package/dist/types/ledger/clients/near.d.ts.map +1 -1
  64. package/dist/types/ledger/clients/sui.d.ts +8 -12
  65. package/dist/types/ledger/clients/sui.d.ts.map +1 -1
  66. package/dist/types/ledger/clients/thorchain/index.d.ts +34 -8
  67. package/dist/types/ledger/clients/thorchain/index.d.ts.map +1 -1
  68. package/dist/types/ledger/clients/thorchain/protocol.d.ts +58 -0
  69. package/dist/types/ledger/clients/thorchain/protocol.d.ts.map +1 -0
  70. package/dist/types/ledger/clients/tron.d.ts +11 -8
  71. package/dist/types/ledger/clients/tron.d.ts.map +1 -1
  72. package/dist/types/ledger/clients/utxo-legacy-adapter.d.ts +3 -2
  73. package/dist/types/ledger/clients/utxo-legacy-adapter.d.ts.map +1 -1
  74. package/dist/types/ledger/clients/utxo-legacy-outputs.d.ts +18 -0
  75. package/dist/types/ledger/clients/utxo-legacy-outputs.d.ts.map +1 -0
  76. package/dist/types/ledger/clients/utxo.d.ts +10 -13
  77. package/dist/types/ledger/clients/utxo.d.ts.map +1 -1
  78. package/dist/types/ledger/clients/xrp.d.ts +4 -1
  79. package/dist/types/ledger/clients/xrp.d.ts.map +1 -1
  80. package/dist/types/ledger/clients/zcash.d.ts +53 -0
  81. package/dist/types/ledger/clients/zcash.d.ts.map +1 -0
  82. package/dist/types/ledger/helpers/dmk.d.ts +24 -0
  83. package/dist/types/ledger/helpers/dmk.d.ts.map +1 -0
  84. package/dist/types/ledger/helpers/executeDeviceAction.d.ts +34 -0
  85. package/dist/types/ledger/helpers/executeDeviceAction.d.ts.map +1 -0
  86. package/dist/types/ledger/helpers/getLedgerAddress.d.ts +1 -1
  87. package/dist/types/ledger/helpers/getLedgerAddress.d.ts.map +1 -1
  88. package/dist/types/ledger/helpers/getLedgerClient.d.ts +10 -3
  89. package/dist/types/ledger/helpers/getLedgerClient.d.ts.map +1 -1
  90. package/dist/types/ledger/helpers/index.d.ts +2 -1
  91. package/dist/types/ledger/helpers/index.d.ts.map +1 -1
  92. package/dist/types/ledger/helpers/ledgerJsDmkBridge.d.ts +26 -0
  93. package/dist/types/ledger/helpers/ledgerJsDmkBridge.d.ts.map +1 -0
  94. package/dist/types/ledger/helpers/ledgerJsDmkTransport.d.ts +13 -0
  95. package/dist/types/ledger/helpers/ledgerJsDmkTransport.d.ts.map +1 -0
  96. package/dist/types/ledger/helpers/lowS.d.ts +6 -0
  97. package/dist/types/ledger/helpers/lowS.d.ts.map +1 -0
  98. package/dist/types/ledger/helpers/rawTx.d.ts +2 -0
  99. package/dist/types/ledger/helpers/rawTx.d.ts.map +1 -0
  100. package/dist/types/ledger/index.d.ts +19 -7
  101. package/dist/types/ledger/index.d.ts.map +1 -1
  102. package/dist/types/ledger/types.d.ts +6 -10
  103. package/dist/types/ledger/types.d.ts.map +1 -1
  104. package/dist/types/trezor/index.d.ts +2 -2
  105. package/dist/types/trezor/index.d.ts.map +1 -1
  106. package/package.json +36 -36
  107. package/src/helpers/psbt.ts +26 -0
  108. package/src/ledger/clients/bitcoin.ts +575 -0
  109. package/src/ledger/clients/cosmos.ts +180 -51
  110. package/src/ledger/clients/evm.ts +238 -195
  111. package/src/ledger/clients/near.ts +43 -13
  112. package/src/ledger/clients/sui.ts +55 -68
  113. package/src/ledger/clients/thorchain/index.ts +210 -86
  114. package/src/ledger/clients/thorchain/protocol.ts +166 -0
  115. package/src/ledger/clients/tron.ts +49 -37
  116. package/src/ledger/clients/utxo-legacy-adapter.ts +35 -32
  117. package/src/ledger/clients/utxo-legacy-outputs.ts +122 -0
  118. package/src/ledger/clients/utxo.ts +108 -206
  119. package/src/ledger/clients/xrp.ts +39 -23
  120. package/src/ledger/clients/zcash.ts +440 -0
  121. package/src/ledger/helpers/dmk.ts +231 -0
  122. package/src/ledger/helpers/executeDeviceAction.ts +201 -0
  123. package/src/ledger/helpers/getLedgerClient.ts +101 -72
  124. package/src/ledger/helpers/index.ts +6 -1
  125. package/src/ledger/helpers/ledgerJsDmkBridge.ts +109 -0
  126. package/src/ledger/helpers/ledgerJsDmkTransport.ts +88 -0
  127. package/src/ledger/helpers/lowS.ts +30 -0
  128. package/src/ledger/helpers/rawTx.ts +12 -0
  129. package/src/ledger/index.ts +564 -369
  130. package/src/ledger/types.ts +15 -10
  131. package/src/trezor/index.ts +117 -25
  132. package/dist/chunk-3yr76n9s.js +0 -5
  133. package/dist/chunk-3yr76n9s.js.map +0 -10
  134. package/dist/chunk-9gy0xve4.js +0 -4
  135. package/dist/chunk-bwf31zxr.js +0 -4
  136. package/dist/chunk-bwf31zxr.js.map +0 -10
  137. package/dist/chunk-egtwbe6q.js +0 -4
  138. package/dist/chunk-egtwbe6q.js.map +0 -10
  139. package/dist/chunk-n05bv2n5.js +0 -5
  140. package/dist/chunk-vwv6z2mk.js +0 -4
  141. package/dist/types/ledger/clients/thorchain/common.d.ts +0 -28
  142. package/dist/types/ledger/clients/thorchain/common.d.ts.map +0 -1
  143. package/dist/types/ledger/clients/thorchain/helpers.d.ts +0 -7
  144. package/dist/types/ledger/clients/thorchain/helpers.d.ts.map +0 -1
  145. package/dist/types/ledger/clients/thorchain/lib.d.ts +0 -22
  146. package/dist/types/ledger/clients/thorchain/lib.d.ts.map +0 -1
  147. package/dist/types/ledger/clients/thorchain/utils.d.ts +0 -2
  148. package/dist/types/ledger/clients/thorchain/utils.d.ts.map +0 -1
  149. package/dist/types/ledger/clients/utxo-psbt.d.ts +0 -18
  150. package/dist/types/ledger/clients/utxo-psbt.d.ts.map +0 -1
  151. package/dist/types/ledger/cosmosTypes.d.ts +0 -40
  152. package/dist/types/ledger/cosmosTypes.d.ts.map +0 -1
  153. package/dist/types/ledger/helpers/getLedgerTransport.d.ts +0 -3
  154. package/dist/types/ledger/helpers/getLedgerTransport.d.ts.map +0 -1
  155. package/dist/types/ledger/helpers/ledgerNetworkInfo.d.ts +0 -14
  156. package/dist/types/ledger/helpers/ledgerNetworkInfo.d.ts.map +0 -1
  157. package/dist/types/ledger/interfaces/CosmosLedgerInterface.d.ts +0 -14
  158. package/dist/types/ledger/interfaces/CosmosLedgerInterface.d.ts.map +0 -1
  159. package/src/ledger/clients/thorchain/common.ts +0 -93
  160. package/src/ledger/clients/thorchain/helpers.ts +0 -120
  161. package/src/ledger/clients/thorchain/lib.ts +0 -258
  162. package/src/ledger/clients/thorchain/utils.ts +0 -69
  163. package/src/ledger/clients/utxo-psbt.ts +0 -172
  164. package/src/ledger/cosmosTypes.ts +0 -98
  165. package/src/ledger/helpers/getLedgerTransport.ts +0 -102
  166. package/src/ledger/helpers/ledgerNetworkInfo.ts +0 -127
  167. package/src/ledger/interfaces/CosmosLedgerInterface.ts +0 -67
@@ -1,4 +1,5 @@
1
1
  import type Transport from "@ledgerhq/hw-transport";
2
+ import { hex } from "@scure/base";
2
3
  import {
3
4
  type AssetValue,
4
5
  Chain,
@@ -6,8 +7,7 @@ import {
6
7
  derivationPathToString,
7
8
  FeeOption,
8
9
  filterSupportedChains,
9
- type GenericTransferParams,
10
- getRPCUrl,
10
+ getUTXOScriptTypeForPath,
11
11
  NetworkDerivationPath,
12
12
  SwapKitError,
13
13
  type UTXOChain,
@@ -27,34 +27,143 @@ import {
27
27
  getUtxoApi,
28
28
  type UTXOBuildTxParams,
29
29
  type UTXOForMultiAddressTransfer,
30
+ type UTXOTransferParams,
31
+ type UTXOType,
30
32
  } from "@swapkit/toolboxes/utxo";
31
- import type { Transaction } from "@swapkit/utxo-signer";
33
+ import type { Transaction, ZcashTransaction } from "@swapkit/utxo-signer";
32
34
  import { createWallet, getWalletSupportedChains, type HardwareExtendedPublicKeyInfo } from "@swapkit/wallet-core";
33
- import { getLedgerAddress, getLedgerClient } from "./helpers";
35
+ import { match, P } from "ts-pattern";
36
+ import {
37
+ getLedgerAddress,
38
+ getLedgerClient,
39
+ type LedgerDeviceActionStateHandler,
40
+ type LedgerDMKSession,
41
+ } from "./helpers";
42
+ import { createCachedRawTxResolver } from "./helpers/rawTx";
43
+
44
+ type BitcoinLedgerClient = ReturnType<typeof import("./clients/bitcoin")["BitcoinLedger"]>;
45
+ type LegacyUTXOLedgerClient = ReturnType<typeof import("./clients/utxo")["BitcoinCashLedger"]>;
46
+ type ZcashLedgerClient = ReturnType<typeof import("./clients/zcash")["ZcashLedger"]>;
47
+ type UTXOLedgerClient = BitcoinLedgerClient | LegacyUTXOLedgerClient | ZcashLedgerClient;
34
48
 
35
49
  /**
36
50
  * Options passed to `connectLedger` at call time.
37
51
  *
38
- * When `transport` is supplied, the consumer opens and owns its lifecycle —
39
- * wallet-hardware will use that exact instance for every per-chain Ledger
40
- * client and will NOT recreate it on `forceReconnect`. When omitted, the
41
- * default browser flow (WebHID / WebUSB via `navigator.usb`) is used.
52
+ * When `dmkSession` is omitted, the default browser WebHID flow discovers a
53
+ * device and reuses one session across chains. Lazy-loaded apps can call
54
+ * `preloadLedgerDMK` before the user gesture that starts connection.
42
55
  */
43
- export type ConnectLedgerOptions = { address?: string; transport?: Transport };
56
+ export interface ConnectLedgerOptions {
57
+ address?: string;
58
+ /** Caller-owned DMK session used by every supported chain. */
59
+ dmkSession?: LedgerDMKSession;
60
+ /** Receives DMK progress and device-interaction states. */
61
+ onDeviceActionState?: LedgerDeviceActionStateHandler;
62
+ /** Ledger-provided token required for Transaction Checks. */
63
+ originToken?: string;
64
+ /** @deprecated Temporary non-EVM fallback. Cannot be combined with `dmkSession`. */
65
+ transport?: Transport;
66
+ }
67
+
68
+ interface WalletMethodsParams<C extends Chain = Chain> extends ConnectLedgerOptions {
69
+ chain: C;
70
+ derivationPath?: DerivationPathArray;
71
+ }
72
+
73
+ interface LedgerAccountXpubParams {
74
+ chain: UTXOChain;
75
+ path: string;
76
+ signer: UTXOLedgerClient;
77
+ xpubVersion: number;
78
+ }
79
+
80
+ interface SignLedgerTransactionParams {
81
+ inputUtxos?: UTXOType[];
82
+ transaction: Transaction | ZcashTransaction;
83
+ }
84
+
85
+ const LEDGER_SUPPORTED_CHAINS_CONFIG = [
86
+ Chain.Arbitrum,
87
+ Chain.Arc,
88
+ Chain.Aurora,
89
+ Chain.Avalanche,
90
+ Chain.Base,
91
+ Chain.Berachain,
92
+ Chain.BinanceSmartChain,
93
+ Chain.Bitcoin,
94
+ Chain.BitcoinCash,
95
+ Chain.Cosmos,
96
+ Chain.Dash,
97
+ Chain.Dogecoin,
98
+ Chain.Ethereum,
99
+ Chain.Gnosis,
100
+ Chain.Litecoin,
101
+ Chain.Monad,
102
+ Chain.Near,
103
+ Chain.Optimism,
104
+ Chain.Polygon,
105
+ Chain.Ripple,
106
+ Chain.Sui,
107
+ Chain.THORChain,
108
+ Chain.XLayer,
109
+ Chain.Tron,
110
+ Chain.Zcash,
111
+ ] as const;
112
+
113
+ type LedgerSupportedChain = (typeof LEDGER_SUPPORTED_CHAINS_CONFIG)[number];
114
+
115
+ const LEDGER_UTXO_CHAINS = [
116
+ Chain.BitcoinCash,
117
+ Chain.Bitcoin,
118
+ Chain.Dash,
119
+ Chain.Dogecoin,
120
+ Chain.Litecoin,
121
+ Chain.Zcash,
122
+ ] as const satisfies readonly UTXOChain[];
123
+
124
+ const LEDGER_EVM_CHAINS = [
125
+ Chain.Ethereum,
126
+ Chain.Avalanche,
127
+ Chain.Arbitrum,
128
+ Chain.Arc,
129
+ Chain.Berachain,
130
+ Chain.Optimism,
131
+ Chain.Polygon,
132
+ Chain.BinanceSmartChain,
133
+ Chain.Base,
134
+ Chain.Aurora,
135
+ Chain.Gnosis,
136
+ Chain.Monad,
137
+ Chain.XLayer,
138
+ ] as const satisfies readonly LedgerSupportedChain[];
139
+
140
+ type LedgerEVMChain = (typeof LEDGER_EVM_CHAINS)[number];
141
+
142
+ function isLedgerSupportedChain(chain: Chain): chain is LedgerSupportedChain {
143
+ return LEDGER_SUPPORTED_CHAINS_CONFIG.includes(chain as LedgerSupportedChain);
144
+ }
44
145
 
45
146
  export const ledgerWallet = createWallet({
46
147
  connect: ({ addChain, supportedChains, walletType }) =>
47
148
  async function connectLedger(
48
149
  chains: Chain[],
49
150
  derivationPath?: DerivationPathArray,
50
- { address, transport }: ConnectLedgerOptions = {},
151
+ { address, dmkSession, onDeviceActionState, originToken, transport }: ConnectLedgerOptions = {},
51
152
  ) {
52
153
  const [chain] = filterSupportedChains({ chains, supportedChains, walletType });
53
154
 
54
155
  if (!chain) return false;
55
156
 
56
157
  const resolvedPath = derivationPath ?? (NetworkDerivationPath[chain] as DerivationPathArray | undefined);
57
- const walletMethods = await getWalletMethods({ address, chain, derivationPath: resolvedPath, transport });
158
+ const walletMethods = await getWalletMethods({
159
+ address,
160
+ chain,
161
+ derivationPath: resolvedPath,
162
+ dmkSession,
163
+ onDeviceActionState,
164
+ originToken,
165
+ transport,
166
+ });
58
167
 
59
168
  addChain({ ...walletMethods, chain, walletType: WalletOption.LEDGER });
60
169
 
@@ -85,436 +194,522 @@ export const ledgerWallet = createWallet({
85
194
  [Chain.THORChain]: true,
86
195
  [Chain.Tron]: true,
87
196
  [Chain.XLayer]: true,
88
- // ZEC: still on bespoke signPCZT path
197
+ // ZEC transparent transfers use DSK, but route-level PCZT signing remains unsupported.
89
198
  },
90
199
  getExtendedPublicKey: getLedgerExtendedPublicKey,
91
200
  name: "connectLedger",
92
- supportedChains: [
93
- Chain.Arbitrum,
94
- Chain.Arc,
95
- Chain.Aurora,
96
- Chain.Avalanche,
97
- Chain.Base,
98
- Chain.Berachain,
99
- Chain.BinanceSmartChain,
100
- Chain.Bitcoin,
101
- Chain.BitcoinCash,
102
- Chain.Cosmos,
103
- Chain.Dash,
104
- Chain.Dogecoin,
105
- Chain.Ethereum,
106
- Chain.Gnosis,
107
- Chain.Litecoin,
108
- Chain.Monad,
109
- Chain.Near,
110
- Chain.Optimism,
111
- Chain.Polygon,
112
- Chain.Ripple,
113
- Chain.Sui,
114
- Chain.THORChain,
115
- Chain.XLayer,
116
- Chain.Tron,
117
- Chain.Zcash,
118
- ],
201
+ supportedChains: [...LEDGER_SUPPORTED_CHAINS_CONFIG],
119
202
  walletType: WalletOption.LEDGER,
120
203
  });
121
204
 
122
205
  export const LEDGER_SUPPORTED_CHAINS = getWalletSupportedChains(ledgerWallet);
123
206
 
124
- // reduce memo length by removing trade limit
125
- function reduceMemo(memo?: string, affiliateAddress = "t") {
126
- if (!memo?.includes("=:")) return memo;
207
+ function getLedgerAccountXpub({ chain, path, signer, xpubVersion }: LedgerAccountXpubParams) {
208
+ const ledgerPath = match(chain)
209
+ .with(Chain.Bitcoin, Chain.Litecoin, () => path)
210
+ .with(Chain.BitcoinCash, Chain.Dash, Chain.Dogecoin, Chain.Zcash, () => path.replace(/^m\//, ""))
211
+ .exhaustive();
212
+
213
+ return match(chain)
214
+ .with(Chain.Bitcoin, () => (signer as BitcoinLedgerClient).getExtendedPublicKey({ path: ledgerPath }))
215
+ .with(Chain.Zcash, () => (signer as ZcashLedgerClient).getExtendedPublicKey({ path: ledgerPath, xpubVersion }))
216
+ .with(Chain.BitcoinCash, Chain.Dash, Chain.Dogecoin, Chain.Litecoin, () =>
217
+ (signer as LegacyUTXOLedgerClient).getExtendedPublicKey(ledgerPath, xpubVersion),
218
+ )
219
+ .exhaustive();
220
+ }
127
221
 
128
- const removedAffiliate = memo.includes(`:${affiliateAddress}:`) ? memo.split(`:${affiliateAddress}:`)[0] : memo;
222
+ // Toolbox-built transactions keep input txids in display order, the order the UTXO API looks transactions up by,
223
+ // while ZcashTransaction.fromBytes (raw API route transactions) keeps them in wire order, so try both. A txid and its
224
+ // byte reversal cannot both name real transactions, so the fallback cannot fetch an unrelated one.
225
+ function resolveZcashInputUtxos(transaction: ZcashTransaction): Promise<UTXOType[]> {
226
+ const zcashApi = getUtxoApi(Chain.Zcash);
227
+ const getRawTx = createCachedRawTxResolver((txid) => zcashApi.getRawTx(txid));
228
+
229
+ return Promise.all(
230
+ Array.from({ length: transaction.inputsLength }, async (_, inputIndex) => {
231
+ const input = transaction.getInput(inputIndex);
232
+ const txid = hex.encode(input.txid);
233
+ const reversedTxid = hex.encode(input.txid.slice().reverse());
234
+ const directTxHex = await getRawTx(txid);
235
+ const [hash, txHex] = directTxHex ? [txid, directTxHex] : [reversedTxid, await getRawTx(reversedTxid)];
236
+ if (!txHex) {
237
+ throw new SwapKitError("wallet_ledger_invalid_params", {
238
+ inputIndex,
239
+ outputIndex: input.index,
240
+ reason: "Unable to resolve previous transaction hex for Ledger signing in either txid byte order",
241
+ reversedTxid,
242
+ txid,
243
+ });
244
+ }
129
245
 
130
- return removedAffiliate?.substring(0, removedAffiliate.lastIndexOf(":"));
246
+ // The Zcash client takes amounts from the device's trusted inputs; fromBytes leaves input values at zero.
247
+ return { hash, index: input.index, txHex, value: Number(input.value) };
248
+ }),
249
+ );
131
250
  }
132
251
 
133
252
  export async function getLedgerExtendedPublicKey(
134
253
  chain: Chain,
135
254
  derivationPath?: DerivationPathArray,
136
- { accountIndex }: { accountIndex?: number } = {},
255
+ {
256
+ accountIndex,
257
+ dmkSession,
258
+ onDeviceActionState,
259
+ transport,
260
+ }: {
261
+ accountIndex?: number;
262
+ dmkSession?: LedgerDMKSession;
263
+ onDeviceActionState?: LedgerDeviceActionStateHandler;
264
+ transport?: Transport;
265
+ } = {},
137
266
  ): Promise<HardwareExtendedPublicKeyInfo | undefined> {
138
267
  if (![Chain.BitcoinCash, Chain.Bitcoin, Chain.Dash, Chain.Dogecoin, Chain.Litecoin, Chain.Zcash].includes(chain)) {
139
268
  throw new SwapKitError("wallet_chain_not_supported", { chain, wallet: WalletOption.LEDGER });
140
269
  }
141
270
 
142
271
  const utxoChain = chain as UTXOChain;
143
- const signer = await getLedgerClient({ chain: utxoChain, derivationPath });
272
+ const signer = await getLedgerClient({
273
+ chain: utxoChain,
274
+ derivationPath,
275
+ dmkSession,
276
+ onDeviceActionState,
277
+ transport,
278
+ });
144
279
  if (!signer.getExtendedPublicKey) return undefined;
145
280
 
146
281
  const accountPath = getUTXOAccountPath({ accountIndex, chain: utxoChain, derivationPath });
147
282
  const path = derivationPathToString(accountPath);
148
- const ledgerPath = chain === Chain.Bitcoin || chain === Chain.Litecoin ? path : path.replace(/^m\//, "");
149
283
  const xpubVersion = getNetworkForChain(utxoChain).bip32.public;
150
284
  try {
151
- const xpub = await signer.getExtendedPublicKey(ledgerPath, xpubVersion);
285
+ const xpub = await getLedgerAccountXpub({ chain: utxoChain, path, signer, xpubVersion });
152
286
  return { accountIndex: getUTXOAccountIndexFromPath(accountPath), path, xpub };
153
287
  } finally {
154
288
  await (signer as { disconnect?: () => Promise<void> }).disconnect?.();
155
289
  }
156
290
  }
157
291
 
158
- async function getWalletMethods({
292
+ async function getUTXOWalletMethods({
159
293
  address: providedAddress,
160
294
  chain,
295
+ dmkSession,
161
296
  derivationPath,
297
+ onDeviceActionState,
162
298
  transport,
163
- }: ConnectLedgerOptions & { chain: Chain; derivationPath?: DerivationPathArray }) {
164
- switch (chain) {
165
- case Chain.BitcoinCash:
166
- case Chain.Bitcoin:
167
- case Chain.Dash:
168
- case Chain.Dogecoin:
169
- case Chain.Litecoin:
170
- case Chain.Zcash: {
171
- const { getUtxoToolbox } = await import("@swapkit/toolboxes/utxo");
172
- const utxoChain = chain as UTXOChain;
173
-
174
- const signer = await getLedgerClient({ chain, derivationPath, transport });
175
-
176
- const address = providedAddress ?? (await getLedgerAddress({ chain, ledgerClient: signer }));
177
- const scriptType = getScriptTypeForAddress(address, utxoChain);
178
-
179
- // V3 toolbox signer:
180
- // - BTC uses the modern `ledger-bitcoin` AppClient with native PSBT signing.
181
- // - LTC/BCH/DOGE/DASH use the legacy `hw-app-btc` adapter that pulls
182
- // `nonWitnessUtxo` (full prev-tx hex) out of the API PSBT.
183
- // The Litecoin Ledger app does not support the `ledger-bitcoin`
184
- // policy APDUs and returns CLA_NOT_SUPPORTED.
185
- // - ZEC stays on the bespoke `signPCZT` flow for now.
186
- let toolboxSigner:
187
- | {
188
- getAddress: () => Promise<string>;
189
- publicKey?: Uint8Array;
190
- signTransaction: (tx: Transaction) => Promise<Transaction>;
299
+ }: WalletMethodsParams<UTXOChain>) {
300
+ const { getUtxoToolbox } = await import("@swapkit/toolboxes/utxo");
301
+ const utxoChain = chain;
302
+
303
+ const signer = await getLedgerClient({ chain, derivationPath, dmkSession, onDeviceActionState, transport });
304
+
305
+ const address = providedAddress ?? (await getLedgerAddress({ chain, ledgerClient: signer }));
306
+ // On Bitcoin the account's path states its encoding (BIP44/49/84/86). The other apps sign for the address format
307
+ // the client derives (an 86' Litecoin account is ltc1q), so they, like a custom purpose, follow the address form.
308
+ const scriptType =
309
+ (chain === Chain.Bitcoin ? getUTXOScriptTypeForPath(derivationPath) : undefined) ??
310
+ getScriptTypeForAddress(address, utxoChain);
311
+
312
+ const { publicKey, signLedgerTransaction, toolboxSigner } = await match(chain)
313
+ .with(Chain.Bitcoin, async () => {
314
+ const bitcoinSigner = signer as BitcoinLedgerClient;
315
+ // Nested SegWit and taproot inputs need the key behind them (redeemScript, tapInternalKey).
316
+ const needsPublicKey = scriptType === UTXOScriptType.P2SH_P2WPKH || scriptType === UTXOScriptType.P2TR;
317
+ const publicKey = needsPublicKey ? await bitcoinSigner.getPublicKey() : undefined;
318
+ return {
319
+ publicKey,
320
+ signLedgerTransaction: ({ inputUtxos, transaction }: SignLedgerTransactionParams) =>
321
+ bitcoinSigner.signTransactionHex({ inputUtxos, tx: transaction as Transaction }),
322
+ toolboxSigner: {
323
+ getAddress: bitcoinSigner.getAddress,
324
+ publicKey,
325
+ signTransaction: bitcoinSigner.signTransaction,
326
+ },
327
+ };
328
+ })
329
+ .with(Chain.BitcoinCash, Chain.Dogecoin, Chain.Dash, Chain.Litecoin, async () => {
330
+ const { createLegacyPsbtSigner, signLegacyPsbtTransaction } = await import("./clients/utxo-legacy-adapter");
331
+ const legacySigner = signer as LegacyUTXOLedgerClient;
332
+ return {
333
+ publicKey: undefined,
334
+ signLedgerTransaction: ({ inputUtxos, transaction }: SignLedgerTransactionParams) =>
335
+ signLegacyPsbtTransaction({
336
+ chain: utxoChain,
337
+ inputUtxos,
338
+ legacyClient: legacySigner,
339
+ tx: transaction as Transaction,
340
+ }),
341
+ toolboxSigner: createLegacyPsbtSigner({ address, chain: utxoChain, legacyClient: legacySigner }),
342
+ };
343
+ })
344
+ .with(Chain.Zcash, () => {
345
+ const zcashSigner = signer as ZcashLedgerClient;
346
+ return {
347
+ publicKey: undefined,
348
+ signLedgerTransaction: async ({ inputUtxos, transaction }: SignLedgerTransactionParams) => {
349
+ if (!("consensusBranchId" in transaction)) {
350
+ throw new SwapKitError("wallet_ledger_method_not_supported", {
351
+ method: "signPCZT",
352
+ wallet: WalletOption.LEDGER,
353
+ });
191
354
  }
192
- | undefined;
193
- let publicKey: Uint8Array | undefined;
194
- let signAndBroadcastLegacyPsbtTransaction: ((tx: Transaction) => Promise<string>) | undefined;
195
- if (chain === Chain.Bitcoin) {
196
- const { BitcoinPsbtLedger } = await import("./clients/utxo-psbt");
197
- const psbtClient = BitcoinPsbtLedger(derivationPath, transport);
198
- publicKey = scriptType === UTXOScriptType.P2SH_P2WPKH ? await psbtClient.getPublicKey() : undefined;
199
- toolboxSigner = { getAddress: psbtClient.getAddress, publicKey, signTransaction: psbtClient.signTransaction };
200
- } else if (
201
- chain === Chain.BitcoinCash ||
202
- chain === Chain.Dogecoin ||
203
- chain === Chain.Dash ||
204
- chain === Chain.Litecoin
205
- ) {
206
- const { createLegacyPsbtSigner, signLegacyPsbtTransaction } = await import("./clients/utxo-legacy-adapter");
207
- toolboxSigner = createLegacyPsbtSigner({ address, chain: utxoChain, legacyClient: signer });
208
- signAndBroadcastLegacyPsbtTransaction = async (tx) => {
209
- const signedTxHex = await signLegacyPsbtTransaction({ chain: utxoChain, legacyClient: signer, tx });
210
- return toolbox.broadcastTx(signedTxHex);
211
- };
212
- }
213
355
 
214
- const toolbox = toolboxSigner
215
- ? await getUtxoToolbox(utxoChain, { scriptType, signer: toolboxSigner })
216
- : getUtxoToolbox(utxoChain, { scriptType });
217
- const signAndBroadcastTransaction = signAndBroadcastLegacyPsbtTransaction ?? toolbox.signAndBroadcastTransaction;
218
-
219
- const transfer = async (params: UTXOBuildTxParams) => {
220
- const feeRate = params.feeRate || (await toolbox.getFeeRates())[FeeOption.Average];
221
- const memo = [Chain.Bitcoin].includes(chain as typeof Chain.Bitcoin) ? params.memo : reduceMemo(params.memo);
222
-
223
- const { tx, inputs } = await toolbox.createTransaction({
224
- ...params,
225
- feeRate,
226
- fetchTxHex: true,
227
- memo,
228
- publicKey: params.publicKey ?? publicKey,
229
- sender: address,
230
- });
356
+ const resolvedInputUtxos = inputUtxos ?? (await resolveZcashInputUtxos(transaction));
357
+ return zcashSigner.signTransaction({ inputUtxos: resolvedInputUtxos, tx: transaction });
358
+ },
359
+ toolboxSigner: undefined,
360
+ };
361
+ })
362
+ .exhaustive();
231
363
 
232
- // Legacy Ledger UTXO signing returns finalized raw tx hex, so transfer
233
- // broadcasts directly instead of routing through toolbox finalization.
234
- const txHex = await signer.signTransaction(tx as Transaction, inputs);
235
- const txHash = await toolbox.broadcastTx(txHex);
364
+ const toolbox = toolboxSigner
365
+ ? await getUtxoToolbox(utxoChain, { scriptType, signer: toolboxSigner })
366
+ : getUtxoToolbox(utxoChain, { scriptType });
367
+ const signAndBroadcastTransaction = async (transaction: Transaction | ZcashTransaction) =>
368
+ toolbox.broadcastTx(await signLedgerTransaction({ transaction }));
236
369
 
237
- return txHash;
238
- };
370
+ // The toolbox's transfer parameters plus a public key override; the sender is always the Ledger account.
371
+ const transfer = async (params: UTXOTransferParams & Pick<UTXOBuildTxParams, "enableRBF" | "publicKey">) => {
372
+ const feeRate = params.feeRate || (await toolbox.getFeeRates())[params.feeOptionKey || FeeOption.Fast];
239
373
 
240
- async function getExtendedPublicKeyInfo({ accountIndex }: { accountIndex?: number } = {}) {
241
- if (!signer.getExtendedPublicKey) return undefined;
374
+ const { tx, inputs } = await toolbox.createTransaction({
375
+ ...params,
376
+ feeRate,
377
+ fetchTxHex: true,
378
+ publicKey: params.publicKey ?? publicKey,
379
+ sender: address,
380
+ });
242
381
 
243
- const accountPath = getUTXOAccountPath({ accountIndex, chain: utxoChain, derivationPath });
244
- const path = derivationPathToString(accountPath);
245
- const ledgerPath = chain === Chain.Bitcoin || chain === Chain.Litecoin ? path : path.replace(/^m\//, "");
246
- const xpubVersion = getNetworkForChain(utxoChain).bip32.public;
247
- const xpub = await signer.getExtendedPublicKey(ledgerPath, xpubVersion);
382
+ const txHex = await signLedgerTransaction({ inputUtxos: inputs, transaction: tx });
383
+ const txHash = await toolbox.broadcastTx(txHex);
248
384
 
249
- return { accountIndex: getUTXOAccountIndexFromPath(accountPath), path, xpub };
250
- }
385
+ return txHash;
386
+ };
251
387
 
252
- function getExtendedPublicKey(params: { accountIndex?: number } = {}) {
253
- return getExtendedPublicKeyInfo(params);
254
- }
388
+ async function getExtendedPublicKeyInfo({ accountIndex }: { accountIndex?: number } = {}) {
389
+ if (!signer.getExtendedPublicKey) return undefined;
255
390
 
256
- async function deriveAddressAtIndex({
257
- accountIndex,
258
- index,
259
- change = false,
260
- }: {
261
- accountIndex?: number;
262
- index: number;
263
- change?: boolean;
264
- }) {
265
- try {
266
- const fullPath = getUTXOAddressPath({ accountIndex, chain: utxoChain, change, derivationPath, index });
267
-
268
- const indexedSigner = await getLedgerClient({ chain: utxoChain, derivationPath: fullPath, transport });
269
- const derivedAddress = await getLedgerAddress({ chain: utxoChain, ledgerClient: indexedSigner });
270
-
271
- return {
272
- accountIndex: getUTXOAccountIndexFromPath(fullPath),
273
- address: derivedAddress,
274
- change,
275
- index,
276
- path: derivationPathToString(fullPath),
277
- pubkey: "",
278
- };
279
- } catch {
280
- return undefined;
281
- }
282
- }
391
+ const accountPath = getUTXOAccountPath({ accountIndex, chain: utxoChain, derivationPath });
392
+ const path = derivationPathToString(accountPath);
393
+ const xpubVersion = getNetworkForChain(utxoChain).bip32.public;
394
+ const xpub = await getLedgerAccountXpub({ chain, path, signer, xpubVersion });
283
395
 
284
- async function deriveAddresses({
285
- accountIndex,
286
- count,
287
- startIndex = 0,
288
- change = false,
289
- }: {
290
- accountIndex?: number;
291
- count: number;
292
- startIndex?: number;
293
- change?: boolean;
294
- }) {
295
- assertDerivationIndex("count", count);
296
- assertDerivationIndex("startIndex", startIndex);
297
-
298
- const addresses = await Promise.all(
299
- Array.from({ length: count }, (_, i) =>
300
- deriveAddressAtIndex({ accountIndex, change, index: startIndex + i }),
301
- ),
302
- );
303
-
304
- return addresses.filter((address) => !!address);
305
- }
396
+ return { accountIndex: getUTXOAccountIndexFromPath(accountPath), path, xpub };
397
+ }
306
398
 
307
- const hdHelpers = createHDWalletHelpers({
399
+ function getExtendedPublicKey(params: { accountIndex?: number } = {}) {
400
+ return getExtendedPublicKeyInfo(params);
401
+ }
402
+
403
+ async function deriveAddressAtIndex({
404
+ accountIndex,
405
+ index,
406
+ change = false,
407
+ }: {
408
+ accountIndex?: number;
409
+ index: number;
410
+ change?: boolean;
411
+ }) {
412
+ try {
413
+ const fullPath = getUTXOAddressPath({ accountIndex, chain: utxoChain, change, derivationPath, index });
414
+
415
+ const indexedSigner = await getLedgerClient({
308
416
  chain: utxoChain,
309
- deriveAddress: deriveAddressAtIndex,
310
- getBalance: toolbox.getBalance,
311
- getUtxos: (addr: string) => getUtxoApi(utxoChain).getUtxos({ address: addr, fetchTxHex: true }),
417
+ derivationPath: fullPath,
418
+ dmkSession,
419
+ onDeviceActionState,
420
+ transport,
312
421
  });
422
+ const derivedAddress = await getLedgerAddress({ chain: utxoChain, ledgerClient: indexedSigner });
313
423
 
314
- async function transferFromMultipleAddresses({
315
- utxos,
316
- recipient,
317
- assetValue,
318
- memo,
319
- feeRate,
320
- feeOptionKey,
321
- changeAddress,
322
- }: {
323
- utxos: UTXOForMultiAddressTransfer[];
324
- recipient: string;
325
- assetValue: AssetValue;
326
- memo?: string;
327
- feeRate?: number;
328
- feeOptionKey?: FeeOption;
329
- changeAddress?: string;
330
- }) {
331
- if (!utxos.length) {
332
- throw new SwapKitError("wallet_ledger_invalid_params", {
333
- message: "No UTXOs provided for multi-address transfer",
334
- });
335
- }
336
-
337
- const txFeeRate = feeRate || (await toolbox.getFeeRates())[feeOptionKey || FeeOption.Fast];
338
- const memoScript = memo ? compileMemo(memo) : null;
339
-
340
- const targetOutputs: Array<{ address: string; value: number } | { script: Uint8Array; value: number }> = [
341
- { address: recipient, value: assetValue.getBaseValue("number") },
342
- ];
343
- if (memoScript) {
344
- targetOutputs.push({ script: memoScript, value: 0 });
345
- }
346
-
347
- const basicUtxos = utxos.map(({ hash, index, value, txHex, witnessUtxo }) => ({
348
- hash,
349
- index,
350
- txHex,
351
- value,
352
- witnessUtxo,
353
- }));
354
-
355
- const { inputs: selectedInputs, outputs } = toolbox.accumulative({
356
- chain: utxoChain,
357
- feeRate: txFeeRate,
358
- inputs: basicUtxos,
359
- outputs: targetOutputs,
360
- });
424
+ return {
425
+ accountIndex: getUTXOAccountIndexFromPath(fullPath),
426
+ address: derivedAddress,
427
+ change,
428
+ index,
429
+ path: derivationPathToString(fullPath),
430
+ pubkey: "",
431
+ };
432
+ } catch {
433
+ return undefined;
434
+ }
435
+ }
361
436
 
362
- if (!(selectedInputs && outputs)) {
363
- throw new SwapKitError("wallet_ledger_connection_error", {
364
- message: "Insufficient balance for multi-address transfer",
365
- });
366
- }
367
-
368
- const { Transaction } = await import("@swapkit/utxo-signer");
369
- const tx = new Transaction({ allowLegacyWitnessUtxo: true, version: 1 });
370
- const senderAddress = changeAddress || utxos[0]?.address || recipient;
371
-
372
- addInputsAndOutputs({
373
- chain: utxoChain,
374
- compiledMemo: memoScript,
375
- inputs: selectedInputs,
376
- outputs,
377
- sender: senderAddress,
378
- tx,
379
- });
437
+ async function deriveAddresses({
438
+ accountIndex,
439
+ count,
440
+ startIndex = 0,
441
+ change = false,
442
+ }: {
443
+ accountIndex?: number;
444
+ count: number;
445
+ startIndex?: number;
446
+ change?: boolean;
447
+ }) {
448
+ assertDerivationIndex("count", count);
449
+ assertDerivationIndex("startIndex", startIndex);
450
+
451
+ const addresses = await Promise.all(
452
+ Array.from({ length: count }, (_, i) => deriveAddressAtIndex({ accountIndex, change, index: startIndex + i })),
453
+ );
454
+
455
+ return addresses.filter((address) => !!address);
456
+ }
380
457
 
381
- const basePath = getUTXOAccountPath({ chain: utxoChain, derivationPath });
382
- const inputDerivationPaths = selectedInputs.map((input: { hash: string; index: number }) => {
383
- const utxoInfo = utxos.find((u) => u.hash === input.hash && u.index === input.index);
384
- const derivationIndex = utxoInfo?.derivationIndex ?? 0;
385
- const isChange = utxoInfo?.isChange ?? false;
386
- const fullPath = [...basePath, Number(isChange), derivationIndex] as unknown as DerivationPathArray;
387
- return derivationPathToString(fullPath);
388
- });
458
+ const hdHelpers = createHDWalletHelpers({
459
+ chain: utxoChain,
460
+ deriveAddress: deriveAddressAtIndex,
461
+ getBalance: toolbox.getBalance,
462
+ getUtxos: (addr: string) => getUtxoApi(utxoChain).getUtxos({ address: addr, fetchTxHex: true }),
463
+ });
464
+
465
+ async function transferFromMultipleAddresses({
466
+ utxos,
467
+ recipient,
468
+ assetValue,
469
+ memo,
470
+ feeRate,
471
+ feeOptionKey,
472
+ changeAddress,
473
+ }: {
474
+ utxos: UTXOForMultiAddressTransfer[];
475
+ recipient: string;
476
+ assetValue: AssetValue;
477
+ memo?: string;
478
+ feeRate?: number;
479
+ feeOptionKey?: FeeOption;
480
+ changeAddress?: string;
481
+ }) {
482
+ if (chain === Chain.Zcash) {
483
+ throw new SwapKitError("wallet_ledger_method_not_supported", {
484
+ method: "transferFromMultipleAddresses",
485
+ wallet: WalletOption.LEDGER,
486
+ });
487
+ }
389
488
 
390
- if (!signer.signTransactionWithMultiplePaths) {
391
- throw new SwapKitError("wallet_ledger_method_not_supported", { method: "signTransactionWithMultiplePaths" });
392
- }
489
+ if (!utxos.length) {
490
+ throw new SwapKitError("wallet_ledger_invalid_params", {
491
+ message: "No UTXOs provided for multi-address transfer",
492
+ });
493
+ }
393
494
 
394
- const txHex = await signer.signTransactionWithMultiplePaths(tx, selectedInputs, inputDerivationPaths);
395
- return toolbox.broadcastTx(txHex);
396
- }
495
+ const txFeeRate = feeRate || (await toolbox.getFeeRates())[feeOptionKey || FeeOption.Fast];
496
+ const memoScript = memo ? compileMemo(memo) : null;
397
497
 
398
- return {
399
- ...toolbox,
400
- ...hdHelpers,
401
- address,
402
- deriveAddressAtIndex,
403
- deriveAddresses,
404
- getExtendedPublicKey,
405
- getExtendedPublicKeyInfo,
406
- signAndBroadcastTransaction,
407
- transfer,
408
- transferFromMultipleAddresses,
409
- };
498
+ const targetOutputs: Array<{ address: string; value: number } | { script: Uint8Array; value: number }> = [
499
+ { address: recipient, value: assetValue.getBaseValue("number") },
500
+ ];
501
+ if (memoScript) {
502
+ targetOutputs.push({ script: memoScript, value: 0 });
410
503
  }
411
504
 
412
- case Chain.Ethereum:
413
- case Chain.Avalanche:
414
- case Chain.Arbitrum:
415
- case Chain.Arc:
416
- case Chain.Berachain:
417
- case Chain.Optimism:
418
- case Chain.Polygon:
419
- case Chain.BinanceSmartChain:
420
- case Chain.Base:
421
- case Chain.Aurora:
422
- case Chain.Gnosis:
423
- case Chain.Monad:
424
- case Chain.XLayer: {
425
- const { getEvmToolboxAsync } = await import("@swapkit/toolboxes/evm");
426
- const signer = await getLedgerClient({ chain, derivationPath, transport });
427
- const address = await getLedgerAddress({ chain, ledgerClient: signer });
428
- const toolbox = await getEvmToolboxAsync(chain, { signer });
429
-
430
- return { ...toolbox, address };
505
+ const basicUtxos = utxos.map(({ hash, index, value, txHex, witnessUtxo }) => ({
506
+ hash,
507
+ index,
508
+ txHex,
509
+ value,
510
+ witnessUtxo,
511
+ }));
512
+
513
+ const { inputs: selectedInputs, outputs } = toolbox.accumulative({
514
+ chain: utxoChain,
515
+ feeRate: txFeeRate,
516
+ inputs: basicUtxos,
517
+ outputs: targetOutputs,
518
+ });
519
+
520
+ if (!(selectedInputs && outputs)) {
521
+ throw new SwapKitError("wallet_ledger_connection_error", {
522
+ message: "Insufficient balance for multi-address transfer",
523
+ });
431
524
  }
432
525
 
433
- case Chain.Cosmos: {
434
- const { createSigningStargateClient, getDefaultChainFee, getMsgSendDenom, getCosmosToolbox } = await import(
435
- "@swapkit/toolboxes/cosmos"
436
- );
437
- const signer = await getLedgerClient({ chain, derivationPath, transport });
438
- const address = await getLedgerAddress({ chain, ledgerClient: signer });
439
- const toolbox = await getCosmosToolbox(Chain.Cosmos, { signer });
440
-
441
- const transfer = async ({ assetValue, recipient, memo }: GenericTransferParams) => {
442
- if (!assetValue) throw new SwapKitError("wallet_ledger_invalid_asset");
443
-
444
- const sendCoinsMessage = {
445
- amount: [
446
- {
447
- amount: assetValue.getBaseValue("string"),
448
- denom: getMsgSendDenom(`u${assetValue.symbol}`).toLowerCase(),
449
- },
450
- ],
451
- fromAddress: address,
452
- toAddress: recipient,
453
- };
454
-
455
- const rpcUrl = await getRPCUrl(chain);
456
- const signingClient = await createSigningStargateClient(rpcUrl, signer, "0.007uatom");
457
-
458
- const { transactionHash } = await signingClient.signAndBroadcast(
459
- address,
460
- [{ typeUrl: "/cosmos.bank.v1beta1.MsgSend", value: sendCoinsMessage }],
461
- getDefaultChainFee(Chain.Cosmos),
462
- memo,
463
- );
464
-
465
- return transactionHash;
466
- };
526
+ const { Transaction } = await import("@swapkit/utxo-signer");
527
+ const tx = new Transaction({ allowLegacyWitnessUtxo: true, allowUnknownOutputs: !!memoScript, version: 1 });
528
+ const senderAddress = changeAddress || utxos[0]?.address || recipient;
529
+
530
+ addInputsAndOutputs({
531
+ chain: utxoChain,
532
+ compiledMemo: memoScript,
533
+ inputs: selectedInputs,
534
+ outputs,
535
+ sender: senderAddress,
536
+ tx,
537
+ });
538
+
539
+ const inputDerivationPaths = selectedInputs.map((input: { hash: string; index: number }) => {
540
+ const utxoInfo = utxos.find((u) => u.hash === input.hash && u.index === input.index);
541
+ const fullPath = getUTXOAddressPath({
542
+ chain: utxoChain,
543
+ change: utxoInfo?.isChange ?? false,
544
+ derivationPath,
545
+ index: utxoInfo?.derivationIndex ?? 0,
546
+ });
547
+ return derivationPathToString(fullPath);
548
+ });
467
549
 
468
- return { ...toolbox, address, transfer };
550
+ if (!signer.signTransactionWithMultiplePaths) {
551
+ throw new SwapKitError("wallet_ledger_method_not_supported", { method: "signTransactionWithMultiplePaths" });
469
552
  }
470
553
 
471
- case Chain.THORChain: {
472
- const signer = await getLedgerClient({ chain, derivationPath, transport });
473
- const { getCosmosToolbox } = await import("@swapkit/toolboxes/cosmos");
474
- const toolbox = getCosmosToolbox(chain, { signer });
475
- const address = await getLedgerAddress({ chain, ledgerClient: signer });
476
- const { sign: signMessage } = signer;
554
+ const txHex = await match(chain)
555
+ .with(Chain.Bitcoin, () =>
556
+ (signer as BitcoinLedgerClient).signTransactionWithMultiplePaths({
557
+ derivationPaths: inputDerivationPaths,
558
+ inputUtxos: selectedInputs,
559
+ tx,
560
+ }),
561
+ )
562
+ .with(Chain.BitcoinCash, Chain.Dash, Chain.Dogecoin, Chain.Litecoin, () =>
563
+ (signer as LegacyUTXOLedgerClient).signTransactionWithMultiplePaths(tx, selectedInputs, inputDerivationPaths),
564
+ )
565
+ .exhaustive();
566
+ return toolbox.broadcastTx(txHex);
567
+ }
477
568
 
478
- return { ...toolbox, address, signMessage };
479
- }
569
+ return {
570
+ ...toolbox,
571
+ ...hdHelpers,
572
+ address,
573
+ deriveAddressAtIndex,
574
+ deriveAddresses,
575
+ getExtendedPublicKey,
576
+ getExtendedPublicKeyInfo,
577
+ signAndBroadcastTransaction,
578
+ transfer,
579
+ transferFromMultipleAddresses,
580
+ };
581
+ }
480
582
 
481
- case Chain.Near: {
482
- const { getNearToolbox } = await import("@swapkit/toolboxes/near");
483
- const signer = await getLedgerClient({ chain, derivationPath, transport });
484
- const accountId = await signer.getAddress();
485
- const toolbox = getNearToolbox({ signer });
583
+ async function getEVMWalletMethods({
584
+ chain,
585
+ derivationPath,
586
+ dmkSession,
587
+ onDeviceActionState,
588
+ originToken,
589
+ transport,
590
+ }: WalletMethodsParams<LedgerEVMChain>) {
591
+ const signer = await getLedgerClient({
592
+ chain,
593
+ derivationPath,
594
+ dmkSession,
595
+ onDeviceActionState,
596
+ originToken,
597
+ transport,
598
+ });
599
+ const { getEvmToolboxAsync } = await import("@swapkit/toolboxes/evm");
600
+ const address = await getLedgerAddress({ chain, ledgerClient: signer });
601
+ const toolbox = await getEvmToolboxAsync(chain, { signer });
602
+
603
+ return { ...toolbox, address };
604
+ }
486
605
 
487
- return { ...toolbox, address: accountId };
488
- }
606
+ async function getCosmosWalletMethods({
607
+ chain,
608
+ derivationPath,
609
+ dmkSession,
610
+ onDeviceActionState,
611
+ transport,
612
+ }: WalletMethodsParams<Chain.Cosmos>) {
613
+ const signer = await getLedgerClient({ chain, derivationPath, dmkSession, onDeviceActionState, transport });
614
+ const { getCosmosToolbox } = await import("@swapkit/toolboxes/cosmos");
615
+ const toolbox = getCosmosToolbox(chain, { signer });
616
+ const address = await getLedgerAddress({ chain, ledgerClient: signer });
489
617
 
490
- case Chain.Ripple: {
491
- const { getRippleToolbox } = await import("@swapkit/toolboxes/ripple");
492
- const signer = await getLedgerClient({ chain, derivationPath, transport });
493
- const address = signer.getAddress();
494
- const toolbox = getRippleToolbox({ signer });
618
+ return { ...toolbox, address };
619
+ }
495
620
 
496
- return { ...toolbox, address };
497
- }
621
+ async function getTHORChainWalletMethods({
622
+ chain,
623
+ derivationPath,
624
+ dmkSession,
625
+ onDeviceActionState,
626
+ transport,
627
+ }: WalletMethodsParams<Chain.THORChain>) {
628
+ const signer = await getLedgerClient({ chain, derivationPath, dmkSession, onDeviceActionState, transport });
629
+ const { getCosmosToolbox } = await import("@swapkit/toolboxes/cosmos");
630
+ const toolbox = getCosmosToolbox(chain, { signer });
631
+ const address = await getLedgerAddress({ chain, ledgerClient: signer });
632
+ const { sign: signMessage } = signer;
633
+
634
+ return { ...toolbox, address, signMessage };
635
+ }
498
636
 
499
- case Chain.Tron: {
500
- const { getTronToolbox } = await import("@swapkit/toolboxes/tron");
501
- const signer = await getLedgerClient({ chain, derivationPath, transport });
502
- const address = await getLedgerAddress({ chain, ledgerClient: signer });
503
- const toolbox = getTronToolbox({ signer });
637
+ async function getNearWalletMethods({
638
+ chain,
639
+ derivationPath,
640
+ dmkSession,
641
+ onDeviceActionState,
642
+ transport,
643
+ }: WalletMethodsParams<Chain.Near>) {
644
+ const { getNearToolbox } = await import("@swapkit/toolboxes/near");
645
+ const signer = await getLedgerClient({ chain, derivationPath, dmkSession, onDeviceActionState, transport });
646
+ const accountId = await signer.getAddress();
647
+ const toolbox = getNearToolbox({ signer });
504
648
 
505
- return { ...toolbox, address };
506
- }
649
+ return { ...toolbox, address: accountId };
650
+ }
651
+
652
+ async function getRippleWalletMethods({
653
+ chain,
654
+ derivationPath,
655
+ dmkSession,
656
+ onDeviceActionState,
657
+ transport,
658
+ }: WalletMethodsParams<Chain.Ripple>) {
659
+ const { getRippleToolbox } = await import("@swapkit/toolboxes/ripple");
660
+ const signer = await getLedgerClient({ chain, derivationPath, dmkSession, onDeviceActionState, transport });
661
+ const address = signer.getAddress();
662
+ const toolbox = getRippleToolbox({ signer });
507
663
 
508
- case Chain.Sui: {
509
- const { getSuiToolbox } = await import("@swapkit/toolboxes/sui");
510
- const signer = await getLedgerClient({ chain, derivationPath, transport });
511
- const address = await getLedgerAddress({ chain, ledgerClient: signer });
512
- const toolbox = getSuiToolbox({ signer });
664
+ return { ...toolbox, address };
665
+ }
513
666
 
514
- return { ...toolbox, address };
515
- }
667
+ async function getTronWalletMethods({
668
+ chain,
669
+ derivationPath,
670
+ dmkSession,
671
+ onDeviceActionState,
672
+ transport,
673
+ }: WalletMethodsParams<Chain.Tron>) {
674
+ const { getTronToolbox } = await import("@swapkit/toolboxes/tron");
675
+ const signer = await getLedgerClient({ chain, derivationPath, dmkSession, onDeviceActionState, transport });
676
+ const address = await getLedgerAddress({ chain, ledgerClient: signer });
677
+ const toolbox = getTronToolbox({ signer });
678
+
679
+ return { ...toolbox, address };
680
+ }
681
+
682
+ async function getSuiWalletMethods({
683
+ chain,
684
+ derivationPath,
685
+ dmkSession,
686
+ onDeviceActionState,
687
+ transport,
688
+ }: WalletMethodsParams<Chain.Sui>) {
689
+ const { getSuiToolbox } = await import("@swapkit/toolboxes/sui");
690
+ const signer = await getLedgerClient({ chain, derivationPath, dmkSession, onDeviceActionState, transport });
691
+ const address = await getLedgerAddress({ chain, ledgerClient: signer });
692
+ const toolbox = getSuiToolbox({ signer });
693
+
694
+ return { ...toolbox, address };
695
+ }
516
696
 
517
- default:
518
- throw new SwapKitError("wallet_ledger_chain_not_supported", { chain });
697
+ function getWalletMethods(params: WalletMethodsParams) {
698
+ if (!isLedgerSupportedChain(params.chain)) {
699
+ throw new SwapKitError("wallet_ledger_chain_not_supported", { chain: params.chain });
519
700
  }
701
+
702
+ return match(params.chain)
703
+ .with(P.union(...LEDGER_UTXO_CHAINS), (chain) => getUTXOWalletMethods({ ...params, chain }))
704
+ .with(P.union(...LEDGER_EVM_CHAINS), (chain) => getEVMWalletMethods({ ...params, chain }))
705
+ .with(Chain.Cosmos, (chain) => getCosmosWalletMethods({ ...params, chain }))
706
+ .with(Chain.THORChain, (chain) => getTHORChainWalletMethods({ ...params, chain }))
707
+ .with(Chain.Near, (chain) => getNearWalletMethods({ ...params, chain }))
708
+ .with(Chain.Ripple, (chain) => getRippleWalletMethods({ ...params, chain }))
709
+ .with(Chain.Tron, (chain) => getTronWalletMethods({ ...params, chain }))
710
+ .with(Chain.Sui, (chain) => getSuiWalletMethods({ ...params, chain }))
711
+ .exhaustive();
520
712
  }
713
+
714
+ export type { LedgerDeviceActionState, LedgerDeviceActionStateHandler, LedgerDMKSession } from "./helpers";
715
+ export { disconnectLedgerDMKSession, getLedgerDMKSession, preloadLedgerDMK } from "./helpers";