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

fix(website): offer to switch to right safe chain #1021

Merged
merged 3 commits into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions packages/website/src/features/Deploy/QueueDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -380,14 +380,24 @@ export const QueuedTxns = ({
colorScheme="teal"
w="100%"
isDisabled={
stager.signing ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in accordance with showing a reason any time a button is disabled, can we put some indication somewhere... somehow on the page that says "currently signing txn" to indicate the button is disabled (maybe the user doenst realize their signing window was opened when they click on the button)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch, sure, I'll add it ASAP, thank you

!targetTxn ||
txnHasError ||
!!stager.signConditionFailed ||
queuedIdentifiableTxns.length === 0
}
onClick={() => stager.sign()}
onClick={async () => {
await stager.sign();
}}
>
Stage & Sign
{stager.signing ? (
<>
Currently Signing
<Spinner size="sm" ml={2} />
</>
) : (
'Stage & Sign'
)}
</Button>
</Tooltip>
) : null}
Expand Down
18 changes: 14 additions & 4 deletions packages/website/src/features/Deploy/QueueFromGitOpsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ export default function QueueFromGitOpsPage() {
function QueueFromGitOps() {
const router = useRouter();
const currentSafe = useStore((s) => s.currentSafe);

const [cannonfileUrlInput, setCannonfileUrlInput] = useState('');
const [previousPackageInput, setPreviousPackageInput] = useState('');
const [partialDeployIpfs, setPartialDeployIpfs] = useState('');
Expand Down Expand Up @@ -586,12 +585,23 @@ function QueueFromGitOps() {
{stager.execConditionFailed ? (
<Tooltip label={stager.signConditionFailed}>
<Button
isDisabled={!!stager.signConditionFailed}
isDisabled={
!!stager.signConditionFailed || stager.signing
}
size="lg"
w="100%"
onClick={() => stager.sign()}
onClick={async () => {
await stager.sign();
}}
>
Queue &amp; Sign
{stager.signing ? (
<>
Currently Signing
<Spinner size="sm" ml={2} />
</>
) : (
'Queue & Sign'
)}
</Button>
</Tooltip>
) : null}
Expand Down
15 changes: 12 additions & 3 deletions packages/website/src/features/Deploy/TransactionDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ const TransactionDetailsPage: FC<{
const publicClient = usePublicClient();
const walletChainId = useChainId();
const account = useAccount();

const parsedChainId = parseInt(chainId ?? '0') || 0;
const parsedNonce = parseInt(nonce ?? '0') || 0;

Expand Down Expand Up @@ -390,14 +389,24 @@ const TransactionDetailsPage: FC<{
mb={3}
w="100%"
isDisabled={
stager.signing ||
stager.alreadySigned ||
executionTxnHash ||
((safeTxn &&
!!stager.signConditionFailed) as any)
}
onClick={() => stager.sign()}
onClick={async () => {
await stager.sign();
}}
>
Sign
{stager.signing ? (
<>
Currently Signing
<Spinner size="sm" ml={2} />
</>
) : (
'Sign'
)}
</Button>
</Tooltip>
<Tooltip label={stager.execConditionFailed}>
Expand Down
86 changes: 58 additions & 28 deletions packages/website/src/hooks/backend.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
import SafeABIJSON from '@/abi/Safe.json';
import { SafeDefinition, useStore } from '@/helpers/store';
import { useToast } from '@chakra-ui/react';
import { useSafeAddress } from '@/hooks/safe';
import { SafeTransaction } from '@/types/SafeTransaction';
import { useMutation, useQuery } from '@tanstack/react-query';
import axios from 'axios';
import _ from 'lodash';
import { useEffect, useState } from 'react';
import * as viem from 'viem';
import { useAccount, useChainId, useReadContract, useReadContracts, useSimulateContract, useWalletClient } from 'wagmi';
import {
useAccount,
useChainId,
useReadContract,
useReadContracts,
useSimulateContract,
useWalletClient,
useSwitchChain,
} from 'wagmi';

const SafeABI = SafeABIJSON as viem.Abi;

Expand Down Expand Up @@ -87,16 +96,15 @@ export function useTxnStager(
const account = useAccount();
const walletClient = useWalletClient();
const safeAddress = useSafeAddress();

const [signing, setSigning] = useState(false);
const toast = useToast();
const [alreadyStagedSigners, setAlreadyStagedSigners] = useState<viem.Address[]>([]);

const queryChainId = options.safe?.chainId || chainId.toString();
const querySafeAddress = options.safe?.address || safeAddress;

const stagingUrl = useStore((s) => s.settings.stagingUrl);
const currentSafe = useStore((s) => s.currentSafe);

const { nonce, staged, stagedQuery } = useSafeTransactions((options.safe || currentSafe) as any);
const { switchChainAsync } = useSwitchChain();

const safeTxn: SafeTransaction = {
to: txn.to || viem.zeroAddress,
Expand Down Expand Up @@ -261,36 +269,58 @@ export function useTxnStager(
safeTxn,

sign: async () => {
const signature = await walletClient.data!.signMessage({
account: account.address,
message: { raw: hashToSign as any },
});

const gnosisSignature = viem.toBytes(signature);

// sometimes the signature comes back with a `v` of 0 or 1 when when it should 27 or 28, called a "recid" apparently
// Allow a recid to be used as the v
if (gnosisSignature[gnosisSignature.length - 1] < 27) {
if (gnosisSignature[gnosisSignature.length - 1] === 0 || gnosisSignature[gnosisSignature.length - 1] === 1) {
gnosisSignature[gnosisSignature.length - 1] += 27;
} else {
throw new Error(`signature invalid v byte ${signature}`);
if (signing) return;

setSigning(true);
try {
if (currentSafe?.chainId !== account.chainId) {
await switchChainAsync({
chainId: currentSafe?.chainId as number,
});
}
}

// gnosis for some reason requires adding 4 to the signature version code
gnosisSignature[gnosisSignature.length - 1] += 4;
const signature = await walletClient.data!.signMessage({
account: account.address,
message: { raw: hashToSign as any },
});

const gnosisSignature = viem.toBytes(signature);

await mutation.mutateAsync({
txn: safeTxn,
sig: viem.toHex(gnosisSignature),
});
// sometimes the signature comes back with a `v` of 0 or 1 when when it should 27 or 28, called a "recid" apparently
// Allow a recid to be used as the v
if (gnosisSignature[gnosisSignature.length - 1] < 27) {
if (gnosisSignature[gnosisSignature.length - 1] === 0 || gnosisSignature[gnosisSignature.length - 1] === 1) {
gnosisSignature[gnosisSignature.length - 1] += 27;
} else {
throw new Error(`signature invalid v byte ${signature}`);
}
}

// gnosis for some reason requires adding 4 to the signature version code
gnosisSignature[gnosisSignature.length - 1] += 4;

await mutation.mutateAsync({
txn: safeTxn,
sig: viem.toHex(gnosisSignature),
});

if (options.onSignComplete) {
options.onSignComplete();
if (options.onSignComplete) {
options.onSignComplete();
}
} catch (e: any) {
toast({
title: e.message || 'Failed to sign transaction.',
status: 'error',
duration: 5000,
isClosable: true,
});
} finally {
setSigning(false);
}
},

signing,

signMutation: mutation,

existingSigners: alreadyStagedSigners,
Expand Down
Loading