Skip to content

Commit

Permalink
WIP nonce migration
Browse files Browse the repository at this point in the history
  • Loading branch information
Robinnnnn committed Mar 27, 2024
1 parent e17f3d9 commit 0e79485
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { signMessage } from '@wagmi/core';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useFragment } from 'react-relay';
import { graphql } from 'relay-runtime';
import { useGetUserByWalletAddressImperatively } from 'shared/hooks/useGetUserByWalletAddress';
import styled from 'styled-components';
import { useAccount } from 'wagmi';

Expand Down Expand Up @@ -80,6 +81,7 @@ export const EthereumAddWallet = ({ queryRef, reset, onSuccess = noop }: Props)
const { hideModal } = useModalActions();

const createNonce = useCreateNonce();
const getUserByWalletAddress = useGetUserByWalletAddressImperatively();
const trackAddWalletAttempt = useTrackAddWalletAttempt();
const trackAddWalletSuccess = useTrackAddWalletSuccess();
const trackAddWalletError = useTrackAddWalletError();
Expand All @@ -98,19 +100,23 @@ export const EthereumAddWallet = ({ queryRef, reset, onSuccess = noop }: Props)
setPendingState(PROMPT_SIGNATURE);

trackAddWalletAttempt('Ethereum');
const { nonce, user_exists: userExists } = await createNonce(address, 'Ethereum');

const userExists = Boolean(await getUserByWalletAddress({ address, chain: 'Ethereum' }));

if (userExists) {
throw { code: 'EXISTING_USER' } as Web3Error;
}

const signature = await signMessage({ message: nonce });
const { nonce, message } = await createNonce();

const signature = await signMessage({ message });

