diff --git a/packages/website/src/hooks/routing/useSafeUrl.ts b/packages/website/src/hooks/routing/useSafeUrl.ts
new file mode 100644
index 000000000..310a92e71
--- /dev/null
+++ b/packages/website/src/hooks/routing/useSafeUrl.ts
@@ -0,0 +1,24 @@
+import isInteger from 'lodash/isInteger';
+import { useRouter } from 'next/router';
+import { Address, isAddress } from 'viem';
+
+export function useSafeUrl() {
+ const router = useRouter();
+ const { query: params } = router;
+ const { chainId, safeAddress } = params;
+
+ const isSafeUrl = router.pathname.includes('/deploy/[chainId]/[safeAddress]');
+
+ if (isSafeUrl && !isInteger(Number(chainId))) throw new Error('Invalid chainId URL param');
+
+ if (isSafeUrl && (typeof safeAddress !== 'string' || !isAddress(safeAddress))) {
+ throw new Error('Invalid safeAddress URL param');
+ }
+
+ return {
+ isSafeUrl,
+ chainId: chainId ? +chainId : null,
+ safeAddress: (safeAddress as Address) || null,
+ safeString: `${chainId}:${safeAddress}`,
+ };
+}
diff --git a/packages/website/src/pages/deploy/[chainId]/[safeAddress]/gitops/index.tsx b/packages/website/src/pages/deploy/[chainId]/[safeAddress]/gitops/index.tsx
new file mode 100644
index 000000000..e6a889113
--- /dev/null
+++ b/packages/website/src/pages/deploy/[chainId]/[safeAddress]/gitops/index.tsx
@@ -0,0 +1,44 @@
+import dynamic from 'next/dynamic';
+import { ReactElement } from 'react';
+import Layout from '@/pages/deploy/deployLayout';
+import { NextSeo } from 'next-seo';
+import defaultSEO from '@/constants/defaultSeo';
+
+const NoSSR = dynamic(
+ async () => {
+ return import('@/features/Deploy/QueueFromGitOpsPage');
+ },
+ {
+ ssr: false,
+ }
+);
+
+/*export const metadata: Metadata = {
+ title: 'Cannon | Queue From GitOps',
+ description: 'Queue From GitOps',
+ openGraph: {
+ title: 'Cannon | Queue From GitOps',
+ description: 'Queue From GitOps',
+ },
+ };*/
+
+export default function QueueFromGitOps() {
+ return (
+ <>
+
+
+ >
+ );
+}
+QueueFromGitOps.getLayout = function getLayout(page: ReactElement) {
+ return {page};
+};
diff --git a/packages/website/src/pages/deploy/[chainId]/[safeAddress]/queue/index.tsx b/packages/website/src/pages/deploy/[chainId]/[safeAddress]/queue/index.tsx
new file mode 100644
index 000000000..91d5e5fbb
--- /dev/null
+++ b/packages/website/src/pages/deploy/[chainId]/[safeAddress]/queue/index.tsx
@@ -0,0 +1,51 @@
+//import { QueuedTxns } from '@/features/Deploy/QueueDrawer';
+import { Box, Container, Heading, Text } from '@chakra-ui/react';
+import { ReactElement } from 'react';
+import Layout from '../../../deployLayout';
+import { QueuedTxns } from '@/features/Deploy/QueueDrawer';
+import { NextSeo } from 'next-seo';
+import defaultSEO from '@/constants/defaultSeo';
+
+/*export const metadata: Metadata = {
+ title: 'Cannon | Queue Transactions',
+ description: 'Queue Transactions',
+ openGraph: {
+ title: 'Cannon | Queue Transactions',
+ description: 'Queue Transactions',
+ },
+ };*/
+
+const QueueTransactions = () => {
+ return (
+ <>
+
+
+
+
+ Stage Transactions
+
+
+ Queue transactions from a package on the Cannon registry.
+ Transactions executed after being staged with this tool will not
+ result in a package to publish.
+
+
+
+
+ >
+ );
+};
+QueueTransactions.getLayout = function getLayout(page: ReactElement) {
+ return {page};
+};
+
+export default QueueTransactions;
diff --git a/packages/website/src/pages/deploy/[chainId]/[safeAddress]/txn/[nonce]/[sigHash]/index.tsx b/packages/website/src/pages/deploy/[chainId]/[safeAddress]/txn/[nonce]/[sigHash]/index.tsx
new file mode 100644
index 000000000..a99399a3f
--- /dev/null
+++ b/packages/website/src/pages/deploy/[chainId]/[safeAddress]/txn/[nonce]/[sigHash]/index.tsx
@@ -0,0 +1,39 @@
+import PageLoading from '@/components/PageLoading';
+import defaultSEO from '@/constants/defaultSeo';
+import { NextSeo } from 'next-seo';
+import dynamic from 'next/dynamic';
+import { useRouter } from 'next/router';
+import { ReactElement } from 'react';
+import Layout from '@/pages/deploy/deployLayout';
+
+const NoSSRTransactionDetailsPage = dynamic(
+ async () => {
+ return import('@/features/Deploy/TransactionDetailsPage');
+ },
+ {
+ ssr: false,
+ }
+);
+
+export default function TransactionDetails() {
+ const router = useRouter();
+ return (
+ <>
+
+ {router.isReady ? : }
+ >
+ );
+}
+
+TransactionDetails.getLayout = function getLayout(page: ReactElement) {
+ return {page};
+};
diff --git a/packages/website/src/pages/deploy/_gitops/index.tsx b/packages/website/src/pages/deploy/_gitops/index.tsx
new file mode 100644
index 000000000..e6a889113
--- /dev/null
+++ b/packages/website/src/pages/deploy/_gitops/index.tsx
@@ -0,0 +1,44 @@
+import dynamic from 'next/dynamic';
+import { ReactElement } from 'react';
+import Layout from '@/pages/deploy/deployLayout';
+import { NextSeo } from 'next-seo';
+import defaultSEO from '@/constants/defaultSeo';
+
+const NoSSR = dynamic(
+ async () => {
+ return import('@/features/Deploy/QueueFromGitOpsPage');
+ },
+ {
+ ssr: false,
+ }
+);
+
+/*export const metadata: Metadata = {
+ title: 'Cannon | Queue From GitOps',
+ description: 'Queue From GitOps',
+ openGraph: {
+ title: 'Cannon | Queue From GitOps',
+ description: 'Queue From GitOps',
+ },
+ };*/
+
+export default function QueueFromGitOps() {
+ return (
+ <>
+
+
+ >
+ );
+}
+QueueFromGitOps.getLayout = function getLayout(page: ReactElement) {
+ return {page};
+};
diff --git a/packages/website/src/pages/deploy/_queue/index.tsx b/packages/website/src/pages/deploy/_queue/index.tsx
new file mode 100644
index 000000000..4c887542d
--- /dev/null
+++ b/packages/website/src/pages/deploy/_queue/index.tsx
@@ -0,0 +1,51 @@
+//import { QueuedTxns } from '@/features/Deploy/QueueDrawer';
+import { Box, Container, Heading, Text } from '@chakra-ui/react';
+import { ReactElement } from 'react';
+import Layout from '../deployLayout';
+import { QueuedTxns } from '@/features/Deploy/QueueDrawer';
+import { NextSeo } from 'next-seo';
+import defaultSEO from '@/constants/defaultSeo';
+
+/*export const metadata: Metadata = {
+ title: 'Cannon | Queue Transactions',
+ description: 'Queue Transactions',
+ openGraph: {
+ title: 'Cannon | Queue Transactions',
+ description: 'Queue Transactions',
+ },
+ };*/
+
+const QueueTransactions = () => {
+ return (
+ <>
+
+
+
+
+ Stage Transactions
+
+
+ Queue transactions from a package on the Cannon registry.
+ Transactions executed after being staged with this tool will not
+ result in a package to publish.
+
+
+
+
+ >
+ );
+};
+QueueTransactions.getLayout = function getLayout(page: ReactElement) {
+ return {page};
+};
+
+export default QueueTransactions;
diff --git a/packages/website/src/pages/deploy/_txn/[chainId]/[safeAddress]/[nonce]/[sigHash]/index.tsx b/packages/website/src/pages/deploy/_txn/[chainId]/[safeAddress]/[nonce]/[sigHash]/index.tsx
new file mode 100644
index 000000000..18cdb7e32
--- /dev/null
+++ b/packages/website/src/pages/deploy/_txn/[chainId]/[safeAddress]/[nonce]/[sigHash]/index.tsx
@@ -0,0 +1,39 @@
+import PageLoading from '@/components/PageLoading';
+import defaultSEO from '@/constants/defaultSeo';
+import { NextSeo } from 'next-seo';
+import dynamic from 'next/dynamic';
+import { useRouter } from 'next/router';
+import { ReactElement } from 'react';
+import Layout from '../../../../../deployLayout';
+
+const NoSSRTransactionDetailsPage = dynamic(
+ async () => {
+ return import('@/features/Deploy/TransactionDetailsPage');
+ },
+ {
+ ssr: false,
+ }
+);
+
+export default function TransactionDetails() {
+ const router = useRouter();
+ return (
+ <>
+
+ {router.isReady ? : }
+ >
+ );
+}
+
+TransactionDetails.getLayout = function getLayout(page: ReactElement) {
+ return {page};
+};
diff --git a/packages/website/src/pages/deploy/deployLayout.tsx b/packages/website/src/pages/deploy/deployLayout.tsx
new file mode 100644
index 000000000..bb75ab8bc
--- /dev/null
+++ b/packages/website/src/pages/deploy/deployLayout.tsx
@@ -0,0 +1,87 @@
+'use client';
+
+import dynamic from 'next/dynamic';
+import { ReactNode } from 'react';
+import { Box, Flex, useBreakpointValue } from '@chakra-ui/react';
+import { useRouter } from 'next/router';
+import { links } from '@/constants/links';
+import { NavLink } from '@/components/NavLink';
+import { SafeAddressInput } from '@/features/Deploy/SafeAddressInput';
+import ClientOnly from '@/components/ClientOnly';
+import { CustomSpinner } from '@/components/CustomSpinner';
+import { useParams } from 'next/navigation';
+
+const NoSSRWithSafe = dynamic(() => import('@/features/Deploy/WithSafe'), {
+ ssr: false,
+});
+
+export default function DeployLayout({ children }: { children: ReactNode }) {
+ const router = useRouter();
+ const params = useParams();
+ const isLarge = useBreakpointValue({ base: false, lg: true });
+
+ const isSafeUrl = router.pathname.includes('/deploy/[chainId]/[safeAddress]');
+ const isGitOpsPage =
+ router.pathname === '/deploy/[chainId]/[safeAddress]/gitops';
+ const isQueuePage =
+ router.pathname === '/deploy/[chainId]/[safeAddress]/queue';
+ const isTxnPage = router.pathname === '/deploy/[chainId]/[safeAddress]/txn';
+
+ return (
+
+ {params !== null ? (
+ <>
+ {/* Header */}
+
+
+
+
+
+
+
+ {isSafeUrl && (
+
+
+ Sign{isLarge && ' Transactions'}
+
+
+ {isLarge && 'Queue '} Deployment
+
+
+ Stage{isLarge && ' Transactions'}
+
+
+ )}
+
+
+
+ {/* Body */}
+ {children}
+ >
+ ) : (
+
+ )}
+
+ );
+}
diff --git a/packages/website/src/pages/learn/guides/index.tsx b/packages/website/src/pages/learn/guides/index.tsx
index e0095e685..0b0ebdaab 100644
--- a/packages/website/src/pages/learn/guides/index.tsx
+++ b/packages/website/src/pages/learn/guides/index.tsx
@@ -17,7 +17,7 @@ export default function Home() {
.catch(() => {
// do nothing
});
- }, []);
+ }, [router]);
return (
<>