@swapkit/wallet-hardware 6.1.1 → 6.3.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 (112) hide show
  1. package/dist/THIRD_PARTY_LICENSES.md +4 -4
  2. package/dist/{chunk-jzrytcrf.js → chunk-0k1b2ddn.js} +3 -3
  3. package/dist/{chunk-jzrytcrf.js.map → chunk-0k1b2ddn.js.map} +2 -2
  4. package/dist/{chunk-jewm2ag3.js → chunk-2tzsz8f3.js} +4 -4
  5. package/dist/{chunk-jewm2ag3.js.map → chunk-2tzsz8f3.js.map} +1 -1
  6. package/dist/{chunk-yrxe491r.js → chunk-410fpx61.js} +4 -4
  7. package/dist/{chunk-yrxe491r.js.map → chunk-410fpx61.js.map} +1 -1
  8. package/dist/chunk-8mkdjmw2.js +4 -0
  9. package/dist/chunk-8mkdjmw2.js.map +10 -0
  10. package/dist/{chunk-xmvg4cds.js → chunk-96p7njae.js} +3 -3
  11. package/dist/{chunk-xmvg4cds.js.map → chunk-96p7njae.js.map} +2 -2
  12. package/dist/chunk-97nsp4z6.js +5 -0
  13. package/dist/chunk-97nsp4z6.js.map +10 -0
  14. package/dist/{chunk-nhva847d.js → chunk-9a9gn4pb.js} +3 -3
  15. package/dist/{chunk-5p3ttw6y.js.map → chunk-9a9gn4pb.js.map} +1 -1
  16. package/dist/chunk-a39rm9nv.js +4 -0
  17. package/dist/{chunk-gr3eswf1.js.map → chunk-a39rm9nv.js.map} +5 -5
  18. package/dist/chunk-c8mwf9cn.js +4 -0
  19. package/dist/{chunk-k3thje6a.js.map → chunk-c8mwf9cn.js.map} +1 -1
  20. package/dist/{chunk-ds2m83jp.js → chunk-dhhmywmb.js} +4 -4
  21. package/dist/{chunk-ds2m83jp.js.map → chunk-dhhmywmb.js.map} +1 -1
  22. package/dist/{chunk-eenx3cv6.js → chunk-dsa641rq.js} +3 -3
  23. package/dist/{chunk-eenx3cv6.js.map → chunk-dsa641rq.js.map} +2 -2
  24. package/dist/{chunk-0arpsm5f.js → chunk-grj9nvte.js} +3 -3
  25. package/dist/{chunk-0arpsm5f.js.map → chunk-grj9nvte.js.map} +1 -1
  26. package/dist/{chunk-hg71hp4m.js → chunk-napm4102.js} +3 -3
  27. package/dist/{chunk-hg71hp4m.js.map → chunk-napm4102.js.map} +1 -1
  28. package/dist/{chunk-k3thje6a.js → chunk-nb08g045.js} +3 -3
  29. package/dist/{chunk-nhva847d.js.map → chunk-nb08g045.js.map} +1 -1
  30. package/dist/chunk-npe2r2ge.js +4 -0
  31. package/dist/{chunk-xx7cwpzq.js.map → chunk-npe2r2ge.js.map} +1 -1
  32. package/dist/chunk-p9mwkgye.js +4 -0
  33. package/dist/{chunk-09e51wma.js.map → chunk-p9mwkgye.js.map} +14 -12
  34. package/dist/{chunk-wfbg89d3.js → chunk-qjp8j8e5.js} +3 -3
  35. package/dist/{chunk-wfbg89d3.js.map → chunk-qjp8j8e5.js.map} +1 -1
  36. package/dist/{chunk-xn0fnag5.js → chunk-r1fk1sx9.js} +3 -3
  37. package/dist/{chunk-xn0fnag5.js.map → chunk-r1fk1sx9.js.map} +1 -1
  38. package/dist/{chunk-1amf58aq.js → chunk-ta7bqmy3.js} +3 -3
  39. package/dist/{chunk-1amf58aq.js.map → chunk-ta7bqmy3.js.map} +1 -1
  40. package/dist/{chunk-3t17wpjg.js → chunk-tgvgr630.js} +3 -3
  41. package/dist/{chunk-3t17wpjg.js.map → chunk-tgvgr630.js.map} +2 -2
  42. package/dist/chunk-v8x9ptxs.js +4 -0
  43. package/dist/chunk-v8x9ptxs.js.map +10 -0
  44. package/dist/{chunk-dptbnkzm.js → chunk-wm89desc.js} +20 -20
  45. package/dist/{chunk-dptbnkzm.js.map → chunk-wm89desc.js.map} +12 -10
  46. package/dist/{chunk-5c7b7z2d.js → chunk-wtyhjt5e.js} +3 -3
  47. package/dist/{chunk-5c7b7z2d.js.map → chunk-wtyhjt5e.js.map} +1 -1
  48. package/dist/{chunk-tm47brht.js → chunk-xskdbz15.js} +3 -3
  49. package/dist/{chunk-tm47brht.js.map → chunk-xskdbz15.js.map} +2 -2
  50. package/dist/{chunk-8m2sn76p.js → chunk-ytjqb1sd.js} +3 -3
  51. package/dist/{chunk-8m2sn76p.js.map → chunk-ytjqb1sd.js.map} +1 -1
  52. package/dist/{chunk-3h1f3hyp.js → chunk-zbesf7sy.js} +3 -3
  53. package/dist/{chunk-3h1f3hyp.js.map → chunk-zbesf7sy.js.map} +1 -1
  54. package/dist/keepkey/index.cjs +2 -2
  55. package/dist/keepkey/index.cjs.map +3 -3
  56. package/dist/keepkey/index.js +2 -2
  57. package/dist/keepkey/index.js.map +3 -3
  58. package/dist/ledger/index.cjs +30 -30
  59. package/dist/ledger/index.cjs.map +35 -27
  60. package/dist/ledger/index.js +2 -2
  61. package/dist/ledger/index.js.map +11 -8
  62. package/dist/trezor/index.cjs +2 -2
  63. package/dist/trezor/index.cjs.map +7 -5
  64. package/dist/trezor/index.js +2 -2
  65. package/dist/trezor/index.js.map +3 -3
  66. package/dist/types/helpers/cardano.d.ts +35 -0
  67. package/dist/types/helpers/cardano.d.ts.map +1 -0
  68. package/dist/types/keepkey/index.d.ts +1 -0
  69. package/dist/types/keepkey/index.d.ts.map +1 -1
  70. package/dist/types/ledger/clients/cardano.d.ts +24 -0
  71. package/dist/types/ledger/clients/cardano.d.ts.map +1 -0
  72. package/dist/types/ledger/clients/evm.d.ts +1 -0
  73. package/dist/types/ledger/clients/evm.d.ts.map +1 -1
  74. package/dist/types/ledger/clients/stellar.d.ts +13 -0
  75. package/dist/types/ledger/clients/stellar.d.ts.map +1 -0
  76. package/dist/types/ledger/clients/ton.d.ts +13 -0
  77. package/dist/types/ledger/clients/ton.d.ts.map +1 -0
  78. package/dist/types/ledger/clients/zcash.d.ts.map +1 -1
  79. package/dist/types/ledger/helpers/getLedgerAddress.d.ts.map +1 -1
  80. package/dist/types/ledger/helpers/getLedgerClient.d.ts +8 -1
  81. package/dist/types/ledger/helpers/getLedgerClient.d.ts.map +1 -1
  82. package/dist/types/ledger/index.d.ts +3 -2
  83. package/dist/types/ledger/index.d.ts.map +1 -1
  84. package/dist/types/ledger/types.d.ts +2 -2
  85. package/dist/types/ledger/types.d.ts.map +1 -1
  86. package/dist/types/trezor/cardanoSigner.d.ts.map +1 -1
  87. package/dist/types/trezor/hypercoreSigner.d.ts +22 -0
  88. package/dist/types/trezor/hypercoreSigner.d.ts.map +1 -0
  89. package/dist/types/trezor/index.d.ts +3 -2
  90. package/dist/types/trezor/index.d.ts.map +1 -1
  91. package/dist/types/trezor/stellarSigner.d.ts +1 -1
  92. package/package.json +25 -13
  93. package/src/helpers/cardano.ts +112 -0
  94. package/src/keepkey/index.ts +14 -14
  95. package/src/ledger/clients/cardano.ts +204 -0
  96. package/src/ledger/clients/evm.ts +4 -0
  97. package/src/ledger/clients/stellar.ts +83 -0
  98. package/src/ledger/clients/ton.ts +282 -0
  99. package/src/ledger/clients/zcash.ts +16 -6
  100. package/src/ledger/helpers/getLedgerAddress.ts +16 -0
  101. package/src/ledger/helpers/getLedgerClient.ts +13 -0
  102. package/src/ledger/index.ts +102 -30
  103. package/src/ledger/types.ts +2 -0
  104. package/src/trezor/cardanoSigner.ts +24 -80
  105. package/src/trezor/hypercoreSigner.ts +96 -0
  106. package/src/trezor/index.ts +153 -75
  107. package/dist/chunk-09e51wma.js +0 -4
  108. package/dist/chunk-5p3ttw6y.js +0 -4
  109. package/dist/chunk-gr3eswf1.js +0 -4
  110. package/dist/chunk-qjq7gvyx.js +0 -4
  111. package/dist/chunk-qjq7gvyx.js.map +0 -10
  112. package/dist/chunk-xx7cwpzq.js +0 -4
