@coin-voyage/crypto 2.2.3-beta.0 → 2.2.3

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.
@@ -35,7 +35,7 @@ export function createDefaultEVMConfig(props) {
35
35
  }
36
36
  }
37
37
  if (isWalletInstalled("app.phantom.ethereum")) {
38
- connectors.push(extendConnector(injected({ target: "phantom" }), "phantom", "Phantom"));
38
+ connectors.push(extendConnector(injected({ target: "phantom" }), "app.phantom", "Phantom"));
39
39
  }
40
40
  if (!isWalletInstalled("coinbase")) {
41
41
  const coinbase = createCoinbaseConnector(props?.coinbase ?? {
@@ -7,6 +7,7 @@ import { useCallback, useRef, useSyncExternalStore } from "react";
7
7
  import { useAccount as useAccountInternal } from "wagmi";
8
8
  import { getConnector } from "../lib/utils/connector";
9
9
  import { extendsWalletAdapter } from "../solana/utils";
10
+ import { useLastConnector } from "./use-last-connector";
10
11
  const defaultAccount = {
11
12
  chainType: ChainType.EVM,
12
13
  isConnected: false,
@@ -110,11 +111,12 @@ const buildUtxoAccount = (btcAccount) => {
110
111
  * @bigmi/react for Bitcoin, and @mysten/dapp-kit for Sui) and normalizes their responses into a unified Account type.
111
112
  */
112
113
  export const useAccount = (args) => {
114
+ const { lastConnector } = useLastConnector();
115
+ const wagmiAccount = useAccountInternal();
116
+ const { wallet } = useWallet();
117
+ const suiWallet = useCurrentWallet();
118
+ const suiAccount = useCurrentAccount();
113
119
  const btcConfig = useBigmiConfig();
114
- // Use @bigmi/client primitives directly instead of @bigmi/react's useAccount
115
- // to fix "getServerSnapshot should be cached" error. The upstream hook passes
116
- // an uncached getServerSnapshot to useSyncExternalStore, causing infinite loops
117
- // during SSR/hydration in Next.js.
118
120
  const btcSnapshotCache = useRef(getBtcAccount(btcConfig));
119
121
  const btcSubscribe = useCallback((onChange) => watchBtcAccount(btcConfig, {
120
122
  onChange: () => {
@@ -124,33 +126,29 @@ export const useAccount = (args) => {
124
126
  }), [btcConfig]);
125
127
  const btcGetSnapshot = useCallback(() => btcSnapshotCache.current, []);
126
128
  const btcAccount = useSyncExternalStore(btcSubscribe, btcGetSnapshot, btcGetSnapshot);
127
- const wagmiAccount = useAccountInternal();
128
- const { wallet } = useWallet();
129
- const suiWallet = useCurrentWallet();
130
- const suiAccount = useCurrentAccount();
131
129
  const lastConnectedAccount = args?.selectedWallet
132
- ? getConnector(args?.selectedWallet.connectors, args?.chainType)
130
+ ? getConnector(args?.selectedWallet.connectors, args.chainType ?? lastConnector?.chainType)
133
131
  : undefined;
134
- const solana = buildSolanaAccount(wallet);
135
- const sui = buildSuiAccount(suiWallet, suiAccount);
136
- const evm = buildEvmAccount(wagmiAccount);
137
- const utxo = buildUtxoAccount(btcAccount);
138
- const accounts = [evm, solana, sui, utxo];
132
+ const accounts = [
133
+ buildEvmAccount(wagmiAccount),
134
+ buildSolanaAccount(wallet),
135
+ buildSuiAccount(suiWallet, suiAccount),
136
+ buildUtxoAccount(btcAccount),
137
+ ];
139
138
  const connectedAccounts = accounts.filter((account) => account.isConnected);
140
139
  const selectedChainTypeAccount = args?.chainType
141
- ? connectedAccounts.find((account) => account.chainType === args?.chainType) || defaultAccount
140
+ ? connectedAccounts.find((account) => account.chainType === args.chainType)
142
141
  : undefined;
143
142
  // If lastConnectedAccount exists, attempt to find a connected account with a matching connector ID or name.
144
143
  // If no matching account is found, fallback to the first connected account.
145
144
  // If lastConnectedAccount is not present, simply select the first connected account.
146
145
  const selectedAccount = lastConnectedAccount
147
146
  ? connectedAccounts.find((account) => {
148
- const connectorIdMatch = lastConnectedAccount?.id === account.connector?.id;
149
- const connectorNameMatch = !lastConnectedAccount?.id &&
150
- lastConnectedAccount?.name === account.connector?.name;
151
- return connectorIdMatch || connectorNameMatch;
152
- }) || connectedAccounts[0]
153
- : connectedAccounts[0];
147
+ return (account.chainType === args?.chainType &&
148
+ (account.connector?.id === lastConnectedAccount?.id ||
149
+ account.connector?.name === lastConnectedAccount?.name));
150
+ }) || connectedAccounts.find((account) => account.chainType === args?.chainType)
151
+ : connectedAccounts.find((account) => account.chainType === args?.chainType) || connectedAccounts[0];
154
152
  return {
155
153
  account: selectedChainTypeAccount || selectedAccount || defaultAccount,
156
154
  accounts: connectedAccounts,
@@ -9,6 +9,18 @@ import { getConnectorIcon } from "../lib/utils/get-connector-icon";
9
9
  import { getWalletPriority } from "../lib/utils/get-wallet-priority";
10
10
  import { isWalletInstalled } from "../lib/utils/is-wallet-installed";
11
11
  import { extendsWalletAdapter } from "../solana/utils";
12
+ // canonical wallet ID for multi-chain wallets
13
+ const getCanonicalWalletId = (idOrName) => {
14
+ const lower = idOrName.toLowerCase();
15
+ // Explicit multi-chain wallet mapping
16
+ if (lower.includes("phantom"))
17
+ return "app.phantom";
18
+ if (lower.includes("rainbow"))
19
+ return "me.rainbow";
20
+ if (lower.includes("metamask"))
21
+ return "io.metamask";
22
+ return lower.split(" ")[0];
23
+ };
12
24
  export const useInstalledWallets = (filterChainType) => {
13
25
  const bigmiConfig = useBigmiConfig?.() ?? null;
14
26
  const wagmiConfig = useContext(WagmiContext) ?? null;
@@ -17,7 +29,7 @@ export const useInstalledWallets = (filterChainType) => {
17
29
  return useMemo(() => {
18
30
  const shouldInclude = (chainType) => !filterChainType || filterChainType === chainType;
19
31
  const installedUTXO = shouldInclude(ChainType.UTXO)
20
- ? (bigmiConfig?.connectors.filter((c) => isWalletInstalled(c.id)) ?? [])
32
+ ? bigmiConfig?.connectors.filter((c) => isWalletInstalled(c.id)) ?? []
21
33
  : [];
22
34
  const installedEVM = shouldInclude(ChainType.EVM)
23
35
  ? Array.from(wagmiConfig?.connectors ?? []).filter((c) => isWalletInstalled(c.id))
@@ -26,55 +38,38 @@ export const useInstalledWallets = (filterChainType) => {
26
38
  ? solanaWallets.filter((w) => [WalletReadyState.Installed, WalletReadyState.Loadable].includes(w.adapter.readyState))
27
39
  : [];
28
40
  const installedSui = shouldInclude(ChainType.SUI) && suiWallets ? suiWallets : [];
29
- return combineWalletLists(installedUTXO, installedEVM, installedSol, installedSui);
41
+ return combineWalletLists(installedUTXO, installedEVM, installedSol, installedSui, filterChainType);
30
42
  }, [bigmiConfig?.connectors, wagmiConfig?.connectors, solanaWallets, suiWallets, filterChainType]);
31
43
  };
32
- const combineWalletLists = (utxoConnectorList, evmConnectorList, solanaWalletList, suiWalletList) => {
33
- const allConnectors = [];
44
+ const combineWalletLists = (utxoConnectorList, evmConnectorList, solanaWalletList, suiWalletList, filterChainType) => {
45
+ const walletMap = new Map();
46
+ const addConnector = (rawId, displayName, icon, connector) => {
47
+ const walletId = getCanonicalWalletId(rawId);
48
+ if (filterChainType && connector.chainType !== filterChainType)
49
+ return;
50
+ const existing = walletMap.get(walletId);
51
+ if (existing) {
52
+ existing.connectors.push(connector);
53
+ }
54
+ else {
55
+ walletMap.set(walletId, { id: walletId, name: displayName, icon, connectors: [connector] });
56
+ }
57
+ };
34
58
  utxoConnectorList.forEach((utxo) => {
35
- allConnectors.push({
36
- id: utxo.id,
37
- name: utxo.name,
38
- icon: getConnectorIcon(utxo),
39
- connector: { connector: utxo, chainType: ChainType.UTXO },
40
- });
59
+ addConnector(utxo.id, utxo.name, getConnectorIcon(utxo), { connector: utxo, chainType: ChainType.UTXO });
41
60
  });
42
61
  evmConnectorList.forEach((evm) => {
43
- allConnectors.push({
44
- id: evm.id,
45
- name: evm?.displayName || evm?.name,
46
- icon: getConnectorIcon(evm),
47
- connector: { connector: evm, chainType: ChainType.EVM },
48
- });
62
+ const name = evm?.displayName || evm?.name;
63
+ addConnector(evm.id, name, getConnectorIcon(evm), { connector: evm, chainType: ChainType.EVM });
49
64
  });
50
65
  solanaWalletList.forEach((svm) => {
51
- allConnectors.push({
52
- id: svm.adapter.name,
53
- name: svm.adapter.name,
54
- icon: svm.adapter.icon,
55
- connector: {
56
- connector: extendsWalletAdapter(svm.adapter, svm.adapter.name),
57
- chainType: ChainType.SOL,
58
- },
66
+ addConnector(svm.adapter.name, svm.adapter.name, svm.adapter.icon, {
67
+ connector: extendsWalletAdapter(svm.adapter, svm.adapter.name),
68
+ chainType: ChainType.SOL,
59
69
  });
60
70
  });
61
71
  suiWalletList.forEach((moveVM) => {
62
- allConnectors.push({
63
- id: moveVM.name,
64
- name: moveVM.name,
65
- icon: moveVM.icon,
66
- connector: { connector: moveVM, chainType: ChainType.SUI },
67
- });
68
- });
69
- const walletMap = new Map();
70
- allConnectors.forEach(({ id, name, icon, connector }) => {
71
- const existing = walletMap.get(id);
72
- if (existing) {
73
- existing.connectors.push(connector);
74
- }
75
- else {
76
- walletMap.set(id, { id, name, icon, connectors: [connector] });
77
- }
72
+ addConnector(moveVM.name, moveVM.name, moveVM.icon, { connector: moveVM, chainType: ChainType.SUI });
78
73
  });
79
74
  const combinedWallets = Array.from(walletMap.values());
80
75
  combinedWallets.sort(walletComparator);
@@ -83,8 +78,7 @@ const combineWalletLists = (utxoConnectorList, evmConnectorList, solanaWalletLis
83
78
  const walletComparator = (a, b) => {
84
79
  const priorityA = getWalletPriority(a.id);
85
80
  const priorityB = getWalletPriority(b.id);
86
- if (priorityA !== priorityB) {
81
+ if (priorityA !== priorityB)
87
82
  return priorityA - priorityB;
88
- }
89
- return a.id?.localeCompare(b.id);
83
+ return a.id.localeCompare(b.id);
90
84
  };
@@ -1,4 +1,10 @@
1
+ import type { ChainType } from "@coin-voyage/shared/types";
2
+ import type { WalletConnector } from "../types/wallet-connector";
1
3
  export declare const useLastConnector: () => {
2
- lastConnectorId: string | null;
3
- updateLastConnectorId: (id: string) => void;
4
+ lastConnectorId: string | undefined;
5
+ lastConnector: {
6
+ id: string;
7
+ chainType: ChainType;
8
+ } | null;
9
+ updateLastConnectorId: (connector: WalletConnector, chainType: ChainType) => void;
4
10
  };
@@ -1,13 +1,24 @@
1
1
  import { getValue, setValue } from "@coin-voyage/shared/common";
2
- import { useState } from "react";
2
+ import { useCallback, useState } from "react";
3
+ import { getConnectorId } from "../lib/utils/connector";
3
4
  export const useLastConnector = () => {
4
- const [lastConnectorId] = useState(() => {
5
- // Initialize from localStorage on mount
6
- const id = getValue("@coin-voyage/recentConnectorId");
7
- return id ?? "";
5
+ const [lastConnectorIdWithChain, setLastConnectorIdWithChain] = useState(() => {
6
+ const stored = getValue("@coin-voyage/recentConnectorId");
7
+ return stored ?? null;
8
8
  });
9
- const updateLastConnectorId = (id) => {
10
- setValue("@coin-voyage/recentConnectorId", id);
11
- };
12
- return { lastConnectorId, updateLastConnectorId };
9
+ const updateLastConnectorId = useCallback((connector, chainType) => {
10
+ const idWithChain = `${getConnectorId(connector)}-${chainType}`;
11
+ setValue("@coin-voyage/recentConnectorId", idWithChain);
12
+ setLastConnectorIdWithChain(idWithChain);
13
+ }, []);
14
+ // Parse into ID + chainType for easy access
15
+ const lastConnector = lastConnectorIdWithChain
16
+ ? (() => {
17
+ const split = lastConnectorIdWithChain.split("-");
18
+ const id = split[0];
19
+ const chainType = split[1];
20
+ return { id, chainType };
21
+ })()
22
+ : null;
23
+ return { lastConnectorId: lastConnector?.id, lastConnector, updateLastConnectorId };
13
24
  };
@@ -4,7 +4,6 @@ import { useConnectWallet } from "@mysten/dapp-kit";
4
4
  import { useWallet } from "@solana/wallet-adapter-react";
5
5
  import { useCallback } from "react";
6
6
  import { useConfig as useWagmiConfig, useConnect as useWagmiConnect } from "wagmi";
7
- import { getConnectorId } from "../lib/utils/connector";
8
7
  import { useLastConnector } from "./use-last-connector";
9
8
  export const useUniversalConnect = (props) => {
10
9
  const utxoConfig = useBigmiConfig();
@@ -19,7 +18,7 @@ export const useUniversalConnect = (props) => {
19
18
  ...props,
20
19
  onSuccess(data, variables) {
21
20
  const connector = variables.connector || variables.connector;
22
- updateLastConnectorId(getConnectorId(connector));
21
+ updateLastConnectorId(connector, ChainType.EVM);
23
22
  onSuccess?.(data, {
24
23
  chainId: variables?.chainId,
25
24
  connector,
@@ -34,7 +33,7 @@ export const useUniversalConnect = (props) => {
34
33
  ...props,
35
34
  onSuccess(data, variables) {
36
35
  const connector = variables.connector;
37
- updateLastConnectorId(getConnectorId(connector));
36
+ updateLastConnectorId(connector, ChainType.UTXO);
38
37
  onSuccess?.(data, {
39
38
  chainId: ChainId.BTC,
40
39
  connector,
@@ -83,7 +82,7 @@ export const useUniversalConnect = (props) => {
83
82
  walletAdapter.connect().catch(handleError);
84
83
  });
85
84
  }
86
- updateLastConnectorId(getConnectorId(connector));
85
+ updateLastConnectorId(connector, ChainType.SOL);
87
86
  onSuccess?.(undefined, { connector: walletAdapter });
88
87
  break;
89
88
  }
@@ -93,7 +92,7 @@ export const useUniversalConnect = (props) => {
93
92
  }, {
94
93
  onError,
95
94
  onSuccess: async (data) => {
96
- updateLastConnectorId(getConnectorId(connector));
95
+ updateLastConnectorId(connector, ChainType.SUI);
97
96
  onSuccess?.(data, { connector });
98
97
  },
99
98
  onSettled: onSettled,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@coin-voyage/crypto",
3
3
  "description": "Crypto utilities for Coin Voyage",
4
- "version": "2.2.3-beta.0",
4
+ "version": "2.2.3",
5
5
  "private": false,
6
6
  "sideEffects": false,
7
7
  "author": "Lars <lars@coinvoyage.io>",
@@ -50,7 +50,7 @@
50
50
  "@solana/wallet-adapter-walletconnect": "0.1.21",
51
51
  "@solana/wallet-adapter-base": "0.9.27",
52
52
  "@solana/wallet-adapter-coinbase": "0.1.23",
53
- "@coin-voyage/shared": "2.2.5-beta.8"
53
+ "@coin-voyage/shared": "2.2.5"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@types/elliptic": "6.4.18"