Skip to content

Commit

Permalink
Merge pull request #123 from lidofinance/disable-terms-modal-hook-and…
Browse files Browse the repository at this point in the history
…-other

Update useForceDisconnect hook, rework getUnsupportedChainError, hide mumbai network
  • Loading branch information
alx-khramov authored Mar 11, 2024
2 parents d2e0162 + e03382c commit b61aba9
Show file tree
Hide file tree
Showing 26 changed files with 347 additions and 174 deletions.
24 changes: 20 additions & 4 deletions apps/demo-react/components/ConnectDisconnect.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,29 @@
import { useDisconnect, useWeb3 } from 'reef-knot/web3-react';
import { useWeb3 } from 'reef-knot/web3-react';
import {
useConnectorInfo,
useEagerConnect,
useForceDisconnect,
} from 'reef-knot/core-react';
import { Text, AddressBadge, Button } from '@lidofinance/lido-ui';
import { FlexContainer } from '../styles/global';

const ConnectDisconnect = (props: { handleOpen: () => void }) => {
const { handleOpen } = props;
const { disconnect } = useDisconnect();
const { forceDisconnect } = useForceDisconnect();
const { account } = useWeb3();
const { isAutoConnectionSuitable } = useConnectorInfo();
const { eagerConnect } = useEagerConnect();

const handleDisconnect = () => {
disconnect?.();
forceDisconnect?.();
};

const handleConnectStart = () => {
if (isAutoConnectionSuitable) {
void eagerConnect();
} else {
handleOpen();
}
};

return (
Expand All @@ -20,7 +36,7 @@ const ConnectDisconnect = (props: { handleOpen: () => void }) => {
) : (
<Button
style={{ maxWidth: '300px', alignSelf: 'center' }}
onClick={handleOpen}
onClick={handleConnectStart}
>
Connect Wallet
</Button>
Expand Down
2 changes: 1 addition & 1 deletion apps/demo-react/components/ProviderSDKWithProps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const ProviderSDKWithProps = (props: {
wrappedProvider.pollingInterval = POLLING_INTERVAL;
setProviderWeb3(wrappedProvider);
}
});
})();
}, [connector, isConnected, providerWeb3]);

const supportedChainIds = supportedChains.map((chain) => chain.id);
Expand Down
11 changes: 11 additions & 0 deletions packages/connect-wallet-modal/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# @reef-knot/connect-wallet-modal

## 2.1.0

### Minor Changes

- 85b3f79:
- rework getUnsupportedChainError, hide mumbai network in the error
- (web3-react) fix logic of useConnectorError
- (web3-react) deprecate useConnectorInfo, useDisconnect
- (core-react) rework and add hooks: useConnectorInfo, useDisconnect, useEagerConnect
- (reef-knot) add connectors export as "Connectors"

## 2.0.0

