@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,172 +0,0 @@
1
- import type Transport from "@ledgerhq/hw-transport";
2
- import { base64 } from "@scure/base";
3
- import { HDKey } from "@scure/bip32";
4
- import { type DerivationPathArray, derivationPathToString, getWalletFormatFor, SwapKitError } from "@swapkit/helpers";
5
- import type { Transaction } from "@swapkit/utxo-signer";
6
-
7
- import { getLedgerTransport } from "../helpers/getLedgerTransport";
8
-
9
- type SupportedCoin = "bitcoin" | "litecoin";
10
-
11
- type DefaultDescriptorTemplate = "wpkh(@0/**)" | "tr(@0/**)" | "sh(wpkh(@0/**))" | "pkh(@0/**)";
12
-
13
- function templateForFormat(format: ReturnType<typeof getWalletFormatFor>): DefaultDescriptorTemplate {
14
- switch (format) {
15
- case "bech32":
16
- return "wpkh(@0/**)";
17
- case "p2sh":
18
- return "sh(wpkh(@0/**))";
19
- case "legacy":
20
- return "pkh(@0/**)";
21
- default:
22
- return "wpkh(@0/**)";
23
- }
24
- }
25
-
26
- function pathToString(path: DerivationPathArray | string): string {
27
- return typeof path === "string" ? path : derivationPathToString(path);
28
- }
29
-
30
- function normalizeLedgerPath(path: string): string {
31
- return path.replace(/^m\//, "").replace(/^\/+/, "");
32
- }
33
-
34
- function pathToNumberArray(path: string): number[] {
35
- return path
36
- .replace(/^m\//, "")
37
- .split("/")
38
- .filter(Boolean)
39
- .map((p) => {
40
- const hardened = p.endsWith("'");
41
- const num = Number.parseInt(hardened ? p.slice(0, -1) : p, 10);
42
- return hardened ? (num | 0x80000000) >>> 0 : num;
43
- });
44
- }
45
-
46
- function hasBip32Derivation(tx: Transaction, inputIndex: number) {
47
- const input = tx.getInput(inputIndex) as { bip32Derivation?: Array<unknown> };
48
-
49
- return Boolean(input.bip32Derivation?.length);
50
- }
51
-
52
- const BaseLedgerPsbtUTXO = ({ chain }: { chain: SupportedCoin }) => {
53
- return (derivationPathArray?: DerivationPathArray | string, injectedTransport?: Transport) => {
54
- // Per-call state — each BitcoinPsbtLedger/LitecoinPsbtLedger invocation has its own
55
- // AppClient and master fingerprint so different consumers (e.g. concurrent MCP
56
- // sessions on different devices) cannot inherit each other's ledger bindings or xpub.
57
- let appClient: import("ledger-bitcoin").AppClient | undefined;
58
- let masterFingerprint: string | undefined;
59
-
60
- async function getAppClient() {
61
- if (!appClient) {
62
- const transport = injectedTransport ?? (await getLedgerTransport());
63
- const { AppClient } = await import("ledger-bitcoin");
64
- appClient = new AppClient(transport);
65
- }
66
- return appClient;
67
- }
68
-
69
- async function getFingerprint() {
70
- if (!masterFingerprint) {
71
- const app = await getAppClient();
72
- masterFingerprint = await app.getMasterFingerprint();
73
- }
74
- return masterFingerprint;
75
- }
76
-
77
- // Single-address account: change == index == 0 by default.
78
- const derivationPath = normalizeLedgerPath(
79
- derivationPathArray ? pathToString(derivationPathArray) : "84'/0'/0'/0/0",
80
- );
81
- const pathSegments = derivationPath.split("/").filter(Boolean);
82
- const accountPath = pathSegments.slice(0, 3).join("/");
83
- const leafSegments = pathSegments.slice(3);
84
- const change = Number(leafSegments[0] ?? 0);
85
- const addressIndex = Number(leafSegments[1] ?? 0);
86
- // getWalletFormatFor expects an m/-prefixed path (it reads the purpose
87
- // from the second segment); derivationPath is normalized without it.
88
- const format = getWalletFormatFor(`m/${derivationPath}`);
89
- const template = templateForFormat(format);
90
-
91
- let cachedAccountXpub: string | undefined;
92
- let cachedLeafPubkey: Uint8Array | undefined;
93
-
94
- async function buildPolicy() {
95
- const app = await getAppClient();
96
- const fpr = await getFingerprint();
97
- if (!cachedAccountXpub) {
98
- cachedAccountXpub = await app.getExtendedPubkey(`m/${accountPath}`);
99
- }
100
- const { DefaultWalletPolicy } = await import("ledger-bitcoin");
101
- const policy = new DefaultWalletPolicy(template, `[${fpr}/${accountPath}]${cachedAccountXpub}`);
102
- return { app, fpr, policy, xpub: cachedAccountXpub };
103
- }
104
-
105
- async function getLeafPubkey() {
106
- if (!cachedLeafPubkey) {
107
- const { xpub } = await buildPolicy();
108
- const accountKey = HDKey.fromExtendedKey(xpub);
109
- const leaf = accountKey.derive(`m/${change}/${addressIndex}`);
110
- if (!leaf.publicKey) {
111
- throw new SwapKitError("wallet_ledger_get_address_error", {
112
- message: `Cannot derive leaf pubkey for ${chain}`,
113
- });
114
- }
115
- cachedLeafPubkey = leaf.publicKey;
116
- }
117
- return cachedLeafPubkey;
118
- }
119
-
120
- return {
121
- connect: async () => {
122
- await getAppClient();
123
- },
124
- getAddress: async () => {
125
- const { app, policy } = await buildPolicy();
126
- const address = await app.getWalletAddress(policy, null, change, addressIndex, false);
127
- if (!address) {
128
- throw new SwapKitError("wallet_ledger_get_address_error", {
129
- message: `Cannot get ${chain} address from ledger derivation path: ${derivationPath}`,
130
- });
131
- }
132
- return address;
133
- },
134
- getExtendedPublicKey: async (path = `m/${accountPath}`) => {
135
- const app = await getAppClient();
136
- return app.getExtendedPubkey(`m/${normalizeLedgerPath(path)}`);
137
- },
138
- getPublicKey: getLeafPubkey,
139
- signTransaction: async (tx: Transaction): Promise<Transaction> => {
140
- const { app, policy, fpr } = await buildPolicy();
141
- const fingerprintBE = Number.parseInt(fpr, 16) >>> 0;
142
- const pathNumbers = pathToNumberArray(derivationPath);
143
- const missingDerivationIndexes = Array.from({ length: tx.inputsLength }, (_, inputIndex) => inputIndex).filter(
144
- (inputIndex) => !hasBip32Derivation(tx, inputIndex),
145
- );
146
-
147
- if (missingDerivationIndexes.length > 0) {
148
- const leafPubkey = await getLeafPubkey();
149
-
150
- // Fallback for PSBTs that do not include per-input HD key origins.
151
- for (const inputIndex of missingDerivationIndexes) {
152
- tx.updateInput(inputIndex, {
153
- bip32Derivation: [[leafPubkey, { fingerprint: fingerprintBE, path: pathNumbers }]],
154
- });
155
- }
156
- }
157
-
158
- const psbtB64 = base64.encode(tx.toPSBT(0));
159
- const sigs = await app.signPsbt(psbtB64, policy, null);
160
-
161
- for (const [idx, partial] of sigs) {
162
- tx.updateInput(idx, { partialSig: [[new Uint8Array(partial.pubkey), new Uint8Array(partial.signature)]] });
163
- }
164
-
165
- return tx;
166
- },
167
- };
168
- };
169
- };
170
-
171
- export const BitcoinPsbtLedger = BaseLedgerPsbtUTXO({ chain: "bitcoin" });
172
- export const LitecoinPsbtLedger = BaseLedgerPsbtUTXO({ chain: "litecoin" });
@@ -1,98 +0,0 @@
1
- import { SwapKitError } from "@swapkit/helpers";
2
-
3
- export interface Coin {
4
- readonly denom: string;
5
- readonly amount: string;
6
- }
7
-
8
- export interface EncodeObject {
9
- readonly typeUrl: string;
10
- readonly value: any;
11
- }
12
-
13
- export interface AminoMsg {
14
- readonly type: string;
15
- readonly value: any;
16
- }
17
-
18
- export interface AminoMsgSend extends AminoMsg {
19
- readonly type: "cosmos-sdk/MsgSend";
20
- readonly value: {
21
- /** Bech32 account address */
22
- readonly from_address: string;
23
- /** Bech32 account address */
24
- readonly to_address: string;
25
- readonly amount: readonly Coin[];
26
- };
27
- }
28
-
29
- export interface AminoConverter {
30
- readonly aminoType: string;
31
- readonly toAmino: (value: any) => any;
32
- readonly fromAmino: (value: any) => any;
33
- }
34
-
35
- /** A map from protobuf type URL to the AminoConverter implementation if supported on chain */
36
- export type AminoConverters = Record<string, AminoConverter | "not_supported_by_chain">;
37
-
38
- function isAminoConverter(
39
- converter: [string, AminoConverter | "not_supported_by_chain"],
40
- ): converter is [string, AminoConverter] {
41
- return typeof converter[1] !== "string";
42
- }
43
-
44
- /**
45
- * A map from Stargate message types as used in the messages's `Any` type
46
- * to Amino types.
47
- */
48
- export class AminoTypes {
49
- // The map type here ensures uniqueness of the protobuf type URL in the key.
50
- // There is no uniqueness guarantee of the Amino type identifier in the type
51
- // system or constructor. Instead it's the user's responsibility to ensure
52
- // there is no overlap when fromAmino is called.
53
- private readonly register: Record<string, AminoConverter | "not_supported_by_chain">;
54
-
55
- constructor(types: AminoConverters) {
56
- this.register = types;
57
- }
58
-
59
- toAmino({ typeUrl, value }: EncodeObject): AminoMsg {
60
- const converter = this.register[typeUrl];
61
- if (converter === "not_supported_by_chain") {
62
- throw new SwapKitError("wallet_ledger_chain_not_supported", {
63
- reason: `The message type '${typeUrl}' cannot be signed using the Amino JSON sign mode because this is not supported by chain.`,
64
- });
65
- }
66
- if (!converter) {
67
- throw new SwapKitError("wallet_ledger_invalid_params", {
68
- reason: `Type URL '${typeUrl}' does not exist in the Amino message type register.`,
69
- });
70
- }
71
- return { type: converter.aminoType, value: converter.toAmino(value) };
72
- }
73
-
74
- fromAmino({ type, value }: AminoMsg): EncodeObject {
75
- const matches = Object.entries(this.register)
76
- .filter(isAminoConverter)
77
- .filter(([_typeUrl, { aminoType }]) => aminoType === type);
78
-
79
- if (matches.length === 0) {
80
- throw new SwapKitError("wallet_ledger_invalid_params", {
81
- reason: `Amino type identifier '${type}' does not exist in the Amino message type register.`,
82
- });
83
- }
84
-
85
- if (matches.length > 1) {
86
- throw new SwapKitError("wallet_ledger_invalid_params", {
87
- reason: `Multiple types are registered with Amino type identifier '${type}': '${matches
88
- .map(([key, _value]) => key)
89
- .sort()
90
- .join("', '")}'. Thus fromAmino cannot be performed.`,
91
- });
92
- }
93
-
94
- const [typeUrl, converter] = matches[0] as [string, AminoConverter];
95
-
96
- return { typeUrl, value: converter.fromAmino(value) };
97
- }
98
- }
@@ -1,102 +0,0 @@
1
- import type Transport from "@ledgerhq/hw-transport";
2
- import { SwapKitError } from "@swapkit/helpers";
3
-
4
- const getNavigatorUsb = () =>
5
- navigator?.usb as unknown as {
6
- getDevices: () => Promise<any[]>;
7
- requestDevice: (requestObject: any) => Promise<any>;
8
- removeEventListener: (event: string, callback: (e: any) => void) => void;
9
- addEventListener: (event: string, callback: (e: any) => void) => void;
10
- };
11
-
12
- const getLedgerDevices = async (): Promise<USBDevice> => {
13
- const navigatorUsb = getNavigatorUsb();
14
-
15
- if (typeof navigatorUsb?.getDevices !== "function") return {} as USBDevice;
16
- const { ledgerUSBVendorId } = await import("@ledgerhq/devices");
17
-
18
- const devices = await navigatorUsb?.getDevices();
19
- const existingDevices = devices.filter((d) => d.vendorId === ledgerUSBVendorId);
20
- if (existingDevices.length > 0) return existingDevices[0];
21
-
22
- return navigatorUsb?.requestDevice({ filters: [{ vendorId: ledgerUSBVendorId }] });
23
- };
24
-
25
- export const getLedgerTransport = async (): Promise<Transport> => {
26
- const device = await getLedgerDevices();
27
-
28
- if (!device) {
29
- throw new SwapKitError("wallet_ledger_device_not_found");
30
- }
31
-
32
- device.opened || (await device.open());
33
- if (device.configuration === null) await device.selectConfiguration(1);
34
-
35
- try {
36
- await device.reset();
37
- } catch {
38
- // reset fails on devices that are already open
39
- }
40
-
41
- const configuration = device.configuration ?? device.configurations?.[0];
42
-
43
- const iface =
44
- configuration?.interfaces.find(({ alternates }: { alternates: { interfaceClass: number }[] }) =>
45
- alternates.some(({ interfaceClass }) => interfaceClass === 0xff),
46
- ) ||
47
- configuration?.interfaces.find(({ alternates }: { alternates: { interfaceClass: number }[] }) =>
48
- alternates.some(({ interfaceClass }) => interfaceClass === 0x03),
49
- );
50
-
51
- if (!iface) {
52
- await device.close();
53
- throw new SwapKitError("wallet_ledger_connection_error");
54
- }
55
-
56
- const klass0x03 = (iface.alternates as any[])?.find(
57
- ({ interfaceClass }: { interfaceClass: number }) => interfaceClass === 0x03,
58
- )?.interfaceClass as number;
59
-
60
- const klass0xff = (iface.alternates as any[])?.find(
61
- ({ interfaceClass }: { interfaceClass: number }) => interfaceClass === 0xff,
62
- )?.interfaceClass as number;
63
-
64
- if (klass0x03 && !klass0xff) {
65
- // -------- HID class (NEAR, ETH, SOL, etc.) -> WebHID transport --------
66
- const TransportWebHID = (await import("@ledgerhq/hw-transport-webhid")).default;
67
- const supported = await TransportWebHID.isSupported();
68
- if (!supported) {
69
- await device.close();
70
- throw new SwapKitError("wallet_ledger_webhid_not_supported");
71
- }
72
- const transport = await TransportWebHID.create();
73
- return transport;
74
- }
75
-
76
- try {
77
- await device.claimInterface(iface.interfaceNumber);
78
- } catch (error: unknown) {
79
- await device.close();
80
-
81
- throw new SwapKitError("wallet_ledger_connection_claimed", error);
82
- }
83
-
84
- const Transport = (await import("@ledgerhq/hw-transport-webusb")).default;
85
- const isSupported = await Transport.isSupported();
86
- if (!isSupported) throw new SwapKitError("wallet_ledger_webusb_not_supported");
87
-
88
- const { DisconnectedDevice } = await import("@ledgerhq/errors");
89
-
90
- const transport = new Transport(device, iface.interfaceNumber);
91
-
92
- const onDisconnect = (e: any) => {
93
- if (device === e.device) {
94
- getNavigatorUsb()?.removeEventListener("disconnect", onDisconnect);
95
-
96
- transport._emitDisconnect(new DisconnectedDevice());
97
- }
98
- };
99
- getNavigatorUsb()?.addEventListener("disconnect", onDisconnect);
100
-
101
- return transport as Transport;
102
- };
@@ -1,127 +0,0 @@
1
- import type Transport from "@ledgerhq/hw-transport";
2
-
3
- const LEDGER_CAL_SERVICE_URL = "https://crypto-assets-service.api.ledger.com/v1";
4
-
5
- const LEDGER_DEVICE_TO_CAL_MODEL: Record<string, string> = {
6
- apex: "apexp",
7
- europa: "flex",
8
- nanoS: "nanos",
9
- nanoSP: "nanosp",
10
- nanoX: "nanox",
11
- stax: "stax",
12
- };
13
-
14
- const APDU_CLA = 0xe0;
15
- const APDU_CLA_PKI = 0xb0;
16
- const APDU_INS_LOAD_CERTIFICATE = 0x06;
17
- const LEDGER_KEY_USAGE_NETWORK = 0x0c;
18
- const APDU_INS_PROVIDE_NETWORK_INFORMATION = 0x30;
19
- const APDU_P1_FIRST_CHUNK = 0x01;
20
- const APDU_P1_FOLLOWING_CHUNK = 0x00;
21
- const APDU_P2_NETWORK_CONFIG = 0x00;
22
- const APDU_P2_NETWORK_ICON = 0x01;
23
- const APDU_MAX_CHUNK_SIZE = 0xff;
24
- const TLV_TAG_DER_SIGNATURE = 0x15;
25
-
26
- export type LedgerNetworkDescriptor = { data: string; icon?: string; signature: string };
27
-
28
- type CalNetwork = {
29
- chain_id: number;
30
- descriptors?: Record<string, { data?: string; signatures?: { prod?: string } }>;
31
- icons?: Record<string, string>;
32
- };
33
-
34
- export async function fetchLedgerNetworkCertificate(
35
- deviceModelId: string,
36
- fetchFn: typeof fetch = fetch,
37
- ): Promise<string | null> {
38
- const calModel = LEDGER_DEVICE_TO_CAL_MODEL[deviceModelId];
39
- if (!calModel) return null;
40
-
41
- const url = `${LEDGER_CAL_SERVICE_URL}/certificates?output=descriptor&public_key_usage=network&target_device=${calModel}`;
42
- const response = await fetchFn(url);
43
- if (!response.ok) return null;
44
-
45
- const certificates = (await response.json()) as Array<{
46
- descriptor?: { data?: string; signatures?: { prod?: string } };
47
- }>;
48
- const descriptor = certificates[0]?.descriptor;
49
- const signature = descriptor?.signatures?.prod;
50
-
51
- if (!(descriptor?.data && signature)) return null;
52
-
53
- return `${descriptor.data}${appendSignatureTlv(signature)}`;
54
- }
55
-
56
- export async function provideLedgerCertificate(transport: Transport, payloadHex: string) {
57
- await transport.send(
58
- APDU_CLA_PKI,
59
- APDU_INS_LOAD_CERTIFICATE,
60
- LEDGER_KEY_USAGE_NETWORK,
61
- 0x00,
62
- Buffer.from(payloadHex, "hex"),
63
- );
64
- }
65
-
66
- export async function fetchLedgerNetworkDescriptor(
67
- chainId: number,
68
- deviceModelId: string,
69
- fetchFn: typeof fetch = fetch,
70
- ): Promise<LedgerNetworkDescriptor | null> {
71
- const calModel = LEDGER_DEVICE_TO_CAL_MODEL[deviceModelId];
72
- if (!calModel) return null;
73
-
74
- const url = `${LEDGER_CAL_SERVICE_URL}/networks?chain_id=${chainId}&output=chain_id,descriptors,icons`;
75
- const response = await fetchFn(url);
76
- if (!response.ok) return null;
77
-
78
- const networks = (await response.json()) as CalNetwork[];
79
- const network = networks.find((entry) => entry.chain_id === chainId);
80
- const descriptor = network?.descriptors?.[calModel];
81
- const signature = descriptor?.signatures?.prod;
82
-
83
- if (!(descriptor?.data && signature)) return null;
84
-
85
- return { data: descriptor.data, icon: network?.icons?.[calModel], signature };
86
- }
87
-
88
- function appendSignatureTlv(signature: string) {
89
- return encodeTlv(TLV_TAG_DER_SIGNATURE, Buffer.from(signature, "hex")).toString("hex");
90
- }
91
-
92
- function encodeTlv(tag: number, value: Buffer) {
93
- return Buffer.concat([Buffer.from([tag, value.length]), value]);
94
- }
95
-
96
- export function encodeNetworkInfoPayload({ data, signature }: LedgerNetworkDescriptor) {
97
- const tlv = Buffer.concat([
98
- Buffer.from(data, "hex"),
99
- encodeTlv(TLV_TAG_DER_SIGNATURE, Buffer.from(signature, "hex")),
100
- ]);
101
- const length = Buffer.alloc(2);
102
- length.writeUInt16BE(tlv.length, 0);
103
- return Buffer.concat([length, tlv]);
104
- }
105
-
106
- async function sendChunked(transport: Transport, p2: number, payload: Buffer) {
107
- for (let offset = 0; offset < payload.length; offset += APDU_MAX_CHUNK_SIZE) {
108
- const p1 = offset === 0 ? APDU_P1_FIRST_CHUNK : APDU_P1_FOLLOWING_CHUNK;
109
- const chunk = payload.subarray(offset, offset + APDU_MAX_CHUNK_SIZE);
110
- await transport.send(APDU_CLA, APDU_INS_PROVIDE_NETWORK_INFORMATION, p1, p2, chunk);
111
- }
112
- }
113
-
114
- export async function provideLedgerNetworkInformation(transport: Transport, descriptor: LedgerNetworkDescriptor) {
115
- const payload = encodeNetworkInfoPayload(descriptor);
116
- await sendChunked(transport, APDU_P2_NETWORK_CONFIG, payload);
117
-
118
- if (!descriptor.icon) return { iconAccepted: false };
119
-
120
- try {
121
- await sendChunked(transport, APDU_P2_NETWORK_ICON, Buffer.from(descriptor.icon, "hex"));
122
- return { iconAccepted: true };
123
- } catch {
124
- await sendChunked(transport, APDU_P2_NETWORK_CONFIG, payload);
125
- return { iconAccepted: false };
126
- }
127
- }
@@ -1,67 +0,0 @@
1
- import type Transport from "@ledgerhq/hw-transport";
2
- import { type DerivationPathArray, LedgerErrorCode, NetworkDerivationPath, SwapKitError } from "@swapkit/helpers";
3
-
4
- import { THORChainApp } from "../clients/thorchain/lib";
5
- import { getLedgerTransport } from "../helpers/getLedgerTransport";
6
-
7
- export abstract class CosmosLedgerInterface {
8
- ledgerTimeout = 50000;
9
- derivationPath: DerivationPathArray | string = NetworkDerivationPath.GAIA;
10
- transport: any;
11
- ledgerApp: any;
12
- chain: "thor" | "cosmos" = "thor";
13
-
14
- private readonly injectedTransport?: Transport;
15
-
16
- constructor(transport?: Transport) {
17
- this.injectedTransport = transport;
18
- if (transport) this.transport = transport;
19
- }
20
-
21
- checkOrCreateTransportAndLedger = async (forceReconnect = false) => {
22
- if (!forceReconnect && this.transport && this.ledgerApp) return;
23
-
24
- try {
25
- // Consumer owns the lifecycle of an injected transport, so forceReconnect
26
- // only refreshes ledgerApp — the transport itself stays as passed in.
27
- const needsNewTransport = !this.transport || (forceReconnect && !this.injectedTransport);
28
- if (needsNewTransport) {
29
- this.transport = this.injectedTransport ?? (await getLedgerTransport());
30
- }
31
-
32
- switch (this.chain) {
33
- case "thor": {
34
- this.ledgerApp = forceReconnect || !this.ledgerApp ? new THORChainApp(this.transport) : this.ledgerApp;
35
-
36
- break;
37
- }
38
-
39
- case "cosmos": {
40
- const CosmosApp = (await import("@ledgerhq/hw-app-cosmos")).default;
41
- this.ledgerApp = forceReconnect || !this.ledgerApp ? new CosmosApp(this.transport) : this.ledgerApp;
42
- }
43
- }
44
-
45
- return this.ledgerApp;
46
- } catch (error: unknown) {
47
- throw new SwapKitError("wallet_ledger_connection_error", error);
48
- }
49
- };
50
-
51
- validateResponse = (errorCode: LedgerErrorCode, message?: string) => {
52
- switch (errorCode) {
53
- case LedgerErrorCode.NoError:
54
- return;
55
-
56
- case LedgerErrorCode.LockedDevice:
57
- throw new SwapKitError("wallet_ledger_device_locked", { message: `Ledger is locked: ${message}` });
58
-
59
- case LedgerErrorCode.TC_NotFound:
60
- throw new SwapKitError("wallet_ledger_device_not_found");
61
-
62
- default: {
63
- break;
64
- }
65
- }
66
- };
67
- }