@reef-knot/ledger-connector 5.2.0-alpha.3 → 5.2.0-alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/__tests__/ssr.test.d.ts +2 -0
  2. package/dist/__tests__/ssr.test.d.ts.map +1 -0
  3. package/dist/hid/__tests__/connector.test.d.ts +2 -0
  4. package/dist/hid/__tests__/connector.test.d.ts.map +1 -0
  5. package/dist/hid/__tests__/fixtures.d.ts +86 -0
  6. package/dist/hid/__tests__/fixtures.d.ts.map +1 -0
  7. package/dist/hid/__tests__/helpers.test.d.ts +2 -0
  8. package/dist/hid/__tests__/helpers.test.d.ts.map +1 -0
  9. package/dist/hid/__tests__/provider.test.d.ts +2 -0
  10. package/dist/hid/__tests__/provider.test.d.ts.map +1 -0
  11. package/dist/hid/account.d.ts +8 -1
  12. package/dist/hid/account.d.ts.map +1 -1
  13. package/dist/hid/account.js +23 -3
  14. package/dist/hid/connector.d.ts +5 -2
  15. package/dist/hid/connector.d.ts.map +1 -1
  16. package/dist/hid/connector.js +78 -70
  17. package/dist/hid/helpers.d.ts.map +1 -1
  18. package/dist/hid/helpers.js +2 -1
  19. package/dist/hid/index.d.ts +1 -0
  20. package/dist/hid/index.d.ts.map +1 -1
  21. package/dist/hid/index.js +2 -1
  22. package/dist/hid/provider.d.ts +8 -5
  23. package/dist/hid/provider.d.ts.map +1 -1
  24. package/dist/hid/provider.js +5 -3
  25. package/dist/iframe/__tests__/connector.test.d.ts +2 -0
  26. package/dist/iframe/__tests__/connector.test.d.ts.map +1 -0
  27. package/dist/iframe/__tests__/fake-ledger-live.d.ts +27 -0
  28. package/dist/iframe/__tests__/fake-ledger-live.d.ts.map +1 -0
  29. package/dist/iframe/__tests__/helpers.test.d.ts +2 -0
  30. package/dist/iframe/__tests__/helpers.test.d.ts.map +1 -0
  31. package/dist/iframe/connector.d.ts.map +1 -1
  32. package/dist/iframe/connector.js +104 -89
  33. package/dist/index.js +2 -1
  34. package/package.json +5 -1
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ssr.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssr.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/ssr.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=connector.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connector.test.d.ts","sourceRoot":"","sources":["../../../src/hid/__tests__/connector.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,86 @@
1
+ import { RecordStore } from '@ledgerhq/hw-transport-mocker';
2
+ import type { Address } from 'viem';
3
+ import type { LedgerHQProvider } from '../provider';
4
+ export declare const DERIVATION_PATH = "m/44'/60'/0'/0/0";
5
+ export declare const DERIVATION_PATH_B = "m/44'/60'/1'/0/0";
6
+ export declare const ADDRESS_A: Address;
7
+ export declare const ADDRESS_B: Address;
8
+ export declare const SIGNATURE_R: `0x${string}`;
9
+ export declare const SIGNATURE_S: `0x${string}`;
10
+ export declare const MESSAGE_SIGNATURE: string;
11
+ export declare const APP_CONFIG = "=> e006000000\n<= 010109009000";
12
+ export declare const DEVICE_LOCKED = "=> e006000000\n<= 5515";
13
+ export declare const getAddressExchange: (address: Address) => string;
14
+ export declare const getAddressExchangeForPathB: (address: Address) => string;
15
+ export declare const PERSONAL_SIGN_MESSAGE = "reef-knot";
16
+ export declare const PERSONAL_SIGN: string;
17
+ export declare const LEGACY_TX: {
18
+ readonly type: "legacy";
19
+ readonly chainId: 1;
20
+ readonly nonce: 0;
21
+ readonly gasPrice: 1000000000n;
22
+ readonly gas: 21000n;
23
+ readonly to: `0x${string}`;
24
+ readonly value: 1n;
25
+ };
26
+ export declare const SIGN_TX: string;
27
+ export declare const FILLED_LEGACY_TX: {
28
+ readonly gasPrice: 1200000000n;
29
+ readonly type: "legacy";
30
+ readonly chainId: 1;
31
+ readonly nonce: 0;
32
+ readonly gas: 21000n;
33
+ readonly to: `0x${string}`;
34
+ readonly value: 1n;
35
+ };
36
+ export declare const SIGN_TX_FILLED: string;
37
+ export declare const TX_1559: {
38
+ readonly chainId: 1;
39
+ readonly nonce: 0;
40
+ readonly maxPriorityFeePerGas: 1000000000n;
41
+ readonly maxFeePerGas: 2000000000n;
42
+ readonly gas: 21000n;
43
+ readonly to: `0x${string}`;
44
+ readonly value: 1n;
45
+ };
46
+ export declare const SIGN_TX_1559: string;
47
+ export declare const TX_2930: {
48
+ readonly chainId: 1;
49
+ readonly type: "eip2930";
50
+ readonly nonce: 0;
51
+ readonly gasPrice: 1000000000n;
52
+ readonly gas: 21000n;
53
+ readonly to: `0x${string}`;
54
+ readonly value: 1n;
55
+ readonly accessList: readonly [];
56
+ };
57
+ export declare const SIGN_TX_2930: string;
58
+ export declare const TYPED_DATA: {
59
+ domain: {
60
+ name: string;
61
+ version: string;
62
+ chainId: number;
63
+ };
64
+ types: {
65
+ EIP712Domain: {
66
+ name: string;
67
+ type: string;
68
+ }[];
69
+ Mail: {
70
+ name: string;
71
+ type: string;
72
+ }[];
73
+ };
74
+ primaryType: string;
75
+ message: {
76
+ contents: string;
77
+ };
78
+ };
79
+ export declare const EIP712_REJECT = "=> b001000000\n<= 6d00";
80
+ export declare const EIP712_HASHED: string;
81
+ export declare const injectReplayer: (provider: LedgerHQProvider, ...exchanges: string[]) => RecordStore;
82
+ export declare const injectReplayerWithDevice: (provider: LedgerHQProvider, device: HIDDevice, ...exchanges: string[]) => RecordStore;
83
+ export declare const stubHid: () => {
84
+ unplug: (device: HIDDevice) => void;
85
+ };
86
+ //# sourceMappingURL=fixtures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../../../src/hid/__tests__/fixtures.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,WAAW,EACZ,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAOpD,eAAO,MAAM,eAAe,qBAAqB,CAAC;AAClD,eAAO,MAAM,iBAAiB,qBAAqB,CAAC;AAEpD,eAAO,MAAM,SAAS,EAAE,OAAsD,CAAC;AAC/E,eAAO,MAAM,SAAS,EAAE,OAAsD,CAAC;AAE/E,eAAO,MAAM,WAAW,eAAkC,CAAC;AAC3D,eAAO,MAAM,WAAW,eAAkC,CAAC;AAE3D,eAAO,MAAM,iBAAiB,QAA6C,CAAC;AAW5E,eAAO,MAAM,UAAU,mCAAmC,CAAC;AAG3D,eAAO,MAAM,aAAa,2BAA2B,CAAC;AAGtD,eAAO,MAAM,kBAAkB,GAAI,SAAS,OAAO,WAEa,CAAC;AAEjE,eAAO,MAAM,0BAA0B,GAAI,SAAS,OAAO,WAEK,CAAC;AAEjE,eAAO,MAAM,qBAAqB,cAAc,CAAC;AACjD,eAAO,MAAM,aAAa,QAEO,CAAC;AAIlC,eAAO,MAAM,SAAS;;;;;;;;CAQZ,CAAC;AACX,eAAO,MAAM,OAAO,QAEa,CAAC;AAIlC,eAAO,MAAM,gBAAgB;;;;;;;;CAGnB,CAAC;AACX,eAAO,MAAM,cAAc,QAEM,CAAC;AAGlC,eAAO,MAAM,OAAO;;;;;;;;CAQV,CAAC;AACX,eAAO,MAAM,YAAY,QAEQ,CAAC;AAGlC,eAAO,MAAM,OAAO;;;;;;;;;CASV,CAAC;AACX,eAAO,MAAM,YAAY,QAEQ,CAAC;AAElC,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;CAYtB,CAAC;AAIF,eAAO,MAAM,aAAa,2BAA2B,CAAC;AAEtD,eAAO,MAAM,aAAa,QAEO,CAAC;AAKlC,eAAO,MAAM,cAAc,GACzB,UAAU,gBAAgB,EAC1B,GAAG,WAAW,MAAM,EAAE,gBAOvB,CAAC;AAIF,eAAO,MAAM,wBAAwB,GACnC,UAAU,gBAAgB,EAC1B,QAAQ,SAAS,EACjB,GAAG,WAAW,MAAM,EAAE,gBAQvB,CAAC;AAIF,eAAO,MAAM,OAAO;qBAaC,SAAS;CAK7B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=helpers.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.test.d.ts","sourceRoot":"","sources":["../../../src/hid/__tests__/helpers.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=provider.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.test.d.ts","sourceRoot":"","sources":["../../../src/hid/__tests__/provider.test.ts"],"names":[],"mappings":""}
@@ -1,5 +1,12 @@
1
1
  import type Eth from '@ledgerhq/hw-app-eth';