### Minor Changes
Expand Down
6 changes: 3 additions & 3 deletions packages/connect-wallet-modal/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@reef-knot/connect-wallet-modal",
"version": "2.0.0",
"version": "2.1.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
Expand Down Expand Up @@ -45,11 +45,11 @@
"@types/react-dom": "18.2.17"
},
"devDependencies": {
"@reef-knot/core-react": "^2.0.0",
"@reef-knot/core-react": "^2.1.0",
"@reef-knot/types": "^1.5.0",
"@reef-knot/ui-react": "^1.0.8",
"@reef-knot/wallets-helpers": "^1.1.5",
"@reef-knot/web3-react": "^2.0.0",
"@reef-knot/web3-react": "^2.1.0",
"@reef-knot/ledger-connector": "^3.0.0",
"@types/ua-parser-js": "^0.7.36",
"eslint-config-custom": "*",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React from 'react';
import { Button, Modal } from '@reef-knot/ui-react';
import styled, { css } from '@reef-knot/ui-react/styled-wrapper';
import { helpers } from '@reef-knot/web3-react';
import { Terms, WalletModalConnectTermsProps } from '../../Terms';
import { CommonButtonsContainer } from '../styles';
import { useReefKnotContext } from '@reef-knot/core-react';
import {
useReefKnotContext,
getUnsupportedChainError,
} from '@reef-knot/core-react';

export interface AcceptTermsModalProps {
open: boolean;
Expand Down Expand Up @@ -37,7 +39,7 @@ export const AcceptTermsModal = ({
const { chains: supportedChains } = useReefKnotContext();
let errorMessage = error?.message;
if (error && error.name == 'UnsupportedChainIdError') {
errorMessage = helpers.getUnsupportedChainError(supportedChains).message;
errorMessage = getUnsupportedChainError(supportedChains).message;
}

return (
Expand Down
11 changes: 11 additions & 0 deletions packages/core-react/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# @reef-knot/core-react

## 2.1.0

### Minor Changes

- 85b3f79:
- rework getUnsupportedChainError, hide mumbai network in the error
- (web3-react) fix logic of useConnectorError
- (web3-react) deprecate useConnectorInfo, useDisconnect
- (core-react) rework and add hooks: useConnectorInfo, useDisconnect, useEagerConnect
- (reef-knot) add connectors export as "Connectors"

## 2.0.0

### Major Changes
Expand Down
4 changes: 3 additions & 1 deletion packages/core-react/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@reef-knot/core-react",
"version": "2.0.0",
"version": "2.1.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
Expand Down Expand Up @@ -40,6 +40,7 @@
"ua-parser-js": "1.0.33"
},
"devDependencies": {
"@reef-knot/ledger-connector": "^3.0.0",
"@reef-knot/wallets-list": "^1.12.0",
"@reef-knot/types": "^1.5.0",
"@reef-knot/ui-react": "^1.0.8",
Expand All @@ -49,6 +50,7 @@
"wagmi": "^0.12.19"
},
"peerDependencies": {
"@reef-knot/ledger-connector": "^3.0.0",
"@reef-knot/wallets-list": "^1.4.1",
"@reef-knot/types": "^1.2.1",
"@reef-knot/ui-react": "^1.0.4",
Expand Down
4 changes: 1 addition & 3 deletions packages/core-react/src/components/AutoConnect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,13 @@ import { useAutoConnect } from '../hooks/useAutoConnect';
export const AutoConnect = ({
children,
autoConnect,
walletDataList,
chains,
}: {
children: React.ReactNode;
autoConnect: boolean;
walletDataList: WalletAdapterData[];
chains: Chain[];
}) => {
useAutoConnect(autoConnect, walletDataList, chains);
useAutoConnect(autoConnect);

return <>{children}</>;
};
5 changes: 5 additions & 0 deletions packages/core-react/src/helpers/getUnsupportedChainError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ export const getUnsupportedChainError = (supportedChains: Chain[]) => {
// Get names of supported chains to suggest them in case of "unsupported network" error
const supportedChainsNames = (() => {
const chains = supportedChains
// On Lido widgets the Polygon Mumbai network was added as a temporary workaround for the wagmi and walletconnect bug,
// when some wallets are failing to connect if there are only one supported network, so we need at least 2 of them.
// But we don't want to mention it in the error as a suggested supported network, because it is not really true, so temporary filtering it out.
// TODO: the issue is fixed in wagmi v1+, remove the filter after updating wagmi to v1+
.filter((chain) => chain.id !== 80001)
.map(({ name }) => name)
.filter((chainName) => chainName !== 'unknown');
const lastChain = chains.pop();
Expand Down
3 changes: 3 additions & 0 deletions packages/core-react/src/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './getUnsupportedChainError';
export * from './checkTermsAccepted';
export * from './userAgents';
11 changes: 11 additions & 0 deletions packages/core-react/src/helpers/userAgents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { UAParser } from 'ua-parser-js';

const parser = new UAParser();

export const device = parser.getDevice();
export const browser = parser.getBrowser();
export const os = parser.getOS();

export const isMobile = device.type === 'mobile';
export const isTablet = device.type === 'tablet';
export const isMobileOrTablet = isMobile || isTablet;
3 changes: 3 additions & 0 deletions packages/core-react/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
export * from './useReefKnotContext';
export * from './useAutoConnect';
export * from './useEagerConnect';
export * from './useDisconnect';
export * from './useConnectorInfo';
153 changes: 21 additions & 132 deletions packages/core-react/src/hooks/useAutoConnect.ts
Original file line number Diff line number Diff line change
@@ -1,145 +1,34 @@
import { useClient, useAccount, Chain } from 'wagmi';
import { connect, disconnect } from 'wagmi/actions';
import type { ConnectResult, Connector } from '@wagmi/core';
import { useContext, useEffect, useRef } from 'react';
import { WalletAdapterData } from '@reef-knot/types';
import {
AcceptTermsModal,
AcceptTermsModalContext,
} from '../context/acceptTermsModal';
import { getUnsupportedChainError } from '../helpers/getUnsupportedChainError';
import { useClient, useAccount } from 'wagmi';
import { useCallback, useEffect, useRef } from 'react';
import { checkTermsAccepted } from '../helpers/checkTermsAccepted';
import { useEagerConnect } from './useEagerConnect';

const connectAndHandleErrors = async (
connector: Connector,
supportedChains: Chain[],
acceptTermsModal: AcceptTermsModal,
): Promise<{
connectResult: ConnectResult | null;
connectError?: Error;
}> => {
let connectResult = null;
let connectError;

try {
connectResult = await connect({ connector });
} catch (e) {
connectResult = null; // ensure that connectResult is empty in case of an error
connectError = e as Error;
}

if (connectResult?.chain.unsupported) {
// No errors during connection, but the chain is unsupported.
// This case is considered as error for now, and we explicitly call disconnect() here.
// This logic comes from previously used web3-react connection logic, which wasn't reworked yet after web3-react removal.
// web3-react logic was: if a chain is unsupported – break the connection, throw an error
// wagmi logic is: if a chain is unsupported – connect anyway, without errors, set `chain.unsupported` flag to true.
// So, here we are trying to mimic the legacy logic, because we are not ready to rework it yet.
connectResult = null;
connectError = getUnsupportedChainError(supportedChains);
await disconnect();

// A user may change a chain in a wallet app, prepare for that event.
// There is a strong recommendation in the MetaMask documentation
// to reload the page upon chain changes, unless there is a good reason not to.
// This looks like a good general approach.
const provider = await connector.getProvider();
provider.once('chainChanged', () => globalThis.window?.location.reload());
}

if (connectError) {
acceptTermsModal.setError?.(connectError);
acceptTermsModal.setVisible?.(true);
} else {
acceptTermsModal.setVisible?.(false);
acceptTermsModal.setError?.(undefined);
}

return { connectResult, connectError };
};

const connectEagerly = async (
adapters: WalletAdapterData[],
acceptTermsModal: AcceptTermsModal,
supportedChains: Chain[],
): Promise<{
connectResult: ConnectResult | null;
connectError?: Error;
}> => {
const isTermsAccepted = checkTermsAccepted();
let connectResult = null;
let connectError;

const continueConnection = async (connector: Connector) => {
({ connectResult, connectError } = await connectAndHandleErrors(
connector,
supportedChains,
acceptTermsModal,
));
};

for (const adapter of adapters) {
if (adapter.detector?.()) {
// wallet is detected
if (!isTermsAccepted) {
// Terms of service were not accepted previously.
// So, for legal reasons, we must ask a user to accept the terms before connecting.
const onContinue = () => void continueConnection(adapter.connector);
acceptTermsModal.setOnContinue?.(() => onContinue);
acceptTermsModal.setVisible?.(true);
} else {
await continueConnection(adapter.connector);
}
break; // no need to iterate over all other adapters if at least one was detected
}
}

return { connectResult, connectError };
};

export const useAutoConnect = (
autoConnectEnabled: boolean,
walletDataList: WalletAdapterData[],
chains: Chain[],
) => {
export const useAutoConnect = (autoConnectEnabled: boolean) => {
const isAutoConnectCalled = useRef(false);
const client = useClient();
const { isConnected } = useAccount();
const { acceptTermsModal } = useContext(AcceptTermsModalContext);
const autoConnectOnlyAdapters = walletDataList.filter(
(adapter) => adapter.autoConnectOnly,
);
const { eagerConnect } = useEagerConnect();

useEffect(() => {
const autoConnect = useCallback(async () => {
// Don't auto-connect if already connected or if the auto-connect feature is disabled or if already tried to auto-connect.
if (isConnected || !autoConnectEnabled || isAutoConnectCalled.current)
return;

void (async () => {
// The current logic is to try auto-connect only once, even if an error happened and connection was not successful.
isAutoConnectCalled.current = true;
// The current logic is to try auto-connect only once, even if an error happened and connection was not successful.
isAutoConnectCalled.current = true;

// Try to eagerly connect wallets that are meant to be used only with auto-connection.
// For example, wallets with dApp browsers, or using iframes to open dApps.
const { connectResult, connectError } = await eagerConnect();

// Try to eagerly connect wallets that are meant to be used only with auto-connection.
// For example, wallets with dApp browsers, or using iframes to open dApps.
const { connectResult, connectError } = await connectEagerly(
autoConnectOnlyAdapters,
acceptTermsModal,
chains,
);
// If still not connected and there were no errors and the terms of service are accepted,
// call the default wagmi autoConnect method, which attempts to connect to the last used connector.
if (!connectResult && !connectError && checkTermsAccepted()) {
await client.autoConnect();
}
}, [autoConnectEnabled, client, eagerConnect, isConnected]);

// If still not connected and there were no errors and the terms of service are accepted,
// call the default wagmi autoConnect method, which attempts to connect to the last used connector.
if (!connectResult && !connectError && checkTermsAccepted()) {
await client.autoConnect();
}
})();
}, [
autoConnectOnlyAdapters,
autoConnectEnabled,
client,
isConnected,
walletDataList,
acceptTermsModal,
chains,
]);
useEffect(() => {
void autoConnect();
}, [autoConnect]);
};
Loading

0 comments on commit b61aba9

Please sign in to comment.