@rango-dev/provider-ledger 0.0.0-experimental-936229e8-20251208

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.
@@ -0,0 +1,2 @@
1
+ declare const buildProvider: () => import("@rango-dev/wallets-core").Provider;
2
+ export { buildProvider };
@@ -0,0 +1,2 @@
1
+ import type { SignerFactory } from 'rango-types';
2
+ export default function getSigners(): Promise<SignerFactory>;
@@ -0,0 +1,8 @@
1
+ import type { GenericSigner } from 'rango-types';
2
+ import type { EvmTransaction } from 'rango-types/mainApi';
3
+ export declare class EthereumSigner implements GenericSigner<EvmTransaction> {
4
+ signMessage(msg: string): Promise<string>;
5
+ signAndSendTx(tx: EvmTransaction, fromAddress: string, chainId: string | null): Promise<{
6
+ hash: string;
7
+ }>;
8
+ }
@@ -0,0 +1,9 @@
1
+ import type { Transaction, VersionedTransaction } from '@solana/web3.js';
2
+ import type { GenericSigner, SolanaTransaction } from 'rango-types';
3
+ export declare function isVersionedTransaction(transaction: Transaction | VersionedTransaction): transaction is VersionedTransaction;
4
+ export declare class SolanaSigner implements GenericSigner<SolanaTransaction> {
5
+ signMessage(msg: string): Promise<string>;
6
+ signAndSendTx(tx: SolanaTransaction): Promise<{
7
+ hash: string;
8
+ }>;
9
+ }
@@ -0,0 +1,2 @@
1
+ export declare function setDerivationPath(path: string): void;
2
+ export declare function getDerivationPath(): string;
@@ -0,0 +1,10 @@
1
+ import type Transport from '@ledgerhq/hw-transport';
2
+ import { type ProviderConnectResult } from '@rango-dev/wallets-shared';
3
+ export type Provider = Map<string, unknown>;
4
+ export declare function ledger(): Provider | null;
5
+ export declare function getLedgerError(error: any): any;
6
+ export declare function standardizeAndThrowLedgerError(_: unknown, error: unknown): void;
7
+ export declare function getEthereumAccounts(): Promise<ProviderConnectResult>;
8
+ export declare function getSolanaAccounts(): Promise<ProviderConnectResult>;
9
+ export declare function transportConnect(): Promise<Transport>;
10
+ export declare function transportDisconnect(): Promise<void>;
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@rango-dev/provider-ledger",
3
+ "version": "0.0.0-experimental-936229e8-20251208",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "source": "./src/mod.ts",
7
+ "main": "./dist/mod.js",
8
+ "exports": {
9
+ ".": "./dist/mod.js"
10
+ },
11
+ "typings": "dist/mod.d.ts",
12
+ "files": [
13
+ "dist",
14
+ "src"
15
+ ],
16
+ "scripts": {
17
+ "build": "node ../../scripts/build/command.mjs --path wallets/provider-ledger --splitting --external-all-except @ledgerhq/errors,@ledgerhq/hw-app-eth,@ledgerhq/hw-app-solana,@ledgerhq/hw-transport-webhid,@ledgerhq/types-cryptoassets,@ledgerhq/types-devices --inputs src/mod.ts",
18
+ "ts-check": "tsc --declaration --emitDeclarationOnly -p ./tsconfig.json",
19
+ "clean": "rimraf dist",
20
+ "format": "prettier --write '{.,src}/**/*.{ts,tsx}'",
21
+ "lint": "eslint \"**/*.{ts,tsx}\""
22
+ },
23
+ "dependencies": {
24
+ "@ledgerhq/errors": "^6.16.4",
25
+ "@ledgerhq/hw-app-eth": "^6.36.0",
26
+ "@ledgerhq/hw-app-solana": "^7.1.6",
27
+ "@ledgerhq/hw-transport-webhid": "^6.28.6",
28
+ "@ledgerhq/types-cryptoassets": "^7.11.0",
29
+ "@ledgerhq/types-devices": "^6.24.0",
30
+ "@rango-dev/signer-solana": "^0.46.1",
31
+ "@rango-dev/wallets-core": "^0.0.0-experimental-936229e8-20251208",
32
+ "@rango-dev/wallets-shared": "^0.0.0-experimental-936229e8-20251208",
33
+ "@solana/web3.js": "^1.91.4",
34
+ "@types/w3c-web-hid": "^1.0.2",
35
+ "bs58": "^5.0.0",
36
+ "ethers": "^6.13.2",
37
+ "rango-types": "^0.1.89"
38
+ },
39
+ "publishConfig": {
40
+ "access": "public"
41
+ }
42
+ }
package/readme.md ADDED
@@ -0,0 +1,16 @@
1
+ # Ledger
2
+
3
+ Ledger integration for hub.
4
+ [Homepage](https://www.ledger.com/) | [Docs](https://developers.ledger.com/)
5
+
6
+ ## Implementation notes/limitations
7
+
8
+ ### Group
9
+
10
+ #### ⚠️ EVM
11
+
12
+ We only support Ethereum for now.
13
+
14
+ ---
15
+
16
+ More wallet information can be found in [readme.md](../readme.md).
@@ -0,0 +1,91 @@
1
+ import { type ProviderMetadata } from '@rango-dev/wallets-core';
2
+ import { LegacyNetworks } from '@rango-dev/wallets-core/legacy';
3
+ import { Networks } from '@rango-dev/wallets-shared';
4
+ import { type BlockchainMeta } from 'rango-types';
5
+
6
+ import getSigners from './signer.js';
7
+
8
+ export const EVM_SUPPORTED_CHAINS = [
9
+ LegacyNetworks.ETHEREUM,
10
+ LegacyNetworks.POLYGON,
11
+ LegacyNetworks.BASE,
12
+ ];
13
+
14
+ export const HEXADECIMAL_BASE = 16;
15
+ export const WALLET_ID = 'ledger';
16
+
17
+ export const metadata: ProviderMetadata = {
18
+ name: 'Ledger',
19
+ icon: 'https://raw.githubusercontent.com/rango-exchange/assets/main/wallets/ledger/icon.svg',
20
+ extensions: {
21
+ homepage:
22
+ 'https://support.ledger.com/hc/en-us/articles/4404389606417-Download-and-install-Ledger-Live?docs=true',
23
+ },
24
+ properties: [
25
+ {
26
+ name: 'namespaces',
27
+ value: {
28
+ selection: 'single',
29
+ data: [
30
+ {
31
+ label: 'Ethereum',
32
+ value: 'EVM',
33
+ id: 'ETH',
34
+ getSupportedChains: (allBlockchains: BlockchainMeta[]) =>
35
+ allBlockchains.filter(
36
+ (chain) => chain.name === Networks.ETHEREUM
37
+ ),
38
+ },
39
+ {
40
+ label: 'Solana',
41
+ value: 'Solana',
42
+ id: 'SOLANA',
43
+ getSupportedChains: (allBlockchains: BlockchainMeta[]) =>
44
+ allBlockchains.filter((chain) => chain.name === Networks.SOLANA),
45
+ },
46
+ ],
47
+ },
48
+ },
49
+ {
50
+ name: 'derivationPath',
51
+ value: {
52
+ data: [
53
+ {
54
+ id: 'metamask',
55
+ label: `Metamask (m/44'/60'/0'/0/index)`,
56
+ namespace: 'EVM',
57
+ generateDerivationPath: (index: string) => `44'/60'/0'/0/${index}`,
58
+ },
59
+ {
60
+ id: 'ledgerLive',
61
+ label: `LedgerLive (m/44'/60'/index'/0/0)`,
62
+ namespace: 'EVM',
63
+ generateDerivationPath: (index: string) => `44'/60'/${index}'/0/0`,
64
+ },
65
+ {
66
+ id: 'legacy',
67
+ label: `Legacy (m/44'/60'/0'/index)`,
68
+ namespace: 'EVM',
69
+ generateDerivationPath: (index: string) => `44'/60'/0'/${index}`,
70
+ },
71
+ {
72
+ id: `(m/44'/501'/index')`,
73
+ label: `(m/44'/501'/index')`,
74
+ namespace: 'Solana',
75
+ generateDerivationPath: (index: string) => `44'/501'/${index}'`,
76
+ },
77
+ {
78
+ id: `(m/44'/501'/0'/index)`,
79
+ label: `(m/44'/501'/0'/index)`,
80
+ namespace: 'Solana',
81
+ generateDerivationPath: (index: string) => `44'/501'/0'/${index}`,
82
+ },
83
+ ],
84
+ },
85
+ },
86
+ {
87
+ name: 'signers',
88
+ value: { getSigners: async () => getSigners() },
89
+ },
90
+ ],
91
+ };
@@ -0,0 +1,167 @@
1
+ import type { LegacyProviderInterface } from '@rango-dev/wallets-core/legacy';
2
+ import type {
3
+ Connect,
4
+ Disconnect,
5
+ ProviderConnectResult,
6
+ WalletInfo,
7
+ } from '@rango-dev/wallets-shared';
8
+
9
+ import { Networks, WalletTypes } from '@rango-dev/wallets-shared';
10
+ import { type BlockchainMeta, type SignerFactory } from 'rango-types';
11
+
12
+ import signer from '../signer.js';
13
+ import { setDerivationPath } from '../state.js';
14
+ import {
15
+ getEthereumAccounts,
16
+ ledger as getLedgerInstance,
17
+ getSolanaAccounts,
18
+ transportDisconnect,
19
+ } from '../utils.js';
20
+
21
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
+ type InstanceType = any;
23
+
24
+ export const config = {
25
+ type: WalletTypes.LEDGER,
26
+ };
27
+
28
+ export const getInstance = getLedgerInstance;
29
+ export const connect: Connect = async ({ namespaces }) => {
30
+ const results: ProviderConnectResult[] = [];
31
+
32
+ const solanaNamespace = namespaces?.find(
33
+ (namespaceItem) => namespaceItem.namespace === 'Solana'
34
+ );
35
+ const evmNamespace = namespaces?.find(
36
+ (namespaceItem) => namespaceItem.namespace === 'EVM'
37
+ );
38
+
39
+ if (solanaNamespace) {
40
+ if (solanaNamespace.derivationPath) {
41
+ setDerivationPath(solanaNamespace.derivationPath);
42
+ const accounts = await getSolanaAccounts();
43
+ results.push(accounts);
44
+ } else {
45
+ throw new Error('Derivation Path can not be empty.');
46
+ }
47
+ } else if (evmNamespace) {
48
+ if (evmNamespace.derivationPath) {
49
+ setDerivationPath(evmNamespace.derivationPath);
50
+ const accounts = await getEthereumAccounts();
51
+ results.push(accounts);
52
+ } else {
53
+ throw new Error('Derivation Path can not be empty.');
54
+ }
55
+ } else {
56
+ throw new Error(
57
+ `It appears that you have selected a namespace that is not yet supported by our system. Your namespaces: ${namespaces?.map(
58
+ (namespaceItem) => namespaceItem.namespace
59
+ )}`
60
+ );
61
+ }
62
+
63
+ return results;
64
+ };
65
+
66
+ export const disconnect: Disconnect = async () => {
67
+ void transportDisconnect();
68
+ };
69
+
70
+ export const getSigners: (provider: InstanceType) => Promise<SignerFactory> =
71
+ signer;
72
+
73
+ export const getWalletInfo: (allBlockChains: BlockchainMeta[]) => WalletInfo = (
74
+ allBlockChains
75
+ ) => {
76
+ const supportedChains: BlockchainMeta[] = [];
77
+
78
+ const ethereumBlockchain = allBlockChains.find(
79
+ (chain) => chain.name === Networks.ETHEREUM
80
+ );
81
+ if (ethereumBlockchain) {
82
+ supportedChains.push(ethereumBlockchain);
83
+ }
84
+
85
+ const solanaBlockchain = allBlockChains.find(
86
+ (chain) => chain.name === Networks.SOLANA
87
+ );
88
+ if (solanaBlockchain) {
89
+ supportedChains.push(solanaBlockchain);
90
+ }
91
+
92
+ return {
93
+ name: 'Ledger',
94
+ img: 'https://raw.githubusercontent.com/rango-exchange/assets/main/wallets/ledger/icon.svg',
95
+ installLink: {
96
+ DEFAULT:
97
+ 'https://support.ledger.com/hc/en-us/articles/4404389606417-Download-and-install-Ledger-Live?docs=true',
98
+ },
99
+ color: 'black',
100
+ supportedChains,
101
+ showOnMobile: false,
102
+ needsDerivationPath: {
103
+ data: [
104
+ {
105
+ id: 'metamask',
106
+ label: `Metamask (m/44'/60'/0'/0/index)`,
107
+ namespace: 'EVM',
108
+ generateDerivationPath: (index: string) => `44'/60'/0'/0/${index}`,
109
+ },
110
+ {
111
+ id: 'ledgerLive',
112
+ label: `LedgerLive (m/44'/60'/index'/0/0)`,
113
+ namespace: 'EVM',
114
+ generateDerivationPath: (index: string) => `44'/60'/${index}'/0/0`,
115
+ },
116
+ {
117
+ id: 'legacy',
118
+ label: `Legacy (m/44'/60'/0'/index)`,
119
+ namespace: 'EVM',
120
+ generateDerivationPath: (index: string) => `44'/60'/0'/${index}`,
121
+ },
122
+ {
123
+ id: `(m/44'/501'/index')`,
124
+ label: `(m/44'/501'/index')`,
125
+ namespace: 'Solana',
126
+ generateDerivationPath: (index: string) => `44'/501'/${index}'`,
127
+ },
128
+ {
129
+ id: `(m/44'/501'/0'/index)`,
130
+ label: `(m/44'/501'/0'/index)`,
131
+ namespace: 'Solana',
132
+ generateDerivationPath: (index: string) => `44'/501'/0'/${index}`,
133
+ },
134
+ ],
135
+ },
136
+
137
+ needsNamespace: {
138
+ selection: 'single',
139
+ data: [
140
+ {
141
+ label: 'EVM',
142
+ value: 'EVM',
143
+ id: 'ETH',
144
+ getSupportedChains: (allBlockchains: BlockchainMeta[]) =>
145
+ allBlockchains.filter((chain) => chain.name === Networks.ETHEREUM),
146
+ },
147
+ {
148
+ label: 'Solana',
149
+ value: 'Solana',
150
+ id: 'SOLANA',
151
+ getSupportedChains: (allBlockchains: BlockchainMeta[]) =>
152
+ allBlockchains.filter((chain) => chain.name === Networks.SOLANA),
153
+ },
154
+ ],
155
+ },
156
+ };
157
+ };
158
+
159
+ const buildLegacyProvider: () => LegacyProviderInterface = () => ({
160
+ config,
161
+ getInstance,
162
+ connect,
163
+ getSigners,
164
+ getWalletInfo,
165
+ });
166
+
167
+ export { buildLegacyProvider };
package/src/mod.ts ADDED
@@ -0,0 +1,12 @@
1
+ import { defineVersions } from '@rango-dev/wallets-core/utils';
2
+
3
+ import { buildLegacyProvider } from './legacy/index.js';
4
+ import { buildProvider } from './provider.js';
5
+
6
+ const versions = () =>
7
+ defineVersions()
8
+ .version('0.0.0', buildLegacyProvider())
9
+ .version('1.0.0', buildProvider())
10
+ .build();
11
+
12
+ export { versions };
@@ -0,0 +1,65 @@
1
+ import type { EvmActions } from '@rango-dev/wallets-core/namespaces/evm';
2
+
3
+ import { NamespaceBuilder } from '@rango-dev/wallets-core';
4
+ import {
5
+ type CaipAccount,
6
+ builders as commonBuilders,
7
+ } from '@rango-dev/wallets-core/namespaces/common';
8
+ import {
9
+ builders,
10
+ CAIP_NAMESPACE,
11
+ } from '@rango-dev/wallets-core/namespaces/evm';
12
+ import { CAIP } from '@rango-dev/wallets-core/utils';
13
+ import { ETHEREUM_CHAIN_ID } from '@rango-dev/wallets-shared';
14
+
15
+ import { WALLET_ID } from '../constants.js';
16
+ import { setDerivationPath } from '../state.js';
17
+ import {
18
+ getEthereumAccounts,
19
+ standardizeAndThrowLedgerError,
20
+ } from '../utils.js';
21
+
22
+ const connect = builders
23
+ .connect()
24
+ .action(async function (_context, _chain, options) {
25
+ if (!options?.derivationPath) {
26
+ throw new Error('Derivation Path can not be empty.');
27
+ }
28
+
29
+ setDerivationPath(options.derivationPath);
30
+
31
+ const result = await getEthereumAccounts();
32
+
33
+ const formatAccounts = result.accounts.map(
34
+ (account) =>
35
+ CAIP.AccountId.format({
36
+ address: account,
37
+ chainId: {
38
+ namespace: CAIP_NAMESPACE,
39
+ reference: result.chainId,
40
+ },
41
+ }) as CaipAccount
42
+ );
43
+
44
+ return {
45
+ accounts: formatAccounts,
46
+ network: result.chainId,
47
+ };
48
+ })
49
+ .or(standardizeAndThrowLedgerError)
50
+ .build();
51
+
52
+ const disconnect = commonBuilders.disconnect<EvmActions>().build();
53
+
54
+ const getChainId = builders
55
+ .getChainId()
56
+ .action(() => ETHEREUM_CHAIN_ID)
57
+ .build();
58
+
59
+ const evm = new NamespaceBuilder<EvmActions>('EVM', WALLET_ID)
60
+ .action(connect)
61
+ .action(disconnect)
62
+ .action(getChainId)
63
+ .build();
64
+
65
+ export { evm };
@@ -0,0 +1,50 @@
1
+ import type { CaipAccount } from '@rango-dev/wallets-core/namespaces/common';
2
+ import type { SolanaActions } from '@rango-dev/wallets-core/namespaces/solana';
3
+
4
+ import { NamespaceBuilder } from '@rango-dev/wallets-core';
5
+ import { builders as commonBuilders } from '@rango-dev/wallets-core/namespaces/common';
6
+ import {
7
+ builders,
8
+ CAIP_NAMESPACE,
9
+ } from '@rango-dev/wallets-core/namespaces/solana';
10
+ import { CAIP } from '@rango-dev/wallets-core/utils';
11
+
12
+ import { WALLET_ID } from '../constants.js';
13
+ import { setDerivationPath } from '../state.js';
14
+ import { getSolanaAccounts, standardizeAndThrowLedgerError } from '../utils.js';
15
+
16
+ const connect = builders
17
+ .connect()
18
+ .action(async function (_context, options) {
19
+ if (!options?.derivationPath) {
20
+ throw new Error('Derivation Path can not be empty.');
21
+ }
22
+
23
+ setDerivationPath(options.derivationPath);
24
+
25
+ const result = await getSolanaAccounts();
26
+
27
+ const formatAccounts = result.accounts.map(
28
+ (account) =>
29
+ CAIP.AccountId.format({
30
+ address: account,
31
+ chainId: {
32
+ namespace: CAIP_NAMESPACE,
33
+ reference: result.chainId,
34
+ },
35
+ }) as CaipAccount
36
+ );
37
+
38
+ return formatAccounts;
39
+ })
40
+ .or(standardizeAndThrowLedgerError)
41
+ .build();
42
+
43
+ const disconnect = commonBuilders.disconnect<SolanaActions>().build();
44
+
45
+ const solana = new NamespaceBuilder<SolanaActions>('Solana', WALLET_ID)
46
+ .action(connect)
47
+ .action(disconnect)
48
+ .build();
49
+
50
+ export { solana };
@@ -0,0 +1,22 @@
1
+ import { ProviderBuilder } from '@rango-dev/wallets-core';
2
+
3
+ import { metadata, WALLET_ID } from './constants.js';
4
+ import { evm } from './namespaces/evm.js';
5
+ import { solana } from './namespaces/solana.js';
6
+ import { ledger as ledgerInstance } from './utils.js';
7
+
8
+ const buildProvider = () =>
9
+ new ProviderBuilder(WALLET_ID)
10
+ .init(function (context) {
11
+ const [, setState] = context.state();
12
+
13
+ if (ledgerInstance()) {
14
+ setState('installed', true);
15
+ }
16
+ })
17
+ .config('metadata', metadata)
18
+ .add('solana', solana)
19
+ .add('evm', evm)
20
+ .build();
21
+
22
+ export { buildProvider };
package/src/signer.ts ADDED
@@ -0,0 +1,13 @@
1
+ import type { SignerFactory } from 'rango-types';
2
+
3
+ import { DefaultSignerFactory, TransactionType as TxType } from 'rango-types';
4
+
5
+ import { EthereumSigner } from './signers/ethereum';
6
+ import { SolanaSigner } from './signers/solana';
7
+
8
+ export default async function getSigners(): Promise<SignerFactory> {
9
+ const signers = new DefaultSignerFactory();
10
+ signers.registerSigner(TxType.EVM, new EthereumSigner());
11
+ signers.registerSigner(TxType.SOLANA, new SolanaSigner());
12
+ return signers;
13
+ }
@@ -0,0 +1,107 @@
1
+ import type { TransactionLike } from 'ethers';
2
+ import type { GenericSigner } from 'rango-types';
3
+ import type { EvmTransaction } from 'rango-types/mainApi';
4
+
5
+ import {
6
+ DEFAULT_ETHEREUM_RPC_URL,
7
+ dynamicImportWithRefinedError,
8
+ } from '@rango-dev/wallets-shared';
9
+ import { JsonRpcProvider, Transaction } from 'ethers';
10
+ import { SignerError, SignerErrorCode } from 'rango-types';
11
+
12
+ import { getDerivationPath } from '../state.js';
13
+ import {
14
+ getLedgerError,
15
+ transportConnect,
16
+ transportDisconnect,
17
+ } from '../utils.js';
18
+
19
+ export class EthereumSigner implements GenericSigner<EvmTransaction> {
20
+ async signMessage(msg: string): Promise<string> {
21
+ try {
22
+ const transport = await transportConnect();
23
+ const LedgerAppEth = (
24
+ await dynamicImportWithRefinedError(
25
+ async () => await import('@ledgerhq/hw-app-eth')
26
+ )
27
+ ).default;
28
+ const eth = new LedgerAppEth(transport);
29
+
30
+ const result = await eth.signPersonalMessage(
31
+ getDerivationPath(),
32
+ Buffer.from(msg).toString('hex')
33
+ );
34
+ // eslint-disable-next-line @typescript-eslint/no-magic-numbers
35
+ let v = (result['v'] - 27).toString(16);
36
+ if (v.length < 2) {
37
+ v = '0' + v;
38
+ }
39
+ return '0x' + result['r'] + result['s'] + v;
40
+ } catch (error) {
41
+ throw new SignerError(SignerErrorCode.SIGN_TX_ERROR, undefined, error);
42
+ }
43
+ }
44
+
45
+ async signAndSendTx(
46
+ tx: EvmTransaction,
47
+ fromAddress: string,
48
+ chainId: string | null
49
+ ): Promise<{ hash: string }> {
50
+ try {
51
+ const provider = new JsonRpcProvider(DEFAULT_ETHEREUM_RPC_URL); // Provider to broadcast transaction
52
+
53
+ const transactionCount = await provider.getTransactionCount(fromAddress); // Get nonce
54
+
55
+ const transaction: TransactionLike<string> = {
56
+ to: tx.to,
57
+ gasPrice: tx.gasPrice,
58
+ gasLimit: tx.gasLimit,
59
+ nonce: transactionCount,
60
+ chainId: chainId,
61
+ data: tx.data,
62
+ value: tx.value,
63
+ maxPriorityFeePerGas: tx.maxPriorityFeePerGas,
64
+ maxFeePerGas: tx.maxFeePerGas,
65
+ };
66
+
67
+ const unsignedTx =
68
+ Transaction.from(transaction).unsignedSerialized.substring(2); // Create unsigned transaction
69
+
70
+ const transport = await transportConnect();
71
+ const LedgerHqAppEth = await dynamicImportWithRefinedError(
72
+ async () => await import('@ledgerhq/hw-app-eth')
73
+ );
74
+
75
+ const LedgerAppEth = LedgerHqAppEth.default;
76
+ const eth = new LedgerAppEth(transport);
77
+
78
+ const resolution = await LedgerHqAppEth.ledgerService.resolveTransaction(
79
+ unsignedTx,
80
+ {},
81
+ {}
82
+ ); // metadata necessary to allow the device to clear sign information
83
+ const signature = await eth.signTransaction(
84
+ getDerivationPath(),
85
+ unsignedTx,
86
+ resolution
87
+ );
88
+
89
+ const signedTx = Transaction.from({
90
+ ...transaction,
91
+ signature: {
92
+ r: '0x' + signature.r,
93
+ s: '0x' + signature.s,
94
+ v: parseInt(signature.v),
95
+ },
96
+ }).serialized;
97
+
98
+ const broadcastResult = await provider.broadcastTransaction(signedTx);
99
+
100
+ return { hash: broadcastResult.hash };
101
+ } catch (error) {
102
+ throw getLedgerError(error);
103
+ } finally {
104
+ await transportDisconnect();
105
+ }
106
+ }
107
+ }