@@ -1,7 +1,10 @@
1
1
  import { Chain, SwapKitError, WalletOption } from "@swapkit/helpers";
2
2
 
3
+ import type { CardanoLedger } from "../clients/cardano";
3
4
  import type { getNearLedgerClient } from "../clients/near";
5
+ import type { StellarLedger } from "../clients/stellar";
4
6
  import type { SuiLedger } from "../clients/sui";
7
+ import type { TONLedger } from "../clients/ton";
5
8
  import type { TronLedger } from "../clients/tron";
6
9
  import type { XRPLedger } from "../clients/xrp";
7
10
  import type { LEDGER_SUPPORTED_CHAINS } from "../index";
@@ -38,6 +41,7 @@ export const getLedgerAddress = async <
38
41
  case Chain.Aurora:
39
42
  case Chain.Gnosis:
40
43
  case Chain.Monad:
44
+ case Chain.Robinhood:
41
45
  case Chain.XLayer: {
42
46
  return (ledgerClient as EVMLedgerClients).getAddress();
43
47
  }
@@ -63,6 +67,14 @@ export const getLedgerAddress = async <
63
67
  return (ledgerClient as Awaited<ReturnType<typeof XRPLedger>>).getAddress();
64
68
  }
65
69
 
70
+ case Chain.Stellar: {
71
+ return (ledgerClient as Awaited<ReturnType<typeof StellarLedger>>).getAddress();
72
+ }
73
+
74
+ case Chain.Cardano: {
75
+ return (ledgerClient as Awaited<ReturnType<typeof CardanoLedger>>).getAddress();
76
+ }
77
+
66
78
  case Chain.Tron: {
67
79
  return (ledgerClient as Awaited<ReturnType<typeof TronLedger>>).getAddress();
68
80
  }
@@ -71,6 +83,10 @@ export const getLedgerAddress = async <
71
83
  return (ledgerClient as Awaited<ReturnType<typeof SuiLedger>>).connect();
72
84
  }
73
85
 
86
+ case Chain.Ton: {
87
+ return (ledgerClient as Awaited<ReturnType<typeof TONLedger>>).getAddress();
88
+ }
89
+
74
90
  default:
75
91
  throw new SwapKitError("wallet_chain_not_supported", { chain, wallet: WalletOption.LEDGER });
76
92
  }
@@ -9,6 +9,7 @@ import {
9
9
  WalletOption,
10
10
  } from "@swapkit/helpers";
11
11
  import { BitcoinLedger } from "../clients/bitcoin";
12
+ import { CardanoLedger } from "../clients/cardano";
12
13
  import { CosmosLedger } from "../clients/cosmos";
13
14
  import {
14
15
  ArbitrumLedger,
@@ -23,11 +24,14 @@ import {
23
24
  MonadLedger,
24
25
  OptimismLedger,
25
26
  PolygonLedger,
27
+ RobinhoodLedger,
26
28
  XLayerLedger,
27
29
  } from "../clients/evm";
28
30
  import { getNearLedgerClient } from "../clients/near";
31
+ import { StellarLedger } from "../clients/stellar";
29
32
  import { SuiLedger } from "../clients/sui";
30
33
  import { THORChainLedger } from "../clients/thorchain";
34
+ import { TONLedger } from "../clients/ton";
31
35
  import { TronLedger } from "../clients/tron";
32
36
  import { BitcoinCashLedger, DashLedger, DogecoinLedger, LitecoinLedger } from "../clients/utxo";
33
37
  import { XRPLedger } from "../clients/xrp";
@@ -45,6 +49,7 @@ type LedgerSignerMap = {
45
49
  [Chain.BinanceSmartChain]: ReturnType<typeof BinanceSmartChainLedger>;
46
50
  [Chain.BitcoinCash]: ReturnType<typeof BitcoinCashLedger>;
47
51
  [Chain.Bitcoin]: ReturnType<typeof BitcoinLedger>;
52
+ [Chain.Cardano]: ReturnType<typeof CardanoLedger>;
48
53
  [Chain.Cosmos]: CosmosLedger;
49
54
  [Chain.Dash]: ReturnType<typeof DashLedger>;
50
55
  [Chain.Dogecoin]: ReturnType<typeof DogecoinLedger>;
@@ -56,8 +61,11 @@ type LedgerSignerMap = {
56
61
  [Chain.Optimism]: ReturnType<typeof OptimismLedger>;
57
62
  [Chain.Polygon]: ReturnType<typeof PolygonLedger>;
58
63
  [Chain.Ripple]: ReturnType<typeof XRPLedger>;
64
+ [Chain.Robinhood]: ReturnType<typeof RobinhoodLedger>;
65
+ [Chain.Stellar]: ReturnType<typeof StellarLedger>;
59
66
  [Chain.Sui]: ReturnType<typeof SuiLedger>;
60
67
  [Chain.THORChain]: THORChainLedger;
68
+ [Chain.Ton]: ReturnType<typeof TONLedger>;
61
69
  [Chain.Tron]: ReturnType<typeof TronLedger>;
62
70
  [Chain.XLayer]: ReturnType<typeof XLayerLedger>;
63
71
  [Chain.Zcash]: ReturnType<typeof ZcashLedger>;
@@ -114,8 +122,11 @@ export async function getLedgerClient<T extends LedgerSupportedChain>({
114
122
  .with(Chain.Litecoin, () => Promise.resolve(LitecoinLedger(clientParams) as LedgerSignerMap[T]))
115
123
  .with(Chain.Zcash, () => Promise.resolve(ZcashLedger(clientParams) as LedgerSignerMap[T]))
116
124
  .with(Chain.Ripple, () => Promise.resolve(XRPLedger(clientParams) as LedgerSignerMap[T]))
125
+ .with(Chain.Stellar, () => Promise.resolve(StellarLedger(clientParams) as LedgerSignerMap[T]))
126
+ .with(Chain.Cardano, () => Promise.resolve(CardanoLedger(clientParams) as LedgerSignerMap[T]))
117
127
  .with(Chain.Tron, () => Promise.resolve(TronLedger(clientParams) as LedgerSignerMap[T]))
118
128
  .with(Chain.Sui, () => Promise.resolve(SuiLedger(clientParams) as LedgerSignerMap[T]))
129
+ .with(Chain.Ton, () => Promise.resolve(TONLedger(clientParams) as LedgerSignerMap[T]))
119
130
  .with(Chain.Near, () => Promise.resolve(getNearLedgerClient(clientParams) as LedgerSignerMap[T]))
120
131
  .with(
121
132
  Chain.Arbitrum,
@@ -130,6 +141,7 @@ export async function getLedgerClient<T extends LedgerSupportedChain>({
130
141
  Chain.Optimism,
131
142
  Chain.Polygon,
132
143
  Chain.Base,
144
+ Chain.Robinhood,
133
145
  Chain.XLayer,
134
146
  async () => {
135
147
  const { getProvider } = await import("@swapkit/toolboxes/evm");
@@ -154,6 +166,7 @@ export async function getLedgerClient<T extends LedgerSupportedChain>({
154
166
  .with(Chain.Aurora, () => AuroraLedger(params) as LedgerSignerMap[T])
155
167
  .with(Chain.Gnosis, () => GnosisLedger(params) as LedgerSignerMap[T])
156
168
  .with(Chain.Monad, () => MonadLedger(params) as LedgerSignerMap[T])
169
+ .with(Chain.Robinhood, () => RobinhoodLedger(params) as LedgerSignerMap[T])
157
170
  .with(Chain.XLayer, () => XLayerLedger(params) as LedgerSignerMap[T])
158
171
  .otherwise(() => EthereumLedger(params) as LedgerSignerMap[T]);
159
172
  },
@@ -1,12 +1,13 @@
1
1
  import type Transport from "@ledgerhq/hw-transport";
2
2
  import { hex } from "@scure/base";
3
3
  import {
4
- type AssetValue,
4
+ AssetValue,
5
5
  Chain,
6
6
  type DerivationPathArray,
7
7
  derivationPathToString,
8
8
  FeeOption,
9
9
  filterSupportedChains,
10
+ type GenericTransferParams,
10
11
  getUTXOScriptTypeForPath,
11
12
  NetworkDerivationPath,
12
13
  SwapKitError,
@@ -14,6 +15,7 @@ import {
14
15
  UTXOScriptType,
15
16
  WalletOption,
16
17
  } from "@swapkit/helpers";
18
+ import type { TONTransactionInput } from "@swapkit/toolboxes/ton";
17
19
  import {
18
20
  addInputsAndOutputs,
19
21
  assertDerivationIndex,
@@ -92,6 +94,7 @@ const LEDGER_SUPPORTED_CHAINS_CONFIG = [
92
94
  Chain.BinanceSmartChain,
93
95
  Chain.Bitcoin,
94
96
  Chain.BitcoinCash,
97
+ Chain.Cardano,
95
98
  Chain.Cosmos,
96
99
  Chain.Dash,
97
100
  Chain.Dogecoin,
@@ -103,8 +106,11 @@ const LEDGER_SUPPORTED_CHAINS_CONFIG = [
103
106
  Chain.Optimism,
104
107
  Chain.Polygon,
105
108
  Chain.Ripple,
109
+ Chain.Robinhood,
110
+ Chain.Stellar,
106
111
  Chain.Sui,
107
112
  Chain.THORChain,
113
+ Chain.Ton,
108
114
  Chain.XLayer,
109
115
  Chain.Tron,
110
116
  Chain.Zcash,
@@ -134,6 +140,7 @@ const LEDGER_EVM_CHAINS = [
134
140
  Chain.Aurora,
135
141
  Chain.Gnosis,
136
142
  Chain.Monad,
143
+ Chain.Robinhood,
137
144
  Chain.XLayer,
138
145
  ] as const satisfies readonly LedgerSupportedChain[];
139
146
 
@@ -169,35 +176,26 @@ export const ledgerWallet = createWallet({
169
176
 
170
177
  return true;
171
178
  },
172
- directSigningSupport: {
173
- [Chain.Arbitrum]: true,
174
- [Chain.Arc]: true,
175
- [Chain.Aurora]: true,
176
- [Chain.Avalanche]: true,
177
- [Chain.Base]: true,
178
- [Chain.Berachain]: true,
179
- [Chain.BinanceSmartChain]: true,
180
- [Chain.Ethereum]: true,
181
- [Chain.Gnosis]: true,
182
- [Chain.Monad]: true,
183
- [Chain.Bitcoin]: true,
184
- [Chain.BitcoinCash]: true,
185
- [Chain.Cosmos]: true,
186
- [Chain.Dash]: true,
187
- [Chain.Dogecoin]: true,
188
- [Chain.Litecoin]: true,
189
- [Chain.Near]: true,
190
- [Chain.Optimism]: true,
191
- [Chain.Polygon]: true,
192
- [Chain.Ripple]: true,
193
- [Chain.Sui]: true,
194
- [Chain.THORChain]: true,
195
- [Chain.Tron]: true,
196
- [Chain.XLayer]: true,
197
- // ZEC transparent transfers use DSK, but route-level PCZT signing remains unsupported.
198
- },
199
179
  getExtendedPublicKey: getLedgerExtendedPublicKey,
200
180
  name: "connectLedger",
181
+ signingSupport: {
182
+ ...Object.fromEntries(LEDGER_EVM_CHAINS.map((chain) => [chain, { direct: true, typedData: true }])),
183
+ [Chain.Bitcoin]: { direct: true },
184
+ [Chain.BitcoinCash]: { direct: true },
185
+ [Chain.Cardano]: { direct: true },
186
+ [Chain.Cosmos]: { direct: true },
187
+ [Chain.Dash]: { direct: true },
188
+ [Chain.Dogecoin]: { direct: true },
189
+ [Chain.Litecoin]: { direct: true },
190
+ [Chain.Near]: { direct: true },
191
+ [Chain.Ripple]: { direct: true },
192
+ [Chain.Stellar]: { direct: true },
193
+ [Chain.Sui]: { direct: true },
194
+ [Chain.THORChain]: { direct: true },
195
+ [Chain.Ton]: { direct: true },
196
+ [Chain.Tron]: { direct: true },
197
+ // ZEC transparent transfers use DSK, but route-level PCZT signing remains unsupported.
198
+ },
201
199
  supportedChains: [...LEDGER_SUPPORTED_CHAINS_CONFIG],
202
200
  walletType: WalletOption.LEDGER,
203
201
  });
@@ -219,8 +217,8 @@ function getLedgerAccountXpub({ chain, path, signer, xpubVersion }: LedgerAccoun
219
217
  .exhaustive();
220
218
  }
221
219
 
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
220
+ // Txids are looked up in display order, the order the UTXO API names transactions by. utxo-signer below 3.2
221
+ // parsed raw transactions with wire-order txids, so the reversed order is tried as a fallback. A txid and its
224
222
  // byte reversal cannot both name real transactions, so the fallback cannot fetch an unrelated one.
225
223
  function resolveZcashInputUtxos(transaction: ZcashTransaction): Promise<UTXOType[]> {
226
224
  const zcashApi = getUtxoApi(Chain.Zcash);
@@ -649,6 +647,21 @@ async function getNearWalletMethods({
649
647
  return { ...toolbox, address: accountId };
650
648
  }
651
649
 
650
+ async function getCardanoWalletMethods({
651
+ chain,
652
+ derivationPath,
653
+ dmkSession,
654
+ onDeviceActionState,
655
+ transport,
656
+ }: WalletMethodsParams<Chain.Cardano>) {
657
+ const { getCardanoToolbox } = await import("@swapkit/toolboxes/cardano");
658
+ const signer = await getLedgerClient({ chain, derivationPath, dmkSession, onDeviceActionState, transport });
659
+ const address = signer.getAddress();
660
+ const toolbox = getCardanoToolbox({ signer });
661
+
662
+ return { ...toolbox, address };
663
+ }
664
+
652
665
  async function getRippleWalletMethods({
653
666
  chain,
654
667
  derivationPath,
@@ -664,6 +677,21 @@ async function getRippleWalletMethods({
664
677
  return { ...toolbox, address };
665
678
  }
666
679
 
680
+ async function getStellarWalletMethods({
681
+ chain,
682
+ derivationPath,
683
+ dmkSession,
684
+ onDeviceActionState,
685
+ transport,
686
+ }: WalletMethodsParams<Chain.Stellar>) {
687
+ const { getStellarToolbox } = await import("@swapkit/toolboxes/stellar");
688
+ const signer = await getLedgerClient({ chain, derivationPath, dmkSession, onDeviceActionState, transport });
689
+ const address = await getLedgerAddress({ chain, ledgerClient: signer });
690
+ const toolbox = getStellarToolbox({ signer });
691
+
692
+ return { ...toolbox, address };
693
+ }
694
+
667
695
  async function getTronWalletMethods({
668
696
  chain,
669
697
  derivationPath,
@@ -694,6 +722,47 @@ async function getSuiWalletMethods({
694
722
  return { ...toolbox, address };
695
723
  }
696
724
 
725
+ async function getTONWalletMethods({
726
+ chain,
727
+ derivationPath,
728
+ dmkSession,
729
+ onDeviceActionState,
730
+ transport,
731
+ }: WalletMethodsParams<Chain.Ton>) {
732
+ const { getTONToolbox } = await import("@swapkit/toolboxes/ton");
733
+ const signer = await getLedgerClient({ chain, derivationPath, dmkSession, onDeviceActionState, transport });
734
+ const address = await getLedgerAddress({ chain, ledgerClient: signer });
735
+ // Signerless toolbox: it builds messages and reads balances, the device signs the external message.
736
+ const toolbox = getTONToolbox();
737
+
738
+ const signAndBroadcastTransaction = async (transaction: TONTransactionInput) =>
739
+ toolbox.broadcastTransaction(await signer.signTransaction(transaction));
740
+
741
+ const transfer = async ({ assetValue, memo, recipient, sweep }: GenericTransferParams) =>
742
+ signAndBroadcastTransaction(
743
+ await toolbox.createTransaction({ assetValue, memo, recipient, sender: address, sweep }),
744
+ );
745
+
746
+ // Same budget as TON Connect: the signerless toolbox underestimates jetton fees
747
+ const estimateTransactionFee = ({ assetValue }: GenericTransferParams) =>
748
+ Promise.resolve(AssetValue.from({ chain: Chain.Ton, value: assetValue.isGasAsset ? "0.01" : "0.06" }));
749
+
750
+ const sign = () => {
751
+ throw new SwapKitError("wallet_ledger_method_not_supported", { method: "sign", wallet: WalletOption.LEDGER });
752
+ };
753
+
754
+ return {
755
+ ...toolbox,
756
+ address,
757
+ estimateTransactionFee,
758
+ getAddress: () => address,
759
+ getBalance: () => toolbox.getBalance(address),
760
+ sign,
761
+ signAndBroadcastTransaction,
762
+ transfer,
763
+ };
764
+ }
765
+
697
766
  function getWalletMethods(params: WalletMethodsParams) {
698
767
  if (!isLedgerSupportedChain(params.chain)) {
699
768
  throw new SwapKitError("wallet_ledger_chain_not_supported", { chain: params.chain });
@@ -706,8 +775,11 @@ function getWalletMethods(params: WalletMethodsParams) {
706
775
  .with(Chain.THORChain, (chain) => getTHORChainWalletMethods({ ...params, chain }))
707
776
  .with(Chain.Near, (chain) => getNearWalletMethods({ ...params, chain }))
708
777
  .with(Chain.Ripple, (chain) => getRippleWalletMethods({ ...params, chain }))
778
+ .with(Chain.Stellar, (chain) => getStellarWalletMethods({ ...params, chain }))
779
+ .with(Chain.Cardano, (chain) => getCardanoWalletMethods({ ...params, chain }))
709
780
  .with(Chain.Tron, (chain) => getTronWalletMethods({ ...params, chain }))
710
781
  .with(Chain.Sui, (chain) => getSuiWalletMethods({ ...params, chain }))
782
+ .with(Chain.Ton, (chain) => getTONWalletMethods({ ...params, chain }))
711
783
  .exhaustive();
712
784
  }
713
785
 
@@ -13,6 +13,7 @@ import type {
13
13
  MonadLedger,
14
14
  OptimismLedger,
15
15
  PolygonLedger,
16
+ RobinhoodLedger,
16
17
  XLayerLedger,
17
18
  } from "./clients/evm";
18
19
  import type { SuiLedger } from "./clients/sui";
@@ -42,6 +43,7 @@ export type EVMLedgerClients =
42
43
  | ReturnType<typeof MonadLedger>
43
44
  | ReturnType<typeof OptimismLedger>
44
45
  | ReturnType<typeof PolygonLedger>
46
+ | ReturnType<typeof RobinhoodLedger>
45
47
  | ReturnType<typeof XLayerLedger>;
46
48
  export type TronLedgerClient = ReturnType<typeof TronLedger>;
47
49
  export type SuiLedgerClient = ReturnType<typeof SuiLedger>;
@@ -8,13 +8,13 @@ import {
8
8
  } from "@swapkit/toolboxes/cardano";
9
9
  import type {
10
10
  CardanoAssetGroup,
11
- CardanoInput,
12
11
  CardanoOutput,
13
12
  CardanoSignedTxData,
14
13
  CardanoSignTransaction,
15
- PROTO,
16
14
  } from "@trezor/connect-web";
17
15
 
16
+ import { type CardanoOutputFields, readCardanoBody } from "../helpers/cardano";
17
+
18
18
  type CardanoAddressParams = { address: string; path: string; stakingPath: string };
19
19
  export type TrezorConnectModule = typeof import("@trezor/connect-web");
20
20
 
@@ -26,9 +26,6 @@ function getConnectParams({ CARDANO, PROTO }: TrezorConnectModule) {
26
26
  };
27
27
  }
28
28
 
29
- const BODY = { AUXILIARY_DATA_HASH: 7, FEE: 2, INPUTS: 0, OUTPUTS: 1, TTL: 3, VALIDITY_INTERVAL_START: 8 } as const;
30
- const SET_TAG = 258;
31
-
32
29
  function notSupported(reason: string) {
33
30
  return new SwapKitError({ errorKey: "wallet_trezor_method_not_supported", info: { chain: Chain.Cardano, reason } });
34
31
  }
@@ -40,50 +37,12 @@ function signFailed(error: string) {
40
37
  });
41
38
  }
42
39
 
43
- function toUint(value: unknown, field: string) {
44
- const isBigNumber = typeof value === "object" && value !== null && "toFixed" in value;
45
- const text = isBigNumber || Number.isSafeInteger(value) ? String((value as number).toFixed()) : "";
46
- if (!/^\d+$/.test(text)) throw notSupported(`Invalid ${field} in Cardano transaction`);
47
- return text;
48
- }
49
-
50
- function toTokenBundle(multiAsset: Map<Uint8Array, Map<Uint8Array, unknown>>): CardanoAssetGroup[] {
51
- return [...multiAsset].map(([policyId, assets]) => ({
52
- policyId: hex.encode(policyId),
53
- tokenAmounts: [...assets].map(([assetName, amount]) => ({
54
- amount: toUint(amount, "token amount"),
55
- assetNameBytes: hex.encode(assetName),
56
- })),
57
- }));
58
- }
59
-
60
40
  async function toTrezorOutput(
61
- output: unknown,
41
+ { address, amount, format, tokenGroups }: CardanoOutputFields,
62
42
  own: CardanoAddressParams,
63
43
  proto: TrezorConnectModule["PROTO"],
64
44
  ): Promise<CardanoOutput> {
65
- let address: unknown;
66
- let value: unknown;
67
- let format: PROTO.CardanoTxOutputSerializationFormat;
68
-
69
- if (Array.isArray(output)) {
70
- if (output.length !== 2) throw notSupported("Trezor cannot sign Cardano outputs with a datum");
71
- [address, value] = output;
72
- format = proto.CardanoTxOutputSerializationFormat.ARRAY_LEGACY;
73
- } else if (output instanceof Map) {
74
- if (output.has(2)) throw notSupported("Trezor cannot sign Cardano outputs with a datum");
75
- if (output.has(3)) throw notSupported("Trezor cannot sign Cardano outputs with a reference script");
76
- if (output.size !== 2) throw notSupported("Unsupported field in Cardano output");
77
- address = output.get(0);
78
- value = output.get(1);
79
- format = proto.CardanoTxOutputSerializationFormat.MAP_BABBAGE;
80
- } else {
81
- throw notSupported("Invalid Cardano output");
82
- }
83
-
84
- if (Array.isArray(value) && value.length !== 2) throw notSupported("Invalid Cardano output value");
85
- const [coin, multiAsset] = Array.isArray(value) ? value : [value];
86
- const outputAddress = await getCardanoAddressFromBytes(address as Uint8Array);
45
+ const outputAddress = await getCardanoAddressFromBytes(address);
87
46
  const destination =
88
47
  outputAddress === own.address
89
48
  ? {
@@ -94,29 +53,22 @@ async function toTrezorOutput(
94
53
  },
95
54
  }
96
55
  : { address: outputAddress };
56
+ const tokenBundle: CardanoAssetGroup[] | undefined = tokenGroups?.map(({ policyId, tokens }) => ({
57
+ policyId: hex.encode(policyId),
58
+ tokenAmounts: tokens.map(({ amount, assetName }) => ({ amount, assetNameBytes: hex.encode(assetName) })),
59
+ }));
97
60
 
98
61
  return {
99
62
  ...destination,
100
- amount: toUint(coin, "output amount"),
101
- format,
102
- ...(multiAsset === undefined ? {} : { tokenBundle: toTokenBundle(multiAsset) }),
63
+ amount,
64
+ format:
65
+ format === "legacy"
66
+ ? proto.CardanoTxOutputSerializationFormat.ARRAY_LEGACY
67
+ : proto.CardanoTxOutputSerializationFormat.MAP_BABBAGE,
68
+ ...(tokenBundle === undefined ? {} : { tokenBundle }),
103
69
  };
104
70
  }
105
71
 
106
- function toTrezorInputs(inputs: unknown, path: string) {
107
- const isTagged =
108
- typeof inputs === "object" && inputs !== null && "tag" in inputs && inputs.tag === SET_TAG && "value" in inputs;
109
- const list = (isTagged ? inputs.value : inputs) as unknown[][];
110
-
111
- const trezorInputs: CardanoInput[] = list.map((input) => {
112
- if (input.length !== 2) throw notSupported("Invalid Cardano input");
113
- const [txHash, index] = input as [Uint8Array, unknown];
114
- return { path, prev_hash: hex.encode(txHash), prev_index: Number(toUint(index, "input index")) };
115
- });
116
-
117
- return { inputs: trezorInputs, tagCborSets: isTagged };
118
- }
119
-
120
72
  export async function toTrezorCardanoTransaction(
121
73
  txHex: string,
122
74
  own: CardanoAddressParams,
@@ -125,31 +77,23 @@ export async function toTrezorCardanoTransaction(
125
77
  const { body } = await decodeCardanoTransaction(txHex);
126
78
  const { derivationType, mainnet } = getConnectParams(connect);
127
79
  const { PROTO: proto } = connect;
128
-
129
- const supportedFields: unknown[] = Object.values(BODY);
130
- const unsupported = [...body.keys()].filter((field) => !supportedFields.includes(field));
131
- if (unsupported.length > 0) {
132
- throw notSupported(`Trezor cannot sign Cardano transactions with body fields ${unsupported.join(", ")}`);
133
- }
134
-
135
- const outputs = body.get(BODY.OUTPUTS) as unknown[];
136
- const auxiliaryDataHash = body.get(BODY.AUXILIARY_DATA_HASH) as Uint8Array | undefined;
137
-
138
- const ttl = body.get(BODY.TTL);
139
- const validityIntervalStart = body.get(BODY.VALIDITY_INTERVAL_START);
80
+ const { auxiliaryDataHash, fee, inputs, outputs, tagCborSets, ttl, validityIntervalStart } = readCardanoBody(
81
+ body,
82
+ "Trezor",
83
+ notSupported,
84
+ );
140
85
 
141
86
  return {
142
- ...toTrezorInputs(body.get(BODY.INPUTS), own.path),
87
+ inputs: inputs.map(({ index, txHash }) => ({ path: own.path, prev_hash: hex.encode(txHash), prev_index: index })),
88
+ tagCborSets,
143
89
  ...mainnet,
144
90
  derivationType,
145
- fee: toUint(body.get(BODY.FEE), "fee"),
91
+ fee,
146
92
  outputs: await Promise.all(outputs.map((output) => toTrezorOutput(output, own, proto))),
147
93
  signingMode: proto.CardanoTxSigningMode.ORDINARY_TRANSACTION,
148
94
  ...(auxiliaryDataHash ? { auxiliaryData: { hash: hex.encode(auxiliaryDataHash) } } : {}),
149
- ...(ttl === undefined ? {} : { ttl: toUint(ttl, "ttl") }),
150
- ...(validityIntervalStart === undefined
151
- ? {}
152
- : { validityIntervalStart: toUint(validityIntervalStart, "validity interval start") }),
95
+ ...(ttl === undefined ? {} : { ttl }),
96
+ ...(validityIntervalStart === undefined ? {} : { validityIntervalStart }),
153
97
  };
154
98
  }
155
99
 
@@ -0,0 +1,96 @@
1
+ import { Chain, type DerivationPathArray, derivationPathToString, SwapKitError } from "@swapkit/helpers";
2
+ import type { TypedDataDomain, TypedDataField } from "ethers";
3
+
4
+ type TypedDataTypes = Record<string, TypedDataField[]>;
5
+ type TypedDataValue = Record<string, unknown>;
6
+
7
+ function notSupported(reason: string) {
8
+ return new SwapKitError({ errorKey: "wallet_trezor_method_not_supported", info: { chain: Chain.Hype, reason } });
9
+ }
10
+
11
+ export async function toTrezorHyperCoreTypedData(
12
+ domain: TypedDataDomain,
13
+ types: TypedDataTypes,
14
+ message: TypedDataValue,
15
+ ) {
16
+ const { TypedDataEncoder } = await import("ethers");
17
+ const { EIP712Domain: _, ...structTypes } = types;
18
+
19
+ try {
20
+ // ethers checks the domain, the type graph and every value; the device still gets the caller's own data
21
+ const payload: { primaryType: string; types: TypedDataTypes } = TypedDataEncoder.getPayload(
22
+ domain,
23
+ structTypes,
24
+ message,
25
+ );
26
+ return {
27
+ domain,
28
+ message,
29
+ primaryType: payload.primaryType,
30
+ types: { EIP712Domain: payload.types.EIP712Domain, ...structTypes },
31
+ };
32
+ } catch (error) {
33
+ throw notSupported(`Invalid HyperCore typed data: ${(error as Error).message}`);
34
+ }
35
+ }
36
+
37
+ export function getHyperCoreSigner({
38
+ address: knownAddress,
39
+ derivationPath,
40
+ }: {
41
+ address?: string;
42
+ derivationPath: DerivationPathArray;
43
+ }) {
44
+ const path = derivationPathToString(derivationPath);
45
+ let address = knownAddress ?? "";
46
+
47
+ return {
48
+ getAddress: async () => {
49
+ if (address) return address;
50
+
51
+ const TrezorConnect = (await import("@trezor/connect-web")).default;
52
+ const result = await TrezorConnect.ethereumGetAddress({ path, showOnTrezor: true });
53
+
54
+ if (!result.success) {
55
+ throw new SwapKitError({
56
+ errorKey: "wallet_trezor_failed_to_get_address",
57
+ info: { chain: Chain.Hype, derivationPath, error: result.payload.error },
58
+ });
59
+ }
60
+
61
+ address = result.payload.address;
62
+ return address;
63
+ },
64
+
65
+ // HyperCore has no transactions: every action goes through signTypedData
66
+ signTransaction: () => Promise.reject(notSupported("HyperCore actions are signed with signTypedData")),
67
+
68
+ signTypedData: async (domain: TypedDataDomain, types: TypedDataTypes, message: TypedDataValue) => {
69
+ const data = await toTrezorHyperCoreTypedData(domain, types, message);
70
+ const TrezorConnect = (await import("@trezor/connect-web")).default;
71
+
72
+ const result = await TrezorConnect.ethereumSignTypedData({
73
+ data: data as Parameters<typeof TrezorConnect.ethereumSignTypedData>[0]["data"],
74
+ metamask_v4_compat: true,
75
+ path,
76
+ });
77
+
78
+ if (!result.success) {
79
+ throw new SwapKitError({
80
+ errorKey: "wallet_trezor_failed_to_sign_transaction",
81
+ info: { chain: Chain.Hype, error: result.payload.error },
82
+ });
83
+ }
84
+
85
+ // Hyperliquid takes the account from the signature, so a stale known address would act from another account
86
+ if (result.payload.address.toLowerCase() !== address.toLowerCase()) {
87
+ throw new SwapKitError({
88
+ errorKey: "wallet_trezor_failed_to_sign_transaction",
89
+ info: { chain: Chain.Hype, error: "Trezor signed with a different address", signer: result.payload.address },
90
+ });
91
+ }
92
+
93
+ return result.payload.signature.startsWith("0x") ? result.payload.signature : `0x${result.payload.signature}`;
94
+ },
95
+ };
96
+ }