Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add post-connection warning to evm/svm connectors #455

Merged
merged 12 commits into from
Jan 18, 2025
Merged
6 changes: 6 additions & 0 deletions .changeset/clean-bats-hear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@e2e-tests/runner": minor
"@fuels/react": minor
---

Add a post-connection warning for EVM/SVM about Fuel predicate address difference.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
import type {
ApproveTransferFunction,
ConnectFunction,
ConnectorFunctions,
} from '../../../common/types';
import { fundWallet } from '../setup';
import phantomExtended from './phantom/phantom';
Expand All @@ -18,7 +19,7 @@ import { test } from './setup';
test.skip('SolanaConnector', () => {
test.slow();

const connect: ConnectFunction = async (page: Page) => {
const commonConnect: ConnectFunction = async (page: Page) => {
const connectButton = getButtonByText(page, 'Connect Wallet', true);
await connectButton.click();
await getByAriaLabel(page, 'Connect to Solana Wallets', true).click();
Expand All @@ -32,6 +33,15 @@ test.skip('SolanaConnector', () => {
}
};

// First-time connection requires to confirm the fuel predicate address difference
const connect: ConnectorFunctions['connect'] = async (page) => {
await commonConnect(page);
await page.getByText('Continue to application').click();
};

// From here on, we'll skip the predicate warning step
const secondConnect: ConnectorFunctions['connect'] = commonConnect;

const approveTransfer: ApproveTransferFunction = async () => {
await phantomExtended.confirmSignatureRequest();
};
Expand All @@ -45,7 +55,7 @@ test.skip('SolanaConnector', () => {
await sessionTests(page, { connect, approveTransfer });
});

await connect(page);
await secondConnect(page);
await skipBridgeFunds(page);

const addressElement = await page.locator('#address');
Expand All @@ -61,15 +71,15 @@ test.skip('SolanaConnector', () => {
}
await test.step('Transfer tests', async () => {
await transferTests(page, {
connect,
connect: secondConnect,
approveTransfer,
keepSession: true,
});
});

await test.step('Increment tests', async () => {
await incrementTests(page, {
connect,
connect: secondConnect,
approveTransfer,
keepSession: true,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,10 @@ test.describe('WalletConnectConnector', () => {
await commonConnect(page);
await page.getByText('Sign', { exact: true }).click();
await metamask.confirmSignature();
await page.getByText('Continue to application').click();
};

// From here on, we'll skip the signature step
// From here on, we'll skip the signature step and predicate address warning disclaimer
const secondConnect: ConnectorFunctions['connect'] = commonConnect;

const approveTransfer: ConnectorFunctions['approveTransfer'] = async () => {
Expand Down
6 changes: 6 additions & 0 deletions packages/react/src/constants/themes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ const light = {
'--fuel-green-3': '#D9FCE3',
'--fuel-green-11': '#008347',
'--fuel-blue-3': '#E6F4FE',
'--fuel-blue-6': '#ACD8FC',
'--fuel-blue-11': '#0D74CE',
'--fuel-blue-a3': 'color(display-p3 0.7686 0.898 1/0.334)',
'--fuel-blue-a11': 'color(display-p3 0 0.3059 0.7333/0.794)',
'--fuel-gray-10': '#838383',
'--fuel-gray-11': '#646464',
'--fuel-gray-12': '#202020',
Expand All @@ -53,7 +56,10 @@ const dark = {
'--fuel-green-3': '#0F2E1B',
'--fuel-green-11': '#00DD75',
'--fuel-blue-3': '#0D2847',
'--fuel-blue-6': '#104D87',
'--fuel-blue-11': '#70B9FF',
'--fuel-blue-a3': 'color(display-p3 0.1216 0.4627 1/0.219)',
'--fuel-blue-a11': 'color(display-p3 0.5176 0.7373 1/0.975)',
'--fuel-gray-10': '#7b7b7b',
'--fuel-gray-11': '#b4b4b4',
'--fuel-gray-12': '#eee',
Expand Down
29 changes: 13 additions & 16 deletions packages/react/src/providers/FuelUIProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ export type FuelUIProviderProps = {
};

export enum Routes {
LIST = 'list',
INSTALL = 'install',
CONNECTING = 'connecting',
EXTERNAL_DISCLAIMER = 'disclaimer',
List = 'LIST',
Install = 'INSTALL',
Connecting = 'CONNECTING',
PredicateExternalDisclaimer = 'PREDICATE_EXTERNAL_DISCLAIMER',
PredicateAddressDisclaimer = 'PREDICATE_ADDRESS_DISCLAIMER',
}

export type FuelUIContextType = {
Expand Down Expand Up @@ -107,7 +108,7 @@ export function FuelUIProvider({
});
const { isConnected } = useIsConnected();
const [connector, setConnector] = useState<FuelConnector | null>(null);
const [dialogRoute, setDialogRoute] = useState<Routes>(Routes.LIST);
const [dialogRoute, setDialogRoute] = useState<Routes>(Routes.List);
const [isOpen, setOpen] = useState(false);
const [error, setError] = useState<Error | null>(null);

Expand All @@ -127,7 +128,7 @@ export function FuelUIProvider({
const handleBack = useCallback(() => {
setError(null);
setConnector(null);
setDialogRoute(Routes.LIST);
setDialogRoute(Routes.List);
}, []);

const handleRetryConnect = useCallback(
Expand All @@ -144,7 +145,7 @@ export function FuelUIProvider({

const handleStartConnection = useCallback(
async (connector: FuelConnector) => {
setDialogRoute(Routes.CONNECTING);
setDialogRoute(Routes.Connecting);
await handleRetryConnect(connector);
},
[handleRetryConnect],
Expand All @@ -154,11 +155,11 @@ export function FuelUIProvider({
async (connector: FuelConnector) => {
setConnector(connector);
if (!connector.installed) {
setDialogRoute(Routes.INSTALL);
setDialogRoute(Routes.Install);
} else if (isNativeConnector(connector)) {
handleStartConnection(connector);
} else {
setDialogRoute(Routes.EXTERNAL_DISCLAIMER);
setDialogRoute(Routes.PredicateExternalDisclaimer);
}
},
[handleStartConnection],
Expand All @@ -173,24 +174,20 @@ export function FuelUIProvider({
const handleConnect = useCallback(() => {
setConnector(null);
setError(null);
setDialogRoute(Routes.LIST);
setDialogRoute(Routes.List);
setOpen(true);
}, []);

const handleCancel = useCallback(({ clean }: { clean?: boolean } = {}) => {
const handleCancel = useCallback(() => {
setError(null);
setOpen(false);
if (clean) {
setConnector(null);
setDialogRoute(Routes.LIST);
}
}, []);

useEffect(() => {
const css = document.createElement('style');
css.appendChild(
document.createTextNode(
`@import url("https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,100..900&display=swap"); .fuel-connectors * { box-sizing: border-box; } .fuel-connectors .fuel-connectors-dialog-content:focus { outline: none; } @media (max-width: 430px) { .fuel-connectors .fuel-connectors-dialog-content { top: 50%; width: 100%; border-radius: 36px; } } .fuel-connectors .fuel-connectors-connector-item { transition: background-color 50ms cubic-bezier(0.16, 1, 0.3, 1); background-color: var(--fuel-connector-background); } .fuel-connectors .fuel-connectors-connector-item:active { opacity: 0.8; } .fuel-connectors .fuel-connectors-connector-item:hover { background-color: var(--fuel-connector-hover); } .fuel-connectors .fuel-connectors-connector-button { transition: background-color 50ms cubic-bezier(0.16, 1, 0.3, 1); background-color: var(--fuel-button-background); color: var(--fuel-color-bold); } .fuel-connectors .fuel-connectors-connector-button:visited { color: var(--fuel-color-bold); } .fuel-connectors .fuel-connectors-connector-button:hover { background-color: var(--fuel-button-background-hover); } .fuel-connectors .fuel-connectors-connector-button-primary { transition: background-color 50ms cubic-bezier(0.16, 1, 0.3, 1); background-color: var(--fuel-green-11); color: var(--fuel-black-color); } .fuel-connectors .fuel-connectors-connector-button-primary:visited { color: var(--fuel-black-color); } .fuel-connectors .fuel-connectors-connector-button-primary:hover { background-color: var(--fuel-green-11); } .fuel-connectors .fuel-connectors-back-icon { transition: background-color 50ms cubic-bezier(0.16, 1, 0.3, 1); } .fuel-connectors .fuel-connectors-back-icon[data-connector='false'] { visibility: hidden; } .fuel-connectors .fuel-connectors-back-icon:hover, .fuel-connectors .fuel-connectors-back-icon:active { opacity: 1; background-color: var(--fuel-connector-hover); } .fuel-connectors .fuel-connectors-close-icon { transition: background-color 50ms cubic-bezier(0.16, 1, 0.3, 1); } .fuel-connectors .fuel-connectors-close-icon:hover, .fuel-connectors .fuel-connectors-close-icon:active { opacity: 1; background-color: var(--fuel-connector-hover); } .fuel-connectors .fuel-connectors-button-base { cursor: pointer; } .fuel-connectors .fuel-connectors-button:disabled { cursor: not-allowed; } .fuel-connectors .fuel-connectors-button { transition: background-color 50ms cubic-bezier(0.16, 1, 0.3, 1); background-color: var(--fuel-green-11); } .fuel-connectors .fuel-connectors-button:disabled { background-color: var(--fuel-border-color); } .fuel-connectors .fuel-connectors-button-disconnect { transition: background-color 50ms cubic-bezier(0.16, 1, 0.3, 1); background-color: var(--fuel-button-background); } .fuel-connectors .fuel-connectors-button-disconnect:hover { background-color: var(--fuel-button-background-hover); } @keyframes fuelOverlayShow { from { opacity: 0; } to { opacity: 1; } } @keyframes fuelSpin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } @keyframes fuelContentShow { from { opacity: 0; transform: translate(-50%, -48%) scale(0.96); } to { opacity: 1; transform: translate(-50%, -50%) scale(1); } } @keyframes fuelLoader { 0% { background-position: -468px 0 } 100% { background-position: 468px 0 } }`,
`@import url("https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,100..900&display=swap"); .fuel-connectors * { box-sizing: border-box; } .fuel-connectors .fuel-connectors-dialog-content:focus { outline: none; } @media (max-width: 430px) { .fuel-connectors .fuel-connectors-dialog-content { top: 50%; width: 100%; border-radius: 36px; } } .fuel-connectors .fuel-connectors-connector-item { transition: background-color 50ms cubic-bezier(0.16, 1, 0.3, 1); background-color: var(--fuel-connector-background); } .fuel-connectors .fuel-connectors-connector-item:active { opacity: 0.8; } .fuel-connectors .fuel-connectors-connector-item:hover { background-color: var(--fuel-connector-hover); } .fuel-connectors .fuel-connectors-connector-button { transition: background-color 50ms cubic-bezier(0.16, 1, 0.3, 1); background-color: var(--fuel-button-background); color: var(--fuel-color-bold); } .fuel-connectors .fuel-connectors-connector-button:visited { color: var(--fuel-color-bold); } .fuel-connectors .fuel-connectors-connector-button:hover { background-color: var(--fuel-button-background-hover); } .fuel-connectors .fuel-connectors-connector-button-primary { transition: background-color 50ms cubic-bezier(0.16, 1, 0.3, 1); background-color: var(--fuel-green-11); color: var(--fuel-black-color); } .fuel-connectors .fuel-connectors-connector-button-primary:visited { color: var(--fuel-black-color); } .fuel-connectors .fuel-connectors-connector-button-primary:hover { background-color: var(--fuel-green-11); } .fuel-connectors .fuel-connectors-back-icon { transition: background-color 50ms cubic-bezier(0.16, 1, 0.3, 1); } .fuel-connectors .fuel-connectors-back-icon[data-connector='false'] { visibility: hidden; } .fuel-connectors .fuel-connectors-back-icon:hover, .fuel-connectors .fuel-connectors-back-icon:active { opacity: 1; background-color: var(--fuel-connector-hover); } .fuel-connectors .fuel-connectors-close-icon { transition: background-color 50ms cubic-bezier(0.16, 1, 0.3, 1); } .fuel-connectors .fuel-connectors-close-icon:hover, .fuel-connectors .fuel-connectors-close-icon:active { opacity: 1; background-color: var(--fuel-connector-hover); } .fuel-connectors .fuel-connectors-button-base { cursor: pointer; } .fuel-connectors .fuel-connectors-button:disabled { cursor: not-allowed; } .fuel-connectors .fuel-connectors-button { transition: background-color 50ms cubic-bezier(0.16, 1, 0.3, 1); background-color: var(--fuel-green-11); } .fuel-connectors .fuel-connectors-button:disabled { background-color: var(--fuel-border-color); } .fuel-connectors .fuel-connectors-button-disconnect { transition: background-color 50ms cubic-bezier(0.16, 1, 0.3, 1); background-color: var(--fuel-button-background); } .fuel-connectors .fuel-connectors-button-disconnect:hover { background-color: var(--fuel-button-background-hover); } .fuel-connectors .fuel-connectors-link-underline:hover { text-decoration: underline; } @keyframes fuelOverlayShow { from { opacity: 0; } to { opacity: 1; } } @keyframes fuelSpin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } @keyframes fuelContentShow { from { opacity: 0; transform: translate(-50%, -48%) scale(0.96); } to { opacity: 1; transform: translate(-50%, -50%) scale(1); } } @keyframes fuelLoader { 0% { background-position: -468px 0 } 100% { background-position: 468px 0 } }`,
),
);
document.head.appendChild(css);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import type { ConnectorEvent } from 'fuels';
import { useEffect, useMemo, useState } from 'react';
import { Spinner } from '../../../../icons/Spinner';
import { useFuel } from '../../../../providers/FuelHooksProvider';
import { isNativeConnector } from '../../../../utils/isNativeConnector';
import { PREDICATE_DISCLAIMER_KEY } from '../PredicateAddressDisclaimer/PredicateAddressDisclaimer';
import {
ConnectorButton,
ConnectorButtonPrimary,
Expand Down Expand Up @@ -37,7 +39,7 @@ export function Connecting({ className }: ConnectorProps) {
isConnecting,
theme,
cancel,
dialog: { route, connector, retryConnect },
dialog: { route, setRoute, connector, retryConnect },
isConnected,
} = useConnectUI();

Expand All @@ -64,10 +66,23 @@ export function Connecting({ className }: ConnectorProps) {

// Auto-close connecting
useEffect(() => {
if (isConnected && route === Routes.CONNECTING && !isConnecting) {
cancel();
if (isConnected && route === Routes.Connecting && !isConnecting) {
// Connected to a native connector, we can close the dialog
if (connector && isNativeConnector(connector)) {
cancel();
return;
}

// If the connector is not native, let's check if we have already displayed the disclaimer
if (localStorage.getItem(PREDICATE_DISCLAIMER_KEY)) {
cancel();
return;
}

// So we need to show the disclaimer about predicates
setRoute(Routes.PredicateAddressDisclaimer);
}
}, [isConnected, route, isConnecting, cancel]);
}, [isConnected, connector, route, setRoute, isConnecting, cancel]);

// Switching to signing ownership mode
useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export function Connector() {
useEffect(() => {
const ping = async () => {
const isInstalled = await connector.ping();
if (isInstalled) setRoute(Routes.CONNECTING);
if (isInstalled) setRoute(Routes.Connecting);
};

ping();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { useConnectUI } from '../../../../providers/FuelUIProvider';
import { ConnectorButtonPrimary, ConnectorContent } from '../Connector/styles';

export const PREDICATE_DISCLAIMER_KEY = '@fuels/predicate-address-disclaimer';

export function PredicateAddressDisclaimer() {
const { cancel } = useConnectUI();

const onContinueToApplication = () => {
localStorage.setItem(PREDICATE_DISCLAIMER_KEY, Date.now().toString());
cancel();
};

return (
<div>
<ConnectorContent>
<div
style={{
color: 'var(--fuel-blue-a11)',
backgroundColor: 'var(--fuel-blue-a3)',
fontSize: 'var(--fuel-font-size-xs)',
margin: '0 1.2em',
border: '1px solid var(--fuel-blue-6)',
borderRadius: 'var(--fuel-border-radius)',
padding: 12,
}}
>
<b>Please Note:</b> EVM/SVM addresses will differ from your{' '}
<b>Fuel predicate address</b>. This is expected behavior.
<br />
<br />
For more details,{' '}
<a
href="https://github.com/FuelLabs/fuel-connectors/wiki"
target="_blank"
rel="noreferrer"
className="fuel-connectors-link-underline"
style={{
fontWeight: 600,
}}
>
check our documentation
</a>
.
</div>
</ConnectorContent>

<ConnectorButtonPrimary onClick={onContinueToApplication}>
Continue to application
</ConnectorButtonPrimary>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
} from '../Connector/styles';
import { DisclaimerContainer, DisclaimerList } from './styles';

export function ExternalDisclaimer() {
export function PredicateExternalDisclaimer() {
const {
dialog: { connector, _startConnection, back },
} = useConnectUI();
Expand Down
15 changes: 9 additions & 6 deletions packages/react/src/ui/Connect/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,20 @@ import {
import { Connecting } from './components/Connector/Connecting';
import { DialogContent } from './components/Core/DialogContent';
import { DialogFuel } from './components/Core/DialogFuel';
import { ExternalDisclaimer } from './components/ExternalDisclaimer/ExternalDisclaimer';
import { PredicateAddressDisclaimer } from './components/PredicateAddressDisclaimer/PredicateAddressDisclaimer';
import { PredicateExternalDisclaimer } from './components/PredicateExternalDisclaimer/PredicateExternalDisclaimer';

const ConnectRoutes = ({ route }: { route: Routes }) => {
switch (route) {
case Routes.LIST:
case Routes.List:
return <Connectors />;
case Routes.INSTALL:
case Routes.Install:
return <Connector />;
case Routes.EXTERNAL_DISCLAIMER:
return <ExternalDisclaimer />;
case Routes.CONNECTING:
case Routes.PredicateExternalDisclaimer:
return <PredicateExternalDisclaimer />;
case Routes.PredicateAddressDisclaimer:
return <PredicateAddressDisclaimer />;
case Routes.Connecting:
return <Connecting />;
default:
return null;
Expand Down
Loading