2
2
  import { type Address, type LocalAccount } from 'viem';
3
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;
4
+ export type LedgerAccountOptions = {
5
+ forceClearSign?: boolean;
6
+ };
7
+ export declare class ClearSignUnavailableError extends Error {
8
+ name: string;
9
+ constructor();
10
+ }
11
+ export declare const createLedgerAccount: (address: Address, path: string, withEthApp: WithEthApp, { forceClearSign }?: LedgerAccountOptions) => LocalAccount;
5
12
  //# sourceMappingURL=account.d.ts.map
@@ -1 +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"}
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,EAEZ,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;AAEhB,MAAM,MAAM,oBAAoB,GAAG;IAEjC,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAMF,qBAAa,yBAA0B,SAAQ,KAAK;IACzC,IAAI,SAA+B;;CAK7C;AA4BD,eAAO,MAAM,mBAAmB,GAC9B,SAAS,OAAO,EAChB,MAAM,MAAM,EACZ,YAAY,UAAU,EACtB,qBAA4B,oBAAyB,KACpD,YA4FC,CAAC"}
@@ -1,6 +1,19 @@
1
1
  import { bytesToHex, getTypesForEIP712Domain, hashStruct, serializeSignature, serializeTransaction, stringToHex } from "viem";
2
2
  import { toAccount } from "viem/accounts";
3
3
  //#region src/hid/account.ts
4
+ var ClearSignUnavailableError = class extends Error {
5
+ constructor() {
6
+ super("The transaction cannot be clear signed on the device.");
7
+ this.name = "ClearSignUnavailableError";
8
+ }
9
+ };
10
+ const hasCalldata = (data) => !!data && data !== "0x";
11
+ const isEmptyResolution = (resolution) => !resolution || [
12
+ resolution.erc20Tokens,
13
+ resolution.nfts,
14
+ resolution.externalPlugin,
15
+ resolution.plugin
16
+ ].every((descriptors) => descriptors.length === 0);
4
17
  const RESOLUTION_CONFIG = {
5
18
  erc20: true,
6
19
  externalPlugins: true,
@@ -12,11 +25,18 @@ const toSignatureHex = ({ r, s, v }) => serializeSignature({
12
25
  s: `0x${s}`,
13
26
  v: BigInt(v)
14
27
  });
15
- const createLedgerAccount = (address, path, withEthApp) => toAccount({
28
+ const createLedgerAccount = (address, path, withEthApp, { forceClearSign = false } = {}) => toAccount({
16
29
  address,
17
30
  async signTransaction(transaction, { serializer = serializeTransaction } = {}) {
18
31
  const unsignedRawTx = (await serializer(transaction)).slice(2);
19
- const { r, s, v } = await withEthApp((eth) => eth.clearSignTransaction(path, unsignedRawTx, RESOLUTION_CONFIG, true));
32
+ const { r, s, v } = await withEthApp(async (eth) => {
33
+ const resolution = await eth.ledgerService.resolveTransaction(unsignedRawTx, eth.loadConfig, RESOLUTION_CONFIG).catch((error) => {
34
+ if (forceClearSign) throw error;
35
+ return null;
36
+ });
37
+ if (forceClearSign && hasCalldata(transaction.data) && isEmptyResolution(resolution)) throw new ClearSignUnavailableError();
38
+ return eth.signTransaction(path, unsignedRawTx, resolution);
39
+ });
20
40
  return serializer(transaction, {
21
41
  r: `0x${r}`,
22
42
  s: `0x${s}`,
@@ -53,4 +73,4 @@ const createLedgerAccount = (address, path, withEthApp) => toAccount({
53
73
  }
54
74
  });
55
75
  //#endregion
56
- export { createLedgerAccount };
76
+ export { ClearSignUnavailableError, createLedgerAccount };
@@ -2,11 +2,14 @@ import { Chain } from 'wagmi/chains';
2
2
  import type { LedgerHQProvider } from './provider';
3
3
  export declare const idLedgerHid = "ledgerHID";
4
4
  export declare const name = "Ledger";
5
- export declare function ledgerHIDConnector({ rpc, defaultChain, }: {
5
+ type LedgerHIDConnectorOptions = {
6
6
  rpc: Record<number, string>;
7
7
  defaultChain: Chain;
8
- }): import("wagmi").CreateConnectorFn<LedgerHQProvider, Record<string, unknown>, Record<string, unknown>>;
8
+ forceClearSign?: boolean;
9
+ };
10
+ export declare function ledgerHIDConnector({ rpc, defaultChain, forceClearSign, }: LedgerHIDConnectorOptions): import("wagmi").CreateConnectorFn<LedgerHQProvider, Record<string, unknown>, Record<string, unknown>>;
9
11
  export declare namespace ledgerHIDConnector {
10
12
  var type: "ledgerHID";
11
13
  }
14
+ export {};
12
15
  //# sourceMappingURL=connector.d.ts.map
@@ -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;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,yGAiIA;yBAvIe,kBAAkB"}
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;AAE7B,KAAK,yBAAyB,GAAG;IAC/B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,YAAY,EAAE,KAAK,CAAC;IAGpB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAGF,wBAAgB,kBAAkB,CAAC,EACjC,GAAG,EACH,YAAY,EACZ,cAAc,GACf,EAAE,yBAAyB,yGAqI3B;yBAzIe,kBAAkB"}
@@ -5,86 +5,94 @@ import { SwitchChainError } from "viem";
5
5
  const idLedgerHid = "ledgerHID";
6
6
  const name = "Ledger";
7
7
  ledgerHIDConnector.type = "ledgerHID";
8
- function ledgerHIDConnector({ rpc, defaultChain }) {
8
+ function ledgerHIDConnector({ rpc, defaultChain, forceClearSign }) {
9
9
  const providers = {};
10
10
  let currentChainId = restoreLedgerChainId();
11
- return createConnector(({ chains, emitter }) => ({
12
- id: idLedgerHid,
13
- name,
14
- type: ledgerHIDConnector.type,
15
- async getProvider({ chainId } = {}) {
16
- const chain = chains.find((x) => x.id === (chainId ?? currentChainId)) ?? defaultChain;
17
- if (!providers[chain.id]) {
18
- const { LedgerHQProvider } = await import("./provider.js");
19
- providers[chain.id] = new LedgerHQProvider({
20
- chain,
21
- rpcUrl: rpc?.[chain.id],
22
- supportedChainIds: chains.map((x) => x.id)
23
- });
24
- }
25
- return providers[chain.id];
26
- },
27
- async connect({ chainId, withCapabilities = false } = {}) {
28
- try {
29
- currentChainId = chainId ?? currentChainId;
30
- const provider = await this.getProvider({ chainId: currentChainId });
31
- provider.on("disconnect", this.onDisconnect);
32
- const account = await provider.enable();
33
- const connectedChainId = await this.getChainId();
34
- currentChainId = connectedChainId;
35
- saveLedgerChainId(connectedChainId);
36
- return {
37
- accounts: withCapabilities ? [{
38
- address: account,
39
- capabilities: {}
40
- }] : [account],
41
- chainId: connectedChainId
42
- };
43
- } catch (error) {
44
- currentChainId = restoreLedgerChainId();
45
- return checkError(error);
46
- }
47
- },
48
- async disconnect() {
11
+ return createConnector(({ chains, emitter }) => {
12
+ const forgetSession = () => {
49
13
  Object.values(providers).forEach((provider) => {
50
- provider.removeListener("disconnect", this.onDisconnect);
14
+ provider.removeListener("disconnect", onDisconnect);
51
15
  provider.resetAccount();
52
16
  });
53
17
  currentChainId = void 0;
54
18
  clearLedgerDerivationPath();
55
19
  clearLedgerChainId();
56
20
  clearLedgerAccount();
57
- },
58
- async getAccounts() {
59
- return [await (await this.getProvider()).getAddress()];
60
- },
61
- async getChainId() {
62
- return (await this.getProvider()).chain.id;
63
- },
64
- async isAuthorized() {
65
- try {
66
- if (!await this.getProvider()) throw new ConnectorNotFoundError();
67
- const [account] = await this.getAccounts();
68
- return !!account;
69
- } catch (e) {
70
- console.error(e);
71
- return false;
72
- }
73
- },
74
- async switchChain({ chainId }) {
75
- const chain = chains.find((x) => x.id === chainId);
76
- if (!chain) throw new SwitchChainError(new ChainNotConfiguredError());
77
- currentChainId = chainId;
78
- saveLedgerChainId(chainId);
79
- emitter.emit("change", { chainId });
80
- return chain;
81
- },
82
- onDisconnect() {
21
+ };
22
+ const onDisconnect = () => {
23
+ forgetSession();
83
24
  emitter.emit("disconnect");
84
- },
85
- onAccountsChanged() {},
86
- onChainChanged() {}
87
- }));
25
+ };
26
+ return {
27
+ id: idLedgerHid,
28
+ name,
29
+ type: ledgerHIDConnector.type,
30
+ async getProvider({ chainId } = {}) {
31
+ const chain = chains.find((x) => x.id === (chainId ?? currentChainId)) ?? defaultChain;
32
+ if (!providers[chain.id]) {
33
+ const { LedgerHQProvider } = await import("./provider.js");
34
+ providers[chain.id] = new LedgerHQProvider({
35
+ chain,
36
+ rpcUrl: rpc?.[chain.id],
37
+ supportedChainIds: chains.map((x) => x.id),
38
+ forceClearSign
39
+ });
40
+ }
41
+ return providers[chain.id];
42
+ },
43
+ async connect({ chainId, withCapabilities = false } = {}) {
44
+ try {
45
+ currentChainId = chainId ?? currentChainId;
46
+ const provider = await this.getProvider({ chainId: currentChainId });
47
+ provider.on("disconnect", onDisconnect);
48
+ const account = await provider.enable();
49
+ const connectedChainId = await this.getChainId();
50
+ currentChainId = connectedChainId;
51
+ saveLedgerChainId(connectedChainId);
52
+ return {
53
+ accounts: withCapabilities ? [{
54
+ address: account,
55
+ capabilities: {}
56
+ }] : [account],
57
+ chainId: connectedChainId
58
+ };
59
+ } catch (error) {
60
+ currentChainId = restoreLedgerChainId();
61
+ return checkError(error);
62
+ }
63
+ },
64
+ async disconnect() {
65
+ forgetSession();
66
+ },
67
+ async getAccounts() {
68
+ return [await (await this.getProvider()).getAddress()];
69
+ },
70
+ async getChainId() {
71
+ return (await this.getProvider()).chain.id;
72
+ },
73
+ async isAuthorized() {
74
+ try {
75
+ if (!await this.getProvider()) throw new ConnectorNotFoundError();
76
+ const [account] = await this.getAccounts();
77
+ return !!account;
78
+ } catch (e) {
79
+ console.error(e);
80
+ return false;
81
+ }
82
+ },
83
+ async switchChain({ chainId }) {
84
+ const chain = chains.find((x) => x.id === chainId);
85
+ if (!chain) throw new SwitchChainError(new ChainNotConfiguredError());
86
+ currentChainId = chainId;
87
+ saveLedgerChainId(chainId);
88
+ emitter.emit("change", { chainId });
89
+ return chain;
90
+ },
91
+ onDisconnect,
92
+ onAccountsChanged() {},
93
+ onChainChanged() {}
94
+ };
95
+ });
88
96
  }
89
97
  //#endregion
90
98
  export { idLedgerHid, ledgerHIDConnector, name };
@@ -1 +1 @@
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"}
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,YAIrC,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"}
@@ -9,7 +9,8 @@ const checkError = (error) => {
9
9
  throw error;
10
10
  };
11
11
  const clearLedgerDerivationPath = () => {
12
- window?.localStorage.removeItem(LS_KEY_DERIVATION_PATH);
12
+ if (typeof window === "undefined") return;
13
+ window.localStorage.removeItem(LS_KEY_DERIVATION_PATH);
13
14
  };
14
15
  const saveLedgerChainId = (chainId) => {
15
16
  if (typeof window === "undefined") return;
@@ -1,3 +1,4 @@
1
1
  export { idLedgerHid, ledgerHIDConnector } from './connector';
2
2
  export { LS_KEY_DERIVATION_PATH } from './constants';
3
+ export { ClearSignUnavailableError } from './account';
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hid/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hid/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC"}
package/dist/hid/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  import { LS_KEY_DERIVATION_PATH } from "./constants.js";
2
2
  import { idLedgerHid, ledgerHIDConnector } from "./connector.js";
3
- export { LS_KEY_DERIVATION_PATH, idLedgerHid, ledgerHIDConnector };
3
+ import { ClearSignUnavailableError } from "./account.js";
4
+ export { ClearSignUnavailableError, LS_KEY_DERIVATION_PATH, idLedgerHid, ledgerHIDConnector };
@@ -7,6 +7,12 @@ type RequestArguments = {
7
7
  };
8
8
  type ProviderEvent = 'disconnect';
9
9
  type Listener = () => void;
10
+ type LedgerHQProviderOptions = {
11
+ chain: Chain;
12
+ rpcUrl?: string;
13
+ supportedChainIds?: number[];
14
+ forceClearSign?: boolean;
15
+ };
10
16
  export declare class LedgerHQProvider {
11
17
  readonly chain: Chain;
12
18
  device?: HIDDevice;
@@ -18,11 +24,8 @@ export declare class LedgerHQProvider {
18
24
  private walletClient?;
19
25
  private listeners;
20
26
  private readonly supportedChainIds;
21
- constructor({ chain, rpcUrl, supportedChainIds, }: {
22
- chain: Chain;
23
- rpcUrl?: string;
24
- supportedChainIds?: number[];
25
- });
27
+ private readonly forceClearSign;
28
+ constructor({ chain, rpcUrl, supportedChainIds, forceClearSign, }: LedgerHQProviderOptions);
26
29
  on(event: ProviderEvent, listener: Listener): void;
27
30
  removeListener(event: ProviderEvent, listener: Listener): void;
28
31
  emit(event: ProviderEvent): void;
@@ -1 +1 @@
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;IAItE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAW;gBAEjC,EACV,KAAK,EACL,MAAM,EACN,iBAAiB,GAClB,EAAE;QACD,KAAK,EAAE,KAAK,CAAC;QACb,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;KAC9B;IAgBD,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;CA2E3E"}
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,KAAK,uBAAuB,GAAG;IAC7B,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,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;IAItE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAW;IAE7C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;gBAE7B,EACV,KAAK,EACL,MAAM,EACN,iBAAiB,EACjB,cAAsB,GACvB,EAAE,uBAAuB;IAiB1B,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;YAuC3B,WAAW;IAoBnB,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC;IAOpC,OAAO,CAAC,eAAe;YAQT,eAAe;YAUf,eAAe;IA4EvB,OAAO,CAAC,EAAE,MAAM,EAAE,MAAW,EAAE,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;CA2E3E"}
@@ -26,11 +26,12 @@ const withDeviceSession = (fn) => {
26
26
  return result;
27
27
  };
28
28
  var LedgerHQProvider = class {
29
- constructor({ chain, rpcUrl, supportedChainIds }) {
29
+ constructor({ chain, rpcUrl, supportedChainIds, forceClearSign = false }) {
30
30
  this.listeners = {};
31
31
  this.chain = chain;
32
32
  this.rpcUrl = rpcUrl;
33
33
  this.supportedChainIds = supportedChainIds ?? [chain.id];
34
+ this.forceClearSign = forceClearSign;
34
35
  if (!rpcUrl) console.warn(`[reef-knot] No RPC URL configured for chain ${chain.id} (${chain.name}), falling back to its default public RPC endpoint`);
35
36
  }
36
37
  on(event, listener) {
@@ -118,7 +119,7 @@ var LedgerHQProvider = class {
118
119
  }
119
120
  return cb(eth);
120
121
  });
121
- this.account = createLedgerAccount(address, path, withVerifyingEthApp);
122
+ this.account = createLedgerAccount(address, path, withVerifyingEthApp, { forceClearSign: this.forceClearSign });
122
123
  this.accountPath = path;
123
124
  this.walletClient = void 0;
124
125
  }
@@ -169,7 +170,8 @@ var LedgerHQProvider = class {
169
170
  const walletClient = await this.getWalletClient();
170
171
  const account = walletClient.account;
171
172
  if (transaction.from && (!isAddress(transaction.from) || !isAddressEqual(transaction.from, account.address))) throw new Error("from address mismatch");
172
- const type = transaction.type ? TX_TYPES[transaction.type] : void 0;
173
+ const type = transaction.type != null ? TX_TYPES[transaction.type] : void 0;
174
+ if (transaction.type != null && !type) throw new Error(`Unsupported transaction type ${transaction.type}`);
173
175
  const request = {
174
176
  account,
175
177
  chain: this.chain,
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=connector.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connector.test.d.ts","sourceRoot":"","sources":["../../../src/iframe/__tests__/connector.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,27 @@
1
+ import type { MinimalEventSourceInterface, MinimalEventTargetInterface } from '@ledgerhq/iframe-provider';
2
+ type JsonRpcRequest = {
3
+ jsonrpc: '2.0';
4
+ id: number;
5
+ method: string;
6
+ params?: unknown[];
7
+ };
8
+ export type HostHandlers = Record<string, (params: unknown[]) => unknown>;
9
+ export declare class HostRpcError extends Error {
10
+ readonly code: number;
11
+ constructor(code: number, message: string);
12
+ }
13
+ export declare class FakeLedgerLive implements MinimalEventSourceInterface, MinimalEventTargetInterface {
14
+ private readonly handlers;
15
+ readonly calls: {
16
+ method: string;
17
+ params: unknown[];
18
+ }[];
19
+ private listeners;
20
+ constructor(handlers: HostHandlers);
21
+ addEventListener(_type: 'message', listener: (event: MessageEvent) => void): void;
22
+ postMessage(message: JsonRpcRequest): void;
23
+ emitEvent(method: string, params: unknown[]): void;
24
+ private dispatch;
25
+ }
26
+ export {};
27
+ //# sourceMappingURL=fake-ledger-live.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fake-ledger-live.d.ts","sourceRoot":"","sources":["../../../src/iframe/__tests__/fake-ledger-live.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,2BAA2B,EAC3B,2BAA2B,EAC5B,MAAM,2BAA2B,CAAC;AAEnC,KAAK,cAAc,GAAG;IACpB,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,CAAC;AAG1E,qBAAa,YAAa,SAAQ,KAAK;aAEnB,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM,EAC5B,OAAO,EAAE,MAAM;CAIlB;AAKD,qBAAa,cACX,YAAW,2BAA2B,EAAE,2BAA2B;IAMvD,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAJrC,QAAQ,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,EAAE,CAAA;KAAE,EAAE,CAAM;IAE7D,OAAO,CAAC,SAAS,CAA4C;gBAEhC,QAAQ,EAAE,YAAY;IAEnD,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI;IAI1E,WAAW,CAAC,OAAO,EAAE,cAAc;IA0BnC,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE;IAI3C,OAAO,CAAC,QAAQ;CAKjB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=helpers.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.test.d.ts","sourceRoot":"","sources":["../../../src/iframe/__tests__/helpers.test.ts"],"names":[],"mappings":""}
@@ -1 +1 @@
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"}
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,6GAmKzB;yBAtKe,mBAAmB"}
@@ -6,99 +6,114 @@ const name = "Ledger Live";
6
6
  ledgerLiveConnector.type = "ledgerLive";
7
7
  function ledgerLiveConnector({ options, defaultChain }) {
8
8
  const providers = {};
9
- return createConnector(({ chains, emitter }) => ({
10
- id: idLedgerLive,
11
- name,
12
- type: ledgerLiveConnector.type,
13
- async getProvider({ chainId } = {}) {
14
- const chain = chains.find((x) => x.id === chainId) ?? defaultChain;
15
- if (!providers[chain.id]) {
16
- const { LedgerIFrameProvider } = await import("./provider.js");
17
- providers[chain.id] = new LedgerIFrameProvider(options);
9
+ return createConnector(({ chains, emitter }) => {
10
+ const handlers = {
11
+ onAccountsChanged: (accounts) => {
12
+ if (accounts.length === 0 || !accounts[0]) {
13
+ emitter.emit("disconnect");
14
+ handlers.detach();
15
+ } else emitter.emit("change", { accounts });
16
+ },
17
+ onChainChanged: (chainId) => {
18
+ emitter.emit("change", { chainId: Number(chainId) });
19
+ },
20
+ detach: () => {
21
+ Object.values(providers).forEach((provider) => {
22
+ provider.removeListener("accountsChanged", handlers.onAccountsChanged);
23
+ provider.removeListener("chainChanged", handlers.onChainChanged);
24
+ });
18
25
  }
19
- return providers[chain.id];
20
- },
21
- async connect({ chainId, withCapabilities } = {}) {
22
- try {
23
- const provider = await this.getProvider();
24
- provider.on("accountsChanged", this.onAccountsChanged);
25
- provider.on("chainChanged", this.onChainChanged);
26
- emitter.emit("message", { type: "connecting" });
27
- const accounts = await this.getAccounts();
28
- let currentChainId = await this.getChainId();
29
- if (chainId && currentChainId !== chainId && this.switchChain) currentChainId = (await this.switchChain({ chainId })).id;
30
- return {
31
- accounts: withCapabilities ? accounts.map((address) => ({
32
- address,
33
- capabilities: {}
34
- })) : accounts,
35
- chainId: currentChainId
36
- };
37
- } catch (error) {
38
- if (error instanceof Error) {
39
- if (error.code === 4001) throw new UserRejectedRequestError(error);
40
- throw new ResourceUnavailableRpcError(error);
26
+ };
27
+ return {
28
+ id: idLedgerLive,
29
+ name,
30
+ type: ledgerLiveConnector.type,
31
+ async getProvider({ chainId } = {}) {
32
+ const chain = chains.find((x) => x.id === chainId) ?? defaultChain;
33
+ if (!providers[chain.id]) {
34
+ const { LedgerIFrameProvider } = await import("./provider.js");
35
+ providers[chain.id] = new LedgerIFrameProvider(options);
41
36
  }
42
- throw error;
43
- }
44
- },
45
- async switchChain({ chainId }) {
46
- const provider = await this.getProvider();
47
- const id = numberToHex(chainId);
48
- try {
49
- await provider.send("wallet_switchEthereumChain", [{ chainId: id }]);
50
- return chains.find((x) => x.id === chainId) ?? {
51
- id: chainId,
52
- name: `Chain ${id}`,
53
- network: `${id}`,
54
- nativeCurrency: {
55
- decimals: 18,
56
- name: "Ether",
57
- symbol: "ETH"
58
- },
59
- rpcUrls: {
60
- default: { http: [""] },
61
- public: { http: [""] }
37
+ return providers[chain.id];
38
+ },
39
+ async connect({ chainId, withCapabilities } = {}) {
40
+ try {
41
+ const provider = await this.getProvider();
42
+ handlers.detach();
43
+ provider.on("accountsChanged", handlers.onAccountsChanged);
44
+ provider.on("chainChanged", handlers.onChainChanged);
45
+ emitter.emit("message", { type: "connecting" });
46
+ const accounts = await this.getAccounts();
47
+ let currentChainId = await this.getChainId();
48
+ if (chainId && currentChainId !== chainId && this.switchChain) currentChainId = (await this.switchChain({ chainId })).id;
49
+ return {
50
+ accounts: withCapabilities ? accounts.map((address) => ({
51
+ address,
52
+ capabilities: {}
53
+ })) : accounts,
54
+ chainId: currentChainId
55
+ };
56
+ } catch (error) {
57
+ if (error instanceof Error) {
58
+ if (error?.code === 4001) throw new UserRejectedRequestError(error);
59
+ throw new ResourceUnavailableRpcError(error);
62
60
  }
63
- };
64
- } catch (error) {
65
- const message = typeof error === "string" ? error : error?.message;
66
- if (/user rejected request/i.test(message)) throw new UserRejectedRequestError(error);
67
- throw new SwitchChainNotSupportedError({ connector: { name } });
68
- }
69
- },
70
- async getAccounts() {
71
- return (await (await this.getProvider()).send("eth_requestAccounts")).map((account) => getAddress(account));
72
- },
73
- async getChainId() {
74
- const chainId = await (await this.getProvider()).send("eth_chainId");
75
- return Number(chainId);
76
- },
77
- async isAuthorized() {
78
- try {
79
- return !!(await (await this.getProvider()).send("eth_accounts"))[0];
80
- } catch {
81
- return false;
82
- }
83
- },
84
- onAccountsChanged(accounts) {
85
- if (accounts.length === 0 || !accounts[0]) {
61
+ throw error;
62
+ }
63
+ },
64
+ async switchChain({ chainId }) {
65
+ const provider = await this.getProvider();
66
+ const id = numberToHex(chainId);
67
+ try {
68
+ await provider.send("wallet_switchEthereumChain", [{ chainId: id }]);
69
+ return chains.find((x) => x.id === chainId) ?? {
70
+ id: chainId,
71
+ name: `Chain ${id}`,
72
+ network: `${id}`,
73
+ nativeCurrency: {
74
+ decimals: 18,
75
+ name: "Ether",
76
+ symbol: "ETH"
77
+ },
78
+ rpcUrls: {
79
+ default: { http: [""] },
80
+ public: { http: [""] }
81
+ }
82
+ };
83
+ } catch (error) {
84
+ const message = typeof error === "string" ? error : error?.message;
85
+ if (/user rejected request/i.test(message)) throw new UserRejectedRequestError(error);
86
+ throw new SwitchChainNotSupportedError({ connector: { name } });
87
+ }
88
+ },
89
+ async getAccounts() {
90
+ return (await (await this.getProvider()).send("eth_requestAccounts")).map((account) => getAddress(account));
91
+ },
92
+ async getChainId() {
93
+ const chainId = await (await this.getProvider()).send("eth_chainId");
94
+ return Number(chainId);
95
+ },
96
+ async isAuthorized() {
97
+ try {
98
+ return !!(await (await this.getProvider()).send("eth_accounts"))[0];
99
+ } catch {
100
+ return false;
101
+ }
102
+ },
103
+ onAccountsChanged(accounts) {
104
+ handlers.onAccountsChanged(accounts);
105
+ },
106
+ onChainChanged(chainId) {
107
+ handlers.onChainChanged(chainId);
108
+ },
109
+ async disconnect() {
110
+ handlers.detach();
111
+ },
112
+ onDisconnect() {
86
113
  emitter.emit("disconnect");
87
- this.disconnect();
88
- } else emitter.emit("change", { accounts });
89
- },
90
- onChainChanged(chainId) {
91
- emitter.emit("change", { chainId: Number(chainId) });
92
- },
93
- async disconnect() {
94
- const provider = await this.getProvider();
95
- provider.removeListener("accountsChanged", this.onAccountsChanged);
96
- provider.removeListener("chainChanged", this.onChainChanged);
97
- },
98
- onDisconnect() {
99
- emitter.emit("disconnect");
100
- }
101
- }));
114
+ }
115
+ };
116
+ });
102
117
  }
103
118
  //#endregion
104
119
  export { idLedgerLive, ledgerLiveConnector, name };
package/dist/index.js CHANGED
@@ -3,5 +3,6 @@ import { isIframe, isLedgerLive } from "./iframe/helpers.js";
3
3
  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
+ import { ClearSignUnavailableError } from "./hid/account.js";
6
7
  import "./hid/index.js";
7
- export { LS_KEY_DERIVATION_PATH, idLedgerHid, idLedgerLive, isIframe, isLedgerLive, ledgerHIDConnector, ledgerLiveConnector, name };
8
+ export { ClearSignUnavailableError, 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.2.0-alpha.3",
3
+ "version": "5.2.0-alpha.5",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "exports": {
@@ -35,13 +35,17 @@
35
35
  "build": "yarn run -T tsdown",
36
36
  "dev": "yarn run -T tsdown --watch",
37
37
  "lint": "eslint --ext ts,tsx,js,mjs .",
38
+ "test": "vitest run",
38
39
  "typecheck": "tsc --noEmit"
39
40
  },
40
41
  "devDependencies": {
42
+ "@ledgerhq/hw-transport-mocker": "6.34.7",
41
43
  "@types/w3c-web-hid": "^1.0.7",
42
44
  "build-config": "*",
43
45
  "eslint-config-custom": "*",
46
+ "happy-dom": "^20.0.0",
44
47
  "viem": ">=2.50",
48
+ "vitest": "^4.1.11",
45
49
  "wagmi": ">=3.6"
46
50
  },
47
51
  "dependencies": {