@reef-knot/ledger-connector 5.1.0 → 5.2.0-alpha.2
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/dist/hid/account.d.ts +5 -0
- package/dist/hid/account.d.ts.map +1 -0
- package/dist/hid/account.js +56 -0
- package/dist/hid/connector.d.ts.map +1 -1
- package/dist/hid/connector.js +31 -30
- package/dist/hid/constants.d.ts +2 -0
- package/dist/hid/constants.d.ts.map +1 -1
- package/dist/hid/constants.js +3 -2
- package/dist/hid/helpers.d.ts +12 -10
- package/dist/hid/helpers.d.ts.map +1 -1
- package/dist/hid/helpers.js +39 -35
- package/dist/hid/index.js +1 -0
- package/dist/hid/provider.d.ts +35 -14
- package/dist/hid/provider.d.ts.map +1 -1
- package/dist/hid/provider.js +191 -50
- package/dist/iframe/connector.d.ts.map +1 -1
- package/dist/iframe/connector.js +4 -13
- package/dist/iframe/helpers.js +1 -2
- package/dist/iframe/index.js +1 -0
- package/dist/iframe/provider.js +1 -3
- package/dist/index.js +1 -2
- package/package.json +6 -18
- package/dist/hid/signer.d.ts +0 -29
- package/dist/hid/signer.d.ts.map +0 -1
- package/dist/hid/signer.js +0 -146
- package/dist/hid/types.d.ts +0 -10
- package/dist/hid/types.d.ts.map +0 -1
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type Eth from '@ledgerhq/hw-app-eth';
|
|
2
|
+
import { type Address, type LocalAccount } from 'viem';
|
|
3
|
+
export type WithEthApp = <T>(callback: (eth: Eth) => T | Promise<T>) => Promise<T>;
|
|
4
|
+
export declare const createLedgerAccount: (address: Address, path: string, withEthApp: WithEthApp) => LocalAccount;
|
|
5
|
+
//# sourceMappingURL=account.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account.d.ts","sourceRoot":"","sources":["../../src/hid/account.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,sBAAsB,CAAC;AAE5C,OAAO,EAOL,KAAK,OAAO,EACZ,KAAK,YAAY,EAGlB,MAAM,MAAM,CAAC;AAId,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,EACzB,QAAQ,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,KACnC,OAAO,CAAC,CAAC,CAAC,CAAC;AAiBhB,eAAO,MAAM,mBAAmB,GAC9B,SAAS,OAAO,EAChB,MAAM,MAAM,EACZ,YAAY,UAAU,KACrB,YAiFC,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { bytesToHex, getTypesForEIP712Domain, hashStruct, serializeSignature, serializeTransaction, stringToHex } from "viem";
|
|
2
|
+
import { toAccount } from "viem/accounts";
|
|
3
|
+
//#region src/hid/account.ts
|
|
4
|
+
const RESOLUTION_CONFIG = {
|
|
5
|
+
erc20: true,
|
|
6
|
+
externalPlugins: true,
|
|
7
|
+
nft: true
|
|
8
|
+
};
|
|
9
|
+
const shouldFallbackToHashedSigning = (error) => error?.statusText === "INS_NOT_SUPPORTED";
|
|
10
|
+
const toSignatureHex = ({ r, s, v }) => serializeSignature({
|
|
11
|
+
r: `0x${r}`,
|
|
12
|
+
s: `0x${s}`,
|
|
13
|
+
v: BigInt(v)
|
|
14
|
+
});
|
|
15
|
+
const createLedgerAccount = (address, path, withEthApp) => toAccount({
|
|
16
|
+
address,
|
|
17
|
+
async signTransaction(transaction, { serializer = serializeTransaction } = {}) {
|
|
18
|
+
const unsignedRawTx = (await serializer(transaction)).slice(2);
|
|
19
|
+
const { r, s, v } = await withEthApp((eth) => eth.clearSignTransaction(path, unsignedRawTx, RESOLUTION_CONFIG, true));
|
|
20
|
+
return serializer(transaction, {
|
|
21
|
+
r: `0x${r}`,
|
|
22
|
+
s: `0x${s}`,
|
|
23
|
+
v: BigInt(`0x${v}`)
|
|
24
|
+
});
|
|
25
|
+
},
|
|
26
|
+
async signMessage({ message }) {
|
|
27
|
+
const messageHex = typeof message === "string" ? stringToHex(message) : typeof message.raw === "string" ? message.raw : bytesToHex(message.raw);
|
|
28
|
+
const signature = await withEthApp((eth) => eth.signPersonalMessage(path, messageHex.slice(2)));
|
|
29
|
+
return toSignatureHex(signature);
|
|
30
|
+
},
|
|
31
|
+
async signTypedData(parameters) {
|
|
32
|
+
const { domain = {}, types, primaryType, message } = parameters;
|
|
33
|
+
const signature = await withEthApp(async (eth) => {
|
|
34
|
+
try {
|
|
35
|
+
return await eth.signEIP712Message(path, parameters);
|
|
36
|
+
} catch (error) {
|
|
37
|
+
if (!shouldFallbackToHashedSigning(error)) throw error;
|
|
38
|
+
const typedDataDomain = domain;
|
|
39
|
+
const domainSeparator = hashStruct({
|
|
40
|
+
data: typedDataDomain,
|
|
41
|
+
primaryType: "EIP712Domain",
|
|
42
|
+
types: { EIP712Domain: getTypesForEIP712Domain({ domain: typedDataDomain }) }
|
|
43
|
+
});
|
|
44
|
+
const messageHash = hashStruct({
|
|
45
|
+
data: message,
|
|
46
|
+
primaryType,
|
|
47
|
+
types
|
|
48
|
+
});
|
|
49
|
+
return eth.signEIP712HashedMessage(path, domainSeparator.slice(2), messageHash.slice(2));
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
return toSignatureHex(signature);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
//#endregion
|
|
56
|
+
export { createLedgerAccount };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connector.d.ts","sourceRoot":"","sources":["../../src/hid/connector.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"connector.d.ts","sourceRoot":"","sources":["../../src/hid/connector.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAUrC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,eAAO,MAAM,WAAW,cAAc,CAAC;AACvC,eAAO,MAAM,IAAI,WAAW,CAAC;AAG7B,wBAAgB,kBAAkB,CAAC,EACjC,GAAG,EACH,YAAY,GACb,EAAE;IACD,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,YAAY,EAAE,KAAK,CAAC;CACrB,yGAgIA;yBAtIe,kBAAkB"}
|
package/dist/hid/connector.js
CHANGED
|
@@ -1,52 +1,64 @@
|
|
|
1
|
-
import { checkError, clearLedgerDerivationPath } from "./helpers.js";
|
|
1
|
+
import { checkError, clearLedgerAccount, clearLedgerChainId, clearLedgerDerivationPath, restoreLedgerChainId, saveLedgerChainId } from "./helpers.js";
|
|
2
2
|
import { ChainNotConfiguredError, ConnectorNotFoundError, createConnector } from "wagmi";
|
|
3
|
-
|
|
3
|
+
import { SwitchChainError } from "viem";
|
|
4
4
|
//#region src/hid/connector.ts
|
|
5
5
|
const idLedgerHid = "ledgerHID";
|
|
6
6
|
const name = "Ledger";
|
|
7
7
|
ledgerHIDConnector.type = "ledgerHID";
|
|
8
8
|
function ledgerHIDConnector({ rpc, defaultChain }) {
|
|
9
9
|
const providers = {};
|
|
10
|
+
let currentChainId = restoreLedgerChainId();
|
|
10
11
|
return createConnector(({ chains, emitter }) => ({
|
|
11
12
|
id: idLedgerHid,
|
|
12
13
|
name,
|
|
13
14
|
type: ledgerHIDConnector.type,
|
|
14
15
|
async getProvider({ chainId } = {}) {
|
|
15
|
-
const chain = chains.find((x) => x.id === chainId) ?? defaultChain;
|
|
16
|
+
const chain = chains.find((x) => x.id === (chainId ?? currentChainId)) ?? defaultChain;
|
|
16
17
|
if (!providers[chain.id]) {
|
|
17
18
|
const { LedgerHQProvider } = await import("./provider.js");
|
|
18
|
-
providers[chain.id] = new LedgerHQProvider({
|
|
19
|
+
providers[chain.id] = new LedgerHQProvider({
|
|
20
|
+
chain,
|
|
21
|
+
rpcUrl: rpc?.[chain.id]
|
|
22
|
+
});
|
|
19
23
|
}
|
|
20
24
|
return providers[chain.id];
|
|
21
25
|
},
|
|
22
|
-
async connect({ withCapabilities = false } = {}) {
|
|
26
|
+
async connect({ chainId, withCapabilities = false } = {}) {
|
|
23
27
|
try {
|
|
24
|
-
|
|
28
|
+
currentChainId = chainId ?? currentChainId;
|
|
29
|
+
const provider = await this.getProvider({ chainId: currentChainId });
|
|
25
30
|
provider.on("disconnect", this.onDisconnect);
|
|
26
31
|
const account = await provider.enable();
|
|
27
|
-
const
|
|
32
|
+
const connectedChainId = await this.getChainId();
|
|
33
|
+
currentChainId = connectedChainId;
|
|
34
|
+
saveLedgerChainId(connectedChainId);
|
|
28
35
|
return {
|
|
29
36
|
accounts: withCapabilities ? [{
|
|
30
37
|
address: account,
|
|
31
38
|
capabilities: {}
|
|
32
39
|
}] : [account],
|
|
33
|
-
chainId
|
|
40
|
+
chainId: connectedChainId
|
|
34
41
|
};
|
|
35
42
|
} catch (error) {
|
|
43
|
+
currentChainId = restoreLedgerChainId();
|
|
36
44
|
return checkError(error);
|
|
37
45
|
}
|
|
38
46
|
},
|
|
39
47
|
async disconnect() {
|
|
40
|
-
|
|
48
|
+
Object.values(providers).forEach((provider) => {
|
|
49
|
+
provider.removeListener("disconnect", this.onDisconnect);
|
|
50
|
+
provider.resetAccount();
|
|
51
|
+
});
|
|
52
|
+
currentChainId = void 0;
|
|
41
53
|
clearLedgerDerivationPath();
|
|
54
|
+
clearLedgerChainId();
|
|
55
|
+
clearLedgerAccount();
|
|
42
56
|
},
|
|
43
57
|
async getAccounts() {
|
|
44
58
|
return [await (await this.getProvider()).getAddress()];
|
|
45
59
|
},
|
|
46
60
|
async getChainId() {
|
|
47
|
-
|
|
48
|
-
if (chainId) return chainId;
|
|
49
|
-
throw new ChainNotConfiguredError();
|
|
61
|
+
return (await this.getProvider()).chain.id;
|
|
50
62
|
},
|
|
51
63
|
async isAuthorized() {
|
|
52
64
|
try {
|
|
@@ -59,22 +71,12 @@ function ledgerHIDConnector({ rpc, defaultChain }) {
|
|
|
59
71
|
}
|
|
60
72
|
},
|
|
61
73
|
async switchChain({ chainId }) {
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
nativeCurrency: {
|
|
69
|
-
decimals: 18,
|
|
70
|
-
name: "Ether",
|
|
71
|
-
symbol: "ETH"
|
|
72
|
-
},
|
|
73
|
-
rpcUrls: {
|
|
74
|
-
default: { http: [""] },
|
|
75
|
-
public: { http: [""] }
|
|
76
|
-
}
|
|
77
|
-
});
|
|
74
|
+
const chain = chains.find((x) => x.id === chainId);
|
|
75
|
+
if (!chain) throw new SwitchChainError(new ChainNotConfiguredError());
|
|
76
|
+
currentChainId = chainId;
|
|
77
|
+
saveLedgerChainId(chainId);
|
|
78
|
+
emitter.emit("change", { chainId });
|
|
79
|
+
return chain;
|
|
78
80
|
},
|
|
79
81
|
onDisconnect() {
|
|
80
82
|
emitter.emit("disconnect");
|
|
@@ -83,6 +85,5 @@ function ledgerHIDConnector({ rpc, defaultChain }) {
|
|
|
83
85
|
onChainChanged() {}
|
|
84
86
|
}));
|
|
85
87
|
}
|
|
86
|
-
|
|
87
88
|
//#endregion
|
|
88
|
-
export { idLedgerHid, ledgerHIDConnector };
|
|
89
|
+
export { idLedgerHid, ledgerHIDConnector, name };
|
package/dist/hid/constants.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/hid/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,qCAAqC,CAAC"}
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/hid/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,qCAAqC,CAAC;AACzE,eAAO,MAAM,eAAe,8BAA8B,CAAC;AAC3D,eAAO,MAAM,cAAc,6BAA6B,CAAC"}
|
package/dist/hid/constants.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
//#region src/hid/constants.ts
|
|
2
2
|
const LS_KEY_DERIVATION_PATH = "reef-knot_ledger-derivation-path";
|
|
3
|
-
|
|
3
|
+
const LS_KEY_CHAIN_ID = "reef-knot_ledger-chain-id";
|
|
4
|
+
const LS_KEY_ACCOUNT = "reef-knot_ledger-account";
|
|
4
5
|
//#endregion
|
|
5
|
-
export { LS_KEY_DERIVATION_PATH };
|
|
6
|
+
export { LS_KEY_ACCOUNT, LS_KEY_CHAIN_ID, LS_KEY_DERIVATION_PATH };
|
package/dist/hid/helpers.d.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
maxPriorityFeePerGas?: BigNumberish;
|
|
8
|
-
}) => boolean;
|
|
9
|
-
export declare const toNumber: (value: BigNumberish | undefined | null) => number | undefined;
|
|
10
|
-
export declare const convertToUnsigned: (tx: TransactionRequestExtended) => Promise<UnsignedTransactionStrict>;
|
|
1
|
+
import { type Address } from 'viem';
|
|
2
|
+
export type PersistedLedgerAccount = {
|
|
3
|
+
address: Address;
|
|
4
|
+
path: string;
|
|
5
|
+
};
|
|
6
|
+
export declare const isLockedDeviceError: (error: unknown) => boolean;
|
|
11
7
|
export declare const checkError: (error: any) => never;
|
|
12
8
|
export declare const clearLedgerDerivationPath: () => void;
|
|
9
|
+
export declare const saveLedgerChainId: (chainId: number) => void;
|
|
10
|
+
export declare const restoreLedgerChainId: () => number | undefined;
|
|
11
|
+
export declare const clearLedgerChainId: () => void;
|
|
12
|
+
export declare const saveLedgerAccount: (account: PersistedLedgerAccount) => void;
|
|
13
|
+
export declare const restoreLedgerAccount: () => PersistedLedgerAccount | undefined;
|
|
14
|
+
export declare const clearLedgerAccount: () => void;
|
|
13
15
|
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/hid/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/hid/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,OAAO,EAAE,MAAM,MAAM,CAAC;AAO/C,MAAM,MAAM,sBAAsB,GAAG;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,OAAO,OAAO,KAAG,OACc,CAAC;AAEpE,eAAO,MAAM,UAAU,GAAI,OAAO,GAAG,KAAG,KAmBvC,CAAC;AAEF,eAAO,MAAM,yBAAyB,YAErC,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,SAAS,MAAM,SAGhD,CAAC;AAEF,eAAO,MAAM,oBAAoB,QAAO,MAAM,GAAG,SAMhD,CAAC;AAEF,eAAO,MAAM,kBAAkB,YAG9B,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,SAAS,sBAAsB,SAGhE,CAAC;AAEF,eAAO,MAAM,oBAAoB,QAAO,sBAAsB,GAAG,SAchE,CAAC;AAEF,eAAO,MAAM,kBAAkB,YAG9B,CAAC"}
|
package/dist/hid/helpers.js
CHANGED
|
@@ -1,37 +1,7 @@
|
|
|
1
|
-
import { LS_KEY_DERIVATION_PATH } from "./constants.js";
|
|
2
|
-
import {
|
|
3
|
-
import { resolveProperties } from "@ethersproject/properties";
|
|
4
|
-
|
|
1
|
+
import { LS_KEY_ACCOUNT, LS_KEY_CHAIN_ID, LS_KEY_DERIVATION_PATH } from "./constants.js";
|
|
2
|
+
import { isAddress } from "viem";
|
|
5
3
|
//#region src/hid/helpers.ts
|
|
6
|
-
const
|
|
7
|
-
return tx.type === 2 || tx.maxFeePerGas != null || tx.maxPriorityFeePerGas != null;
|
|
8
|
-
};
|
|
9
|
-
const toNumber = (value) => {
|
|
10
|
-
return value == null ? void 0 : BigNumber.from(value).toNumber();
|
|
11
|
-
};
|
|
12
|
-
const convertToUnsigned = async (tx) => {
|
|
13
|
-
const resolvedTx = await resolveProperties(tx);
|
|
14
|
-
const { chainId, data, gasLimit, gas, gasPrice, value, to } = resolvedTx;
|
|
15
|
-
const nonce = toNumber(resolvedTx.nonce);
|
|
16
|
-
const type = toNumber(resolvedTx.type);
|
|
17
|
-
const baseTx = {
|
|
18
|
-
gasLimit: gasLimit || gas,
|
|
19
|
-
type: type ?? 2,
|
|
20
|
-
chainId,
|
|
21
|
-
gasPrice,
|
|
22
|
-
nonce,
|
|
23
|
-
value,
|
|
24
|
-
data,
|
|
25
|
-
to
|
|
26
|
-
};
|
|
27
|
-
if (tx.accessList != null) baseTx.accessList = tx.accessList;
|
|
28
|
-
if (hasEIP1559(tx)) {
|
|
29
|
-
baseTx.maxFeePerGas = tx.maxFeePerGas;
|
|
30
|
-
baseTx.maxPriorityFeePerGas = tx.maxPriorityFeePerGas;
|
|
31
|
-
baseTx.type = 2;
|
|
32
|
-
}
|
|
33
|
-
return baseTx;
|
|
34
|
-
};
|
|
4
|
+
const isLockedDeviceError = (error) => error?.name === "LockedDeviceError";
|
|
35
5
|
const checkError = (error) => {
|
|
36
6
|
if (error.statusText === "INS_NOT_SUPPORTED") error.message = "Device is not supported. Make sure the Ethereum app is open on the device.";
|
|
37
7
|
if (error.statusText === "UNKNOWN_ERROR") error.message = "Unknown error. Make sure the device is connected and the Ethereum app is open on the device.";
|
|
@@ -41,6 +11,40 @@ const checkError = (error) => {
|
|
|
41
11
|
const clearLedgerDerivationPath = () => {
|
|
42
12
|
window?.localStorage.removeItem(LS_KEY_DERIVATION_PATH);
|
|
43
13
|
};
|
|
44
|
-
|
|
14
|
+
const saveLedgerChainId = (chainId) => {
|
|
15
|
+
if (typeof window === "undefined") return;
|
|
16
|
+
window.localStorage.setItem(LS_KEY_CHAIN_ID, String(chainId));
|
|
17
|
+
};
|
|
18
|
+
const restoreLedgerChainId = () => {
|
|
19
|
+
if (typeof window === "undefined") return void 0;
|
|
20
|
+
const stored = window.localStorage.getItem(LS_KEY_CHAIN_ID);
|
|
21
|
+
if (!stored) return void 0;
|
|
22
|
+
const chainId = Number(stored);
|
|
23
|
+
return Number.isInteger(chainId) && chainId > 0 ? chainId : void 0;
|
|
24
|
+
};
|
|
25
|
+
const clearLedgerChainId = () => {
|
|
26
|
+
if (typeof window === "undefined") return;
|
|
27
|
+
window.localStorage.removeItem(LS_KEY_CHAIN_ID);
|
|
28
|
+
};
|
|
29
|
+
const saveLedgerAccount = (account) => {
|
|
30
|
+
if (typeof window === "undefined") return;
|
|
31
|
+
window.localStorage.setItem(LS_KEY_ACCOUNT, JSON.stringify(account));
|
|
32
|
+
};
|
|
33
|
+
const restoreLedgerAccount = () => {
|
|
34
|
+
if (typeof window === "undefined") return void 0;
|
|
35
|
+
const stored = window.localStorage.getItem(LS_KEY_ACCOUNT);
|
|
36
|
+
if (!stored) return void 0;
|
|
37
|
+
try {
|
|
38
|
+
const { address, path } = JSON.parse(stored);
|
|
39
|
+
if (address && isAddress(address) && typeof path === "string") return {
|
|
40
|
+
address,
|
|
41
|
+
path
|
|
42
|
+
};
|
|
43
|
+
} catch {}
|
|
44
|
+
};
|
|
45
|
+
const clearLedgerAccount = () => {
|
|
46
|
+
if (typeof window === "undefined") return;
|
|
47
|
+
window.localStorage.removeItem(LS_KEY_ACCOUNT);
|
|
48
|
+
};
|
|
45
49
|
//#endregion
|
|
46
|
-
export { checkError, clearLedgerDerivationPath,
|
|
50
|
+
export { checkError, clearLedgerAccount, clearLedgerChainId, clearLedgerDerivationPath, isLockedDeviceError, restoreLedgerAccount, restoreLedgerChainId, saveLedgerAccount, saveLedgerChainId };
|
package/dist/hid/index.js
CHANGED
package/dist/hid/provider.d.ts
CHANGED
|
@@ -1,21 +1,42 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type Eth from '@ledgerhq/hw-app-eth';
|
|
2
2
|
import type TransportWebHID from '@ledgerhq/hw-transport-webhid';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import { type Address, type Chain, type LocalAccount } from 'viem';
|
|
4
|
+
type RequestArguments = {
|
|
5
|
+
method: string;
|
|
6
|
+
params?: unknown[];
|
|
7
|
+
};
|
|
8
|
+
type ProviderEvent = 'disconnect';
|
|
9
|
+
type Listener = () => void;
|
|
10
|
+
export declare class LedgerHQProvider {
|
|
11
|
+
readonly chain: Chain;
|
|
6
12
|
device?: HIDDevice;
|
|
7
13
|
transport?: typeof TransportWebHID;
|
|
8
|
-
|
|
14
|
+
private readonly rpcUrl?;
|
|
15
|
+
private account?;
|
|
16
|
+
private accountPath?;
|
|
17
|
+
private publicClient?;
|
|
18
|
+
private walletClient?;
|
|
19
|
+
private listeners;
|
|
20
|
+
constructor({ chain, rpcUrl }: {
|
|
21
|
+
chain: Chain;
|
|
22
|
+
rpcUrl?: string;
|
|
23
|
+
});
|
|
24
|
+
on(event: ProviderEvent, listener: Listener): void;
|
|
25
|
+
removeListener(event: ProviderEvent, listener: Listener): void;
|
|
26
|
+
emit(event: ProviderEvent): void;
|
|
27
|
+
get derivationPath(): string;
|
|
9
28
|
loadTransport(): Promise<void>;
|
|
10
|
-
getSigner(): LedgerHQSigner;
|
|
11
|
-
listAccounts(): Promise<Array<string>>;
|
|
12
|
-
detectNetwork(): Promise<Network>;
|
|
13
29
|
getTransport(): Promise<TransportWebHID>;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
30
|
+
withEthApp<T>(callback: (eth: Eth) => T | Promise<T>): Promise<T>;
|
|
31
|
+
enable(): Promise<Address>;
|
|
32
|
+
resetAccount(): void;
|
|
33
|
+
getAccount(): Promise<LocalAccount>;
|
|
34
|
+
private readAddress;
|
|
35
|
+
getAddress(): Promise<Address>;
|
|
36
|
+
private getPublicClient;
|
|
37
|
+
private getWalletClient;
|
|
38
|
+
private sendTransaction;
|
|
39
|
+
request({ method, params }: RequestArguments): Promise<unknown>;
|
|
20
40
|
}
|
|
41
|
+
export {};
|
|
21
42
|
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/hid/provider.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/hid/provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,sBAAsB,CAAC;AAC5C,OAAO,KAAK,eAAe,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAYL,KAAK,OAAO,EACZ,KAAK,KAAK,EAEV,KAAK,YAAY,EAIlB,MAAM,MAAM,CAAC;AA6Cd,KAAK,gBAAgB,GAAG;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;CACpB,CAAC;AAEF,KAAK,aAAa,GAAG,YAAY,CAAC;AAClC,KAAK,QAAQ,GAAG,MAAM,IAAI,CAAC;AAE3B,qBAAa,gBAAgB;IAC3B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAEf,MAAM,CAAC,EAAE,SAAS,CAAC;IAEnB,SAAS,CAAC,EAAE,OAAO,eAAe,CAAC;IAE1C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAS;IAEjC,OAAO,CAAC,OAAO,CAAC,CAAe;IAE/B,OAAO,CAAC,WAAW,CAAC,CAAS;IAE7B,OAAO,CAAC,YAAY,CAAC,CAAe;IAEpC,OAAO,CAAC,YAAY,CAAC,CAAe;IAEpC,OAAO,CAAC,SAAS,CAAqD;gBAE1D,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QAAE,KAAK,EAAE,KAAK,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;IAehE,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ;IAI3C,cAAc,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ;IAIvD,IAAI,CAAC,KAAK,EAAE,aAAa;IAMzB,IAAI,cAAc,WAQjB;IAEK,aAAa;IAQb,YAAY,IAAI,OAAO,CAAC,eAAe,CAAC;IAc9C,UAAU,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAoB3D,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAyBhC,YAAY;IAMN,UAAU,IAAI,OAAO,CAAC,YAAY,CAAC;YAqC3B,WAAW;IAoBnB,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC;IAOpC,OAAO,CAAC,eAAe;YAQT,eAAe;YAUf,eAAe;IAuEvB,OAAO,CAAC,EAAE,MAAM,EAAE,MAAW,EAAE,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;CA0D3E"}
|
package/dist/hid/provider.js
CHANGED
|
@@ -1,14 +1,50 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import invariant from "tiny-invariant";
|
|
6
|
-
|
|
1
|
+
import "./constants.js";
|
|
2
|
+
import { checkError, clearLedgerAccount, isLockedDeviceError, restoreLedgerAccount, saveLedgerAccount } from "./helpers.js";
|
|
3
|
+
import { createLedgerAccount } from "./account.js";
|
|
4
|
+
import { MethodNotSupportedRpcError, createPublicClient, createWalletClient, getAddress, hexToBigInt, hexToNumber, http, isAddress, isAddressEqual, isHex, numberToHex } from "viem";
|
|
7
5
|
//#region src/hid/provider.ts
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
const NOOP = () => {};
|
|
7
|
+
const DEFAULT_DERIVATION_PATH = "m/44'/60'/0'/0/0";
|
|
8
|
+
const TX_TYPES = {
|
|
9
|
+
"0x0": "legacy",
|
|
10
|
+
"0x1": "eip2930",
|
|
11
|
+
"0x2": "eip1559"
|
|
12
|
+
};
|
|
13
|
+
const UNSUPPORTED_WALLET_METHODS = /* @__PURE__ */ new Set([
|
|
14
|
+
"eth_signTransaction",
|
|
15
|
+
"eth_signTypedData",
|
|
16
|
+
"eth_signTypedData_v1",
|
|
17
|
+
"eth_signTypedData_v3",
|
|
18
|
+
"eth_decrypt",
|
|
19
|
+
"eth_getEncryptionPublicKey",
|
|
20
|
+
"personal_ecRecover"
|
|
21
|
+
]);
|
|
22
|
+
let deviceSessionLock = Promise.resolve();
|
|
23
|
+
const withDeviceSession = (fn) => {
|
|
24
|
+
const result = deviceSessionLock.then(fn);
|
|
25
|
+
deviceSessionLock = result.then(NOOP, NOOP);
|
|
26
|
+
return result;
|
|
27
|
+
};
|
|
28
|
+
var LedgerHQProvider = class {
|
|
29
|
+
constructor({ chain, rpcUrl }) {
|
|
30
|
+
this.listeners = {};
|
|
31
|
+
this.chain = chain;
|
|
32
|
+
this.rpcUrl = rpcUrl;
|
|
33
|
+
if (!rpcUrl) console.warn(`[reef-knot] No RPC URL configured for chain ${chain.id} (${chain.name}), falling back to its default public RPC endpoint`);
|
|
34
|
+
}
|
|
35
|
+
on(event, listener) {
|
|
36
|
+
var _this$listeners;
|
|
37
|
+
((_this$listeners = this.listeners)[event] ?? (_this$listeners[event] = /* @__PURE__ */ new Set())).add(listener);
|
|
38
|
+
}
|
|
39
|
+
removeListener(event, listener) {
|
|
40
|
+
this.listeners[event]?.delete(listener);
|
|
41
|
+
}
|
|
42
|
+
emit(event) {
|
|
43
|
+
this.listeners[event]?.forEach((listener) => listener());
|
|
44
|
+
}
|
|
45
|
+
get derivationPath() {
|
|
46
|
+
if (typeof window !== "undefined") return window.localStorage.getItem("reef-knot_ledger-derivation-path") || DEFAULT_DERIVATION_PATH;
|
|
47
|
+
return DEFAULT_DERIVATION_PATH;
|
|
12
48
|
}
|
|
13
49
|
async loadTransport() {
|
|
14
50
|
if (!this.transport) {
|
|
@@ -16,15 +52,6 @@ var LedgerHQProvider = class extends JsonRpcBatchProvider {
|
|
|
16
52
|
this.transport = TransportWebHID;
|
|
17
53
|
}
|
|
18
54
|
}
|
|
19
|
-
getSigner() {
|
|
20
|
-
return new LedgerHQSigner(this);
|
|
21
|
-
}
|
|
22
|
-
listAccounts() {
|
|
23
|
-
throw new Error("method is not implemented");
|
|
24
|
-
}
|
|
25
|
-
detectNetwork() {
|
|
26
|
-
return Promise.resolve(this._network);
|
|
27
|
-
}
|
|
28
55
|
async getTransport() {
|
|
29
56
|
await this.loadTransport();
|
|
30
57
|
try {
|
|
@@ -35,6 +62,21 @@ var LedgerHQProvider = class extends JsonRpcBatchProvider {
|
|
|
35
62
|
return checkError(error);
|
|
36
63
|
}
|
|
37
64
|
}
|
|
65
|
+
withEthApp(callback) {
|
|
66
|
+
return withDeviceSession(async () => {
|
|
67
|
+
const transport = await this.getTransport();
|
|
68
|
+
try {
|
|
69
|
+
const { default: Eth } = await import("@ledgerhq/hw-app-eth");
|
|
70
|
+
const eth = new Eth(transport);
|
|
71
|
+
await eth.getAppConfiguration();
|
|
72
|
+
return await callback(eth);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
return checkError(error);
|
|
75
|
+
} finally {
|
|
76
|
+
await transport.close();
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
38
80
|
async enable() {
|
|
39
81
|
try {
|
|
40
82
|
await this.loadTransport();
|
|
@@ -46,52 +88,151 @@ var LedgerHQProvider = class extends JsonRpcBatchProvider {
|
|
|
46
88
|
}
|
|
47
89
|
};
|
|
48
90
|
hid.addEventListener("disconnect", onDisconnect);
|
|
49
|
-
|
|
91
|
+
this.resetAccount();
|
|
50
92
|
return await this.getAddress();
|
|
51
93
|
} catch (error) {
|
|
52
94
|
return checkError(error);
|
|
53
95
|
}
|
|
54
96
|
}
|
|
97
|
+
resetAccount() {
|
|
98
|
+
this.account = void 0;
|
|
99
|
+
this.accountPath = void 0;
|
|
100
|
+
this.walletClient = void 0;
|
|
101
|
+
}
|
|
102
|
+
async getAccount() {
|
|
103
|
+
const path = this.derivationPath;
|
|
104
|
+
if (!this.account || this.accountPath !== path) {
|
|
105
|
+
const { address, verifiedByDevice } = await this.readAddress(path);
|
|
106
|
+
let verified = verifiedByDevice;
|
|
107
|
+
const withVerifyingEthApp = (cb) => this.withEthApp(async (eth) => {
|
|
108
|
+
if (!verified) {
|
|
109
|
+
const actual = await eth.getAddress(path);
|
|
110
|
+
if (!isAddressEqual(getAddress(actual.address), address)) {
|
|
111
|
+
clearLedgerAccount();
|
|
112
|
+
this.resetAccount();
|
|
113
|
+
this.emit("disconnect");
|
|
114
|
+
throw new Error("The connected account does not match the device. Please reconnect.");
|
|
115
|
+
}
|
|
116
|
+
verified = true;
|
|
117
|
+
}
|
|
118
|
+
return cb(eth);
|
|
119
|
+
});
|
|
120
|
+
this.account = createLedgerAccount(address, path, withVerifyingEthApp);
|
|
121
|
+
this.accountPath = path;
|
|
122
|
+
this.walletClient = void 0;
|
|
123
|
+
}
|
|
124
|
+
return this.account;
|
|
125
|
+
}
|
|
126
|
+
async readAddress(path) {
|
|
127
|
+
try {
|
|
128
|
+
const { address } = await this.withEthApp((eth) => eth.getAddress(path));
|
|
129
|
+
const checksummed = getAddress(address);
|
|
130
|
+
saveLedgerAccount({
|
|
131
|
+
address: checksummed,
|
|
132
|
+
path
|
|
133
|
+
});
|
|
134
|
+
return {
|
|
135
|
+
address: checksummed,
|
|
136
|
+
verifiedByDevice: true
|
|
137
|
+
};
|
|
138
|
+
} catch (error) {
|
|
139
|
+
const persisted = restoreLedgerAccount();
|
|
140
|
+
if (isLockedDeviceError(error) && persisted?.path === path) return {
|
|
141
|
+
address: persisted.address,
|
|
142
|
+
verifiedByDevice: false
|
|
143
|
+
};
|
|
144
|
+
return checkError(error);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
55
147
|
async getAddress() {
|
|
56
|
-
|
|
57
|
-
return
|
|
148
|
+
const { address } = await this.getAccount();
|
|
149
|
+
return address;
|
|
150
|
+
}
|
|
151
|
+
getPublicClient() {
|
|
152
|
+
this.publicClient ?? (this.publicClient = createPublicClient({
|
|
153
|
+
chain: this.chain,
|
|
154
|
+
transport: http(this.rpcUrl, { batch: true })
|
|
155
|
+
}));
|
|
156
|
+
return this.publicClient;
|
|
157
|
+
}
|
|
158
|
+
async getWalletClient() {
|
|
159
|
+
const account = await this.getAccount();
|
|
160
|
+
this.walletClient ?? (this.walletClient = createWalletClient({
|
|
161
|
+
account,
|
|
162
|
+
chain: this.chain,
|
|
163
|
+
transport: http(this.rpcUrl, { batch: true })
|
|
164
|
+
}));
|
|
165
|
+
return this.walletClient;
|
|
58
166
|
}
|
|
59
|
-
async
|
|
60
|
-
|
|
167
|
+
async sendTransaction(transaction) {
|
|
168
|
+
const walletClient = await this.getWalletClient();
|
|
169
|
+
const account = walletClient.account;
|
|
170
|
+
if (transaction.from && (!isAddress(transaction.from) || !isAddressEqual(transaction.from, account.address))) throw new Error("from address mismatch");
|
|
171
|
+
const type = transaction.type ? TX_TYPES[transaction.type] : void 0;
|
|
172
|
+
const request = {
|
|
173
|
+
account,
|
|
174
|
+
chain: this.chain,
|
|
175
|
+
to: transaction.to ?? void 0,
|
|
176
|
+
data: transaction.data,
|
|
177
|
+
value: transaction.value ? hexToBigInt(transaction.value) : void 0,
|
|
178
|
+
gas: transaction.gas ? hexToBigInt(transaction.gas) : void 0,
|
|
179
|
+
nonce: transaction.nonce != null ? hexToNumber(transaction.nonce) : void 0
|
|
180
|
+
};
|
|
181
|
+
const accessList = transaction.accessList;
|
|
182
|
+
const gasPrice = transaction.gasPrice ? hexToBigInt(transaction.gasPrice) : void 0;
|
|
183
|
+
if (type === "legacy") return walletClient.sendTransaction({
|
|
184
|
+
...request,
|
|
185
|
+
type: "legacy",
|
|
186
|
+
gasPrice
|
|
187
|
+
});
|
|
188
|
+
if (type === "eip2930") return walletClient.sendTransaction({
|
|
189
|
+
...request,
|
|
190
|
+
type: "eip2930",
|
|
191
|
+
gasPrice,
|
|
192
|
+
accessList
|
|
193
|
+
});
|
|
194
|
+
if (!type && gasPrice) return walletClient.sendTransaction({
|
|
195
|
+
...request,
|
|
196
|
+
gasPrice,
|
|
197
|
+
accessList
|
|
198
|
+
});
|
|
199
|
+
return walletClient.sendTransaction({
|
|
200
|
+
...request,
|
|
201
|
+
type,
|
|
202
|
+
accessList,
|
|
203
|
+
maxFeePerGas: transaction.maxFeePerGas ? hexToBigInt(transaction.maxFeePerGas) : void 0,
|
|
204
|
+
maxPriorityFeePerGas: transaction.maxPriorityFeePerGas ? hexToBigInt(transaction.maxPriorityFeePerGas) : void 0
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
async request({ method, params = [] }) {
|
|
61
208
|
switch (method) {
|
|
62
|
-
case "
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return this.send("eth_sendRawTransaction", [signedTx]);
|
|
67
|
-
}
|
|
68
|
-
case "eth_accounts": return [await this.getAddress()];
|
|
69
|
-
case "eth_signTypedData_v4": {
|
|
70
|
-
if (typeof params[1] !== "string") throw new Error("eth_signTypedData_v4 arg 1 is not a string");
|
|
71
|
-
const payload = JSON.parse(params[1]);
|
|
72
|
-
return await this.signer.__signEIP712Message({
|
|
73
|
-
domain: payload.domain,
|
|
74
|
-
types: payload.types,
|
|
75
|
-
primaryType: payload.primaryType,
|
|
76
|
-
message: payload.message
|
|
77
|
-
});
|
|
78
|
-
}
|
|
209
|
+
case "eth_chainId": return numberToHex(this.chain.id);
|
|
210
|
+
case "eth_accounts":
|
|
211
|
+
case "eth_requestAccounts": return [await this.getAddress()];
|
|
212
|
+
case "eth_sendTransaction": return this.sendTransaction(params[0]);
|
|
79
213
|
case "personal_sign": {
|
|
80
214
|
const messageHex = params[0];
|
|
81
|
-
if (
|
|
82
|
-
|
|
83
|
-
return await this.signer.signMessage(messageBytes);
|
|
215
|
+
if (!isHex(messageHex)) throw new Error("personal_sign message must be a hex string");
|
|
216
|
+
return (await this.getAccount()).signMessage({ message: { raw: messageHex } });
|
|
84
217
|
}
|
|
85
218
|
case "eth_sign": {
|
|
86
219
|
const messageHex = params[1];
|
|
87
|
-
if (
|
|
88
|
-
|
|
89
|
-
|
|
220
|
+
if (!isHex(messageHex)) throw new Error("eth_sign message must be a hex string");
|
|
221
|
+
return (await this.getAccount()).signMessage({ message: { raw: messageHex } });
|
|
222
|
+
}
|
|
223
|
+
case "eth_signTypedData_v4":
|
|
224
|
+
if (typeof params[1] !== "string") throw new Error("eth_signTypedData_v4 arg 1 is not a string");
|
|
225
|
+
return (await this.getAccount()).signTypedData(JSON.parse(params[1]));
|
|
226
|
+
default: {
|
|
227
|
+
if (method.startsWith("wallet_") || UNSUPPORTED_WALLET_METHODS.has(method)) throw new MethodNotSupportedRpcError(/* @__PURE__ */ new Error(`The method ${method} is not supported`), { method });
|
|
228
|
+
const rpcRequest = this.getPublicClient().request;
|
|
229
|
+
return rpcRequest({
|
|
230
|
+
method,
|
|
231
|
+
params
|
|
232
|
+
});
|
|
90
233
|
}
|
|
91
|
-
default: return this.send(method, params);
|
|
92
234
|
}
|
|
93
235
|
}
|
|
94
236
|
};
|
|
95
|
-
|
|
96
237
|
//#endregion
|
|
97
|
-
export { LedgerHQProvider };
|
|
238
|
+
export { LedgerHQProvider };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connector.d.ts","sourceRoot":"","sources":["../../src/iframe/connector.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"connector.d.ts","sourceRoot":"","sources":["../../src/iframe/connector.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,2BAA2B,CAAC;AAC/E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,eAAO,MAAM,YAAY,eAAe,CAAC;AACzC,eAAO,MAAM,IAAI,gBAAgB,CAAC;AAElC,KAAK,uBAAuB,GAAG;IAC7B,OAAO,CAAC,EAAE,6BAA6B,CAAC;IACxC,YAAY,EAAE,KAAK,CAAC;CACrB,CAAC;AAGF,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,YAAY,GACb,EAAE,uBAAuB,6GA0IzB;yBA7Ie,mBAAmB"}
|
package/dist/iframe/connector.js
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { SwitchChainNotSupportedError, createConnector } from "wagmi";
|
|
2
|
-
import { ResourceUnavailableRpcError, UserRejectedRequestError } from "viem";
|
|
3
|
-
import { getAddress } from "@ethersproject/address";
|
|
4
|
-
import { hexValue } from "@ethersproject/bytes";
|
|
5
|
-
import { Web3Provider } from "@ethersproject/providers";
|
|
6
|
-
|
|
2
|
+
import { ResourceUnavailableRpcError, UserRejectedRequestError, getAddress, numberToHex } from "viem";
|
|
7
3
|
//#region src/iframe/connector.ts
|
|
8
4
|
const idLedgerLive = "ledgerLive";
|
|
9
5
|
const name = "Ledger Live";
|
|
@@ -48,7 +44,7 @@ function ledgerLiveConnector({ options, defaultChain }) {
|
|
|
48
44
|
},
|
|
49
45
|
async switchChain({ chainId }) {
|
|
50
46
|
const provider = await this.getProvider();
|
|
51
|
-
const id =
|
|
47
|
+
const id = numberToHex(chainId);
|
|
52
48
|
try {
|
|
53
49
|
await provider.send("wallet_switchEthereumChain", [{ chainId: id }]);
|
|
54
50
|
return chains.find((x) => x.id === chainId) ?? {
|
|
@@ -72,16 +68,12 @@ function ledgerLiveConnector({ options, defaultChain }) {
|
|
|
72
68
|
}
|
|
73
69
|
},
|
|
74
70
|
async getAccounts() {
|
|
75
|
-
return (await (await this.getProvider()).send("eth_requestAccounts")).map(getAddress);
|
|
71
|
+
return (await (await this.getProvider()).send("eth_requestAccounts")).map((account) => getAddress(account));
|
|
76
72
|
},
|
|
77
73
|
async getChainId() {
|
|
78
74
|
const chainId = await (await this.getProvider()).send("eth_chainId");
|
|
79
75
|
return Number(chainId);
|
|
80
76
|
},
|
|
81
|
-
async getSigner() {
|
|
82
|
-
const [provider, accounts] = await Promise.all([this.getProvider(), this.getAccounts()]);
|
|
83
|
-
return new Web3Provider(provider).getSigner(accounts[0]);
|
|
84
|
-
},
|
|
85
77
|
async isAuthorized() {
|
|
86
78
|
try {
|
|
87
79
|
return !!(await (await this.getProvider()).send("eth_accounts"))[0];
|
|
@@ -108,6 +100,5 @@ function ledgerLiveConnector({ options, defaultChain }) {
|
|
|
108
100
|
}
|
|
109
101
|
}));
|
|
110
102
|
}
|
|
111
|
-
|
|
112
103
|
//#endregion
|
|
113
|
-
export { idLedgerLive, ledgerLiveConnector, name };
|
|
104
|
+
export { idLedgerLive, ledgerLiveConnector, name };
|
package/dist/iframe/helpers.js
CHANGED
package/dist/iframe/index.js
CHANGED
package/dist/iframe/provider.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { IFrameEthereumProvider } from "@ledgerhq/iframe-provider";
|
|
2
|
-
|
|
3
2
|
//#region src/iframe/provider.ts
|
|
4
3
|
var LedgerIFrameProvider = class extends IFrameEthereumProvider {
|
|
5
4
|
request({ method, params }) {
|
|
6
5
|
return this.send(method, params);
|
|
7
6
|
}
|
|
8
7
|
};
|
|
9
|
-
|
|
10
8
|
//#endregion
|
|
11
|
-
export { LedgerIFrameProvider };
|
|
9
|
+
export { LedgerIFrameProvider };
|
package/dist/index.js
CHANGED
|
@@ -4,5 +4,4 @@ import "./iframe/index.js";
|
|
|
4
4
|
import { LS_KEY_DERIVATION_PATH } from "./hid/constants.js";
|
|
5
5
|
import { idLedgerHid, ledgerHIDConnector } from "./hid/connector.js";
|
|
6
6
|
import "./hid/index.js";
|
|
7
|
-
|
|
8
|
-
export { LS_KEY_DERIVATION_PATH, idLedgerHid, idLedgerLive, isIframe, isLedgerLive, ledgerHIDConnector, ledgerLiveConnector, name };
|
|
7
|
+
export { LS_KEY_DERIVATION_PATH, idLedgerHid, idLedgerLive, isIframe, isLedgerLive, ledgerHIDConnector, ledgerLiveConnector, name };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reef-knot/ledger-connector",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.0-alpha.2",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -38,29 +38,17 @@
|
|
|
38
38
|
"typecheck": "tsc --noEmit"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@types/w3c-web-hid": "^1.0.
|
|
41
|
+
"@types/w3c-web-hid": "^1.0.7",
|
|
42
42
|
"build-config": "*",
|
|
43
43
|
"eslint-config-custom": "*",
|
|
44
44
|
"viem": ">=2.50",
|
|
45
45
|
"wagmi": ">=3.6"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@
|
|
49
|
-
"@
|
|
50
|
-
"@
|
|
51
|
-
"@
|
|
52
|
-
"@ethersproject/hash": "^5.7.0",
|
|
53
|
-
"@ethersproject/properties": "^5.7.0",
|
|
54
|
-
"@ethersproject/providers": "^5.7.2",
|
|
55
|
-
"@ethersproject/strings": "^5.7.0",
|
|
56
|
-
"@ethersproject/transactions": "^5.7.0",
|
|
57
|
-
"@ledgerhq/hw-app-eth": "^6.37.1",
|
|
58
|
-
"@ledgerhq/hw-transport": "^6.31.0",
|
|
59
|
-
"@ledgerhq/hw-transport-webhid": "^6.29.0",
|
|
60
|
-
"@ledgerhq/iframe-provider": "^0.4.3",
|
|
61
|
-
"@web3-react/abstract-connector": "^6.0.7",
|
|
62
|
-
"@web3-react/types": "^6.0.7",
|
|
63
|
-
"tiny-invariant": "^1.2.0"
|
|
48
|
+
"@ledgerhq/hw-app-eth": "7.8.12",
|
|
49
|
+
"@ledgerhq/hw-transport": "6.35.7",
|
|
50
|
+
"@ledgerhq/hw-transport-webhid": "6.36.0",
|
|
51
|
+
"@ledgerhq/iframe-provider": "^0.4.3"
|
|
64
52
|
},
|
|
65
53
|
"peerDependencies": {
|
|
66
54
|
"@tanstack/react-query": "^5.29.0",
|
package/dist/hid/signer.d.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type Eth from '@ledgerhq/hw-app-eth';
|
|
2
|
-
import { LoadConfig, ResolutionConfig } from '@ledgerhq/hw-app-eth/lib/services/types';
|
|
3
|
-
import { EIP712Message } from '@ledgerhq/types-live';
|
|
4
|
-
import { JsonRpcSigner, TransactionRequest } from '@ethersproject/providers';
|
|
5
|
-
import { Signer, TypedDataDomain, TypedDataField, TypedDataSigner } from '@ethersproject/abstract-signer';
|
|
6
|
-
import { UnsignedTransaction } from '@ethersproject/transactions';
|
|
7
|
-
import { Bytes } from '@ethersproject/bytes';
|
|
8
|
-
import type { LedgerHQProvider } from './provider';
|
|
9
|
-
import type { UnsignedTransactionStrict } from './types';
|
|
10
|
-
export declare class LedgerHQSigner extends Signer implements TypedDataSigner {
|
|
11
|
-
readonly provider: LedgerHQProvider;
|
|
12
|
-
get path(): string;
|
|
13
|
-
_index: number;
|
|
14
|
-
_address: string;
|
|
15
|
-
constructor(provider: LedgerHQProvider);
|
|
16
|
-
withEthApp<T>(callback: (eth: Eth) => T): Promise<T>;
|
|
17
|
-
getAddress(): Promise<string>;
|
|
18
|
-
signMessage(message: Bytes | string): Promise<string>;
|
|
19
|
-
populateUnsigned(transaction: UnsignedTransactionStrict): Promise<UnsignedTransaction>;
|
|
20
|
-
signTransaction(transaction: TransactionRequest, loadConfig?: LoadConfig, resolutionConfig?: ResolutionConfig): Promise<string>;
|
|
21
|
-
connect(provider: LedgerHQProvider): JsonRpcSigner;
|
|
22
|
-
connectUnchecked(): JsonRpcSigner;
|
|
23
|
-
sendUncheckedTransaction(): Promise<string>;
|
|
24
|
-
unlock(): Promise<boolean>;
|
|
25
|
-
_legacySignMessage(_message: Bytes | string): Promise<string>;
|
|
26
|
-
_signTypedData(domain: TypedDataDomain, _types: Record<string, Array<TypedDataField>>, value: Record<string, any>): Promise<string>;
|
|
27
|
-
__signEIP712Message(data: EIP712Message): Promise<string>;
|
|
28
|
-
}
|
|
29
|
-
//# sourceMappingURL=signer.d.ts.map
|
package/dist/hid/signer.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"signer.d.ts","sourceRoot":"","sources":["../../src/hid/signer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EACL,UAAU,EACV,gBAAgB,EACjB,MAAM,yCAAyC,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAsB,MAAM,sBAAsB,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAE7E,OAAO,EACL,MAAM,EACN,eAAe,EACf,cAAc,EACd,eAAe,EAChB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAa,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAE7E,OAAO,EAAE,KAAK,EAA0B,MAAM,sBAAsB,CAAC;AAMrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAC;AAIzD,qBAAa,cAAe,SAAQ,MAAO,YAAW,eAAe;IACnE,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;IAEpC,IAAI,IAAI,WAKP;IAGD,MAAM,SAAK;IACX,QAAQ,SAAM;gBAEF,QAAQ,EAAE,gBAAgB;IAKhC,UAAU,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAiBpD,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAM7B,WAAW,CAAC,OAAO,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAiBrD,gBAAgB,CACpB,WAAW,EAAE,yBAAyB,GACrC,OAAO,CAAC,mBAAmB,CAAC;IAczB,eAAe,CACnB,WAAW,EAAE,kBAAkB,EAC/B,UAAU,GAAE,UAAe,EAC3B,gBAAgB,GAAE,gBAAqB,GACtC,OAAO,CAAC,MAAM,CAAC;IAuBlB,OAAO,CAAC,QAAQ,EAAE,gBAAgB,GAAG,aAAa;IAKlD,gBAAgB,IAAI,aAAa;IAKjC,wBAAwB,IAAI,OAAO,CAAC,MAAM,CAAC;IAKrC,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAK1B,kBAAkB,CAAC,QAAQ,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK7D,cAAc,CAClB,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC,EAC7C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACzB,OAAO,CAAC,MAAM,CAAC;IA8BZ,mBAAmB,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;CAOhE"}
|
package/dist/hid/signer.js
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { LS_KEY_DERIVATION_PATH } from "./constants.js";
|
|
2
|
-
import { checkError, convertToUnsigned, toNumber } from "./helpers.js";
|
|
3
|
-
import { hexlify, joinSignature } from "@ethersproject/bytes";
|
|
4
|
-
import { BigNumber } from "@ethersproject/bignumber";
|
|
5
|
-
import { Signer } from "@ethersproject/abstract-signer";
|
|
6
|
-
import { serialize } from "@ethersproject/transactions";
|
|
7
|
-
import { toUtf8Bytes } from "@ethersproject/strings";
|
|
8
|
-
import { _TypedDataEncoder } from "@ethersproject/hash";
|
|
9
|
-
|
|
10
|
-
//#region src/hid/signer.ts
|
|
11
|
-
const defaultPath = "m/44'/60'/0'/0/0";
|
|
12
|
-
var LedgerHQSigner = class LedgerHQSigner extends Signer {
|
|
13
|
-
get path() {
|
|
14
|
-
if (typeof window !== "undefined") return window.localStorage.getItem(LS_KEY_DERIVATION_PATH) || defaultPath;
|
|
15
|
-
return defaultPath;
|
|
16
|
-
}
|
|
17
|
-
constructor(provider) {
|
|
18
|
-
super();
|
|
19
|
-
this._index = 0;
|
|
20
|
-
this._address = "";
|
|
21
|
-
this.provider = provider;
|
|
22
|
-
}
|
|
23
|
-
async withEthApp(callback) {
|
|
24
|
-
const transport = await this.provider.getTransport();
|
|
25
|
-
try {
|
|
26
|
-
const { default: Eth } = await import("@ledgerhq/hw-app-eth");
|
|
27
|
-
const eth = new Eth(transport);
|
|
28
|
-
await eth.getAppConfiguration();
|
|
29
|
-
return await callback(eth);
|
|
30
|
-
} catch (error) {
|
|
31
|
-
return checkError(error);
|
|
32
|
-
} finally {
|
|
33
|
-
await transport.close();
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
async getAddress() {
|
|
37
|
-
const account = await this.withEthApp((eth) => eth.getAddress(this.path));
|
|
38
|
-
this._address = this.provider.formatter.address(account.address);
|
|
39
|
-
return this._address;
|
|
40
|
-
}
|
|
41
|
-
async signMessage(message) {
|
|
42
|
-
if (typeof message === "string") message = toUtf8Bytes(message);
|
|
43
|
-
const messageHex = hexlify(message).substring(2);
|
|
44
|
-
const sig = await this.withEthApp((eth) => eth.signPersonalMessage(this.path, messageHex));
|
|
45
|
-
sig.r = `0x${sig.r}`;
|
|
46
|
-
sig.s = `0x${sig.s}`;
|
|
47
|
-
return joinSignature(sig);
|
|
48
|
-
}
|
|
49
|
-
async populateUnsigned(transaction) {
|
|
50
|
-
const populated = await this.populateTransaction(transaction);
|
|
51
|
-
const nonce = toNumber(populated.nonce);
|
|
52
|
-
if (populated.type === 0) {
|
|
53
|
-
const { gasLimit, type, chainId, gasPrice, value, data, to } = populated;
|
|
54
|
-
return {
|
|
55
|
-
gasLimit,
|
|
56
|
-
type,
|
|
57
|
-
chainId,
|
|
58
|
-
gasPrice,
|
|
59
|
-
nonce,
|
|
60
|
-
value,
|
|
61
|
-
data,
|
|
62
|
-
to
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
return {
|
|
66
|
-
...populated,
|
|
67
|
-
nonce
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
async signTransaction(transaction, loadConfig = {}, resolutionConfig = {}) {
|
|
71
|
-
const unsignedTx = await convertToUnsigned(transaction);
|
|
72
|
-
const populatedTx = await this.populateUnsigned(unsignedTx);
|
|
73
|
-
const serializedTx = serialize(populatedTx).substring(2);
|
|
74
|
-
const { ledgerService } = await import("@ledgerhq/hw-app-eth");
|
|
75
|
-
const resolution = await ledgerService.resolveTransaction(serializedTx, loadConfig, resolutionConfig);
|
|
76
|
-
const sig = await this.withEthApp((eth) => eth.signTransaction(this.path, serializedTx, resolution));
|
|
77
|
-
return serialize(populatedTx, {
|
|
78
|
-
v: BigNumber.from(`0x${sig.v}`).toNumber(),
|
|
79
|
-
r: `0x${sig.r}`,
|
|
80
|
-
s: `0x${sig.s}`
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
connect(provider) {
|
|
84
|
-
return new LedgerHQSigner(provider);
|
|
85
|
-
}
|
|
86
|
-
connectUnchecked() {
|
|
87
|
-
throw new Error("method is not implemented");
|
|
88
|
-
}
|
|
89
|
-
sendUncheckedTransaction() {
|
|
90
|
-
throw new Error("method is not implemented");
|
|
91
|
-
}
|
|
92
|
-
async unlock() {
|
|
93
|
-
throw new Error("method is not implemented");
|
|
94
|
-
}
|
|
95
|
-
async _legacySignMessage(_message) {
|
|
96
|
-
throw new Error("method is not implemented");
|
|
97
|
-
}
|
|
98
|
-
async _signTypedData(domain, _types, value) {
|
|
99
|
-
const types = { ..._types };
|
|
100
|
-
delete types.EIP712Domain;
|
|
101
|
-
const encoder = new _TypedDataEncoder(types);
|
|
102
|
-
const data = {
|
|
103
|
-
domain: {
|
|
104
|
-
name: domain.name,
|
|
105
|
-
verifyingContract: domain.verifyingContract,
|
|
106
|
-
version: domain.version,
|
|
107
|
-
chainId: domain.chainId ? parseInt(domain.chainId.toString()) : void 0
|
|
108
|
-
},
|
|
109
|
-
types: {
|
|
110
|
-
...types,
|
|
111
|
-
EIP712Domain: [
|
|
112
|
-
{
|
|
113
|
-
name: "name",
|
|
114
|
-
type: "string"
|
|
115
|
-
},
|
|
116
|
-
{
|
|
117
|
-
name: "version",
|
|
118
|
-
type: "string"
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
name: "chainId",
|
|
122
|
-
type: "uint256"
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
name: "verifyingContract",
|
|
126
|
-
type: "address"
|
|
127
|
-
}
|
|
128
|
-
]
|
|
129
|
-
},
|
|
130
|
-
primaryType: encoder.primaryType,
|
|
131
|
-
message: value
|
|
132
|
-
};
|
|
133
|
-
return this.__signEIP712Message(data);
|
|
134
|
-
}
|
|
135
|
-
async __signEIP712Message(data) {
|
|
136
|
-
const { r, s, v } = await this.withEthApp((eth) => eth.signEIP712Message(this.path, data));
|
|
137
|
-
return joinSignature({
|
|
138
|
-
r: `0x${r}`,
|
|
139
|
-
s: `0x${s}`,
|
|
140
|
-
v
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
//#endregion
|
|
146
|
-
export { LedgerHQSigner };
|
package/dist/hid/types.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { BigNumberish } from '@ethersproject/bignumber';
|
|
2
|
-
import { TransactionRequest } from '@ethersproject/providers';
|
|
3
|
-
import { UnsignedTransaction } from '@ethersproject/transactions';
|
|
4
|
-
export type UnsignedTransactionStrict = Omit<UnsignedTransaction, 'type'> & {
|
|
5
|
-
type?: number;
|
|
6
|
-
};
|
|
7
|
-
export type TransactionRequestExtended = TransactionRequest & {
|
|
8
|
-
gas?: BigNumberish;
|
|
9
|
-
};
|
|
10
|
-
//# sourceMappingURL=types.d.ts.map
|
package/dist/hid/types.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/hid/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAElE,MAAM,MAAM,yBAAyB,GAAG,IAAI,CAAC,mBAAmB,EAAE,MAAM,CAAC,GAAG;IAC1E,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG,kBAAkB,GAAG;IAC5D,GAAG,CAAC,EAAE,YAAY,CAAC;CACpB,CAAC"}
|