diff --git a/.github/workflows/check-branch-name.yml b/.github/workflows/check-branch-name.yml index 23a70c3be..028a190ec 100644 --- a/.github/workflows/check-branch-name.yml +++ b/.github/workflows/check-branch-name.yml @@ -18,15 +18,17 @@ jobs: switch(base) { case 'main': case 'alpha': { - if (!/^hotfix\/[a-z0-9-_]+$/.test(branch)) { + if (!/^hotfix\/[A-Za-z0-9-_\.]+$/.test(branch)) { console.error(`::error::Branch name must start with 'hotfix/*' for PRs to ${base}`) process.exit(1) } + break; } case 'dev': { - if (!/^(feat|fix|chore|docs|test)\/[a-z0-9-_]+$/.test(branch)) { + if (!/^(feat|fix|chore|docs|test)\/[A-Za-z0-9-_\.]+$/.test(branch)) { console.error(`::error::Branch name must start with '(feat|fix|chore|docs|test)/*' for PRs to ${base}`) process.exit(1) } + break; } } diff --git a/.gitignore b/.gitignore index 0b9cbce77..46f3db973 100644 --- a/.gitignore +++ b/.gitignore @@ -72,5 +72,5 @@ jspm_packages/ # local env vars .env.local -# nx +# nx cache folder .nx diff --git a/examples/sample-hardhat-toolbox/package.json b/examples/sample-hardhat-toolbox/package.json index 57d638a4d..c69becb21 100644 --- a/examples/sample-hardhat-toolbox/package.json +++ b/examples/sample-hardhat-toolbox/package.json @@ -9,6 +9,6 @@ "@nomicfoundation/hardhat-toolbox": "^4.0.0", "hardhat": "^2.19.2", "hardhat-cannon": "workspace:*", - "viem": "^2.16.5" + "viem": "^2.21.15" } } diff --git a/packages/api/package.json b/packages/api/package.json index 56296a90b..174b3be19 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -33,7 +33,7 @@ "helmet": "^7.1.0", "prom-client": "^15.1.2", "redis": "^4.6.13", - "viem": "^2.16.5" + "viem": "^2.21.15" }, "devDependencies": { "@types/cors": "^2.8.17", diff --git a/packages/builder/jest.config.ts b/packages/builder/jest.config.ts index e7d6cc838..5bb0bbd21 100644 --- a/packages/builder/jest.config.ts +++ b/packages/builder/jest.config.ts @@ -4,7 +4,7 @@ */ export default { - testTimeout: 15000, + testTimeout: 30000, // All imported modules in your tests should be mocked automatically // automock: false, diff --git a/packages/builder/package.json b/packages/builder/package.json index 654d1a2c5..47a6b9747 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -54,7 +54,7 @@ "pako": "^2.1.0", "promise-events": "^0.2.4", "typestub-ipfs-only-hash": "^4.0.0", - "viem": "^2.16.5", + "viem": "^2.21.15", "zod": "^3.23.6" }, "engines": { diff --git a/packages/builder/src/ipfs.ts b/packages/builder/src/ipfs.ts index e64448139..951c1d9f1 100644 --- a/packages/builder/src/ipfs.ts +++ b/packages/builder/src/ipfs.ts @@ -24,6 +24,12 @@ export async function getContentCID(value: string | Buffer): Promise { return Hash.of(value); } +export async function getContentUrl(content?: any): Promise { + if (!content) return null; + const buffer = compress(JSON.stringify(content)); + return 'ipfs://' + (await getContentCID(Buffer.from(buffer))); +} + export function setAxiosRetries(totalRetries = 3) { axiosRetry(axios, { retries: totalRetries, diff --git a/packages/builder/src/steps/clone.ts b/packages/builder/src/steps/clone.ts index c4186a959..cf8f07653 100644 --- a/packages/builder/src/steps/clone.ts +++ b/packages/builder/src/steps/clone.ts @@ -18,6 +18,7 @@ import { PackageState, } from '../types'; import { template } from '../utils/template'; +import { getContentUrl } from '../ipfs'; const debug = Debug('cannon:builder:clone'); @@ -238,7 +239,7 @@ const cloneSpec = { }; } - const newMiscUrl = await importRuntime.recordMisc(); + const newMiscUrl = await getContentUrl(importRuntime.misc); debug(`[clone.${importLabel}]`, 'new misc:', newMiscUrl); @@ -256,6 +257,12 @@ const cloneSpec = { chainId: runtime.chainId, }); + const uploadedMiscUrl = await importRuntime.recordMisc(); + + if (newMiscUrl && newMiscUrl !== uploadedMiscUrl) { + throw new Error(`Misc url mismatch: ${newMiscUrl} | ${uploadedMiscUrl}`); + } + if (!newSubDeployUrl) { debug(`[clone.${importLabel}]`, 'warn: cannot record built state for import nested state'); } else { diff --git a/packages/builder/src/steps/deploy.ts b/packages/builder/src/steps/deploy.ts index 6ce91b837..dde9823df 100644 --- a/packages/builder/src/steps/deploy.ts +++ b/packages/builder/src/steps/deploy.ts @@ -216,7 +216,8 @@ const deploySpec = { }, getOutputs(_: Config, packageState: PackageState) { - return [`contracts.${packageState.currentLabel.split('.')[1]}`, `${packageState.currentLabel.split('.')[1]}`]; + const stepName = packageState.currentLabel.split('.')[1]; + return [`contracts.${stepName}`, stepName]; }, async exec( diff --git a/packages/cli/package.json b/packages/cli/package.json index fb4cf85b6..1b3e5a4a1 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -78,7 +78,7 @@ "table": "^6.8.2", "tildify": "3.0.0", "untildify": "^4.0.0", - "viem": "^2.16.5", + "viem": "^2.21.15", "znv": "^0.4.0", "zod": "^3.23.8" }, diff --git a/packages/hardhat-cannon/package.json b/packages/hardhat-cannon/package.json index ae7e08f8b..52fbed247 100644 --- a/packages/hardhat-cannon/package.json +++ b/packages/hardhat-cannon/package.json @@ -52,7 +52,7 @@ "chalk": "^4.1.2", "debug": "^4.3.6", "fs-extra": "^11.2.0", - "viem": "^2.16.5" + "viem": "^2.21.15" }, "gitHead": "81979336c4cfe5f1f8ee7677b2b968f886a20a87" } diff --git a/packages/indexer/package.json b/packages/indexer/package.json index 82bb4b373..cc7e04d37 100644 --- a/packages/indexer/package.json +++ b/packages/indexer/package.json @@ -22,7 +22,7 @@ "envalid": "^8.0.0", "lodash": "^4.17.21", "redis": "^4.6.13", - "viem": "^2.16.5" + "viem": "^2.21.15" }, "devDependencies": { "@types/connect-busboy": "^1.0.3", diff --git a/packages/website/package.json b/packages/website/package.json index 83b697fd1..60ceb2334 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -88,7 +88,7 @@ "remark-gfm": "^3.0.1", "simple-url": "^1.1.8", "styled-components": "^6.0.7", - "viem": "2.16.5", + "viem": "^2.21.15", "wagmi": "^2.5.13", "zod": "^3.23.8", "zod-to-json-schema": "^3.21.4", diff --git a/packages/website/src/components/Alert.tsx b/packages/website/src/components/Alert.tsx index 8bf30d9ae..94806aea6 100644 --- a/packages/website/src/components/Alert.tsx +++ b/packages/website/src/components/Alert.tsx @@ -9,22 +9,30 @@ import { type Props = { status?: 'info' | 'success' | 'error' | 'warning'; + borderless?: boolean; title?: string; - children: ReactNode; + children?: ReactNode; } & ChakraAlertProps; -export function Alert({ status = 'info', title, children, ...rest }: Props) { +export function Alert({ + status = 'info', + borderless, + title, + children, + ...rest +}: Props) { return ( {title && {title}} - {children} + {children && {children}} ); } diff --git a/packages/website/src/features/Deploy/NoncePicker.tsx b/packages/website/src/features/Deploy/NoncePicker.tsx index ed51ea336..9efc0fdae 100644 --- a/packages/website/src/features/Deploy/NoncePicker.tsx +++ b/packages/website/src/features/Deploy/NoncePicker.tsx @@ -12,7 +12,7 @@ export default function NoncePicker({ safe, handleChange }: Params) { const [isOverridingNonce, setNonceOverride] = useState(false); const [currentNonce, setCurrentNonce] = useState(null); - const safeTxs = useSafeTransactions(safe); + const safeTxs = useSafeTransactions(safe, Number.POSITIVE_INFINITY); useEffect(() => { handleChange(currentNonce); diff --git a/packages/website/src/features/Deploy/QueueFromGitOpsPage.tsx b/packages/website/src/features/Deploy/QueueFromGitOpsPage.tsx index ee2d0fce4..62f837fea 100644 --- a/packages/website/src/features/Deploy/QueueFromGitOpsPage.tsx +++ b/packages/website/src/features/Deploy/QueueFromGitOpsPage.tsx @@ -1,18 +1,18 @@ 'use client'; import { links } from '@/constants/links'; -import { parseIpfsHash } from '@/helpers/ipfs'; -import { makeMultisend } from '@/helpers/multisend'; +import { useMultisendQuery } from '@/helpers/multisend'; import * as onchainStore from '@/helpers/onchain-store'; import { useStore } from '@/helpers/store'; import { useTxnStager } from '@/hooks/backend'; import { useDeployerWallet } from '@/hooks/deployer'; import { - useCannonBuild, useCannonPackage, useCannonWriteDeployToIpfs, useLoadCannonDefinition, useCannonFindUpgradeFromUrl, + CannonWriteDeployToIpfsMutationResult, + useCannonBuildTmp, } from '@/hooks/cannon'; import { useGitRefsList } from '@/hooks/git'; import { useGetPreviousGitInfoQuery } from '@/hooks/safe'; @@ -40,15 +40,16 @@ import { Tooltip, useToast, VStack, - Radio, - RadioGroup, - Stack, - Checkbox, } from '@chakra-ui/react'; import { useConnectModal } from '@rainbow-me/rainbowkit'; -import { ChainBuilderContext, DeploymentInfo } from '@usecannon/builder'; +import { + ChainBuilderContext, + DeploymentInfo, + PackageReference, +} from '@usecannon/builder'; import NextLink from 'next/link'; import { useRouter } from 'next/navigation'; +import { Alert as AlertCannon } from '@/components/Alert'; import React, { useEffect, useMemo, useState } from 'react'; import { encodeAbiParameters, @@ -69,158 +70,173 @@ import { ChainDefinition } from '@usecannon/builder/dist/src'; const EMPTY_IPFS_MISC_URL = 'ipfs://QmeSt2mnJKE8qmRhLyYbHQQxDKpsFbcWnw5e7JF4xVbN6k'; -export default function QueueFromGitOpsPage() { - return ; +const cannonfileUrlRegex = + // eslint-disable-next-line no-useless-escape + /^(https?:\/\/)?([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?\.toml$/i; + +// TODO: is there any way to make a better context? maybe this means we should get rid of name using context? +const ctx: ChainBuilderContext = { + chainId: 0, + package: {}, + timestamp: 0 as any, // TODO: fix this + settings: {}, + contracts: {}, + txns: {}, + imports: {}, + overrideSettings: {}, +}; + +function useMergedCannonDefInfo( + gitUrl: string, + gitRef: string, + gitFile: string, + partialDeployIpfs: string, + chainId?: number +) { + const originalCannonDefInfo = useLoadCannonDefinition( + gitUrl, + gitRef, + gitFile + ); + + const partialDeployInfo = useCannonPackage( + partialDeployIpfs ? `ipfs://${partialDeployIpfs}` : '', + chainId + ); + + return useMemo(() => { + const isLoading = + originalCannonDefInfo.isLoading || partialDeployInfo?.isLoading; + const isError = originalCannonDefInfo.isError || partialDeployInfo?.isError; + const isFetching = + originalCannonDefInfo.isFetching || partialDeployInfo?.isFetching; + const error = partialDeployInfo?.error || originalCannonDefInfo.error; + + // Merge the definitions if partial deploy info is available + const def = partialDeployInfo?.pkg + ? new ChainDefinition(partialDeployInfo.pkg.def) + : originalCannonDefInfo.def; + + return { + isLoading, + isFetching, + isError, + error, + def, + }; + }, [originalCannonDefInfo, partialDeployInfo]); } -function QueueFromGitOps() { - const [selectedDeployType, setSelectedDeployType] = useState('new'); - const [overridePreviousState, setOverridePreviousState] = useState(false); +type DeployType = 'git' | 'partial'; + +export default function QueueFromGitOps() { + const [selectedDeployType, setSelectedDeployType] = + useState('git'); const router = useRouter(); const currentSafe = useStore((s) => s.currentSafe)!; const { chainId, isConnected } = useAccount(); const { switchChainAsync } = useSwitchChain(); + + const [genericInput, setGenericInput] = useState(''); const [cannonfileUrlInput, setCannonfileUrlInput] = useState(''); - const [previousPackageInput, setPreviousPackageInput] = useState(''); const [partialDeployIpfs, setPartialDeployIpfs] = useState(''); + const [prevPackageInputRef, setPrevPackageInputRef] = + useState(null); + + const [previousPackageInput, setPreviousPackageInput] = useState(''); const [pickedNonce, setPickedNonce] = useState(null); const { openConnectModal } = useConnectModal(); + const [writeToIpfsMutationRes, setWriteToIpfsMutationRes] = useState<{ + isLoading: boolean; + error: Error | null; + data: CannonWriteDeployToIpfsMutationResult | null; + } | null>(null); + const settings = useStore((s) => s.settings); const deployer = useDeployerWallet(currentSafe?.chainId); - const cannonfileUrlRegex = - // eslint-disable-next-line no-useless-escape - /^(https?:\/\/)?([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?\.toml$/i; - - const gitUrl = useMemo(() => { - if (!cannonfileUrlRegex.test(cannonfileUrlInput)) { - return ''; - } - - if (!cannonfileUrlInput.includes('/blob/')) { - return ''; - } - - return cannonfileUrlInput.split('/blob/')[0]; - }, [cannonfileUrlInput]); - - const gitRef = useMemo(() => { - if (!cannonfileUrlRegex.test(cannonfileUrlInput)) { - return ''; - } - - if (!cannonfileUrlInput.includes('/blob/')) { - return ''; - } - - const branchAndFile = cannonfileUrlInput.split('/blob/')[1]; - if (!branchAndFile) { - return ''; + const { gitUrl, gitRef, gitFile } = useMemo(() => { + if ( + !cannonfileUrlRegex.test(cannonfileUrlInput) || + !cannonfileUrlInput.includes('/blob/') + ) { + return { gitUrl: '', gitRef: '', gitFile: '' }; } - const branchName = branchAndFile.split('/')[0]; - if (!branchName) { - return ''; - } + const [url, blobPath] = cannonfileUrlInput.split('/blob/'); + const urlComponents = blobPath.split('/'); + const branchName = urlComponents[0]; + const filePath = urlComponents.slice(1).join('/'); - return branchName; + return { + gitUrl: url, + gitRef: branchName, + gitFile: filePath, + }; }, [cannonfileUrlInput]); - const gitFile = useMemo(() => { - if (!cannonfileUrlRegex.test(cannonfileUrlInput)) { - return ''; - } - - if (!cannonfileUrlInput.includes('/blob/')) { - return ''; - } - - const branchAndFile = cannonfileUrlInput.split('/blob/')[1]; - if (!branchAndFile) { - return ''; - } - - const urlComponents = branchAndFile.split('/'); - urlComponents.shift(); - return urlComponents.join('/'); - }, [cannonfileUrlInput]); + const partialDeployInfo = useCannonPackage( + partialDeployIpfs ? `ipfs://${partialDeployIpfs}` : '', + currentSafe?.chainId + ); - let cannonDefInfo = useLoadCannonDefinition(gitUrl, gitRef, gitFile); + const cannonDefInfo = useMergedCannonDefInfo( + gitUrl, + gitRef, + gitFile, + partialDeployIpfs, + currentSafe?.chainId + ); const cannonDefInfoError: string = gitUrl - ? (cannonDefInfo.error as any)?.toString() + ? (cannonDefInfo?.error as any)?.toString() : cannonfileUrlInput && 'The format of your URL appears incorrect. Please double check and try again.'; - const partialDeployInfo = useCannonPackage( - partialDeployIpfs ? `ipfs://${partialDeployIpfs}` : '' - ); - - // If its a partial deployment, create the chain definition from the - // IPFS partial deployment data - if (partialDeployInfo.pkg) { - cannonDefInfo = { - ...cannonDefInfo, - isLoading: partialDeployInfo.isLoading, - isFetching: partialDeployInfo.isFetching, - isError: partialDeployInfo.isError, - error: partialDeployInfo.error, - def: new ChainDefinition(partialDeployInfo.pkg.def), - }; - } - - // TODO: is there any way to make a better context? maybe this means we should get rid of name using context? - const ctx: ChainBuilderContext = { - chainId: 0, - package: {}, - timestamp: 0, - settings: {}, - contracts: {}, - txns: {}, - imports: {}, - overrideSettings: {}, - }; - - const settings = useStore((s) => s.settings); - - const fullPackageRef = cannonDefInfo.def?.getPackageRef(ctx) ?? null; + const fullPackageRef = cannonDefInfo?.def?.getPackageRef(ctx) ?? null; const onChainPrevPkgQuery = useCannonFindUpgradeFromUrl( - fullPackageRef || undefined, - chainId, - cannonDefInfo.def?.getDeployers() + prevPackageInputRef || fullPackageRef || undefined, + currentSafe?.chainId, + cannonDefInfo?.def?.getDeployers() ); const prevDeployLocation = partialDeployIpfs ? `ipfs://${partialDeployIpfs}` : onChainPrevPkgQuery.url || ''; - const prevCannonDeployInfo = useCannonPackage(prevDeployLocation); + const prevCannonDeployInfo = useCannonPackage( + prevDeployLocation, + currentSafe?.chainId + ); + + useEffect(() => { + if (previousPackageInput) { + setPrevPackageInputRef(new PackageReference(previousPackageInput)); + } else { + setPrevPackageInputRef(null); + } + }, [previousPackageInput]); useEffect(() => { - if (!cannonDefInfo.def) return setPreviousPackageInput(''); + if (!cannonDefInfo?.def) return setPreviousPackageInput(''); - const name = cannonDefInfo.def.getName(ctx); + const name = cannonDefInfo?.def.getName(ctx); const version = 'latest'; - const preset = cannonDefInfo.def.getPreset(ctx); + const preset = cannonDefInfo?.def.getPreset(ctx); setPreviousPackageInput(`${name}:${version}@${preset}`); - if (selectedDeployType == 'new') setSelectedDeployType('upgrade'); - }, [cannonDefInfo.def, ctx, selectedDeployType]); + }, [cannonDefInfo?.def, selectedDeployType]); // run the build and get the list of transactions we need to run - const buildInfo = useCannonBuild( - currentSafe, - cannonDefInfo.def, - prevCannonDeployInfo.pkg - ); + const { buildState, doBuild, resetState } = useCannonBuildTmp(currentSafe); const nextCannonDeployInfo = useMemo(() => { - return cannonDefInfo.def + return cannonDefInfo?.def ? ({ generator: `cannon website ${pkg.version}`, timestamp: Math.floor(Date.now() / 1000), def: cannonDefInfo.def.toJson(), - state: buildInfo.buildResult?.state || {}, + state: buildState.result?.state || {}, options: prevCannonDeployInfo.pkg?.options || {}, meta: prevCannonDeployInfo.pkg?.meta, miscUrl: prevCannonDeployInfo.pkg?.miscUrl || EMPTY_IPFS_MISC_URL, @@ -228,28 +244,47 @@ function QueueFromGitOps() { } satisfies DeploymentInfo) : undefined; }, [ - buildInfo.buildResult?.state, - cannonDefInfo.def, + buildState.result?.state, + cannonDefInfo?.def, currentSafe.chainId, prevCannonDeployInfo.pkg?.meta, prevCannonDeployInfo.pkg?.miscUrl, prevCannonDeployInfo.pkg?.options, ]); - const uploadToPublishIpfs = useCannonWriteDeployToIpfs( - buildInfo.buildResult?.runtime, - nextCannonDeployInfo, - prevCannonDeployInfo.metaUrl - ); + const writeToIpfsMutation = useCannonWriteDeployToIpfs(); useEffect(() => { - if (['success', 'error'].includes(buildInfo.buildStatus)) { - uploadToPublishIpfs.writeToIpfsMutation.mutate(); - } - }, [ - buildInfo.buildStatus, - // DO NOT ADD: uploadToPublishIpfs.writeToIpfsMutation - ]); + const callMutation = async () => { + if (['success'].includes(buildState.status)) { + try { + setWriteToIpfsMutationRes({ + isLoading: true, + error: null, + data: null, + }); + const res = await writeToIpfsMutation.mutateAsync({ + runtime: buildState.result?.runtime, + deployInfo: nextCannonDeployInfo, + metaUrl: prevCannonDeployInfo.metaUrl, + }); + setWriteToIpfsMutationRes({ + isLoading: false, + error: null, + data: res, + }); + } catch (error) { + setWriteToIpfsMutationRes({ + isLoading: false, + error: error as Error, + data: null, + }); + } + } + }; + + void callMutation(); + }, [buildState.status]); // TODO fix this const refsInfo = useGitRefsList(gitUrl); const foundRef = refsInfo.refs?.find( @@ -264,102 +299,115 @@ function QueueFromGitOps() { gitUrl + ':' + gitFile ); - const multicallTxn: /*Partial*/ any = - buildInfo.buildResult && - !prevInfoQuery.isLoading && - buildInfo.buildResult.safeSteps.indexOf(null as any) === -1 - ? makeMultisend( + const multisendTxsParam = useMemo(() => { + return [ + // supply the hint data + { + to: zeroAddress, + data: encodeAbiParameters( + [{ type: 'string[]' }], [ - // supply the hint data - { - to: zeroAddress, - data: encodeAbiParameters( - [{ type: 'string[]' }], - [ - [ - 'deploy', - uploadToPublishIpfs.deployedIpfsHash, - prevDeployLocation || '', - gitUrl && gitFile ? `${gitUrl}:${gitFile}` : '', - gitHash || '', - prevInfoQuery.data && - typeof prevInfoQuery.data?.[0].result == 'string' && - (prevInfoQuery.data[0].result as any).length > 2 - ? ((prevInfoQuery.data[0].result as any).slice(2) as any) - : '', - ], - ] - ), - } as Partial, - // write data needed for the subsequent deployment to chain - gitUrl && gitFile - ? ({ - to: onchainStore.deployAddress, - data: encodeFunctionData({ - abi: onchainStore.ABI, - functionName: 'set', - args: [ - keccak256(toBytes(`${gitUrl}:${gitFile}gitHash`)), - '0x' + gitHash, - ], - }), - } as Partial) - : {}, - gitUrl && gitFile - ? ({ - to: onchainStore.deployAddress, - data: encodeFunctionData({ - abi: onchainStore.ABI, - functionName: 'set', - args: [ - keccak256(toBytes(`${gitUrl}:${gitFile}cannonPackage`)), - stringToHex(uploadToPublishIpfs.deployedIpfsHash ?? ''), - ], - }), - } as Partial) - : {}, - { - to: onchainStore.deployAddress, - data: encodeFunctionData({ - abi: onchainStore.ABI, - functionName: 'set', - args: [ - keccak256( - toBytes( - cannonDefInfo.def - ? `${cannonDefInfo.def.getName( - ctx - )}@${cannonDefInfo.def.getPreset(ctx)}` - : '' - ) - ), - // TODO: we would really rather have the timestamp be when the txn was executed. something to fix when we have a new state contract - stringToHex( - `${Math.floor(Date.now() / 1000)}_${ - uploadToPublishIpfs.deployedIpfsHash ?? '' - }` - ), - ], - }), - } as Partial, - ].concat( - buildInfo.buildResult.safeSteps.map( - (s) => s.tx as unknown as Partial - ) - ) - ) - : { value: BigInt(0) }; + [ + 'deploy', + writeToIpfsMutationRes?.data?.mainUrl, + prevDeployLocation || '', + gitUrl && gitFile ? `${gitUrl}:${gitFile}` : '', + gitHash || '', + prevInfoQuery.data && + typeof prevInfoQuery.data?.[0].result == 'string' && + (prevInfoQuery.data[0].result as any).length > 2 + ? ((prevInfoQuery.data[0].result as any).slice(2) as any) + : '', + ], + ] + ), + } as Partial, + // write data needed for the subsequent deployment to chain + gitUrl && gitFile + ? ({ + to: onchainStore.deployAddress, + data: encodeFunctionData({ + abi: onchainStore.ABI, + functionName: 'set', + args: [ + keccak256(toBytes(`${gitUrl}:${gitFile}gitHash`)), + '0x' + gitHash, + ], + }), + } as Partial) + : {}, + gitUrl && gitFile + ? ({ + to: onchainStore.deployAddress, + data: encodeFunctionData({ + abi: onchainStore.ABI, + functionName: 'set', + args: [ + keccak256(toBytes(`${gitUrl}:${gitFile}cannonPackage`)), + stringToHex(writeToIpfsMutationRes?.data?.mainUrl ?? ''), + ], + }), + } as Partial) + : {}, + { + to: onchainStore.deployAddress, + data: encodeFunctionData({ + abi: onchainStore.ABI, + functionName: 'set', + args: [ + keccak256( + toBytes( + cannonDefInfo?.def + ? `${cannonDefInfo.def.getName( + ctx + )}@${cannonDefInfo.def.getPreset(ctx)}` + : '' + ) + ), + // TODO: we would really rather have the timestamp be when the txn was executed. something to fix when we have a new state contract + stringToHex( + `${Math.floor(Date.now() / 1000)}_${ + writeToIpfsMutationRes?.data?.mainUrl ?? '' + }` + ), + ], + }), + } as Partial, + ].concat( + buildState.result?.safeSteps.map( + (s) => s.tx as unknown as Partial + ) || [] + ); + }, [ + buildState.result?.safeSteps, + cannonDefInfo?.def, + gitFile, + gitHash, + gitUrl, + prevDeployLocation, + prevInfoQuery.data, + writeToIpfsMutationRes?.data?.mainUrl, + ]); + + const { data: multicallTxn } = useMultisendQuery( + Boolean( + !prevInfoQuery.isLoading && + buildState.result && + buildState.status == 'success' + ), + multisendTxsParam + ); let totalGas = BigInt(0); - for (const step of buildInfo.buildResult?.safeSteps || []) { + for (const step of buildState.result?.safeSteps || []) { totalGas += BigInt(step.gas.toString()); } const toast = useToast(); const stager = useTxnStager( - multicallTxn.data + multicallTxn?.data ? ({ to: multicallTxn.to, value: multicallTxn.value.toString(), @@ -386,14 +434,14 @@ function QueueFromGitOps() { const execTxn = useWriteContract(); const isOutsideSafeTxnsRequired = - (buildInfo.buildResult?.deployerSteps.length || 0) > 0 && - !deployer.isComplete; + (buildState.result?.deployerSteps.length || 0) > 0 && !deployer.isComplete; const loadingDataForDeploy = prevCannonDeployInfo.isFetching || - partialDeployInfo.isFetching || + partialDeployInfo?.isFetching || onChainPrevPkgQuery.isFetching || - buildInfo.buildStatus === 'building'; + buildState.status === 'building' || + writeToIpfsMutationRes?.isLoading; const handlePreviewTxnsClick = async () => { if (!isConnected) { @@ -412,9 +460,7 @@ function QueueFromGitOps() { if (chainId !== currentSafe?.chainId) { try { - await switchChainAsync({ chainId: currentSafe?.chainId || 1 }); - buildInfo.doBuild(); - return; + await switchChainAsync({ chainId: currentSafe?.chainId || 10 }); } catch (e) { toast({ title: @@ -427,7 +473,7 @@ function QueueFromGitOps() { } } - buildInfo.doBuild(); + doBuild(cannonDefInfo?.def, prevCannonDeployInfo.pkg); }; const renderAlertMessage = () => { @@ -443,19 +489,26 @@ function QueueFromGitOps() { ); } - if (cannonDefInfo.def && cannonDefInfo.def.danglingDependencies.size > 0) { + if (cannonDefInfo?.def && cannonDefInfo.def.danglingDependencies.size > 0) { alertMessage = ( - <> - The cannonfile contains invalid dependencies. Please ensure the - following references are defined: - {Array.from(cannonDefInfo.def.danglingDependencies).map( - ([input, node]) => ( - - {input} in {node} - - ) - )} - + + + The cannonfile contains invalid dependencies. Please ensure the + following references are defined: + +
+ {Array.from(cannonDefInfo.def.danglingDependencies).map( + (dependency) => ( + <> + + {dependency} + +
+ + ) + )} +
+
); } @@ -469,61 +522,79 @@ function QueueFromGitOps() { ) : null; }; + const cannonInfoDefinitionLoaded = + cannonfileUrlInput.length > 0 && !cannonDefInfo.error && cannonDefInfo?.def; + + const partialDeployInfoLoaded = + !partialDeployInfo?.isFetching && + !partialDeployInfo?.isError && + partialDeployInfo?.pkg; + + const hasDeployers = Boolean( + cannonDefInfo.def?.getDeployers()?.length ?? 0 > 0 + ); + const tomlRequiresPrevPackage = Boolean( + cannonfileUrlInput && + cannonDefInfo?.def && + !hasDeployers && + cannonDefInfo.def.allActionNames.some((item) => + item.startsWith('deploy.') + ) + ); + const disablePreviewButton = loadingDataForDeploy || chainId !== currentSafe?.chainId || - !cannonDefInfo.def || - buildInfo.buildStatus === 'building'; - - function PreviewButton({ message }: { message?: string }) { - return ( - - - - ); - } + !cannonDefInfo?.def || + buildState.status === 'building' || + buildState.status === 'success' || + (onChainPrevPkgQuery.isFetched && + !prevDeployLocation && + tomlRequiresPrevPackage && + !previousPackageInput); + + const PreviewButton = ({ message }: { message?: string }) => ( + + + + ); - function renderCannonfileInput() { + function renderCannonFileInput() { return ( - - Cannonfile {selectedDeployType == 'partial' ? '(Optional)' : ''} - - - - setCannonfileUrlInput(evt.target.value)} - /> - - {cannonDefInfo.isFetching ? ( - - ) : cannonDefInfo.def ? ( - - ) : null} - - - + Cannonfile (Optional) + + setCannonfileUrlInput(evt.target.value)} + /> + + {cannonfileUrlInput.length > 0 && cannonDefInfo?.isFetching ? ( + + ) : cannonfileUrlInput.length > 0 && + !cannonDefInfo.error && + cannonDefInfo?.def ? ( + + ) : null} + + - Enter a Git or GitHub URL for the cannonfile you’d like to build. + The Cannonfile URL is used to generate the deployment data to display + a git diff in Cannon. {cannonDefInfoError ? ( @@ -548,7 +619,7 @@ function QueueFromGitOps() { ); } - if (partialDeployInfo.isError) { + if (partialDeployInfo?.isError) { const message = `Error fetching partial deploy info, error: ${partialDeployInfo.error?.message}`; return ; } @@ -556,16 +627,16 @@ function QueueFromGitOps() { if ( prevCannonDeployInfo.isFetching || onChainPrevPkgQuery.isFetching || - partialDeployInfo.isFetching + partialDeployInfo?.isFetching ) { return ; } - if (buildInfo.buildStatus === 'building') { + if (buildState.status === 'building') { return ; } - if (!cannonDefInfo.def) { + if (!cannonDefInfo?.def) { return ( ); @@ -574,8 +645,6 @@ function QueueFromGitOps() { return ; } - // eslint-disable-next-line no-console - return ( <> @@ -584,9 +653,8 @@ function QueueFromGitOps() { Queue Deployment - Queue deployments from a cannonfile in a git repository or a partial - deployment ipfs hash. After the transactions are executed, the - resulting package can be published to the registry. + Queue deployments using Safe. After the transactions are executed, + the resulting package can be published to the registry. @@ -600,11 +668,16 @@ function QueueFromGitOps() { borderColor="gray.600" borderRadius="4px" > - + {/* Deployment Source { + resetState(); + setCannonfileUrlInput(''); + setPartialDeployIpfs(''); + setSelectedDeployType(value); + }} > Git URL - + IPFS Hash - - {selectedDeployType == 'git' && renderCannonfileInput()} + + {selectedDeployType == 'git' + ? 'Enter a Git URL repository with a cannonfile to build.' + : 'Use a partial deployment from a IPFS hash.'} + + */} - {onChainPrevPkgQuery.isFetched && - (prevDeployLocation ? ( - - Previous Deployment: {prevDeployLocation} - - ) : ( - Deployment from scratch. - ))} - - - - setOverridePreviousState(evt.currentTarget.checked) - } - />{' '} - Override Previous State + + Cannonfile URL or Deployment Data IPFS Hash + + + { + resetState(); + setCannonfileUrlInput(''); + setPartialDeployIpfs(''); + + setGenericInput(e.target.value); + if (/^Qm[1-9A-Za-z]{44}$/.test(e.target.value)) { + setSelectedDeployType('partial'); + setPartialDeployIpfs(e.target.value); + } else if (cannonfileUrlRegex.test(e.target.value)) { + setSelectedDeployType('git'); + setCannonfileUrlInput(e.target.value); + } + }} + /> + {selectedDeployType == 'git' && ( + + {cannonfileUrlInput.length > 0 && + cannonDefInfo?.isFetching ? ( + + ) : cannonInfoDefinitionLoaded ? ( + + ) : null} + + )} + {selectedDeployType == 'partial' && ( + + {partialDeployInfo?.isError && ( + + )} + {partialDeployInfo?.isFetching && + !partialDeployInfo?.isError && } + {partialDeployInfo?.pkg && } + + )} + + + {cannonDefInfoError ? ( + + + {cannonDefInfoError.toString()} + + ) : undefined} - {overridePreviousState && ( + {selectedDeployType == 'git' && ( + + {onChainPrevPkgQuery.isFetched && + (prevDeployLocation ? ( + + Previous Deployment:{' '} + + {prevDeployLocation.replace('ipfs://', '')} + + + ) : ( + + {tomlRequiresPrevPackage + ? 'We couldn\'t find a previous deployment for your cannonfile. Please, enter a value in the "Previous Package" input or modify your cannonfile to include a "deployers" key.' + : 'Deployment from scratch'} + + ))} + + )} + + {/* ipfs://Qma8R3UNPp2WQZdwZ7Ri95D4ddqT5auSpU8TUxwh4nLHij */} + {/* {(partialDeployIpfs || cannonfileUrlInput) && ( + + + setOverridePreviousState(evt.currentTarget.checked) + } + />{' '} + Override Previous State + + )} */} + + {(partialDeployInfoLoaded || tomlRequiresPrevPackage) && ( Previous Package @@ -685,63 +841,45 @@ function QueueFromGitOps() { )} - {selectedDeployType == 'partial' && ( - - Partial Deployment Data - - - setPartialDeployIpfs(parseIpfsHash(evt.target.value)) - } - /> - - {partialDeployInfo.isError && } - {partialDeployInfo.isFetching && - !partialDeployInfo.isError && } - {partialDeployInfo.pkg && } - - - - If this deployment requires transactions executed in other - contexts (e.g. contract deployments or function calls using - other signers), provide the IPFS hash generated by building the - package using the CLI. - - - )} - {selectedDeployType == 'partial' && renderCannonfileInput()} + {selectedDeployType == 'partial' && + partialDeployIpfs.length > 0 && + partialDeployInfoLoaded && + renderCannonFileInput()} {renderAlertMessage()} - - {buildInfo.buildMessage && ( + + {chainId !== currentSafe?.chainId ? ( + + ) : ( + + )} + + {buildState.message && ( - {buildInfo.buildMessage} + {buildState.message} )} - {buildInfo.buildError && ( + {buildState.error && ( - {buildInfo.buildError} + {buildState.error} )} - {buildInfo.buildSkippedSteps.length > 0 && ( + {buildState.skippedSteps.length > 0 && ( This safe will not be able to complete the following operations: - {buildInfo.buildSkippedSteps.map((s, i) => ( + {buildState.skippedSteps.map((s, i) => ( {`[${s.name}]: `} {s.err.toString()} @@ -749,83 +887,98 @@ function QueueFromGitOps() { ))} )} - {!!buildInfo.buildResult?.deployerSteps?.length && - !!buildInfo.buildResult?.safeSteps.length && ( - - {deployer.queuedTransactions.length === 0 ? ( - - - Some transactions should be executed outside the safe - before staging. You can execute these now in your browser. - By clicking the button below. - - - - ) : deployer.executionProgress.length < - deployer.queuedTransactions.length ? ( - - Deploying txns {deployer.executionProgress.length + 1} /{' '} - {deployer.queuedTransactions.length} - - ) : ( - - All Transactions Queued Successfully. You may now continue - the safe deployment. + + {!!buildState.result?.deployerSteps?.length && ( + + {deployer.queuedTransactions.length === 0 ? ( + + + Some transactions should be executed outside the safe before + staging. You can execute these now in your browser. By + clicking the button below. - )} - - )} - {(buildInfo.buildResult?.safeSteps.length || 1) == 0 && ( - - There are no transactions that would be executed by the gnosis - safe. - + + + ) : deployer.executionProgress.length < + deployer.queuedTransactions.length ? ( + + Deploying txns {deployer.executionProgress.length + 1} /{' '} + {deployer.queuedTransactions.length} + + ) : ( + + All Transactions Queued Successfully. You may now continue the + safe deployment. + + )} + )} - {cannonDefInfo.def && multicallTxn.data && ( - + {cannonDefInfo?.def && multicallTxn?.data && ( + - {cannonDefInfo.def.getName(ctx)}: + Package: {cannonDefInfo.def.getName(ctx)}: {cannonDefInfo.def.getVersion(ctx) || 'latest'}@ {cannonDefInfo.def.getPreset(ctx)} )} - {uploadToPublishIpfs.deployedIpfsHash && - multicallTxn.data && + {buildState.status == 'success' && + writeToIpfsMutationRes?.data?.mainUrl && + !multicallTxn?.data && ( + + No simulated transactions have succeeded. Please ensure you have + selected the correct Safe wallet and that you have sufficient + permissions to execute transactions. + + )} + {writeToIpfsMutationRes?.data?.mainUrl && + multicallTxn?.data && stager.safeTxn && ( - + Transactions - + {buildState.result?.safeSteps.length === 0 ? ( + + There are no transactions that would be executed by the + Safe. + + ) : ( + + )} )} - {uploadToPublishIpfs.writeToIpfsMutation.isPending && ( + {writeToIpfsMutationRes?.isLoading && ( Uploading build result to IPFS... )} - {uploadToPublishIpfs.writeToIpfsMutation.error && ( + {writeToIpfsMutationRes?.error && ( Failed to upload staged transaction to IPFS:{' '} - {uploadToPublishIpfs.writeToIpfsMutation.error.toString()} + {writeToIpfsMutationRes.error.toString()} )} - {uploadToPublishIpfs.deployedIpfsHash && multicallTxn.data && ( + {writeToIpfsMutationRes?.data?.mainUrl && multicallTxn?.data && ( @@ -835,6 +988,7 @@ function QueueFromGitOps() { isDisabled={ !!stager.signConditionFailed || stager.signing } + colorScheme="teal" size="lg" w="100%" onClick={async () => { @@ -858,6 +1012,7 @@ function QueueFromGitOps() { !!stager.execConditionFailed || isOutsideSafeTxnsRequired } size="lg" + colorScheme="teal" w="100%" onClick={() => { execTxn.writeContract(stager.executeTxnConfig!, { diff --git a/packages/website/src/features/Deploy/SignTransactionsPage.tsx b/packages/website/src/features/Deploy/SignTransactionsPage.tsx index c0d18c0a9..c5efa73b4 100644 --- a/packages/website/src/features/Deploy/SignTransactionsPage.tsx +++ b/packages/website/src/features/Deploy/SignTransactionsPage.tsx @@ -22,8 +22,10 @@ export default function SignTransactionsPage() { function SignTransactions() { const currentSafe = useStore((s) => s.currentSafe); - const { staged, isLoading: isLoadingSafeTxs } = - useSafeTransactions(currentSafe); + const { staged, isLoading: isLoadingSafeTxs } = useSafeTransactions( + currentSafe, + 10000 + ); const { data: history } = useExecutedTransactions(currentSafe); const [isChecked, setIsChecked] = useState(true); diff --git a/packages/website/src/features/Docs/DocsCliPage.tsx b/packages/website/src/features/Docs/DocsCliPage.tsx index f4af69018..68591f029 100644 --- a/packages/website/src/features/Docs/DocsCliPage.tsx +++ b/packages/website/src/features/Docs/DocsCliPage.tsx @@ -142,6 +142,7 @@ const DocumentationSection: React.FC<{ description: string; argumentsData?: { key: string; value: string }[]; anvilOptionsData?: { key: string; value: string }[]; + forgeOptionsData?: { key: string; value: string }[]; optionsData?: { key: string; value: string }[]; }> = ({ id, @@ -149,6 +150,7 @@ const DocumentationSection: React.FC<{ description, argumentsData, anvilOptionsData, + forgeOptionsData, optionsData, }) => ( @@ -200,14 +202,48 @@ const DocumentationSection: React.FC<{ > Anvil {' '} - node to execute this command. The following options can also be - passed through to the Anvil process: + to execute this command. The following options can also be passed + through to the Anvil process: )} + {forgeOptionsData && ( + + +

+ + + +

+ + + Cannon uses{' '} + + Forge + {' '} + to execute this command. The following options can also be passed + through to the Forge process: + + + +
+
+ )}
); @@ -261,6 +297,15 @@ const renderCommandConfig = (commandConfig: any) => { (option.defaultValue ? ` (default: "${option.defaultValue}")` : ''), })) } + forgeOptionsData={ + commandConfig.forgeOptions && + commandConfig.forgeOptions.map((option: any) => ({ + key: option.flags, + value: + option.description + + (option.defaultValue ? ` (default: "${option.defaultValue}")` : ''), + })) + } optionsData={ commandConfig.options && commandConfig.options.map((option: any) => ({ diff --git a/packages/website/src/helpers/cannon.ts b/packages/website/src/helpers/cannon.ts index 9d2f750aa..7d4148abd 100644 --- a/packages/website/src/helpers/cannon.ts +++ b/packages/website/src/helpers/cannon.ts @@ -46,7 +46,7 @@ export async function loadCannonfile(repo: string, ref: string, filepath: string package: {}, chainId: CANNON_CHAIN_ID, settings: {}, - timestamp: 0, + timestamp: 0 as any, // TODO: fix this contracts: {}, txns: {}, imports: {}, diff --git a/packages/website/src/helpers/multisend.ts b/packages/website/src/helpers/multisend.ts index 3db0a0320..3e1e07e94 100644 --- a/packages/website/src/helpers/multisend.ts +++ b/packages/website/src/helpers/multisend.ts @@ -1,5 +1,6 @@ import { Address, encodeFunctionData, Hex, TransactionRequestBase, zeroAddress } from 'viem'; import MulticallABI from '@/abi/Multicall.json'; +import { useQuery } from '@tanstack/react-query'; const MULTICALL_ADDRESS = '0xE2C5658cC5C448B48141168f3e475dF8f65A1e3e'; @@ -10,7 +11,6 @@ export function makeMultisend(txns: Partial[]): { data: Hex; } { const totalValue = txns.reduce((val, txn) => val + (txn.value || BigInt(0)), BigInt(0)); - return { operation: '1', // multicall is a DELEGATECALL to: MULTICALL_ADDRESS, @@ -29,3 +29,31 @@ export function makeMultisend(txns: Partial[]): { }), }; } + +export const useMultisendQuery = (enabled: boolean, txns: Partial[]) => { + return useQuery({ + queryKey: ['multisend', txns], + queryFn: async () => { + const totalValue = txns.reduce((val, txn) => val + (txn.value || BigInt(0)), BigInt(0)); + + return { + operation: '1', // multicall is a DELEGATECALL + to: MULTICALL_ADDRESS, + value: totalValue.toString(), + data: encodeFunctionData({ + abi: MulticallABI, + functionName: 'aggregate3Value', + args: [ + txns.map((txn) => ({ + target: txn.to || zeroAddress, + callData: txn.data || '0x', + value: txn.value || '0', + requireSuccess: true, + })), + ], + }), + }; + }, + enabled, + }); +}; diff --git a/packages/website/src/hooks/backend.ts b/packages/website/src/hooks/backend.ts index 10aaaf6e7..68a88404f 100644 --- a/packages/website/src/hooks/backend.ts +++ b/packages/website/src/hooks/backend.ts @@ -25,7 +25,7 @@ interface CannonSafeTransaction { sigs: string[]; } -export function useSafeTransactions(safe: SafeDefinition | null) { +export function useSafeTransactions(safe: SafeDefinition | null, refetchInterval?: number) { const [staged, setStaged] = useState([]); const [nextNonce, setNextNonce] = useState(null); const stagingUrl = useStore((s) => s.settings.stagingUrl); @@ -37,7 +37,7 @@ export function useSafeTransactions(safe: SafeDefinition | null) { if (!safe) return; return axios.get(`${stagingUrl}/${safe.chainId}/${safe.address}`); }, - refetchInterval: 10000, + refetchInterval, }); const nonceQuery = useReadContract({ @@ -180,7 +180,7 @@ export function useTxnStager( setAlreadyStagedSigners(signers); })(); - }, [alreadyStaged?.sigs, hashToSign]); + }, [alreadyStaged, alreadyStaged?.sigs, hashToSign]); const sigInsertIdx = _.sortedIndex( alreadyStagedSigners.map((s) => s.toLowerCase()), diff --git a/packages/website/src/hooks/cannon.ts b/packages/website/src/hooks/cannon.ts index a6c71a6a7..51f3221a1 100644 --- a/packages/website/src/hooks/cannon.ts +++ b/packages/website/src/hooks/cannon.ts @@ -28,7 +28,7 @@ import { findUpgradeFromPackage, } from '@usecannon/builder'; import _ from 'lodash'; -import { useEffect, useMemo, useState } from 'react'; +import { useEffect, useReducer, useState } from 'react'; import { Abi, Address, createPublicClient, createTestClient, createWalletClient, custom, Hex, isAddressEqual } from 'viem'; import { useChainId, usePublicClient } from 'wagmi'; // Needed to prepare mock run step with registerAction @@ -75,6 +75,234 @@ export function useLoadCannonDefinition(repo: string, ref: string, filepath: str }; } +type BuildStateTmp = { + message: string; + status: 'idle' | 'building' | 'success' | 'error'; + result: { + runtime: ChainBuilderRuntime; + state: DeploymentState; + safeSteps: CannonTxRecord[]; + deployerSteps: CannonTxRecord[]; + } | null; + error: string | null; + skippedSteps: StepExecutionError[]; +}; + +const initialState: BuildStateTmp = { + message: '', + status: 'idle', + result: null, + error: null, + skippedSteps: [], +}; + +export function useCannonBuildTmp(safe: SafeDefinition | null) { + const { addLog } = useLogs(); + const settings = useStore((s) => s.settings); + + const [buildState, dispatch] = useReducer( + (state: BuildStateTmp, partial: Partial): BuildStateTmp => ({ ...state, ...partial }), + initialState + ); + + const fallbackRegistry = useCannonRegistry(); + const { getChainById } = useCannonChains(); + const createFork = useCreateFork(); + const { address: deployerWalletAddress } = useDeployerWallet(safe?.chainId); + + function resetState() { + dispatch(initialState); + } + + useEffect(() => { + resetState(); + }, [deployerWalletAddress]); + + const buildFn = async (def?: ChainDefinition, prevDeploy?: DeploymentInfo) => { + // Wait until finished loading + if (!safe || !def || !deployerWalletAddress) { + throw new Error( + `Missing required parameters. has safe: ${!!safe}, def: ${!!def}, deployerWalletAddress: ${deployerWalletAddress}` + ); + } + + const chain = getChainById(safe.chainId); + + dispatch({ message: 'Creating fork...' }); + const fork = await createFork({ + chainId: safe.chainId, + impersonate: [safe.address, deployerWalletAddress], + url: chain?.rpcUrls.default.http[0], + }).catch((err) => { + err.message = `Could not create local fork for build: ${err.message}`; + throw err; + }); + + const ipfsLoader = new IPFSBrowserLoader(settings.ipfsApiUrl || externalLinks.IPFS_CANNON); + + dispatch({ message: 'Loading deployment data...' }); + + addLog('info', `cannon.ts: upgrade from: ${prevDeploy?.def.name}:${prevDeploy?.def.version}`); + + const simulatedTxns: { hash: string; deployedOn: string }[] = []; + const transport = custom({ + request: async (args) => { + const result = await fork.request(args); + if (currentRuntime) { + switch (args.method) { + case 'eth_sendTransaction': + // capture the transaction that needs to be sent + simulatedTxns.push({ hash: result, deployedOn: currentRuntime.currentStep || '' }); + } + } + + return result; + }, + }); + + const provider = createPublicClient({ + chain, + transport, + }); + + const testProvider = createTestClient({ + chain, + transport, + mode: 'ganache', + }); + + // todo: as usual viem provider types refuse to work + await loadPrecompiles(testProvider as any); + + const multiSigWallet = createWalletClient({ + account: safe.address, + chain: getChainById(safe.chainId), + transport, + }); + + const deployerWallet = createWalletClient({ + account: deployerWalletAddress, + chain: getChainById(safe.chainId), + transport, + }); + + const getDefaultSigner = async () => ({ address: deployerWalletAddress, wallet: deployerWallet }); + + const loaders = { mem: inMemoryLoader, ipfs: ipfsLoader }; + + const getSigner = async (addr: Address) => { + if (isAddressEqual(addr, safe.address)) { + return { address: safe.address, wallet: multiSigWallet }; + } else if (isAddressEqual(addr, deployerWalletAddress!)) { + return { address: deployerWalletAddress, wallet: deployerWallet }; + } else { + throw new Error(`Could not get signer for "${addr}"`); + } + }; + + currentRuntime = new ChainBuilderRuntime( + { + provider: provider as any, // TODO: fix type + chainId: safe.chainId, + getSigner: getSigner as any, // TODO: fix type + getDefaultSigner: getDefaultSigner as any, // TODO: fix type + snapshots: false, + allowPartialDeploy: true, + }, + fallbackRegistry, + loaders + ); + + const skippedSteps: StepExecutionError[] = []; + + currentRuntime.on(Events.PostStepExecute, (stepType: string, stepLabel: string, stepOutput: ChainArtifacts) => { + const stepName = `${stepType}.${stepLabel}`; + + addLog('info', `cannon.ts: on Events.PostStepExecute operation ${stepName} output: ${JSON.stringify(stepOutput)}`); + + //simulatedSteps.push(_.cloneDeep(stepOutput)); + + for (const txn in stepOutput.txns || {}) { + // clean out txn hash + stepOutput.txns![txn].hash = ''; + } + + dispatch({ message: `Building ${stepName}...` }); + }); + + currentRuntime.on(Events.SkipDeploy, (stepName: string, err: Error) => { + addLog('error', `cannon.ts: on Events.SkipDeploy error ${err.toString()} happened on the operation ${stepName}`); + skippedSteps.push({ name: stepName, err }); + }); + + currentRuntime.on(Events.Notice, (stepName: string, msg: string) => { + addLog('warn', `${stepName}: ${msg}`); + }); + + if (prevDeploy) { + await currentRuntime.restoreMisc(prevDeploy.miscUrl); + } + + const ctx = await createInitialContext(def, prevDeploy?.meta || {}, safe.chainId, prevDeploy?.options || {}); + + const newState = await cannonBuild(currentRuntime, def, _.cloneDeep(prevDeploy?.state) ?? {}, ctx); + + dispatch({ skippedSteps }); + const allSteps = await Promise.all( + simulatedTxns.map(async (executedTx) => { + if (!executedTx) throw new Error('Invalid operation'); + const tx = await provider.getTransaction({ hash: executedTx.hash as Hex }); + const rx = await provider.getTransactionReceipt({ hash: executedTx.hash as Hex }); + // eslint-disable-next-line no-console + return { + name: executedTx.deployedOn, + gas: rx.gasUsed, + from: tx.from, + tx: { + to: tx.to, + value: tx.value.toString(), + data: tx.input, + } as BaseTransaction, + }; + }) + ); + if (fork) await fork.disconnect(); + + // eslint-disable-next-line no-console + return { + runtime: currentRuntime, + state: newState, + safeSteps: allSteps.filter((step) => step && isAddressEqual(step.from, safe.address)), + deployerSteps: allSteps.filter((step) => isAddressEqual(step.from, deployerWalletAddress)), + }; + }; + + function doBuild(def?: ChainDefinition, prevDeploy?: DeploymentInfo) { + resetState(); + dispatch({ status: 'building' }); + + buildFn(def, prevDeploy) + .then((res) => { + dispatch({ result: res, status: 'success' }); + }) + .catch((err) => { + // eslint-disable-next-line no-console + console.error(err); + addLog('error', `cannon.ts: full build error ${err.toString()}`); + dispatch({ error: err.toString(), status: 'error' }); + }) + .finally(() => { + dispatch({ message: '' }); + }); + } + + return { + buildState, + resetState, + doBuild, + }; +} + export function useCannonBuild(safe: SafeDefinition | null, def?: ChainDefinition, prevDeploy?: DeploymentInfo) { const { addLog } = useLogs(); const settings = useStore((s) => s.settings); @@ -265,7 +493,7 @@ export function useCannonBuild(safe: SafeDefinition | null, def?: ChainDefinitio return { runtime: currentRuntime, state: newState, - safeSteps: allSteps.filter((step) => isAddressEqual(step.from, safe.address)), + safeSteps: allSteps.filter((step) => step && isAddressEqual(step.from, safe.address)), deployerSteps: allSteps.filter((step) => isAddressEqual(step.from, deployerWalletAddress)), }; }; @@ -277,6 +505,7 @@ export function useCannonBuild(safe: SafeDefinition | null, def?: ChainDefinitio buildFn() .then((res) => { setBuildResult(res); + setBuildStatus('success'); }) .catch((err) => { // eslint-disable-next-line no-console @@ -286,7 +515,6 @@ export function useCannonBuild(safe: SafeDefinition | null, def?: ChainDefinitio setBuildStatus('error'); }) .finally(() => { - setBuildStatus('success'); setBuildMessage(''); }); } @@ -301,21 +529,31 @@ export function useCannonBuild(safe: SafeDefinition | null, def?: ChainDefinitio }; } -export function useCannonWriteDeployToIpfs( - runtime?: ChainBuilderRuntime, - deployInfo?: DeploymentInfo | undefined, - metaUrl?: string -) { +type CannonWriteDeployToIpfsResult = ReturnType; +export type CannonWriteDeployToIpfsMutationResult = Awaited>; +export function useCannonWriteDeployToIpfs() { const settings = useStore((s) => s.settings); - const def = useMemo(() => deployInfo && new ChainDefinition(deployInfo.def), [deployInfo]); - - const writeToIpfsMutation = useMutation({ - mutationFn: async () => { + return useMutation({ + mutationFn: async ({ + runtime, + deployInfo, + metaUrl, + }: { + runtime?: ChainBuilderRuntime; + deployInfo?: DeploymentInfo | undefined; + metaUrl?: string; + }) => { if (settings.isIpfsGateway) { throw new Error('You cannot write on an IPFS gateway, only read operations can be done'); } + if (!deployInfo) { + throw new Error('Missing required parameters'); + } + + const def = new ChainDefinition(deployInfo.def); + if (!runtime || !deployInfo || !def) { throw new Error('Missing required parameters'); } @@ -360,11 +598,6 @@ export function useCannonWriteDeployToIpfs( }; }, }); - - return { - writeToIpfsMutation, - deployedIpfsHash: writeToIpfsMutation.data?.mainUrl, - }; } export function useCannonFindUpgradeFromUrl(packageRef?: PackageReference, chainId?: number, deployers?: Address[]) { @@ -374,7 +607,7 @@ export function useCannonFindUpgradeFromUrl(packageRef?: PackageReference, chain const onChainDataQuery = useQuery({ enabled: !!packageRef && !!chainId, - queryKey: ['cannon', 'find-upgrade-from', packageRef?.name, packageRef?.preset, chainId, deployers], + queryKey: ['cannon', 'find-upgrade-from', packageRef?.name, packageRef?.preset, packageRef?.version, chainId, deployers], queryFn: async () => { // eslint-disable-next-line no-console console.log('find with deployers', deployers); @@ -409,14 +642,10 @@ export function useCannonPackage(urlOrRef?: string | PackageReference, chainId?: const registryQuery = useQuery({ queryKey: ['cannon', 'registry-url', urlOrRef, packageChainId], queryFn: async () => { - if (typeof urlOrRef !== 'string' || urlOrRef.length < 3) { - return null; - } - - if (urlOrRef.startsWith('ipfs://')) return { url: urlOrRef }; - if (urlOrRef.startsWith('@ipfs:')) return { url: urlOrRef.replace('@ipfs:', 'ipfs://') }; + if (urlOrRef?.startsWith('ipfs://')) return { url: urlOrRef }; + if (urlOrRef?.startsWith('@ipfs:')) return { url: urlOrRef.replace('@ipfs:', 'ipfs://') }; - const url = await registry.getUrl(urlOrRef, packageChainId); + const url = await registry.getUrl(urlOrRef!, packageChainId); if (url) { return { url }; @@ -424,6 +653,7 @@ export function useCannonPackage(urlOrRef?: string | PackageReference, chainId?: throw new Error(`package not found: ${urlOrRef} (${packageChainId})`); } }, + enabled: typeof urlOrRef === 'string' && urlOrRef.length > 3, refetchOnWindowFocus: false, }); @@ -558,7 +788,7 @@ export function useCannonPackageContracts(packageRef?: string, chainId?: number) }; void getContracts(); - }, [pkg.pkg, packageRef]); + }, [pkg.pkg, packageRef, settings.ipfsApiUrl]); return { contracts, ...pkg }; } diff --git a/packages/website/src/hooks/deployer.ts b/packages/website/src/hooks/deployer.ts index c9ce917eb..41adee2a1 100644 --- a/packages/website/src/hooks/deployer.ts +++ b/packages/website/src/hooks/deployer.ts @@ -29,13 +29,24 @@ export function useDeployerWallet(chainId?: number) { if ((isIdle && !executionProgress.length && queuedTransactions.length) || isConfirmed) { // ensure we are on the correct network await switchChainAsync({ chainId }); - // execute the next transaction - sendTransaction(queuedTransactions[executionProgress.length], { - onSuccess(hash: viem.Hash) { - setExecutionProgress([...executionProgress, hash]); + sendTransaction( + { + ...queuedTransactions[executionProgress.length], + type: queuedTransactions[executionProgress.length].type as + | 'legacy' + | 'eip2930' + | 'eip1559' + | 'eip4844' + | 'eip7702' + | undefined, }, - }); + { + onSuccess(hash: viem.Hash) { + setExecutionProgress([...executionProgress, hash]); + }, + } + ); } })() .then(_.noop) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9c66bf4dd..17e497211 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -154,8 +154,8 @@ importers: specifier: workspace:* version: link:../../packages/hardhat-cannon viem: - specifier: ^2.16.5 - version: 2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) + specifier: ^2.21.15 + version: 2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) packages/api: dependencies: @@ -205,8 +205,8 @@ importers: specifier: ^4.6.13 version: 4.7.0 viem: - specifier: ^2.16.5 - version: 2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) + specifier: ^2.21.15 + version: 2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) devDependencies: '@types/cors': specifier: ^2.8.17 @@ -234,7 +234,7 @@ importers: version: 1.7.5(debug@4.3.6) axios-retry: specifier: ^4.4.2 - version: 4.5.0(axios@1.7.5(debug@4.3.6)) + version: 4.5.0(axios@1.7.5) buffer: specifier: ^6.0.3 version: 6.0.3 @@ -263,8 +263,8 @@ importers: specifier: ^4.0.0 version: 4.0.0(encoding@0.1.13) viem: - specifier: ^2.16.5 - version: 2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) + specifier: ^2.21.15 + version: 2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) zod: specifier: ^3.23.6 version: 3.23.8 @@ -357,8 +357,8 @@ importers: specifier: ^4.0.0 version: 4.0.0 viem: - specifier: ^2.16.5 - version: 2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) + specifier: ^2.21.15 + version: 2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) znv: specifier: ^0.4.0 version: 0.4.0(zod@3.23.8) @@ -451,8 +451,8 @@ importers: specifier: ^11.2.0 version: 11.2.0 viem: - specifier: ^2.16.5 - version: 2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) + specifier: ^2.21.15 + version: 2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) devDependencies: '@nomiclabs/hardhat-ethers': specifier: ^2.2.3 @@ -500,8 +500,8 @@ importers: specifier: ^4.6.13 version: 4.7.0 viem: - specifier: ^2.16.5 - version: 2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) + specifier: ^2.21.15 + version: 2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) devDependencies: '@types/connect-busboy': specifier: ^1.0.3 @@ -659,13 +659,13 @@ importers: version: 14.2.6(next@14.2.6(@babel/core@7.25.2)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8))(react@18.3.1) '@rainbow-me/rainbowkit': specifier: ^2.1.2 - version: 2.1.5(@tanstack/react-query@5.52.1(react@18.3.1))(@types/react@18.2.37)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(viem@2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.12.7(@tanstack/query-core@5.52.0)(@tanstack/react-query@5.52.1(react@18.3.1))(@types/react@18.2.37)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react-native@0.75.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.2.37)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@3.29.4)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)) + version: 2.1.5(@tanstack/react-query@5.52.1(react@18.3.1))(@types/react@18.2.37)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(viem@2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.12.7(@tanstack/query-core@5.52.0)(@tanstack/react-query@5.52.1(react@18.3.1))(@types/react@18.2.37)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react-native@0.75.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.2.37)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@3.29.4)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)) '@safe-global/api-kit': specifier: ^2.2.0 version: 2.4.4(bufferutil@4.0.8)(encoding@0.1.13)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) '@safe-global/protocol-kit': specifier: ^1.2.0 - version: 1.3.0(bufferutil@4.0.8)(encoding@0.1.13)(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) + version: 1.3.0(bufferutil@4.0.8)(encoding@0.1.13)(ethers@5.7.1(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) '@safe-global/safe-apps-sdk': specifier: ^9.1.0 version: 9.1.0(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) @@ -704,7 +704,7 @@ importers: version: 1.3.1(next@14.2.6(@babel/core@7.25.2)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8))(react@18.3.1) '@wagmi/core': specifier: ^2.5.13 - version: 2.13.4(@tanstack/query-core@5.52.0)(@types/react@18.2.37)(react@18.3.1)(typescript@5.5.4)(viem@2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8)) + version: 2.13.4(@tanstack/query-core@5.52.0)(@types/react@18.2.37)(react@18.3.1)(typescript@5.5.4)(viem@2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8)) abitype: specifier: ^1.0.5 version: 1.0.6(typescript@5.5.4)(zod@3.23.8) @@ -743,7 +743,7 @@ importers: version: 14.2.6(eslint@8.43.0)(typescript@5.5.4) ethers: specifier: '>=5.6.0' - version: 6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) + version: 5.7.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) fast-deep-equal: specifier: ^3.1.3 version: 3.1.3 @@ -817,11 +817,11 @@ importers: specifier: ^6.0.7 version: 6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1) viem: - specifier: 2.16.5 - version: 2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) + specifier: ^2.21.15 + version: 2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) wagmi: specifier: ^2.5.13 - version: 2.12.7(@tanstack/query-core@5.52.0)(@tanstack/react-query@5.52.1(react@18.3.1))(@types/react@18.2.37)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react-native@0.75.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.2.37)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@3.29.4)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) + version: 2.12.7(@tanstack/query-core@5.52.0)(@tanstack/react-query@5.52.1(react@18.3.1))(@types/react@18.2.37)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react-native@0.75.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.2.37)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@3.29.4)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) zod: specifier: ^3.23.8 version: 3.23.8 @@ -3216,6 +3216,10 @@ packages: resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} engines: {node: '>= 16'} + '@noble/hashes@1.5.0': + resolution: {integrity: sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==} + engines: {node: ^14.21.3 || >=16} + '@noble/secp256k1@1.7.1': resolution: {integrity: sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==} @@ -4179,6 +4183,9 @@ packages: '@scure/base@1.1.7': resolution: {integrity: sha512-PPNYBslrLNNUQ/Yad37MHYsNQtK67EhWb6WtSvNLLPo7SdVZgkUjD6Dg+5On7zNwmskf8OX7I7Nx5oN+MIWE0g==} + '@scure/base@1.1.9': + resolution: {integrity: sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==} + '@scure/bip32@1.1.5': resolution: {integrity: sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==} @@ -4197,6 +4204,9 @@ packages: '@scure/bip39@1.3.0': resolution: {integrity: sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==} + '@scure/bip39@1.4.0': + resolution: {integrity: sha512-BEEm6p8IueV/ZTfQLp/0vhw4NPnT9oWf5+28nvmeUICjP99f4vr2d+qc7AVGDDtwRep6ifR43Yed9ERVmiITzw==} + '@sentry-internal/browser-utils@8.26.0': resolution: {integrity: sha512-O2Tj+WK33/ZVp5STnz6ZL0OO+/Idk2KqsH0ITQkQmyZ2z0kdzWOeqK7s7q3/My6rB1GfPcyqPcBBv4dVv92FYQ==} engines: {node: '>=14.18'} @@ -13161,8 +13171,8 @@ packages: typescript: optional: true - viem@2.16.5: - resolution: {integrity: sha512-QDESALYDyLSP+pIr7adH3QPZ+3is16aOVMXXZE0X1GVbgL7PDMZQ8xIF1X/B1hgyqkBl2HhMpUaq6ksUdBV/YA==} + viem@2.21.15: + resolution: {integrity: sha512-Ae05NQzMsqPWRwuAHf1OfmL0SjI+1GBgiFB0JA9BAbK/61nJXsTPsQxfV5CbLe4c3ct8IEZTX89rdeW4dqf97g==} peerDependencies: typescript: '>=5.0.4' peerDependenciesMeta: @@ -13451,6 +13461,9 @@ packages: resolution: {integrity: sha512-iFGK5jO32vnXM/ASaJBaI0+gVR6uHozvYdxkdhaeOCD6HIQ4iIXadbO2atVpE9oc/H8l2MovJ4LtPhG7lIBN8A==} engines: {node: '>=8.0.0'} + webauthn-p256@0.0.5: + resolution: {integrity: sha512-drMGNWKdaixZNobeORVIqq7k5DsRC9FnG201K2QjeOoQLmtSDaSsVZdkg6n5jUALJKcAG++zBPJXmv6hy0nWFg==} + webextension-polyfill@0.10.0: resolution: {integrity: sha512-c5s35LgVa5tFaHhrZDnr3FpQpjj1BB+RXhLTYUxGqBVN460HkbM8TBtEqdXWbpTKfzwCcjAZVF7zXCYSKtcp9g==} @@ -17080,7 +17093,7 @@ snapshots: dependencies: '@ethereumjs/tx': 4.2.0 '@metamask/superstruct': 3.1.0 - '@noble/hashes': 1.4.0 + '@noble/hashes': 1.5.0 '@scure/base': 1.1.7 '@types/debug': 4.1.12 debug: 4.3.6(supports-color@8.1.1) @@ -17094,7 +17107,7 @@ snapshots: dependencies: '@ethereumjs/tx': 4.2.0 '@metamask/superstruct': 3.1.0 - '@noble/hashes': 1.4.0 + '@noble/hashes': 1.5.0 '@scure/base': 1.1.7 '@types/debug': 4.1.12 debug: 4.3.6(supports-color@8.1.1) @@ -17233,6 +17246,8 @@ snapshots: '@noble/hashes@1.4.0': {} + '@noble/hashes@1.5.0': {} + '@noble/secp256k1@1.7.1': {} '@nodelib/fs.scandir@2.1.5': @@ -18035,7 +18050,7 @@ snapshots: '@pythnetwork/pyth-sdk-solidity@2.4.1': {} - '@rainbow-me/rainbowkit@2.1.5(@tanstack/react-query@5.52.1(react@18.3.1))(@types/react@18.2.37)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(viem@2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.12.7(@tanstack/query-core@5.52.0)(@tanstack/react-query@5.52.1(react@18.3.1))(@types/react@18.2.37)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react-native@0.75.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.2.37)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@3.29.4)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8))': + '@rainbow-me/rainbowkit@2.1.5(@tanstack/react-query@5.52.1(react@18.3.1))(@types/react@18.2.37)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(viem@2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.12.7(@tanstack/query-core@5.52.0)(@tanstack/react-query@5.52.1(react@18.3.1))(@types/react@18.2.37)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react-native@0.75.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.2.37)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@3.29.4)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8))': dependencies: '@tanstack/react-query': 5.52.1(react@18.3.1) '@vanilla-extract/css': 1.14.0 @@ -18047,8 +18062,8 @@ snapshots: react-dom: 18.3.1(react@18.3.1) react-remove-scroll: 2.5.7(@types/react@18.2.37)(react@18.3.1) ua-parser-js: 1.0.38 - viem: 2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) - wagmi: 2.12.7(@tanstack/query-core@5.52.0)(@tanstack/react-query@5.52.1(react@18.3.1))(@types/react@18.2.37)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react-native@0.75.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.2.37)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@3.29.4)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) + viem: 2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) + wagmi: 2.12.7(@tanstack/query-core@5.52.0)(@tanstack/react-query@5.52.1(react@18.3.1))(@types/react@18.2.37)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react-native@0.75.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.2.37)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@3.29.4)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) transitivePeerDependencies: - '@types/react' @@ -18506,7 +18521,7 @@ snapshots: - utf-8-validate - zod - '@safe-global/protocol-kit@1.3.0(bufferutil@4.0.8)(encoding@0.1.13)(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)': + '@safe-global/protocol-kit@1.3.0(bufferutil@4.0.8)(encoding@0.1.13)(ethers@5.7.1(bufferutil@4.0.8)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)': dependencies: '@ethersproject/address': 5.7.0 '@ethersproject/bignumber': 5.7.0 @@ -18517,7 +18532,7 @@ snapshots: web3: 1.10.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) web3-core: 1.10.4(encoding@0.1.13) web3-utils: 1.10.4 - zksync-web3: 0.14.4(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + zksync-web3: 0.14.4(ethers@5.7.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)) transitivePeerDependencies: - bufferutil - encoding @@ -18527,7 +18542,7 @@ snapshots: '@safe-global/protocol-kit@4.0.4(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: - '@noble/hashes': 1.4.0 + '@noble/hashes': 1.5.0 '@safe-global/safe-core-sdk-types': 5.0.3(typescript@5.5.4)(zod@3.23.8) '@safe-global/safe-deployments': 1.37.3 abitype: 1.0.6(typescript@5.5.4)(zod@3.23.8) @@ -18553,7 +18568,7 @@ snapshots: '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: '@safe-global/safe-gateway-typescript-sdk': 3.22.2 - viem: 2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) transitivePeerDependencies: - bufferutil - typescript @@ -18575,6 +18590,8 @@ snapshots: '@scure/base@1.1.7': {} + '@scure/base@1.1.9': {} + '@scure/bip32@1.1.5': dependencies: '@noble/hashes': 1.2.0 @@ -18591,7 +18608,7 @@ snapshots: dependencies: '@noble/curves': 1.4.2 '@noble/hashes': 1.4.0 - '@scure/base': 1.1.7 + '@scure/base': 1.1.9 '@scure/bip39@1.1.1': dependencies: @@ -18608,6 +18625,11 @@ snapshots: '@noble/hashes': 1.4.0 '@scure/base': 1.1.7 + '@scure/bip39@1.4.0': + dependencies: + '@noble/hashes': 1.5.0 + '@scure/base': 1.1.9 + '@sentry-internal/browser-utils@8.26.0': dependencies: '@sentry/core': 8.26.0 @@ -19771,7 +19793,7 @@ snapshots: dependencies: '@synthetixio/router': 3.4.0(hardhat@2.22.9(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.3.2)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))(solc@0.8.26) axios: 1.7.5(debug@4.3.6) - axios-retry: 4.5.0(axios@1.7.5(debug@4.3.6)) + axios-retry: 4.5.0(axios@1.7.5) buffer: 6.0.3 chalk: 4.1.2 debug: 4.3.6(supports-color@8.1.1) @@ -19781,7 +19803,7 @@ snapshots: pako: 2.1.0 promise-events: 0.2.4 typestub-ipfs-only-hash: 4.0.0(encoding@0.1.13) - viem: 2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) zod: 3.23.8 transitivePeerDependencies: - bufferutil @@ -19809,7 +19831,7 @@ snapshots: table: 6.8.2 tildify: 3.0.0 untildify: 4.0.0 - viem: 2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) znv: 0.4.0(zod@3.23.8) zod: 3.23.8 transitivePeerDependencies: @@ -19852,17 +19874,17 @@ snapshots: next: 14.2.6(@babel/core@7.25.2)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8) react: 18.3.1 - '@wagmi/connectors@5.1.7(@types/react@18.2.37)(@wagmi/core@2.13.4(@tanstack/query-core@5.52.0)(@types/react@18.2.37)(react@18.3.1)(typescript@5.5.4)(viem@2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8)))(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react-native@0.75.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.2.37)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@3.29.4)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)': + '@wagmi/connectors@5.1.7(@types/react@18.2.37)(@wagmi/core@2.13.4(@tanstack/query-core@5.52.0)(@types/react@18.2.37)(react@18.3.1)(typescript@5.5.4)(viem@2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8)))(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react-native@0.75.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.2.37)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@3.29.4)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)': dependencies: '@coinbase/wallet-sdk': 4.0.4 '@metamask/sdk': 0.27.0(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react-native@0.75.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.2.37)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@3.29.4)(utf-8-validate@5.0.10) '@safe-global/safe-apps-provider': 0.18.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) - '@wagmi/core': 2.13.4(@tanstack/query-core@5.52.0)(@types/react@18.2.37)(react@18.3.1)(typescript@5.5.4)(viem@2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8)) + '@wagmi/core': 2.13.4(@tanstack/query-core@5.52.0)(@types/react@18.2.37)(react@18.3.1)(typescript@5.5.4)(viem@2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8)) '@walletconnect/ethereum-provider': 2.15.1(@types/react@18.2.37)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10) '@walletconnect/modal': 2.6.2(@types/react@18.2.37)(react@18.3.1) cbw-sdk: '@coinbase/wallet-sdk@3.9.3' - viem: 2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) optionalDependencies: typescript: 5.5.4 transitivePeerDependencies: @@ -19891,11 +19913,11 @@ snapshots: - utf-8-validate - zod - '@wagmi/core@2.13.4(@tanstack/query-core@5.52.0)(@types/react@18.2.37)(react@18.3.1)(typescript@5.5.4)(viem@2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8))': + '@wagmi/core@2.13.4(@tanstack/query-core@5.52.0)(@types/react@18.2.37)(react@18.3.1)(typescript@5.5.4)(viem@2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8))': dependencies: eventemitter3: 5.0.1 mipd: 0.0.7(typescript@5.5.4) - viem: 2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) zustand: 4.4.1(@types/react@18.2.37)(react@18.3.1) optionalDependencies: '@tanstack/query-core': 5.52.0 @@ -20699,7 +20721,7 @@ snapshots: '@babel/runtime': 7.25.4 is-retry-allowed: 2.2.0 - axios-retry@4.5.0(axios@1.7.5(debug@4.3.6)): + axios-retry@4.5.0(axios@1.7.5): dependencies: axios: 1.7.5(debug@4.3.6) is-retry-allowed: 2.2.0 @@ -23058,7 +23080,7 @@ snapshots: ethereum-bloom-filters@1.2.0: dependencies: - '@noble/hashes': 1.4.0 + '@noble/hashes': 1.5.0 ethereum-cryptography@0.1.3: dependencies: @@ -30539,15 +30561,16 @@ snapshots: - utf-8-validate - zod - viem@2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8): + viem@2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8): dependencies: '@adraffy/ens-normalize': 1.10.0 '@noble/curves': 1.4.0 '@noble/hashes': 1.4.0 '@scure/bip32': 1.4.0 - '@scure/bip39': 1.3.0 + '@scure/bip39': 1.4.0 abitype: 1.0.5(typescript@5.5.4)(zod@3.23.8) isows: 1.0.4(ws@8.17.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + webauthn-p256: 0.0.5 ws: 8.17.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) optionalDependencies: typescript: 5.5.4 @@ -30558,14 +30581,14 @@ snapshots: vlq@1.0.1: {} - wagmi@2.12.7(@tanstack/query-core@5.52.0)(@tanstack/react-query@5.52.1(react@18.3.1))(@types/react@18.2.37)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react-native@0.75.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.2.37)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@3.29.4)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8): + wagmi@2.12.7(@tanstack/query-core@5.52.0)(@tanstack/react-query@5.52.1(react@18.3.1))(@types/react@18.2.37)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react-native@0.75.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.2.37)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@3.29.4)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8): dependencies: '@tanstack/react-query': 5.52.1(react@18.3.1) - '@wagmi/connectors': 5.1.7(@types/react@18.2.37)(@wagmi/core@2.13.4(@tanstack/query-core@5.52.0)(@types/react@18.2.37)(react@18.3.1)(typescript@5.5.4)(viem@2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8)))(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react-native@0.75.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.2.37)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@3.29.4)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) - '@wagmi/core': 2.13.4(@tanstack/query-core@5.52.0)(@types/react@18.2.37)(react@18.3.1)(typescript@5.5.4)(viem@2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8)) + '@wagmi/connectors': 5.1.7(@types/react@18.2.37)(@wagmi/core@2.13.4(@tanstack/query-core@5.52.0)(@types/react@18.2.37)(react@18.3.1)(typescript@5.5.4)(viem@2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8)))(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react-native@0.75.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.2.37)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@3.29.4)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) + '@wagmi/core': 2.13.4(@tanstack/query-core@5.52.0)(@types/react@18.2.37)(react@18.3.1)(typescript@5.5.4)(viem@2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8)) react: 18.3.1 use-sync-external-store: 1.2.0(react@18.3.1) - viem: 2.16.5(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) + viem: 2.21.15(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8) optionalDependencies: typescript: 5.5.4 transitivePeerDependencies: @@ -31220,6 +31243,11 @@ snapshots: - supports-color - utf-8-validate + webauthn-p256@0.0.5: + dependencies: + '@noble/curves': 1.4.2 + '@noble/hashes': 1.5.0 + webextension-polyfill@0.10.0: {} webidl-conversions@3.0.1: {} @@ -31599,9 +31627,9 @@ snapshots: toposort: 2.0.2 type-fest: 2.19.0 - zksync-web3@0.14.4(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10)): + zksync-web3@0.14.4(ethers@5.7.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)): dependencies: - ethers: 6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) + ethers: 5.7.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) znv@0.4.0(zod@3.23.8): dependencies: