@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.
- package/CHANGELOG.md +193 -0
- package/dist/Eth-Y5JFGVQA.js +33 -0
- package/dist/Eth-Y5JFGVQA.js.map +7 -0
- package/dist/Solana-UB4SGWQ3.js +2 -0
- package/dist/Solana-UB4SGWQ3.js.map +7 -0
- package/dist/TransportWebHID-JLT4ETY3.js +2 -0
- package/dist/TransportWebHID-JLT4ETY3.js.map +7 -0
- package/dist/chunk-5QSDSOWH.js +2 -0
- package/dist/chunk-5QSDSOWH.js.map +7 -0
- package/dist/chunk-CVKDPNYM.js +3 -0
- package/dist/chunk-CVKDPNYM.js.map +7 -0
- package/dist/constants.d.ts +6 -0
- package/dist/legacy/index.d.ts +16 -0
- package/dist/mod.d.ts +2 -0
- package/dist/mod.js +2 -0
- package/dist/mod.js.map +7 -0
- package/dist/namespaces/evm.d.ts +3 -0
- package/dist/namespaces/solana.d.ts +3 -0
- package/dist/provider-ledger.build.json +1 -0
- package/dist/provider.d.ts +2 -0
- package/dist/signer.d.ts +2 -0
- package/dist/signers/ethereum.d.ts +8 -0
- package/dist/signers/solana.d.ts +9 -0
- package/dist/state.d.ts +2 -0
- package/dist/utils.d.ts +10 -0
- package/package.json +42 -0
- package/readme.md +16 -0
- package/src/constants.ts +91 -0
- package/src/legacy/index.ts +167 -0
- package/src/mod.ts +12 -0
- package/src/namespaces/evm.ts +65 -0
- package/src/namespaces/solana.ts +50 -0
- package/src/provider.ts +22 -0
- package/src/signer.ts +13 -0
- package/src/signers/ethereum.ts +107 -0
- package/src/signers/solana.ts +94 -0
- package/src/state.ts +10 -0
- package/src/utils.ts +140 -0
package/dist/signer.d.ts
ADDED
|
@@ -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
|
+
}
|
package/dist/state.d.ts
ADDED
package/dist/utils.d.ts
ADDED
|
@@ -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).
|
package/src/constants.ts
ADDED
|
@@ -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 };
|
package/src/provider.ts
ADDED
|
@@ -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
|
+
}
|