const { signatureValid } = await addWallet({
authMechanism: {
eoa: {
signature,
nonce,
message,
signature,
chainPubKey: {
pubKey: address,
chain: 'Ethereum',
Expand Down Expand Up @@ -149,12 +155,13 @@ export const EthereumAddWallet = ({ queryRef, reset, onSuccess = noop }: Props)
},
[
trackAddWalletAttempt,
getUserByWalletAddress,
createNonce,
addWallet,
hideModal,
trackAddWalletSuccess,
trackAddWalletError,
hideModal,
onSuccess,
trackAddWalletError,
]
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { captureException } from '@sentry/nextjs';
import { signMessage } from '@wagmi/core';
import { useCallback, useEffect, useState } from 'react';
import { useGetUserByWalletAddressImperatively } from 'shared/hooks/useGetUserByWalletAddress';
import { useAccount } from 'wagmi';

import { EmptyState } from '~/components/EmptyState/EmptyState';
Expand Down Expand Up @@ -28,6 +29,7 @@ export const EthereumAuthenticateWallet = ({ reset }: Props) => {
const [pendingState, setPendingState] = useState<PendingState>(INITIAL);

const createNonce = useCreateNonce();
const getUserByWalletAddress = useGetUserByWalletAddressImperatively();
const [loginOrRedirectToOnboarding] = useLoginOrRedirectToOnboarding();

const trackSignInAttempt = useTrackSignInAttempt();
Expand All @@ -46,9 +48,11 @@ export const EthereumAuthenticateWallet = ({ reset }: Props) => {
setPendingState(PROMPT_SIGNATURE);
trackSignInAttempt('Ethereum');

const { nonce, user_exists: userExists } = await createNonce(address, 'Ethereum');
const { nonce, message } = await createNonce();

const signature = await signMessage({ message: nonce });
const signature = await signMessage({ message });

const userExists = Boolean(await getUserByWalletAddress({ address, chain: 'Ethereum' }));

const userId = await loginOrRedirectToOnboarding({
authMechanism: {
Expand All @@ -59,6 +63,7 @@ export const EthereumAuthenticateWallet = ({ reset }: Props) => {
pubKey: address,
},
nonce,
message,
signature,
},
},
Expand All @@ -70,7 +75,13 @@ export const EthereumAuthenticateWallet = ({ reset }: Props) => {
trackSignInSuccess('Ethereum');
}
},
[trackSignInAttempt, createNonce, loginOrRedirectToOnboarding, trackSignInSuccess]
[
trackSignInAttempt,
createNonce,
getUserByWalletAddress,
loginOrRedirectToOnboarding,
trackSignInSuccess,
]
);

useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { captureException } from '@sentry/nextjs';
import { useWeb3React } from '@web3-react/core';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { graphql, useFragment } from 'react-relay';
import { useGetUserByWalletAddressImperatively } from 'shared/hooks/useGetUserByWalletAddress';

import { VStack } from '~/components/core/Spacer/Stack';
import { BaseM } from '~/components/core/Text/Text';
Expand Down Expand Up @@ -56,6 +57,7 @@ export const GnosisSafeAddWallet = ({ queryRef, reset }: Props) => {

const previousAttemptNonce = useMemo(() => getLocalStorageItem(GNOSIS_NONCE_STORAGE_KEY), []);
const [nonce, setNonce] = useState('');
const [message, setMessage] = useState('');
const [userExists, setUserExists] = useState(false);
const [authenticationFlowStarted, setAuthenticationFlowStarted] = useState(false);
const [error, setError] = useState<Error>();
Expand Down Expand Up @@ -127,7 +129,7 @@ export const GnosisSafeAddWallet = ({ queryRef, reset }: Props) => {

const addWallet = useAddWallet();
const authenticateWithBackend = useCallback(
async (address: string, nonce: string) => {
async ({ address, nonce, message }: { address: string; nonce: string; message: string }) => {
const { signatureValid } = await addWallet({
chainAddress: {
address,
Expand All @@ -137,6 +139,7 @@ export const GnosisSafeAddWallet = ({ queryRef, reset }: Props) => {
gnosisSafe: {
address,
nonce,
message,
},
},
});
Expand All @@ -154,21 +157,35 @@ export const GnosisSafeAddWallet = ({ queryRef, reset }: Props) => {
// Initiates the full authentication flow including signing the message, listening for the signature, validating it. then calling the backend
// This is the default flow
const attemptAddWallet = useCallback(
async (address: string, nonce: string, userExists: boolean) => {
async ({
address,
nonce,
message,
userExists,
}: {
address: string;
nonce: string;
message: string;
userExists: boolean;
}) => {
try {
if (userExists) {
throw { code: 'EXISTING_USER' } as Web3Error;
}

setPendingState(PROMPT_SIGNATURE);
trackAddWalletAttempt('Gnosis Safe');
await signMessageWithContractAccount(address, nonce, walletconnect);
window.localStorage.setItem(GNOSIS_NONCE_STORAGE_KEY, JSON.stringify(nonce));
await signMessageWithContractAccount(address, message, walletconnect);
window.localStorage.setItem(GNOSIS_NONCE_STORAGE_KEY, JSON.stringify(message));

setPendingState(LISTENING_ONCHAIN);
await listenForGnosisSignature(address, nonce, walletconnect);
await listenForGnosisSignature(address, message, walletconnect);

await authenticateWithBackend(address, nonce);
await authenticateWithBackend({
address,
nonce,
message,
});
} catch (error) {
handleError(error);
}
Expand All @@ -187,30 +204,36 @@ export const GnosisSafeAddWallet = ({ queryRef, reset }: Props) => {

try {
// Immediately check if the message has already been signed and executed on chain
const wasSigned = await validateNonceSignedByGnosis(account, nonce, walletconnect);
const wasSigned = await validateNonceSignedByGnosis(account, message, walletconnect);
if (wasSigned) {
await authenticateWithBackend(account, nonce);
await authenticateWithBackend({ address: account.toLowerCase(), nonce, message });
}

// If it hasn't, set up a listener because the transaction may not have been executed yet
if (pendingState !== LISTENING_ONCHAIN) {
setPendingState(LISTENING_ONCHAIN);
await listenForGnosisSignature(account, nonce, walletconnect);
await listenForGnosisSignature(account, message, walletconnect);
// Once signed, call the backend as usual
void authenticateWithBackend(account, nonce);
void authenticateWithBackend({ address: account.toLowerCase(), nonce, message });
}
} catch (error) {
handleError(error);
}
}, [account, authenticateWithBackend, handleError, pendingState, nonce]);
}, [account, nonce, pendingState, authenticateWithBackend, message, handleError]);

const restartAuthentication = useCallback(async () => {
if (account) {
await attemptAddWallet(account.toLowerCase(), nonce, userExists);
await attemptAddWallet({
address: account.toLowerCase(),
nonce,
message,
userExists,
});
}
}, [account, attemptAddWallet, nonce, userExists]);
}, [account, attemptAddWallet, message, nonce, userExists]);

const createNonce = useCreateNonce();
const getUserByWalletAddress = useGetUserByWalletAddressImperatively();

// This runs once to auto-initiate the authentication flow, when wallet is first connected (ie when 'account' is defined)
useEffect(() => {
Expand All @@ -222,20 +245,32 @@ export const GnosisSafeAddWallet = ({ queryRef, reset }: Props) => {
setAuthenticationFlowStarted(true);

const account = await connectGnosisSafe();
if (authenticatedUserAddresses.includes(account.toLowerCase())) {
const address = account.toLowerCase();
if (authenticatedUserAddresses.includes(address)) {
setPendingState(ADDRESS_ALREADY_CONNECTED);
return;
}

const { nonce, user_exists: userExists } = await createNonce(account, 'Ethereum');
const { nonce, message } = await createNonce();

const userExistsOnGallery = Boolean(
await getUserByWalletAddress({ address, chain: 'Ethereum' })
);

setNonce(nonce);
setUserExists(userExists);
setMessage(message);
setUserExists(userExistsOnGallery);

if (nonce === previousAttemptNonce) {
return;
}

await attemptAddWallet(account.toLowerCase(), nonce, userExists);
await attemptAddWallet({
address,
nonce,
message,
userExists: userExistsOnGallery,
});
}

void initiateAuthentication().catch(handleError);
Expand All @@ -247,6 +282,7 @@ export const GnosisSafeAddWallet = ({ queryRef, reset }: Props) => {
handleError,
previousAttemptNonce,
createNonce,
getUserByWalletAddress,
]);

if (error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export default function useLoginOrRedirectToOnboarding() {
chain: authMechanism.mechanism.eoa.chainPubKey.chain,
address: authMechanism.mechanism.eoa.chainPubKey.pubKey,
nonce: authMechanism.mechanism.eoa.nonce,
message: authMechanism.mechanism.eoa.message,
signature: authMechanism.mechanism.eoa.signature,
userFriendlyWalletName,
},
Expand All @@ -99,6 +100,7 @@ export default function useLoginOrRedirectToOnboarding() {
authMechanismType: 'gnosisSafe',
address: authMechanism.mechanism.gnosisSafe.address,
nonce: authMechanism.mechanism.gnosisSafe.nonce,
message: authMechanism.mechanism.gnosisSafe.message,
userFriendlyWalletName,
},
});
Expand Down
Loading

0 comments on commit 0e79485

Please sign in to comment.