@rango-dev/provider-ledger 0.2.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.
- package/CHANGELOG.md +0 -0
- package/dist/helpers.d.ts +13 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +7 -0
- package/dist/signer.d.ts +3 -0
- package/dist/signer.d.ts.map +1 -0
- package/dist/signers/ethereum.d.ts +10 -0
- package/dist/signers/ethereum.d.ts.map +1 -0
- package/package.json +33 -0
- package/readme.md +1 -0
- package/src/helpers.ts +71 -0
- package/src/index.ts +52 -0
- package/src/signer.ts +11 -0
- package/src/signers/ethereum.ts +80 -0
package/CHANGELOG.md
ADDED
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type Transport from '@ledgerhq/hw-transport';
|
|
2
|
+
export declare const ETH_BIP32_PATH = "44'/60'/0'/0/0";
|
|
3
|
+
export declare function getLedgerInstance(): {
|
|
4
|
+
chainId: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function getLedgerAccounts(): Promise<{
|
|
7
|
+
accounts: string[];
|
|
8
|
+
chainId: string;
|
|
9
|
+
}>;
|
|
10
|
+
export declare function getLedgerError(error: any): any;
|
|
11
|
+
export declare function transportConnect(): Promise<Transport>;
|
|
12
|
+
export declare function transportDisconnect(): Promise<void>;
|
|
13
|
+
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,wBAAwB,CAAC;AAIpD,eAAO,MAAM,cAAc,mBAAmB,CAAC;AAS/C,wBAAgB,iBAAiB;;EAMhC;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC;IACjD,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC,CAoBD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,GAAG,OAOxC;AAID,wBAAsB,gBAAgB,uBAMrC;AAED,wBAAsB,mBAAmB,kBAKxC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Connect, Disconnect, WalletInfo } from '@rango-dev/wallets-shared';
|
|
2
|
+
import type { BlockchainMeta, SignerFactory } from 'rango-types';
|
|
3
|
+
import { WalletTypes } from '@rango-dev/wallets-shared';
|
|
4
|
+
import { getLedgerInstance } from './helpers';
|
|
5
|
+
export declare const config: {
|
|
6
|
+
type: WalletTypes;
|
|
7
|
+
};
|
|
8
|
+
export declare const getInstance: typeof getLedgerInstance;
|
|
9
|
+
export declare const connect: Connect;
|
|
10
|
+
export declare const disconnect: Disconnect;
|
|
11
|
+
export declare const getSigners: (provider: any) => SignerFactory;
|
|
12
|
+
export declare const getWalletInfo: (allBlockChains: BlockchainMeta[]) => WalletInfo;
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,UAAU,EACV,UAAU,EACX,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjE,OAAO,EAAY,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAElE,OAAO,EAEL,iBAAiB,EAElB,MAAM,WAAW,CAAC;AAKnB,eAAO,MAAM,MAAM;;CAElB,CAAC;AAEF,eAAO,MAAM,WAAW,0BAAoB,CAAC;AAC7C,eAAO,MAAM,OAAO,EAAE,OAIrB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,UAExB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,aAAsB,CAAC;AAEnE,eAAO,MAAM,aAAa,EAAE,CAAC,cAAc,EAAE,cAAc,EAAE,KAAK,UAgBjE,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var P=Object.defineProperty;var e=(n,t)=>P(n,"name",{value:t,configurable:!0});import{Networks as k,WalletTypes as R}from"@rango-dev/wallets-shared";var h="0x1",p="44'/60'/0'/0/0",y={21781:"The device is locked",25871:"Related application is not ready on your device",27013:"Action denied by user",INSUFFICIENT_FUNDS:"Insufficient funds for transaction"};function w(){return{chainId:h}}e(w,"getLedgerInstance");async function T(){try{let n=await d(),t=new(await import("@ledgerhq/hw-app-eth")).default(n),r=[],c=await t.getAddress(p,!1,!0);return r.push(c.address),{accounts:r,chainId:h}}catch(n){throw l(n)}finally{await a()}}e(T,"getLedgerAccounts");function l(n){let t=n?.statusCode||n?.code;return t&&y[t]?new Error(y[t]):n}e(l,"getLedgerError");var o=null;async function d(){return o=await(await import("@ledgerhq/hw-transport-webhid")).default.create(),o}e(d,"transportConnect");async function a(){o&&(await o.close(),o=null)}e(a,"transportDisconnect");import{DefaultSignerFactory as A,TransactionType as D}from"rango-types";import{JsonRpcProvider as S,Transaction as E}from"ethers";import{SignerError as C}from"rango-types";var F="https://rpc.ankr.com/eth",i=class{static{e(this,"EthereumSigner")}async signMessage(){throw C.UnimplementedError("signMessage")}async signAndSendTx(t,r,c){try{let s=new S(F),x=await s.getTransactionCount(r),u={to:t.to,gasPrice:t.gasPrice,gasLimit:t.gasLimit,nonce:x,chainId:c,data:t.data,value:t.value,maxPriorityFeePerGas:t.maxPriorityFeePerGas,maxFeePerGas:t.maxFeePerGas},f=E.from(u).unsignedSerialized.substring(2),I=await(await import("@ledgerhq/hw-app-eth")).ledgerService.resolveTransaction(f,{},{}),L=await d(),g=await new(await import("@ledgerhq/hw-app-eth")).default(L).signTransaction(p,f,I),v=E.from({...u,signature:{r:"0x"+g.r,s:"0x"+g.s,v:parseInt(g.v)}}).serialized;return{hash:(await s.broadcastTransaction(v)).hash}}catch(s){throw l(s)}finally{await a()}}};function m(){let n=new A;return n.registerSigner(D.EVM,new i),n}e(m,"getSigners");var b=R.LEDGER,Y={type:b},Z=w,$=e(async()=>await T(),"connect"),tt=e(async()=>{a()},"disconnect"),nt=m,et=e(n=>{let t=n.find(r=>r.name===k.ETHEREUM);return{name:"Ledger",img:"https://raw.githubusercontent.com/rango-exchange/assets/main/wallets/ledger/icon.svg",installLink:{DEFAULT:"https://support.ledger.com/hc/en-us/articles/4404389606417-Download-and-install-Ledger-Live?docs=true"},color:"black",supportedChains:t?[t]:[]}},"getWalletInfo");export{Y as config,$ as connect,tt as disconnect,Z as getInstance,nt as getSigners,et as getWalletInfo};
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts", "../src/helpers.ts", "../src/signer.ts", "../src/signers/ethereum.ts"],
|
|
4
|
+
"sourcesContent": ["import type {\n Connect,\n Disconnect,\n WalletInfo,\n} from '@rango-dev/wallets-shared';\nimport type { BlockchainMeta, SignerFactory } from 'rango-types';\n\nimport { Networks, WalletTypes } from '@rango-dev/wallets-shared';\n\nimport {\n getLedgerAccounts,\n getLedgerInstance,\n transportDisconnect,\n} from './helpers';\nimport signer from './signer';\n\nconst WALLET = WalletTypes.LEDGER;\n\nexport const config = {\n type: WALLET,\n};\n\nexport const getInstance = getLedgerInstance;\nexport const connect: Connect = async () => {\n const ledgerAccounts = await getLedgerAccounts();\n\n return ledgerAccounts;\n};\n\nexport const disconnect: Disconnect = async () => {\n void transportDisconnect();\n};\n\nexport const getSigners: (provider: any) => SignerFactory = signer;\n\nexport const getWalletInfo: (allBlockChains: BlockchainMeta[]) => WalletInfo = (\n allBlockChains\n) => {\n const ethereumBlockchain = allBlockChains.find(\n (chain) => chain.name === Networks.ETHEREUM\n );\n return {\n name: 'Ledger',\n img: 'https://raw.githubusercontent.com/rango-exchange/assets/main/wallets/ledger/icon.svg',\n installLink: {\n DEFAULT:\n 'https://support.ledger.com/hc/en-us/articles/4404389606417-Download-and-install-Ledger-Live?docs=true',\n },\n color: 'black',\n supportedChains: ethereumBlockchain ? [ethereumBlockchain] : [],\n };\n};\n", "import type Transport from '@ledgerhq/hw-transport';\n\nconst ETHEREUM_CHAIN_ID = '0x1';\n\nexport const ETH_BIP32_PATH = \"44'/60'/0'/0/0\";\n\nconst ledgerErrorMessages: { [statusCode: number | string]: string } = {\n 21781: 'The device is locked',\n 25871: 'Related application is not ready on your device',\n 27013: 'Action denied by user',\n INSUFFICIENT_FUNDS: 'Insufficient funds for transaction',\n};\n\nexport function getLedgerInstance() {\n /*\n * Instances have a required property which is `chainId` and is using in swap execution.\n * Here we are setting it as Ethereum always since we are supporting only eth for now.\n */\n return { chainId: ETHEREUM_CHAIN_ID };\n}\n\nexport async function getLedgerAccounts(): Promise<{\n accounts: string[];\n chainId: string;\n}> {\n try {\n const transport = await transportConnect();\n\n const eth = new (await import('@ledgerhq/hw-app-eth')).default(transport);\n\n const accounts: string[] = [];\n\n const result = await eth.getAddress(ETH_BIP32_PATH, false, true);\n accounts.push(result.address);\n\n return {\n accounts: accounts,\n chainId: ETHEREUM_CHAIN_ID,\n };\n } catch (error: any) {\n throw getLedgerError(error);\n } finally {\n await transportDisconnect();\n }\n}\n\nexport function getLedgerError(error: any) {\n const errorCode = error?.statusCode || error?.code; // ledger error || broadcast error\n\n if (errorCode && !!ledgerErrorMessages[errorCode]) {\n return new Error(ledgerErrorMessages[errorCode]);\n }\n return error;\n}\n\nlet transportConnection: Transport | null = null;\n\nexport async function transportConnect() {\n transportConnection = await (\n await import('@ledgerhq/hw-transport-webhid')\n ).default.create();\n\n return transportConnection;\n}\n\nexport async function transportDisconnect() {\n if (transportConnection) {\n await transportConnection.close();\n transportConnection = null;\n }\n}\n", "import type { SignerFactory } from 'rango-types';\n\nimport { DefaultSignerFactory, TransactionType as TxType } from 'rango-types';\n\nimport { EthereumSigner } from './signers/ethereum';\n\nexport default function getSigners(): SignerFactory {\n const signers = new DefaultSignerFactory();\n signers.registerSigner(TxType.EVM, new EthereumSigner());\n return signers;\n}\n", "import type { TransactionLike } from 'ethers';\nimport type { GenericSigner } from 'rango-types';\nimport type { EvmTransaction } from 'rango-types/lib/api/main';\n\nimport { JsonRpcProvider, Transaction } from 'ethers';\nimport { SignerError } from 'rango-types';\n\nimport {\n ETH_BIP32_PATH,\n getLedgerError,\n transportConnect,\n transportDisconnect,\n} from '../helpers';\n\nexport const RPC_PROVIDER_URL = 'https://rpc.ankr.com/eth';\n\nexport class EthereumSigner implements GenericSigner<EvmTransaction> {\n async signMessage(): Promise<string> {\n // TODO: Should be implemented using eth.signPersonalMessage\n throw SignerError.UnimplementedError('signMessage');\n }\n\n async signAndSendTx(\n tx: EvmTransaction,\n fromAddress: string,\n chainId: string | null\n ): Promise<{ hash: string }> {\n try {\n const provider = new JsonRpcProvider(RPC_PROVIDER_URL); // Provider to broadcast transaction\n\n const transactionCount = await provider.getTransactionCount(fromAddress); // Get nonce\n\n const transaction: TransactionLike<string> = {\n to: tx.to,\n gasPrice: tx.gasPrice,\n gasLimit: tx.gasLimit,\n nonce: transactionCount,\n chainId: chainId,\n data: tx.data,\n value: tx.value,\n maxPriorityFeePerGas: tx.maxPriorityFeePerGas,\n maxFeePerGas: tx.maxFeePerGas,\n };\n\n const unsignedTx =\n Transaction.from(transaction).unsignedSerialized.substring(2); // Create unsigned transaction\n\n const resolution = await (\n await import('@ledgerhq/hw-app-eth')\n ).ledgerService.resolveTransaction(unsignedTx, {}, {}); // metadata necessary to allow the device to clear sign information\n\n const transport = await transportConnect();\n\n const eth = new (await import('@ledgerhq/hw-app-eth')).default(transport);\n\n const signature = await eth.signTransaction(\n ETH_BIP32_PATH,\n unsignedTx,\n resolution\n );\n\n const signedTx = Transaction.from({\n ...transaction,\n signature: {\n r: '0x' + signature.r,\n s: '0x' + signature.s,\n v: parseInt(signature.v),\n },\n }).serialized;\n\n const broadcastResult = await provider.broadcastTransaction(signedTx);\n\n return { hash: broadcastResult.hash };\n } catch (error) {\n throw getLedgerError(error);\n } finally {\n await transportDisconnect();\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["Networks", "WalletTypes", "ETHEREUM_CHAIN_ID", "ETH_BIP32_PATH", "ledgerErrorMessages", "getLedgerInstance", "__name", "getLedgerAccounts", "transport", "transportConnect", "eth", "accounts", "result", "error", "getLedgerError", "transportDisconnect", "errorCode", "transportConnection", "DefaultSignerFactory", "TxType", "JsonRpcProvider", "Transaction", "SignerError", "RPC_PROVIDER_URL", "EthereumSigner", "__name", "SignerError", "tx", "fromAddress", "chainId", "provider", "JsonRpcProvider", "transactionCount", "transaction", "unsignedTx", "Transaction", "resolution", "transport", "transportConnect", "signature", "ETH_BIP32_PATH", "signedTx", "error", "getLedgerError", "transportDisconnect", "getSigners", "signers", "DefaultSignerFactory", "TxType", "EthereumSigner", "__name", "WALLET", "WalletTypes", "config", "getInstance", "getLedgerInstance", "connect", "__name", "getLedgerAccounts", "disconnect", "transportDisconnect", "getSigners", "getWalletInfo", "allBlockChains", "ethereumBlockchain", "chain", "Networks"]
|
|
7
|
+
}
|
package/dist/signer.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signer.d.ts","sourceRoot":"","sources":["../src/signer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAMjD,MAAM,CAAC,OAAO,UAAU,UAAU,IAAI,aAAa,CAIlD"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { GenericSigner } from 'rango-types';
|
|
2
|
+
import type { EvmTransaction } from 'rango-types/lib/api/main';
|
|
3
|
+
export declare const RPC_PROVIDER_URL = "https://rpc.ankr.com/eth";
|
|
4
|
+
export declare class EthereumSigner implements GenericSigner<EvmTransaction> {
|
|
5
|
+
signMessage(): Promise<string>;
|
|
6
|
+
signAndSendTx(tx: EvmTransaction, fromAddress: string, chainId: string | null): Promise<{
|
|
7
|
+
hash: string;
|
|
8
|
+
}>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=ethereum.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ethereum.d.ts","sourceRoot":"","sources":["../../src/signers/ethereum.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAY/D,eAAO,MAAM,gBAAgB,6BAA6B,CAAC;AAE3D,qBAAa,cAAe,YAAW,aAAa,CAAC,cAAc,CAAC;IAC5D,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC;IAK9B,aAAa,CACjB,EAAE,EAAE,cAAc,EAClB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,GAAG,IAAI,GACrB,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAqD7B"}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rango-dev/provider-ledger",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"source": "./src/index.ts",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"typings": "dist/index.d.ts",
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"src"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "node ../../scripts/build/command.mjs --path wallets/provider-ledger",
|
|
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}\" --ignore-path ../../.eslintignore"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@ledgerhq/hw-app-eth": "^6.35.7",
|
|
25
|
+
"@ledgerhq/hw-transport-webhid": "^6.28.5",
|
|
26
|
+
"@rango-dev/wallets-shared": "^0.32.0",
|
|
27
|
+
"ethers": "^6.11.1",
|
|
28
|
+
"rango-types": "^0.1.59"
|
|
29
|
+
},
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
}
|
|
33
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# @rango-dev/provider-ledger
|
package/src/helpers.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type Transport from '@ledgerhq/hw-transport';
|
|
2
|
+
|
|
3
|
+
const ETHEREUM_CHAIN_ID = '0x1';
|
|
4
|
+
|
|
5
|
+
export const ETH_BIP32_PATH = "44'/60'/0'/0/0";
|
|
6
|
+
|
|
7
|
+
const ledgerErrorMessages: { [statusCode: number | string]: string } = {
|
|
8
|
+
21781: 'The device is locked',
|
|
9
|
+
25871: 'Related application is not ready on your device',
|
|
10
|
+
27013: 'Action denied by user',
|
|
11
|
+
INSUFFICIENT_FUNDS: 'Insufficient funds for transaction',
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function getLedgerInstance() {
|
|
15
|
+
/*
|
|
16
|
+
* Instances have a required property which is `chainId` and is using in swap execution.
|
|
17
|
+
* Here we are setting it as Ethereum always since we are supporting only eth for now.
|
|
18
|
+
*/
|
|
19
|
+
return { chainId: ETHEREUM_CHAIN_ID };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function getLedgerAccounts(): Promise<{
|
|
23
|
+
accounts: string[];
|
|
24
|
+
chainId: string;
|
|
25
|
+
}> {
|
|
26
|
+
try {
|
|
27
|
+
const transport = await transportConnect();
|
|
28
|
+
|
|
29
|
+
const eth = new (await import('@ledgerhq/hw-app-eth')).default(transport);
|
|
30
|
+
|
|
31
|
+
const accounts: string[] = [];
|
|
32
|
+
|
|
33
|
+
const result = await eth.getAddress(ETH_BIP32_PATH, false, true);
|
|
34
|
+
accounts.push(result.address);
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
accounts: accounts,
|
|
38
|
+
chainId: ETHEREUM_CHAIN_ID,
|
|
39
|
+
};
|
|
40
|
+
} catch (error: any) {
|
|
41
|
+
throw getLedgerError(error);
|
|
42
|
+
} finally {
|
|
43
|
+
await transportDisconnect();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function getLedgerError(error: any) {
|
|
48
|
+
const errorCode = error?.statusCode || error?.code; // ledger error || broadcast error
|
|
49
|
+
|
|
50
|
+
if (errorCode && !!ledgerErrorMessages[errorCode]) {
|
|
51
|
+
return new Error(ledgerErrorMessages[errorCode]);
|
|
52
|
+
}
|
|
53
|
+
return error;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let transportConnection: Transport | null = null;
|
|
57
|
+
|
|
58
|
+
export async function transportConnect() {
|
|
59
|
+
transportConnection = await (
|
|
60
|
+
await import('@ledgerhq/hw-transport-webhid')
|
|
61
|
+
).default.create();
|
|
62
|
+
|
|
63
|
+
return transportConnection;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function transportDisconnect() {
|
|
67
|
+
if (transportConnection) {
|
|
68
|
+
await transportConnection.close();
|
|
69
|
+
transportConnection = null;
|
|
70
|
+
}
|
|
71
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Connect,
|
|
3
|
+
Disconnect,
|
|
4
|
+
WalletInfo,
|
|
5
|
+
} from '@rango-dev/wallets-shared';
|
|
6
|
+
import type { BlockchainMeta, SignerFactory } from 'rango-types';
|
|
7
|
+
|
|
8
|
+
import { Networks, WalletTypes } from '@rango-dev/wallets-shared';
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
getLedgerAccounts,
|
|
12
|
+
getLedgerInstance,
|
|
13
|
+
transportDisconnect,
|
|
14
|
+
} from './helpers';
|
|
15
|
+
import signer from './signer';
|
|
16
|
+
|
|
17
|
+
const WALLET = WalletTypes.LEDGER;
|
|
18
|
+
|
|
19
|
+
export const config = {
|
|
20
|
+
type: WALLET,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const getInstance = getLedgerInstance;
|
|
24
|
+
export const connect: Connect = async () => {
|
|
25
|
+
const ledgerAccounts = await getLedgerAccounts();
|
|
26
|
+
|
|
27
|
+
return ledgerAccounts;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const disconnect: Disconnect = async () => {
|
|
31
|
+
void transportDisconnect();
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const getSigners: (provider: any) => SignerFactory = signer;
|
|
35
|
+
|
|
36
|
+
export const getWalletInfo: (allBlockChains: BlockchainMeta[]) => WalletInfo = (
|
|
37
|
+
allBlockChains
|
|
38
|
+
) => {
|
|
39
|
+
const ethereumBlockchain = allBlockChains.find(
|
|
40
|
+
(chain) => chain.name === Networks.ETHEREUM
|
|
41
|
+
);
|
|
42
|
+
return {
|
|
43
|
+
name: 'Ledger',
|
|
44
|
+
img: 'https://raw.githubusercontent.com/rango-exchange/assets/main/wallets/ledger/icon.svg',
|
|
45
|
+
installLink: {
|
|
46
|
+
DEFAULT:
|
|
47
|
+
'https://support.ledger.com/hc/en-us/articles/4404389606417-Download-and-install-Ledger-Live?docs=true',
|
|
48
|
+
},
|
|
49
|
+
color: 'black',
|
|
50
|
+
supportedChains: ethereumBlockchain ? [ethereumBlockchain] : [],
|
|
51
|
+
};
|
|
52
|
+
};
|
package/src/signer.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
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
|
+
|
|
7
|
+
export default function getSigners(): SignerFactory {
|
|
8
|
+
const signers = new DefaultSignerFactory();
|
|
9
|
+
signers.registerSigner(TxType.EVM, new EthereumSigner());
|
|
10
|
+
return signers;
|
|
11
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { TransactionLike } from 'ethers';
|
|
2
|
+
import type { GenericSigner } from 'rango-types';
|
|
3
|
+
import type { EvmTransaction } from 'rango-types/lib/api/main';
|
|
4
|
+
|
|
5
|
+
import { JsonRpcProvider, Transaction } from 'ethers';
|
|
6
|
+
import { SignerError } from 'rango-types';
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
ETH_BIP32_PATH,
|
|
10
|
+
getLedgerError,
|
|
11
|
+
transportConnect,
|
|
12
|
+
transportDisconnect,
|
|
13
|
+
} from '../helpers';
|
|
14
|
+
|
|
15
|
+
export const RPC_PROVIDER_URL = 'https://rpc.ankr.com/eth';
|
|
16
|
+
|
|
17
|
+
export class EthereumSigner implements GenericSigner<EvmTransaction> {
|
|
18
|
+
async signMessage(): Promise<string> {
|
|
19
|
+
// TODO: Should be implemented using eth.signPersonalMessage
|
|
20
|
+
throw SignerError.UnimplementedError('signMessage');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async signAndSendTx(
|
|
24
|
+
tx: EvmTransaction,
|
|
25
|
+
fromAddress: string,
|
|
26
|
+
chainId: string | null
|
|
27
|
+
): Promise<{ hash: string }> {
|
|
28
|
+
try {
|
|
29
|
+
const provider = new JsonRpcProvider(RPC_PROVIDER_URL); // Provider to broadcast transaction
|
|
30
|
+
|
|
31
|
+
const transactionCount = await provider.getTransactionCount(fromAddress); // Get nonce
|
|
32
|
+
|
|
33
|
+
const transaction: TransactionLike<string> = {
|
|
34
|
+
to: tx.to,
|
|
35
|
+
gasPrice: tx.gasPrice,
|
|
36
|
+
gasLimit: tx.gasLimit,
|
|
37
|
+
nonce: transactionCount,
|
|
38
|
+
chainId: chainId,
|
|
39
|
+
data: tx.data,
|
|
40
|
+
value: tx.value,
|
|
41
|
+
maxPriorityFeePerGas: tx.maxPriorityFeePerGas,
|
|
42
|
+
maxFeePerGas: tx.maxFeePerGas,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const unsignedTx =
|
|
46
|
+
Transaction.from(transaction).unsignedSerialized.substring(2); // Create unsigned transaction
|
|
47
|
+
|
|
48
|
+
const resolution = await (
|
|
49
|
+
await import('@ledgerhq/hw-app-eth')
|
|
50
|
+
).ledgerService.resolveTransaction(unsignedTx, {}, {}); // metadata necessary to allow the device to clear sign information
|
|
51
|
+
|
|
52
|
+
const transport = await transportConnect();
|
|
53
|
+
|
|
54
|
+
const eth = new (await import('@ledgerhq/hw-app-eth')).default(transport);
|
|
55
|
+
|
|
56
|
+
const signature = await eth.signTransaction(
|
|
57
|
+
ETH_BIP32_PATH,
|
|
58
|
+
unsignedTx,
|
|
59
|
+
resolution
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const signedTx = Transaction.from({
|
|
63
|
+
...transaction,
|
|
64
|
+
signature: {
|
|
65
|
+
r: '0x' + signature.r,
|
|
66
|
+
s: '0x' + signature.s,
|
|
67
|
+
v: parseInt(signature.v),
|
|
68
|
+
},
|
|
69
|
+
}).serialized;
|
|
70
|
+
|
|
71
|
+
const broadcastResult = await provider.broadcastTransaction(signedTx);
|
|
72
|
+
|
|
73
|
+
return { hash: broadcastResult.hash };
|
|
74
|
+
} catch (error) {
|
|
75
|
+
throw getLedgerError(error);
|
|
76
|
+
} finally {
|
|
77
|
+
await transportDisconnect();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|