From 7566fab7737d0a8448892dcb2fd41ad6467ec312 Mon Sep 17 00:00:00 2001
From: Victorien Gauch <85494462+VGau@users.noreply.github.com>
Date: Tue, 10 Sep 2024 13:08:37 +0200
Subject: [PATCH] Feat(3102): add menu and history page (#3852)
* fix: refactor state management
* fix: update pnpm lock file and use fixed version for zustand
* feat: add sidebar menu and mobile menu + transaction history page
* feat: add side bar menu and history page
* fix: remove unused code + update TransactionClaimButton component
* fix: update dockerfile to remove warning during build
---
bridge-ui/Dockerfile | 2 +-
bridge-ui/Makefile | 2 +-
bridge-ui/next.config.mjs | 19 +
bridge-ui/package.json | 2 +-
bridge-ui/playwright.config.ts | 6 +-
bridge-ui/src/app/globals.css | 35 -
bridge-ui/src/app/page.tsx | 16 +-
bridge-ui/src/app/transactions/page.tsx | 27 +
bridge-ui/src/assets/icons/bridge.svg | 22 +
bridge-ui/src/assets/icons/docs.svg | 13 +
bridge-ui/src/assets/icons/faq.svg | 15 +
bridge-ui/src/assets/icons/swap.svg | 32 +
bridge-ui/src/assets/icons/transaction.svg | 27 +
bridge-ui/src/components/ConnectButton.tsx | 14 +
.../src/components/bridge/BridgeLayout.tsx | 24 +-
bridge-ui/src/components/bridge/BridgeUI.tsx | 2 -
.../src/components/bridge/forms/Amount.tsx | 5 +-
.../src/components/bridge/forms/Fees.tsx | 5 +-
.../components/bridge/forms/TokenModal.tsx | 2 +-
bridge-ui/src/components/footer/Footer.tsx | 38 --
bridge-ui/src/components/header/Header.tsx | 55 --
bridge-ui/src/components/hero/Hero.tsx | 60 --
.../src/components/history/HistoryClaim.tsx | 138 ----
.../src/components/history/HistoryItem.tsx | 4 +-
bridge-ui/src/components/layouts/Layout.tsx | 33 +-
.../src/components/layouts/MobileMenu.tsx | 78 +++
bridge-ui/src/components/layouts/Modal.tsx | 32 +
.../src/components/layouts/Providers.tsx | 8 +-
bridge-ui/src/components/layouts/Sidebar.tsx | 68 ++
.../{ => layouts}/header/Chains.tsx | 0
.../src/components/layouts/header/Header.tsx | 62 ++
.../{ => layouts}/header/Wallet.tsx | 20 +-
.../src/components/shortcut/Shortcut.tsx | 188 ------
bridge-ui/src/components/terms/TermsModal.tsx | 12 +-
.../components/transactions/NoTransaction.tsx | 12 +
.../components/transactions/StatusText.tsx | 25 +
.../transactions/TransactionItem.tsx | 78 +++
.../transactions/TransactionProgressBar.tsx | 137 ++++
.../components/transactions/Transactions.tsx | 93 +++
.../src/components/transactions/index.tsx | 2 +
.../modals/TransactionClaimButton.tsx | 93 +++
.../modals/TransactionDetailsModal.tsx | 71 +++
.../src/components/widgets/SwitchNetwork.tsx | 8 +-
bridge-ui/src/contexts/modal.context.tsx | 46 ++
bridge-ui/src/data/shortcuts.md | 34 -
bridge-ui/src/hooks/index.ts | 7 +-
bridge-ui/src/hooks/useBridge.ts | 4 +-
.../src/hooks/useFetchBridgeTransactions.ts | 8 +-
bridge-ui/src/hooks/useFetchHistory.ts | 12 +-
bridge-ui/src/hooks/useInitialiseChain.ts | 6 +-
bridge-ui/src/hooks/useInitialiseToken.ts | 27 +-
bridge-ui/src/hooks/useLineaSDK.ts | 52 ++
bridge-ui/src/hooks/useMessageService.ts | 277 --------
bridge-ui/src/hooks/useMessageStatus.ts | 102 +++
bridge-ui/src/hooks/useMinimumFee.ts | 47 ++
.../src/hooks/useTransactionManagement.ts | 118 ++++
bridge-ui/src/models/shortcut.ts | 6 -
bridge-ui/src/services/fetchERC20Image.ts | 48 --
bridge-ui/src/services/fetchTokenInfo.ts | 67 --
bridge-ui/src/services/index.ts | 1 +
bridge-ui/src/services/tokenService.ts | 139 ++++
bridge-ui/src/stores/configStore.ts | 7 +
bridge-ui/src/utils/chainsUtil.ts | 13 +
bridge-ui/src/utils/constants.ts | 38 ++
bridge-ui/src/utils/format.ts | 22 +
.../transactionParsers/parseERC20Events.ts | 2 +-
bridge-ui/svg.d.ts | 4 +
bridge-ui/tailwind.config.ts | 9 +-
bridge-ui/test/wallet-setup/metamask.setup.ts | 2 +
pnpm-lock.yaml | 596 ++++++++++++++----
70 files changed, 2074 insertions(+), 1205 deletions(-)
create mode 100644 bridge-ui/src/app/transactions/page.tsx
create mode 100644 bridge-ui/src/assets/icons/bridge.svg
create mode 100644 bridge-ui/src/assets/icons/docs.svg
create mode 100644 bridge-ui/src/assets/icons/faq.svg
create mode 100644 bridge-ui/src/assets/icons/swap.svg
create mode 100644 bridge-ui/src/assets/icons/transaction.svg
create mode 100644 bridge-ui/src/components/ConnectButton.tsx
delete mode 100644 bridge-ui/src/components/footer/Footer.tsx
delete mode 100644 bridge-ui/src/components/header/Header.tsx
delete mode 100644 bridge-ui/src/components/hero/Hero.tsx
delete mode 100644 bridge-ui/src/components/history/HistoryClaim.tsx
create mode 100644 bridge-ui/src/components/layouts/MobileMenu.tsx
create mode 100644 bridge-ui/src/components/layouts/Modal.tsx
create mode 100644 bridge-ui/src/components/layouts/Sidebar.tsx
rename bridge-ui/src/components/{ => layouts}/header/Chains.tsx (100%)
create mode 100644 bridge-ui/src/components/layouts/header/Header.tsx
rename bridge-ui/src/components/{ => layouts}/header/Wallet.tsx (75%)
delete mode 100644 bridge-ui/src/components/shortcut/Shortcut.tsx
create mode 100644 bridge-ui/src/components/transactions/NoTransaction.tsx
create mode 100644 bridge-ui/src/components/transactions/StatusText.tsx
create mode 100644 bridge-ui/src/components/transactions/TransactionItem.tsx
create mode 100644 bridge-ui/src/components/transactions/TransactionProgressBar.tsx
create mode 100644 bridge-ui/src/components/transactions/Transactions.tsx
create mode 100644 bridge-ui/src/components/transactions/index.tsx
create mode 100644 bridge-ui/src/components/transactions/modals/TransactionClaimButton.tsx
create mode 100644 bridge-ui/src/components/transactions/modals/TransactionDetailsModal.tsx
create mode 100644 bridge-ui/src/contexts/modal.context.tsx
delete mode 100644 bridge-ui/src/data/shortcuts.md
create mode 100644 bridge-ui/src/hooks/useLineaSDK.ts
delete mode 100644 bridge-ui/src/hooks/useMessageService.ts
create mode 100644 bridge-ui/src/hooks/useMessageStatus.ts
create mode 100644 bridge-ui/src/hooks/useMinimumFee.ts
create mode 100644 bridge-ui/src/hooks/useTransactionManagement.ts
delete mode 100644 bridge-ui/src/models/shortcut.ts
delete mode 100644 bridge-ui/src/services/fetchERC20Image.ts
delete mode 100644 bridge-ui/src/services/fetchTokenInfo.ts
create mode 100644 bridge-ui/src/services/index.ts
create mode 100644 bridge-ui/src/services/tokenService.ts
create mode 100644 bridge-ui/src/utils/constants.ts
create mode 100644 bridge-ui/svg.d.ts
diff --git a/bridge-ui/Dockerfile b/bridge-ui/Dockerfile
index ebdacce6..c3d23f0d 100644
--- a/bridge-ui/Dockerfile
+++ b/bridge-ui/Dockerfile
@@ -33,7 +33,7 @@ ARG X_TAG
WORKDIR /app
ENV NODE_ENV=production
-ENV NEXT_TELEMETRY_DISABLED 1
+ENV NEXT_TELEMETRY_DISABLED=1
USER node
diff --git a/bridge-ui/Makefile b/bridge-ui/Makefile
index f27eb9af..af1677a6 100644
--- a/bridge-ui/Makefile
+++ b/bridge-ui/Makefile
@@ -6,4 +6,4 @@ sgr0 := $(shell tput sgr0)
.PHONY: dev
dev:
- npm run dev
+ pnpm run dev
diff --git a/bridge-ui/next.config.mjs b/bridge-ui/next.config.mjs
index b60e2768..98f13e74 100644
--- a/bridge-ui/next.config.mjs
+++ b/bridge-ui/next.config.mjs
@@ -35,6 +35,25 @@ const nextConfig = {
fs: false,
};
config.externals.push("pino-pretty", "lokijs", "encoding");
+
+ const fileLoaderRule = config.module.rules.find((rule) => rule.test?.test?.(".svg"));
+
+ config.module.rules.push(
+ {
+ ...fileLoaderRule,
+ test: /\.svg$/i,
+ resourceQuery: /url/,
+ },
+ {
+ test: /\.svg$/i,
+ issuer: fileLoaderRule.issuer,
+ resourceQuery: { not: [...fileLoaderRule.resourceQuery.not, /url/] },
+ use: ["@svgr/webpack"],
+ },
+ );
+
+ fileLoaderRule.exclude = /\.svg$/i;
+
return config;
},
};
diff --git a/bridge-ui/package.json b/bridge-ui/package.json
index 11798dd1..73959e29 100644
--- a/bridge-ui/package.json
+++ b/bridge-ui/package.json
@@ -30,7 +30,6 @@
"compare-versions": "6.1.1",
"date-fns": "3.6.0",
"framer-motion": "11.3.17",
- "gray-matter": "4.0.3",
"joi": "17.13.3",
"loglevel": "1.9.1",
"next": "14.2.5",
@@ -50,6 +49,7 @@
},
"devDependencies": {
"@playwright/test": "1.45.3",
+ "@svgr/webpack": "^8.1.0",
"@synthetixio/synpress": "4.0.0-alpha.7",
"@types/fs-extra": "11.0.4",
"@types/react": "18.3.3",
diff --git a/bridge-ui/playwright.config.ts b/bridge-ui/playwright.config.ts
index 6f7bd540..001f5384 100644
--- a/bridge-ui/playwright.config.ts
+++ b/bridge-ui/playwright.config.ts
@@ -7,11 +7,11 @@ export default defineConfig({
maxFailures: process.env.CI ? 1 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: process.env.CI
- ? [['html', { open: 'never', outputFolder: `playwright-report-${process.env.HEADLESS ? 'headless' : 'headful'}` }]]
- : 'html',
+ ? [["html", { open: "never", outputFolder: `playwright-report-${process.env.HEADLESS ? "headless" : "headful"}` }]]
+ : "html",
use: {
baseURL: "http://localhost:3000",
- trace: process.env.CI ? 'on' : 'retain-on-failure'
+ trace: process.env.CI ? "on" : "retain-on-failure",
},
projects: [
{
diff --git a/bridge-ui/src/app/globals.css b/bridge-ui/src/app/globals.css
index 694de178..e90b3459 100644
--- a/bridge-ui/src/app/globals.css
+++ b/bridge-ui/src/app/globals.css
@@ -3,35 +3,11 @@
@tailwind utilities;
:root {
- --foreground-rgb: 0, 0, 0;
- --background-start-rgb: 214, 219, 220;
- --background-end-rgb: 255, 255, 255;
-
/* toastify */
--toastify-color-success: #7adffd;
--toastify-color-progress-success: #7adffd;
}
-@media (prefers-color-scheme: dark) {
- :root {
- --foreground-rgb: 255, 255, 255;
- --background-start-rgb: 0, 0, 0;
- --background-end-rgb: 0, 0, 0;
- }
-}
-
-/*
-body {
- color: rgb(var(--foreground-rgb));
- background: linear-gradient(
- to bottom,
- transparent,
- rgb(var(--background-end-rgb))
- )
- rgb(var(--background-start-rgb));
-}
-*/
-
body {
background: #121212;
}
@@ -45,15 +21,4 @@ body {
.btn-custom {
@apply min-h-[2.5rem] h-[2.5rem] px-6;
}
-
-
- .shortcut-card {
- @apply rounded-sm relative transition-all duration-300 ease-in-out px-4 py-5 flex flex-col;
- @apply hover:after:bg-primary hover:border-primary hover:-translate-y-0.5 bg-cardBg border border-card;
-
- &::after {
- @apply absolute top-0 right-0 translate-x-1/2 -translate-y-1/2 w-2.5 h-2.5 bg-card rounded-full z-10 transition-all duration-300 ease-in-out;
- content: "";
- }
- }
}
diff --git a/bridge-ui/src/app/page.tsx b/bridge-ui/src/app/page.tsx
index a2b58aee..5b7a7f45 100644
--- a/bridge-ui/src/app/page.tsx
+++ b/bridge-ui/src/app/page.tsx
@@ -1,14 +1,10 @@
import BridgeLayout from "@/components/bridge/BridgeLayout";
-import { Shortcut } from "@/models/shortcut";
-import matter from "gray-matter";
-
-async function getShortcuts() {
- const { data } = matter.read("src/data/shortcuts.md");
- return data as Shortcut[];
-}
export default async function Home() {
- const shortcuts = await getShortcuts();
-
- return ;
+ return (
+
+
Bridge
+
+
+ );
}
diff --git a/bridge-ui/src/app/transactions/page.tsx b/bridge-ui/src/app/transactions/page.tsx
new file mode 100644
index 00000000..8cc229f8
--- /dev/null
+++ b/bridge-ui/src/app/transactions/page.tsx
@@ -0,0 +1,27 @@
+"use client";
+
+import ConnectButton from "@/components/ConnectButton";
+import { Transactions } from "@/components/transactions";
+import { useAccount } from "wagmi";
+
+export default function TransactionsPage() {
+ const { isConnected } = useAccount();
+
+ if (!isConnected) {
+ return (
+
+
+ Please connect your wallet.
+
+
+
+ );
+ }
+
+ return (
+
+
Transactions
+
+
+ );
+}
diff --git a/bridge-ui/src/assets/icons/bridge.svg b/bridge-ui/src/assets/icons/bridge.svg
new file mode 100644
index 00000000..5ba15821
--- /dev/null
+++ b/bridge-ui/src/assets/icons/bridge.svg
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/bridge-ui/src/assets/icons/docs.svg b/bridge-ui/src/assets/icons/docs.svg
new file mode 100644
index 00000000..44a6830e
--- /dev/null
+++ b/bridge-ui/src/assets/icons/docs.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
diff --git a/bridge-ui/src/assets/icons/faq.svg b/bridge-ui/src/assets/icons/faq.svg
new file mode 100644
index 00000000..9b57d375
--- /dev/null
+++ b/bridge-ui/src/assets/icons/faq.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/bridge-ui/src/assets/icons/swap.svg b/bridge-ui/src/assets/icons/swap.svg
new file mode 100644
index 00000000..f5a5136a
--- /dev/null
+++ b/bridge-ui/src/assets/icons/swap.svg
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/bridge-ui/src/assets/icons/transaction.svg b/bridge-ui/src/assets/icons/transaction.svg
new file mode 100644
index 00000000..ba0f1958
--- /dev/null
+++ b/bridge-ui/src/assets/icons/transaction.svg
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
diff --git a/bridge-ui/src/components/ConnectButton.tsx b/bridge-ui/src/components/ConnectButton.tsx
new file mode 100644
index 00000000..f1a5d4e0
--- /dev/null
+++ b/bridge-ui/src/components/ConnectButton.tsx
@@ -0,0 +1,14 @@
+import { useWeb3Modal } from "@web3modal/wagmi/react";
+
+export default function ConnectButton() {
+ const { open } = useWeb3Modal();
+ return (
+ open()}
+ >
+ Connect Wallet
+
+ );
+}
diff --git a/bridge-ui/src/components/bridge/BridgeLayout.tsx b/bridge-ui/src/components/bridge/BridgeLayout.tsx
index ea30a41d..770021ac 100644
--- a/bridge-ui/src/components/bridge/BridgeLayout.tsx
+++ b/bridge-ui/src/components/bridge/BridgeLayout.tsx
@@ -1,27 +1,7 @@
"use client";
import BridgeUI from "@/components/bridge/BridgeUI";
-import Hero from "@/components/hero/Hero";
-import { UIContext } from "@/contexts/ui.context";
-import { Shortcut } from "@/models/shortcut";
-import { createContext, useContext } from "react";
-const ShortcutContext = createContext([]);
-
-export function useShortcuts() {
- return useContext(ShortcutContext);
-}
-
-export default function BridgeLayout({ shortcuts }: { shortcuts: Shortcut[] }) {
- const { showBridge } = useContext(UIContext);
-
- if (showBridge) {
- return ;
- }
-
- return (
-
-
-
- );
+export default function BridgeLayout() {
+ return ;
}
diff --git a/bridge-ui/src/components/bridge/BridgeUI.tsx b/bridge-ui/src/components/bridge/BridgeUI.tsx
index 9aa32e3a..751cf2b2 100644
--- a/bridge-ui/src/components/bridge/BridgeUI.tsx
+++ b/bridge-ui/src/components/bridge/BridgeUI.tsx
@@ -4,7 +4,6 @@ import React from "react";
import { useAccount } from "wagmi";
import WrongNetwork from "./WrongNetwork";
import TermsModal from "../terms/TermsModal";
-import History from "@/components/history/History";
import Bridge from "@/components/bridge/forms/Bridge";
import { AnimatePresence, motion } from "framer-motion";
import { NetworkType } from "@/config";
@@ -37,7 +36,6 @@ const BridgeUI: React.FC = () => {
{networkType !== NetworkType.WRONG_NETWORK || !isConnected ? : }
- {isConnected && }
{/* */}
diff --git a/bridge-ui/src/components/bridge/forms/Amount.tsx b/bridge-ui/src/components/bridge/forms/Amount.tsx
index e32a3776..665a14da 100644
--- a/bridge-ui/src/components/bridge/forms/Amount.tsx
+++ b/bridge-ui/src/components/bridge/forms/Amount.tsx
@@ -5,9 +5,10 @@ import { useFormContext } from "react-hook-form";
import Image from "next/image";
import { useAccount } from "wagmi";
import { formatEther, parseUnits } from "viem";
-import { useBridge, useMessageService } from "@/hooks";
+import { useBridge } from "@/hooks";
import { TokenType } from "@/config";
import { useChainStore } from "@/stores/chainStore";
+import useMinimumFee from "@/hooks/useMinimumFee";
const MAX_AMOUNT_CHAR = 24;
const FEES_MARGIN_PERCENT = 20;
@@ -35,7 +36,7 @@ export default function Amount({ tokensModalRef }: Props) {
// Hooks
const { isConnected } = useAccount();
const { estimateGasBridge } = useBridge();
- const { minimumFee } = useMessageService();
+ const { minimumFee } = useMinimumFee();
const compareAmountBalance = useCallback(
(_amount: string) => {
diff --git a/bridge-ui/src/components/bridge/forms/Fees.tsx b/bridge-ui/src/components/bridge/forms/Fees.tsx
index 9440645b..6308e95b 100644
--- a/bridge-ui/src/components/bridge/forms/Fees.tsx
+++ b/bridge-ui/src/components/bridge/forms/Fees.tsx
@@ -7,10 +7,11 @@ import { MdInfoOutline } from "react-icons/md";
import classNames from "classnames";
import { formatEther, parseEther, parseUnits } from "viem";
import { useAccount, useBalance } from "wagmi";
-import { useApprove, useMessageService, useExecutionFee } from "@/hooks";
+import { useApprove, useExecutionFee } from "@/hooks";
import useBridge from "@/hooks/useBridge";
import { NetworkLayer, TokenType } from "@/config";
import { useChainStore } from "@/stores/chainStore";
+import useMinimumFee from "@/hooks/useMinimumFee";
export default function Fees() {
const [estimatedGasFee, setEstimatedGasFee] = useState();
@@ -41,7 +42,7 @@ export default function Fees() {
const balance = watch("balance", false);
// Hooks
- const { minimumFee } = useMessageService();
+ const { minimumFee } = useMinimumFee();
const { estimateGasBridge } = useBridge();
const { estimateApprove } = useApprove();
const minFees = useExecutionFee({
diff --git a/bridge-ui/src/components/bridge/forms/TokenModal.tsx b/bridge-ui/src/components/bridge/forms/TokenModal.tsx
index 450d6242..a12f16f4 100644
--- a/bridge-ui/src/components/bridge/forms/TokenModal.tsx
+++ b/bridge-ui/src/components/bridge/forms/TokenModal.tsx
@@ -4,12 +4,12 @@ import { useMemo, useState } from "react";
import { isAddress, getAddress } from "viem";
import TokenDetails from "./TokenDetails";
import { NetworkType, TokenInfo, TokenType } from "@/config/config";
-import fetchTokenInfo from "@/services/fetchTokenInfo";
import useERC20Storage from "@/hooks/useERC20Storage";
import { safeGetAddress } from "@/utils/format";
import { useBridge } from "@/hooks";
import { useChainStore } from "@/stores/chainStore";
import { useTokenStore } from "@/stores/tokenStore";
+import { fetchTokenInfo } from "@/services";
interface Props {
tokensModalRef: React.RefObject;
diff --git a/bridge-ui/src/components/footer/Footer.tsx b/bridge-ui/src/components/footer/Footer.tsx
deleted file mode 100644
index 9091350e..00000000
--- a/bridge-ui/src/components/footer/Footer.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import Link from "next/link";
-
-import PackageJSON from "@/../package.json";
-
-export default function Footer() {
- return (
-
-
- @{new Date().getFullYear()}
-
- LINEA • A Consensys Formation
-
- v{PackageJSON.version}
-
-
-
- Tutorial
-
-
- Documentation
-
-
- Terms of service
-
-
-
- );
-}
diff --git a/bridge-ui/src/components/header/Header.tsx b/bridge-ui/src/components/header/Header.tsx
deleted file mode 100644
index 099f2b8a..00000000
--- a/bridge-ui/src/components/header/Header.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-"use client";
-
-import { useContext } from "react";
-import Image from "next/image";
-import { useAccount } from "wagmi";
-import Wallet from "./Wallet";
-import Chains from "./Chains";
-import { UIContext } from "@/contexts/ui.context";
-import { NetworkType } from "@/config";
-import { useChainStore } from "@/stores/chainStore";
-
-export default function Header() {
- // Hooks
- const { isConnected } = useAccount();
-
- // Context
- const networkType = useChainStore((state) => state.networkType);
-
- const { toggleShowBridge } = useContext(UIContext);
-
- return (
-
- );
-}
diff --git a/bridge-ui/src/components/hero/Hero.tsx b/bridge-ui/src/components/hero/Hero.tsx
deleted file mode 100644
index 1e083ebb..00000000
--- a/bridge-ui/src/components/hero/Hero.tsx
+++ /dev/null
@@ -1,60 +0,0 @@
-"use client";
-
-import React, { useContext } from "react";
-import { UIContext } from "@/contexts/ui.context";
-import ToolTip from "../toolTip/ToolTip";
-import Shortcut from "../shortcut/Shortcut";
-
-const Hero: React.FC = () => {
- const { toggleShowBridge } = useContext(UIContext);
- return (
- <>
-
-
- How would you like to bridge your funds?
-
-
-
-
-
- >
- );
-};
-
-export default Hero;
diff --git a/bridge-ui/src/components/history/HistoryClaim.tsx b/bridge-ui/src/components/history/HistoryClaim.tsx
deleted file mode 100644
index 847d13d4..00000000
--- a/bridge-ui/src/components/history/HistoryClaim.tsx
+++ /dev/null
@@ -1,138 +0,0 @@
-import { useEffect, useState } from "react";
-import { MdCheck } from "react-icons/md";
-import { OnChainMessageStatus } from "@consensys/linea-sdk";
-import classNames from "classnames";
-import { toast } from "react-toastify";
-import { MessageWithStatus } from "@/hooks/useMessageService";
-import { useMessageService, useSwitchNetwork } from "@/hooks";
-import { Transaction } from "@/models";
-import { TransactionHistory } from "@/models/history";
-import { useWaitForTransactionReceipt } from "wagmi";
-import { useChainStore } from "@/stores/chainStore";
-
-interface Props {
- message: MessageWithStatus;
- transaction: TransactionHistory;
-}
-
-export default function HistoryClaim({ message, transaction }: Props) {
- const [waitingTransaction, setWaitingTransaction] = useState();
- const [isClaimingLoading, setIsClaimingLoading] = useState(false);
- const [isSuccess, setIsSuccess] = useState(false);
-
- // Is automatic or manual bridging
- const manualBridging = message.fee === "0";
-
- // Context
- const toChain = useChainStore((state) => state.toChain);
-
- // Hooks
- const { switchChainById } = useSwitchNetwork(toChain?.id);
- const { writeClaimMessage, isLoading: isTxLoading, transaction: claimTx } = useMessageService();
-
- // Wagmi
- const {
- isLoading: isWaitingLoading,
- isSuccess: isWaitingSuccess,
- isError: isWaitingError,
- } = useWaitForTransactionReceipt({
- hash: waitingTransaction?.txHash,
- chainId: waitingTransaction?.chainId,
- });
-
- const BridgingClaimable = ({ enabled = false }) => {
- const claimBusy = isClaimingLoading || isTxLoading || isWaitingLoading || !enabled;
- return (
-
- !claimBusy && onClaimMessage()}
- className={classNames("btn btn-primary w-38 rounded-full btn-sm mt-1 no-animation mr-2 uppercase", {
- "cursor-wait": claimBusy,
- })}
- type="button"
- disabled={!enabled}
- >
- {claimBusy && }
- Claim funds
-
-
- );
- };
-
- const BridgingComplete = () => (
-
-
- Bridging complete
-
- );
-
- const WaitingForTransaction = ({ loading }: { loading: boolean }) => (
-
- {loading && }
- Please wait, your funds are being bridged
-
- );
-
- const getClaimStatus = () => {
- if (message.status === OnChainMessageStatus.CLAIMED || isSuccess) {
- return ;
- } else if (message.status === OnChainMessageStatus.CLAIMABLE) {
- return ;
- } else {
- if (manualBridging) {
- return (
-
-
-
-
- );
- }
- return ;
- }
- };
-
- const onClaimMessage = async () => {
- if (isClaimingLoading) {
- return;
- }
-
- try {
- setIsClaimingLoading(true);
- await switchChainById(transaction.toChain.id);
- await writeClaimMessage(message, transaction);
- // eslint-disable-next-line no-empty
- } catch (error) {
- } finally {
- setIsClaimingLoading(false);
- }
- };
-
- useEffect(() => {
- if (claimTx) {
- setWaitingTransaction({
- txHash: claimTx.txHash,
- chainId: transaction.toChain.id,
- name: transaction.toChain.name,
- });
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [claimTx]);
-
- useEffect(() => {
- if (isWaitingSuccess) {
- toast.success(`Funds claimed on ${transaction.toChain.name}.`);
- setIsSuccess(true);
- setWaitingTransaction(undefined);
- }
- }, [isWaitingSuccess, transaction]);
-
- useEffect(() => {
- if (isWaitingError) {
- toast.error("Funds claiming failed.");
- setWaitingTransaction(undefined);
- }
- }, [isWaitingError]);
-
- return {getClaimStatus()}
;
-}
diff --git a/bridge-ui/src/components/history/HistoryItem.tsx b/bridge-ui/src/components/history/HistoryItem.tsx
index 55522ca0..1b77e96d 100644
--- a/bridge-ui/src/components/history/HistoryItem.tsx
+++ b/bridge-ui/src/components/history/HistoryItem.tsx
@@ -7,9 +7,9 @@ import { MdOutlineArrowRightAlt } from "react-icons/md";
import { Address, formatUnits, zeroAddress } from "viem";
import ChainLogo from "@/components/widgets/ChainLogo";
-import HistoryClaim from "./HistoryClaim";
import { formatAddress, safeGetAddress } from "@/utils/format";
import { TransactionHistory } from "@/models/history";
+import TransactionClaimButton from "../transactions/modals/TransactionClaimButton";
interface Props {
transaction: TransactionHistory;
@@ -112,7 +112,7 @@ export default function HistoryItem({ transaction, variants }: Props) {
{transaction.messages?.map((message) => {
- return ;
+ return ;
})}
diff --git a/bridge-ui/src/components/layouts/Layout.tsx b/bridge-ui/src/components/layouts/Layout.tsx
index 8cda7b1b..d33742c2 100644
--- a/bridge-ui/src/components/layouts/Layout.tsx
+++ b/bridge-ui/src/components/layouts/Layout.tsx
@@ -1,14 +1,13 @@
"use client";
-import Image from "next/image";
import { ToastContainer } from "react-toastify";
import atypTextFont from "@/app/font/atypText";
import atypFont from "@/app/font/atyp";
-import Header from "../header/Header";
+import Header from "./header/Header";
import SwitchNetwork from "../widgets/SwitchNetwork";
-import Footer from "../footer/Footer";
import useInitialiseChain from "@/hooks/useInitialiseChain";
import useInitialiseToken from "@/hooks/useInitialiseToken";
+import Sidebar from "./Sidebar";
export function Layout({ children }: { children: React.ReactNode }) {
useInitialiseChain();
@@ -16,7 +15,7 @@ export function Layout({ children }: { children: React.ReactNode }) {
return (
-
-
-
-
+
+
+
+
+
{children}
+
-
-
);
}
diff --git a/bridge-ui/src/components/layouts/MobileMenu.tsx b/bridge-ui/src/components/layouts/MobileMenu.tsx
new file mode 100644
index 00000000..55c462dc
--- /dev/null
+++ b/bridge-ui/src/components/layouts/MobileMenu.tsx
@@ -0,0 +1,78 @@
+import Image from "next/image";
+import Link from "next/link";
+import { usePathname } from "next/navigation";
+import { MENU_ITEMS } from "@/utils/constants";
+import { MdOutlineClose } from "react-icons/md";
+
+type MobileMenuProps = {
+ toggleMenu: () => void;
+};
+
+export default function MobileMenu({ toggleMenu }: MobileMenuProps) {
+ const pathname = usePathname();
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ {MENU_ITEMS.map(({ title, href, external, Icon }) => (
+
+ {external ? (
+
+
+ {title}
+
+ ) : (
+
+
+ {title}
+
+ )}
+
+ ))}
+
+
+
+
+ Contact Support
+
+
+ Terms of service
+
+
+
+
+ );
+}
diff --git a/bridge-ui/src/components/layouts/Modal.tsx b/bridge-ui/src/components/layouts/Modal.tsx
new file mode 100644
index 00000000..bd759dd8
--- /dev/null
+++ b/bridge-ui/src/components/layouts/Modal.tsx
@@ -0,0 +1,32 @@
+"use client";
+
+import classNames from "classnames";
+import { useContext } from "react";
+import { ModalContext } from "@/contexts/modal.context";
+import { MdOutlineClose } from "react-icons/md";
+
+type ModalProps = Record;
+
+const Modal: React.FC = () => {
+ const { ref, modalContent, options } = useContext(ModalContext);
+
+ const width = options?.width ? options.width : "w-screen md:w-[600px]";
+
+ return (
+
+
+
+ {modalContent}
+
+
+
+ );
+};
+
+export default Modal;
diff --git a/bridge-ui/src/components/layouts/Providers.tsx b/bridge-ui/src/components/layouts/Providers.tsx
index 29f1695d..129c0c5d 100644
--- a/bridge-ui/src/components/layouts/Providers.tsx
+++ b/bridge-ui/src/components/layouts/Providers.tsx
@@ -1,8 +1,8 @@
"use client";
import { State } from "wagmi";
-import { UIProvider } from "@/contexts/ui.context";
import { Web3Provider } from "@/contexts/web3.context";
+import { ModalProvider } from "@/contexts/modal.context";
type ProvidersProps = {
children: JSX.Element;
@@ -11,8 +11,8 @@ type ProvidersProps = {
export function Providers({ children, initialState }: ProvidersProps) {
return (
-
- {children}
-
+
+ {children}
+
);
}
diff --git a/bridge-ui/src/components/layouts/Sidebar.tsx b/bridge-ui/src/components/layouts/Sidebar.tsx
new file mode 100644
index 00000000..f7bc73bd
--- /dev/null
+++ b/bridge-ui/src/components/layouts/Sidebar.tsx
@@ -0,0 +1,68 @@
+import { usePathname } from "next/navigation";
+import Link from "next/link";
+import Image from "next/image";
+import { MENU_ITEMS } from "@/utils/constants";
+
+export default function Sidebar() {
+ const pathname = usePathname();
+
+ return (
+
+ );
+}
diff --git a/bridge-ui/src/components/header/Chains.tsx b/bridge-ui/src/components/layouts/header/Chains.tsx
similarity index 100%
rename from bridge-ui/src/components/header/Chains.tsx
rename to bridge-ui/src/components/layouts/header/Chains.tsx
diff --git a/bridge-ui/src/components/layouts/header/Header.tsx b/bridge-ui/src/components/layouts/header/Header.tsx
new file mode 100644
index 00000000..db3ec0d7
--- /dev/null
+++ b/bridge-ui/src/components/layouts/header/Header.tsx
@@ -0,0 +1,62 @@
+import { usePathname } from "next/navigation";
+import { useAccount } from "wagmi";
+import Image from "next/image";
+import { useEffect, useState } from "react";
+import { MdMenu } from "react-icons/md";
+import Wallet from "./Wallet";
+import Chains from "./Chains";
+import MobileMenu from "../MobileMenu";
+
+function formatPath(pathname: string): string {
+ switch (pathname) {
+ case "/":
+ case "":
+ return "Bridge";
+ case "/transactions":
+ return "Transactions";
+ default:
+ return "";
+ }
+}
+
+export default function Header() {
+ // Hooks
+ const { isConnected } = useAccount();
+ const pathname = usePathname();
+ const [isMenuOpen, setIsMenuOpen] = useState(false);
+
+ const toggleMenu = () => {
+ setIsMenuOpen(!isMenuOpen);
+ };
+
+ useEffect(() => {
+ if (isMenuOpen) {
+ document.body.classList.add("overflow-hidden");
+ } else {
+ document.body.classList.remove("overflow-hidden");
+ }
+ }, [isMenuOpen]);
+
+ return (
+
+
+ {formatPath(pathname)}
+
+
+ {isConnected && (
+
+
+
+ )}
+
+
+
+
+
+
+
+ {isMenuOpen &&
}
+
+
+ );
+}
diff --git a/bridge-ui/src/components/header/Wallet.tsx b/bridge-ui/src/components/layouts/header/Wallet.tsx
similarity index 75%
rename from bridge-ui/src/components/header/Wallet.tsx
rename to bridge-ui/src/components/layouts/header/Wallet.tsx
index 5f1ff3cd..9c3d8689 100644
--- a/bridge-ui/src/components/header/Wallet.tsx
+++ b/bridge-ui/src/components/layouts/header/Wallet.tsx
@@ -4,20 +4,14 @@ import { useEffect, useRef } from "react";
import Image from "next/image";
import { useAccount, useDisconnect } from "wagmi";
import { MdLogout } from "react-icons/md";
-import classNames from "classnames";
import { formatAddress } from "@/utils/format";
-import { useWeb3Modal } from "@web3modal/wagmi/react";
+import ConnectButton from "@/components/ConnectButton";
-type Props = {
- className?: string;
-};
-
-export default function Wallet({ className = "" }: Props) {
+export default function Wallet() {
const detailsRef = useRef(null);
const { address, isConnected } = useAccount();
const { disconnect } = useDisconnect();
- const { open } = useWeb3Modal();
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
@@ -59,15 +53,7 @@ export default function Wallet({ className = "" }: Props) {
return (
- open()}
- >
- Connect Wallet
-
+
);
}
diff --git a/bridge-ui/src/components/shortcut/Shortcut.tsx b/bridge-ui/src/components/shortcut/Shortcut.tsx
deleted file mode 100644
index f6300d20..00000000
--- a/bridge-ui/src/components/shortcut/Shortcut.tsx
+++ /dev/null
@@ -1,188 +0,0 @@
-import Image from "next/image";
-import React, { useRef, useState } from "react";
-import { Swiper, SwiperRef, SwiperSlide } from "swiper/react";
-import { motion } from "framer-motion";
-
-import "swiper/css";
-import classNames from "classnames";
-import { Shortcut as ShortcutInterface } from "@/models/shortcut";
-import { useShortcuts } from "../bridge/BridgeLayout";
-
-const poweredBy = [
- {
- label: "Onthis.xyz",
- url: "https://onthis.xyz/",
- logo: "/images/logo/onthis.svg",
- },
- {
- label: "Across",
- url: "https://across.to/",
- logo: "/images/logo/across.svg",
- },
-];
-
-const Shortcut: React.FC = () => {
- const [progress, setProgress] = useState(0);
- const sliderRef = useRef(null);
-
- const shortcuts = useShortcuts();
-
- const handleClickNext = () => {
- sliderRef.current?.swiper.slideNext();
- };
-
- const handleClickPrev = () => {
- sliderRef.current?.swiper.slidePrev();
- };
-
- return (
-
-
-
- {shortcuts.length > 0 && (
-
{
- setProgress(swiper.progress);
- }}
- className="mt-6 size-full"
- >
- {shortcuts.map((shortcut, index) => (
-
-
-
- ))}
-
- )}
-
-
-
-
- Note: *.onlinea.eth shortcuts are curated by Linea, but provided by Ecosystem Partners and Community. They are
- not canonical solutions and they include additional fees collected only by involved partners.
-
-
- );
-};
-
-const ShortcutCard: React.FC<{
- item: ShortcutInterface;
- className?: string;
- index?: number;
-}> = ({ item, className, index = 0 }) => {
- const { title, description, logo, ens_name } = item;
- const [buttonClicked, setButtonClicked] = useState(false);
-
- const onButtonClick = () => {
- setButtonClicked(true);
- window.navigator.clipboard.writeText(ens_name);
- setTimeout(() => setButtonClicked(false), 3000);
- };
-
- return (
-
-
- {logo &&
}
-
{description}
- {ens_name && (
-
- {buttonClicked ? "Copied to Clipboard" : ens_name}
-
- )}
-
-
- );
-};
-
-export default Shortcut;
diff --git a/bridge-ui/src/components/terms/TermsModal.tsx b/bridge-ui/src/components/terms/TermsModal.tsx
index 79cc1fd6..34f4a7b9 100644
--- a/bridge-ui/src/components/terms/TermsModal.tsx
+++ b/bridge-ui/src/components/terms/TermsModal.tsx
@@ -5,8 +5,10 @@ import { useConfigStore } from "@/stores/configStore";
export default function TermsModal() {
const termsModalRef = useRef(null);
- const { agreeToTerms, setAgreeToTerms } = useConfigStore((state) => ({
+
+ const { agreeToTerms, rehydrated, setAgreeToTerms } = useConfigStore((state) => ({
agreeToTerms: state.agreeToTerms,
+ rehydrated: state.rehydrated,
setAgreeToTerms: state.setAgreeToTerms,
}));
@@ -24,14 +26,18 @@ export default function TermsModal() {
};
useEffect(() => {
- if (window && isFirstTime()) {
+ if (rehydrated && window && isFirstTime()) {
setTimeout(() => {
setOpen(true);
setVideoEnabled(true);
}, 1000);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
+ }, [rehydrated]);
+
+ if (!rehydrated) {
+ return null;
+ }
return (
+ No bridge transactions found
+
+ Bridge assets
+
+
+ );
+}
diff --git a/bridge-ui/src/components/transactions/StatusText.tsx b/bridge-ui/src/components/transactions/StatusText.tsx
new file mode 100644
index 00000000..651ffa33
--- /dev/null
+++ b/bridge-ui/src/components/transactions/StatusText.tsx
@@ -0,0 +1,25 @@
+import { OnChainMessageStatus } from "@consensys/linea-sdk";
+import { useMemo } from "react";
+
+type StatusTextProps = {
+ status: OnChainMessageStatus;
+};
+
+const useFormattedStatus = (status: OnChainMessageStatus): JSX.Element => {
+ return useMemo(() => {
+ switch (status) {
+ case OnChainMessageStatus.CLAIMABLE:
+ return Ready to claim ;
+ case OnChainMessageStatus.UNKNOWN:
+ return Pending ;
+ case OnChainMessageStatus.CLAIMED:
+ return Completed ;
+ default:
+ throw new Error(`Incorrect transaction status: ${status}`);
+ }
+ }, [status]);
+};
+
+export default function StatusText({ status }: StatusTextProps) {
+ return useFormattedStatus(status);
+}
diff --git a/bridge-ui/src/components/transactions/TransactionItem.tsx b/bridge-ui/src/components/transactions/TransactionItem.tsx
new file mode 100644
index 00000000..ecd75a28
--- /dev/null
+++ b/bridge-ui/src/components/transactions/TransactionItem.tsx
@@ -0,0 +1,78 @@
+"use client";
+
+import { useContext } from "react";
+import { formatEther } from "viem";
+import { ModalContext } from "@/contexts/modal.context";
+import StatusText from "./StatusText";
+import TransactionProgressBar from "./TransactionProgressBar";
+import TransactionDetailsModal from "./modals/TransactionDetailsModal";
+import { NETWORK_ID_TO_NAME } from "@/utils/constants";
+import { getChainNetworkLayerByChainId } from "@/utils/chainsUtil";
+import { TransactionHistory } from "@/models/history";
+import { MessageWithStatus } from "@/hooks";
+
+export enum TransactionStatus {
+ READY_TO_CLAIM = "READY_TO_CLAIM",
+ PENDING = "PENDING",
+ COMPLETED = "COMPLETED",
+}
+
+type TransactionItemProps = {
+ transaction: TransactionHistory;
+ message: MessageWithStatus;
+};
+
+export default function TransactionItem({ transaction, message }: TransactionItemProps) {
+ const { handleShow } = useContext(ModalContext);
+
+ return (
+ {
+ handleShow(
);
+ }}
+ >
+
+
+
+
+
From
+
{NETWORK_ID_TO_NAME[transaction.fromChain.id]}
+
+
+
+
+
+
+
+
+
+
To
+
{NETWORK_ID_TO_NAME[transaction.toChain.id]}
+
+
+
+
Amount
+
+ {formatEther(transaction.amount)} {transaction.token.symbol}
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/bridge-ui/src/components/transactions/TransactionProgressBar.tsx b/bridge-ui/src/components/transactions/TransactionProgressBar.tsx
new file mode 100644
index 00000000..2c38cac5
--- /dev/null
+++ b/bridge-ui/src/components/transactions/TransactionProgressBar.tsx
@@ -0,0 +1,137 @@
+import { useMemo } from "react";
+import { fromUnixTime } from "date-fns";
+import { OnChainMessageStatus } from "@consensys/linea-sdk";
+import { NetworkLayer } from "@/config";
+
+type TransactionProgressBarProps = {
+ status: OnChainMessageStatus;
+ transactionTimestamp: bigint;
+ fromChain?: NetworkLayer;
+};
+
+type TimeUnit = "seconds" | "minutes" | "hours";
+
+const L1_L2_ESTIMATED_TIME_IN_SECONDS = 20 * 60; // 20 minutes
+const L2_L1_MIN_TIME_IN_SECONDS = 8 * 60 * 60; // 8 hours
+const L2_L1_MAX_TIME_IN_SECONDS = 32 * 60 * 60; // 32 hours
+
+const getElapsedTime = (startTime: Date, currentTime: Date): number => {
+ return (currentTime.getTime() - startTime.getTime()) / 1000;
+};
+
+const getRemainingTime = (startTime: Date, currentTime: Date, unit: TimeUnit, fromChain: NetworkLayer): string => {
+ const totalTime =
+ fromChain === NetworkLayer.L1
+ ? L1_L2_ESTIMATED_TIME_IN_SECONDS
+ : (L2_L1_MIN_TIME_IN_SECONDS + L2_L1_MAX_TIME_IN_SECONDS) / 2;
+
+ const elapsedTime = getElapsedTime(startTime, currentTime);
+ const remainingTimeInSeconds = Math.max(totalTime - elapsedTime, 0);
+
+ const hours = Math.floor(remainingTimeInSeconds / 3600);
+ const minutes = Math.floor((remainingTimeInSeconds % 3600) / 60);
+ const seconds = Math.floor(remainingTimeInSeconds % 60);
+
+ switch (unit) {
+ case "seconds":
+ return `${seconds} secs`;
+ case "minutes":
+ return `${minutes} min`;
+ case "hours":
+ return `${hours} hrs`;
+ default:
+ throw new Error("Invalid time unit.");
+ }
+};
+
+const getCompletionPercentage = (
+ startTime: Date,
+ currentTime: Date,
+ status: OnChainMessageStatus,
+ fromChain?: NetworkLayer,
+): number => {
+ if (status === OnChainMessageStatus.CLAIMED) {
+ return 100;
+ }
+
+ if (!fromChain) {
+ throw new Error("Invalid network layer.");
+ }
+
+ const elapsedTime = getElapsedTime(startTime, currentTime);
+
+ let totalTime: number;
+
+ if (fromChain === NetworkLayer.L1) {
+ totalTime = L1_L2_ESTIMATED_TIME_IN_SECONDS; // Convert 20 minutes to seconds
+ } else if (fromChain === NetworkLayer.L2) {
+ totalTime = (L2_L1_MIN_TIME_IN_SECONDS + L2_L1_MAX_TIME_IN_SECONDS) / 2;
+ } else {
+ throw new Error("Invalid network layer.");
+ }
+
+ // Calculate the completion percentage
+ const completionPercentage = (elapsedTime / totalTime) * 100;
+
+ // Ensure the percentage does not exceed 100%
+ return Math.min(completionPercentage, 100);
+};
+
+const getProgressBarText = (
+ startTime: Date,
+ currentTime: Date,
+ status: OnChainMessageStatus,
+ fromChain?: NetworkLayer,
+): string => {
+ if (!fromChain) {
+ throw new Error("Invalid network layer.");
+ }
+
+ if (status === OnChainMessageStatus.CLAIMABLE || status === OnChainMessageStatus.CLAIMED) {
+ return "Complete";
+ }
+
+ const unit = fromChain === NetworkLayer.L1 ? "minutes" : "hours";
+
+ return `Est time left ${getRemainingTime(startTime, currentTime, unit, fromChain)}`;
+};
+
+const useProgressBarColor = (status: OnChainMessageStatus): string => {
+ return useMemo(() => {
+ switch (status) {
+ case OnChainMessageStatus.CLAIMABLE:
+ return "progress-secondary";
+ case OnChainMessageStatus.UNKNOWN:
+ return "progress-info";
+ case OnChainMessageStatus.CLAIMED:
+ return "progress-success";
+ default:
+ throw new Error(`Incorrect transaction status: ${status}`);
+ }
+ }, [status]);
+};
+
+export default function TransactionProgressBar({
+ status,
+ transactionTimestamp,
+ fromChain,
+}: TransactionProgressBarProps) {
+ const color = useProgressBarColor(status);
+ return (
+ <>
+
+ {[OnChainMessageStatus.CLAIMABLE, OnChainMessageStatus.UNKNOWN].includes(status) && (
+
+ )}
+
+ {getProgressBarText(fromUnixTime(Number(transactionTimestamp)), new Date(), status, fromChain)}
+
+
+
+ >
+ );
+}
diff --git a/bridge-ui/src/components/transactions/Transactions.tsx b/bridge-ui/src/components/transactions/Transactions.tsx
new file mode 100644
index 00000000..eea2f65e
--- /dev/null
+++ b/bridge-ui/src/components/transactions/Transactions.tsx
@@ -0,0 +1,93 @@
+"use client";
+
+import { useEffect, useMemo } from "react";
+import { useBlockNumber } from "wagmi";
+import { toast } from "react-toastify";
+import TransactionItem from "./TransactionItem";
+import { TransactionHistory } from "@/models/history";
+import { formatDate, fromUnixTime } from "date-fns";
+import { NoTransactions } from "./NoTransaction";
+import useFetchHistory from "@/hooks/useFetchHistory";
+
+const groupByDay = (transactions: TransactionHistory[]): Record => {
+ return transactions.reduce(
+ (acc, transaction) => {
+ const date = formatDate(fromUnixTime(Number(transaction.timestamp)), "yyyy-MM-dd");
+ if (!acc[date]) {
+ acc[date] = [];
+ }
+ acc[date].push(transaction);
+ return acc;
+ },
+ {} as Record,
+ );
+};
+
+export function Transactions() {
+ const { data: blockNumber } = useBlockNumber({
+ watch: true,
+ });
+
+ // Context
+ const { transactions, fetchHistory, isLoading, clearHistory } = useFetchHistory();
+
+ useEffect(() => {
+ if (blockNumber && blockNumber % 5n === 0n) {
+ fetchHistory();
+ }
+ }, [blockNumber, fetchHistory]);
+
+ const groupedTransactions = useMemo(() => groupByDay(transactions), [transactions]);
+
+ if (isLoading && transactions.length === 0) {
+ return (
+
+ );
+ }
+
+ if (transactions.length === 0) {
+ return ;
+ }
+
+ return (
+
+
+ {
+ clearHistory();
+ toast.success("History cleared");
+ }}
+ >
+ Reload history
+
+
+ {Object.keys(groupedTransactions).map((date, groupIndex) => (
+
+ {formatDate(date, "PPP")}
+ {groupedTransactions[date].map((transaction, transactionIndex) => {
+ if (transaction.messages && transaction.messages.length > 0 && transaction.messages[0].status) {
+ const { messages, ...bridgingTransaction } = transaction;
+ return (
+
+ );
+ }
+ })}
+
+ ))}
+
+ );
+}
diff --git a/bridge-ui/src/components/transactions/index.tsx b/bridge-ui/src/components/transactions/index.tsx
new file mode 100644
index 00000000..c073bf48
--- /dev/null
+++ b/bridge-ui/src/components/transactions/index.tsx
@@ -0,0 +1,2 @@
+export * from "./NoTransaction";
+export * from "./Transactions";
diff --git a/bridge-ui/src/components/transactions/modals/TransactionClaimButton.tsx b/bridge-ui/src/components/transactions/modals/TransactionClaimButton.tsx
new file mode 100644
index 00000000..f516b85b
--- /dev/null
+++ b/bridge-ui/src/components/transactions/modals/TransactionClaimButton.tsx
@@ -0,0 +1,93 @@
+import { useEffect, useState } from "react";
+import classNames from "classnames";
+import { toast } from "react-toastify";
+import { useSwitchNetwork } from "@/hooks";
+import { Transaction } from "@/models";
+import { TransactionHistory } from "@/models/history";
+import { useWaitForTransactionReceipt } from "wagmi";
+import { useChainStore } from "@/stores/chainStore";
+import useTransactionManagement, { MessageWithStatus } from "@/hooks/useTransactionManagement";
+
+interface Props {
+ message: MessageWithStatus;
+ transaction: TransactionHistory;
+}
+
+export default function TransactionClaimButton({ message, transaction }: Props) {
+ const [waitingTransaction, setWaitingTransaction] = useState();
+ const [isClaimingLoading, setIsClaimingLoading] = useState(false);
+
+ // Context
+ const toChain = useChainStore((state) => state.toChain);
+
+ // Hooks
+ const { switchChainById } = useSwitchNetwork(toChain?.id);
+ const { writeClaimMessage, isLoading: isTxLoading, transaction: claimTx } = useTransactionManagement();
+
+ // Wagmi
+ const {
+ isLoading: isWaitingLoading,
+ isSuccess: isWaitingSuccess,
+ isError: isWaitingError,
+ } = useWaitForTransactionReceipt({
+ hash: waitingTransaction?.txHash,
+ chainId: waitingTransaction?.chainId,
+ });
+
+ const claimBusy = isClaimingLoading || isTxLoading || isWaitingLoading;
+
+ useEffect(() => {
+ if (claimTx) {
+ setWaitingTransaction({
+ txHash: claimTx.txHash,
+ chainId: transaction.toChain.id,
+ name: transaction.toChain.name,
+ });
+ }
+ }, [claimTx, transaction.toChain.id, transaction.toChain.name]);
+
+ useEffect(() => {
+ if (isWaitingSuccess) {
+ toast.success(`Funds claimed on ${transaction.toChain.name}.`);
+ setWaitingTransaction(undefined);
+ }
+ }, [isWaitingSuccess, transaction.toChain.name]);
+
+ useEffect(() => {
+ if (isWaitingError) {
+ toast.error("Funds claiming failed.");
+ setWaitingTransaction(undefined);
+ }
+ }, [isWaitingError]);
+
+ const onClaimMessage = async () => {
+ if (isClaimingLoading) {
+ return;
+ }
+
+ try {
+ setIsClaimingLoading(true);
+ await switchChainById(transaction.toChain.id);
+ await writeClaimMessage(message, transaction);
+ } catch (error) {
+ toast.error("Failed to claim funds. Please try again.");
+ } finally {
+ setIsClaimingLoading(false);
+ }
+ };
+
+ return (
+ !claimBusy && onClaimMessage()}
+ className={classNames("btn btn-primary w-full rounded-full uppercase", {
+ "cursor-wait": claimBusy,
+ })}
+ type="button"
+ disabled={claimBusy}
+ >
+ {claimBusy && }
+ Claim
+
+ );
+}
diff --git a/bridge-ui/src/components/transactions/modals/TransactionDetailsModal.tsx b/bridge-ui/src/components/transactions/modals/TransactionDetailsModal.tsx
new file mode 100644
index 00000000..1ec33a32
--- /dev/null
+++ b/bridge-ui/src/components/transactions/modals/TransactionDetailsModal.tsx
@@ -0,0 +1,71 @@
+import Link from "next/link";
+import { OnChainMessageStatus } from "@consensys/linea-sdk";
+import { formatHex, formatTimestamp } from "@/utils/format";
+import { NETWORK_ID_TO_NAME } from "@/utils/constants";
+import { MessageWithStatus } from "@/hooks";
+import { TransactionHistory } from "@/models/history";
+import TransactionClaimButton from "./TransactionClaimButton";
+
+type TransactionDetailsModalProps = {
+ transaction: TransactionHistory;
+ message: MessageWithStatus;
+};
+
+const BlockExplorerLink: React.FC<{
+ transactionHash?: string;
+ blockExplorer?: string;
+}> = ({ transactionHash, blockExplorer }) => {
+ if (!transactionHash || !blockExplorer) {
+ return N/A ;
+ }
+ return (
+
+ {formatHex(transactionHash)}
+
+ );
+};
+
+const TransactionDetailsModal: React.FC = ({ transaction, message }) => {
+ return (
+
+
Transaction details
+
+
+ Date & Time
+ {formatTimestamp(Number(transaction.timestamp), "h:mma d MMMM yyyy")}
+
+
+
+ {NETWORK_ID_TO_NAME[transaction.fromChain.id]} Tx Hash
+
+
+
+
+ {NETWORK_ID_TO_NAME[transaction.toChain.id]} Tx Hash
+
+
+
+ Fee
+ 5 ETH ~$15000
+
+
+ {message.status === OnChainMessageStatus.CLAIMABLE && (
+
+ )}
+
+ );
+};
+
+export default TransactionDetailsModal;
diff --git a/bridge-ui/src/components/widgets/SwitchNetwork.tsx b/bridge-ui/src/components/widgets/SwitchNetwork.tsx
index a1b28896..6a787422 100644
--- a/bridge-ui/src/components/widgets/SwitchNetwork.tsx
+++ b/bridge-ui/src/components/widgets/SwitchNetwork.tsx
@@ -38,10 +38,8 @@ export default function SwitchNetwork() {
if (!isConnected) return null;
return (
-
- switchNetworkHandler()}>
- Try {networkType === NetworkType.SEPOLIA ? "Mainnet" : "Testnet"}
-
-
+ switchNetworkHandler()}>
+ Try {networkType === NetworkType.SEPOLIA ? "Mainnet" : "Testnet"}
+
);
}
diff --git a/bridge-ui/src/contexts/modal.context.tsx b/bridge-ui/src/contexts/modal.context.tsx
new file mode 100644
index 00000000..1019ef49
--- /dev/null
+++ b/bridge-ui/src/contexts/modal.context.tsx
@@ -0,0 +1,46 @@
+"use client";
+
+import React, { createContext, ReactNode, useCallback, useRef, useState } from "react";
+import Modal from "@/components/layouts/Modal";
+
+interface ModalContextType {
+ ref: React.MutableRefObject;
+ handleShow: (content: ReactNode, options?: ModalOptions) => void;
+ handleClose: () => void;
+ modalContent: ReactNode;
+ options: ModalOptions;
+}
+
+export const ModalContext = createContext({} as ModalContextType);
+
+interface ModalProviderProps {
+ children: ReactNode;
+}
+
+interface ModalOptions {
+ width?: string;
+}
+
+export const ModalProvider: React.FC = ({ children }) => {
+ const ref = useRef(null);
+ const [modalContent, setModalContent] = useState(null);
+ const [options, setOptions] = useState({});
+
+ const handleShow = useCallback((content: ReactNode, options?: ModalOptions) => {
+ options && setOptions(options);
+ setModalContent(content);
+ ref.current?.showModal();
+ }, []);
+
+ const handleClose = useCallback(() => {
+ ref.current?.close();
+ setModalContent(null);
+ }, []);
+
+ return (
+
+ {children}
+
+
+ );
+};
diff --git a/bridge-ui/src/data/shortcuts.md b/bridge-ui/src/data/shortcuts.md
deleted file mode 100644
index f974a0a9..00000000
--- a/bridge-ui/src/data/shortcuts.md
+++ /dev/null
@@ -1,34 +0,0 @@
----
-- title: 'Bridge and ReStake'
- description: 'Bridge ETH from Mainnet to Linea.'
- logo: '/images/logo/linea-logo.svg'
- ens_name: 'bridge.onlinea.eth'
-
-- title: 'Bridge and ReStake'
- description: 'Bridge ETH from Mainnet, keep 0.005 in ETH and swap the remaining for wstETH.'
- logo: '/images/logo/lido.svg'
- ens_name: 'lido.onlinea.eth'
-
-- title: 'Bridge and ReStake'
- description: 'Bridge ETH from Mainnet, keep 0.005 in ETH and restake the remaining in ezETH.'
- logo: '/images/logo/renzo.svg'
- ens_name: 'renzo.onlinea.eth'
-
-# - title: 'Bridge and ReStake'
-# description: 'Low gas fees and low latency with high throughput backed by the security of Ethereum.'
-# logo: '/images/logo/renzo.svg'
-# ens_name: 'onrenzo.linea.eth'
----
-
-Each shortcut is a block with the following structure:
-
-```
-- title: ...
- description: ...
- logo: ...
- ens_name: ...
-```
-
-Copy and paste the block and change the values to create a new shortcut.
-
-> **_NOTE:_** If you use internal image for `logo`, please make sure to add the image to this repository at `bridge-ui/public/images/...`
diff --git a/bridge-ui/src/hooks/index.ts b/bridge-ui/src/hooks/index.ts
index 07af4ce3..ca623361 100644
--- a/bridge-ui/src/hooks/index.ts
+++ b/bridge-ui/src/hooks/index.ts
@@ -4,7 +4,10 @@ export { default as useBridge } from "./useBridge";
export { default as useExecutionFee } from "./useExecutionFee";
export { default as useFetchAnchoringEvents } from "./useFetchAnchoringEvents";
export { default as useFetchBridgeTransactions } from "./useFetchBridgeTransactions";
-export { default as useMessageService } from "./useMessageService";
+export { default as useMinimumFee } from "./useMinimumFee";
+export { default as useTransactionManagement } from "./useTransactionManagement";
+export { default as useLineaSDK } from "./useLineaSDK";
+export { default as useMessageStatus } from "./useMessageStatus";
export { default as useSwitchNetwork } from "./useSwitchNetwork";
-export type { MessageWithStatus } from "./useMessageService";
+export type { MessageWithStatus } from "./useTransactionManagement";
diff --git a/bridge-ui/src/hooks/useBridge.ts b/bridge-ui/src/hooks/useBridge.ts
index 7a21e3da..c1208515 100644
--- a/bridge-ui/src/hooks/useBridge.ts
+++ b/bridge-ui/src/hooks/useBridge.ts
@@ -7,13 +7,13 @@ import log from "loglevel";
import USDCBridge from "@/abis/USDCBridge.json";
import TokenBridge from "@/abis/TokenBridge.json";
import MessageService from "@/abis/MessageService.json";
-import useMessageService from "./useMessageService";
import { TokenInfo, TokenType, config } from "@/config/config";
import { BridgeError, BridgeErrors, Transaction } from "@/models";
import { getChainNetworkLayer } from "@/utils/chainsUtil";
import { FieldErrors, FieldValues } from "react-hook-form";
import { wagmiConfig } from "@/config";
import { useChainStore } from "@/stores/chainStore";
+import useMinimumFee from "./useMinimumFee";
type UseBridge = {
hash: Address | undefined;
@@ -45,7 +45,7 @@ const useBridge = (): UseBridge => {
toChain: state.toChain,
}));
- const { minimumFee } = useMessageService();
+ const { minimumFee } = useMinimumFee();
const { address, isConnected } = useAccount();
const queryClient = useQueryClient();
diff --git a/bridge-ui/src/hooks/useFetchBridgeTransactions.ts b/bridge-ui/src/hooks/useFetchBridgeTransactions.ts
index 63c424fc..dc2e8c22 100644
--- a/bridge-ui/src/hooks/useFetchBridgeTransactions.ts
+++ b/bridge-ui/src/hooks/useFetchBridgeTransactions.ts
@@ -17,18 +17,17 @@ import useERC20Storage from "./useERC20Storage";
import { BlockRange, TransactionHistory } from "@/models/history";
import useFetchAnchoringEvents from "./useFetchAnchoringEvents";
import { OnChainMessageStatus } from "@consensys/linea-sdk";
-import useMessageService from "./useMessageService";
import useBridge from "./useBridge";
import { getChainNetworkLayer } from "@/utils/chainsUtil";
import { useTokenStore } from "@/stores/tokenStore";
+import useMessageStatus from "./useMessageStatus";
const useFetchBridgeTransactions = () => {
// Wagmi
const { address } = useAccount();
-
const tokensConfig = useTokenStore((state) => state.tokensConfig);
const { fetchAnchoringMessageHashes } = useFetchAnchoringEvents();
- const { getMessagesStatusesByTransactionHash } = useMessageService();
+ const { getMessageStatuses } = useMessageStatus();
const { fetchBridgedToken, fillMissingTokenAddress } = useBridge();
const { updateOrInsertUserTokenList } = useERC20Storage();
@@ -118,7 +117,8 @@ const useFetchBridgeTransactions = () => {
updateOrInsertUserTokenList(transaction.token, networkType);
}
- const newMessages = await getMessagesStatusesByTransactionHash(txHash, fromLayer);
+ const newMessages = await getMessageStatuses(txHash, fromLayer);
+
const updatedTransaction = {
...transaction,
token: {
diff --git a/bridge-ui/src/hooks/useFetchHistory.ts b/bridge-ui/src/hooks/useFetchHistory.ts
index 59b7534e..7db1efa5 100644
--- a/bridge-ui/src/hooks/useFetchHistory.ts
+++ b/bridge-ui/src/hooks/useFetchHistory.ts
@@ -1,4 +1,4 @@
-import { useCallback, useState } from "react";
+import { useCallback } from "react";
import { useAccount } from "wagmi";
import log from "loglevel";
import { useChainStore } from "@/stores/chainStore";
@@ -11,9 +11,6 @@ import useFetchBridgeTransactions from "./useFetchBridgeTransactions";
const DEFAULT_FIRST_BLOCK = BigInt(1000);
const useFetchHistory = () => {
- // Prevent double fetching
- const [isFetching, setIsFetching] = useState(false);
-
// Wagmi
const { address } = useAccount();
@@ -43,11 +40,11 @@ const useFetchHistory = () => {
if (!l1Chain || !l2Chain || !address) {
return;
}
+
// Prevent multiple call
- if (isFetching) return;
+ if (isLoading) return;
try {
- setIsFetching(true);
setIsLoading(true);
// ToBlock: get last onchain block
@@ -75,11 +72,10 @@ const useFetchHistory = () => {
} catch (error) {
log.error(error);
} finally {
- setIsFetching(false);
setIsLoading(false);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [address, currentNetworkType, isFetching, l1Chain, l2Chain]);
+ }, [address, currentNetworkType, l1Chain, l2Chain]);
const clearHistory = useCallback(() => {
// Clear local storage
diff --git a/bridge-ui/src/hooks/useInitialiseChain.ts b/bridge-ui/src/hooks/useInitialiseChain.ts
index 91c5ed34..28eb530b 100644
--- a/bridge-ui/src/hooks/useInitialiseChain.ts
+++ b/bridge-ui/src/hooks/useInitialiseChain.ts
@@ -50,7 +50,7 @@ const useInitialiseChain = () => {
}));
useEffect(() => {
- watchAccount(wagmiConfig, {
+ const unwatch = watchAccount(wagmiConfig, {
onChange(account) {
let networkType = NetworkType.UNKNOWN;
let networkLayer = NetworkLayer.UNKNOWN;
@@ -118,6 +118,10 @@ const useInitialiseChain = () => {
setToChain(toChain);
},
});
+
+ return () => {
+ unwatch();
+ };
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [token, token?.type]);
diff --git a/bridge-ui/src/hooks/useInitialiseToken.ts b/bridge-ui/src/hooks/useInitialiseToken.ts
index 77eeb35d..08ae229d 100644
--- a/bridge-ui/src/hooks/useInitialiseToken.ts
+++ b/bridge-ui/src/hooks/useInitialiseToken.ts
@@ -1,36 +1,13 @@
+import { useEffect } from "react";
import { NetworkTokens, TokenInfo, TokenType } from "@/config";
import { Token } from "@/models/token";
+import { getTokens, USDC_TYPE } from "@/services";
import { defaultTokensConfig, useTokenStore } from "@/stores/tokenStore";
-import log from "loglevel";
-import { useEffect } from "react";
enum NetworkTypes {
MAINNET = "MAINNET",
SEPOLIA = "SEPOLIA",
}
-const CANONICAL_BRIDGED_TYPE = "canonical-bridge";
-const USDC_TYPE = "USDC";
-
-async function getTokens(networkTypes: NetworkTypes): Promise {
- try {
- // Fetch the JSON data from the URL.
- let url = process.env.NEXT_PUBLIC_MAINNET_TOKEN_LIST ? (process.env.NEXT_PUBLIC_MAINNET_TOKEN_LIST as string) : "";
- if (networkTypes === NetworkTypes.SEPOLIA) {
- url = process.env.NEXT_PUBLIC_SEPOLIA_TOKEN_LIST ? (process.env.NEXT_PUBLIC_SEPOLIA_TOKEN_LIST as string) : "";
- }
-
- const response = await fetch(url);
- const data = await response.json();
- const tokens = data.tokens;
- const bridgedTokens = tokens.filter(
- (token: Token) => token.tokenType.includes(CANONICAL_BRIDGED_TYPE) || token.symbol === USDC_TYPE,
- );
- return bridgedTokens;
- } catch (error) {
- log.error("Error getTokens", { error });
- return [];
- }
-}
export async function getConfig(): Promise {
const mainnetTokens = await getTokens(NetworkTypes.MAINNET);
diff --git a/bridge-ui/src/hooks/useLineaSDK.ts b/bridge-ui/src/hooks/useLineaSDK.ts
new file mode 100644
index 00000000..72950827
--- /dev/null
+++ b/bridge-ui/src/hooks/useLineaSDK.ts
@@ -0,0 +1,52 @@
+import { useMemo } from "react";
+import { LineaSDK, Network } from "@consensys/linea-sdk";
+import { L1MessageServiceContract, L2MessageServiceContract } from "@consensys/linea-sdk/dist/lib/contracts";
+import { NetworkType } from "@/config";
+import { useChainStore } from "@/stores/chainStore";
+
+interface LineaSDKContracts {
+ L1: L1MessageServiceContract;
+ L2: L2MessageServiceContract;
+}
+
+const useLineaSDK = () => {
+ const networkType = useChainStore((state) => state.networkType);
+
+ const { lineaSDK, lineaSDKContracts } = useMemo(() => {
+ const infuraKey = process.env.NEXT_PUBLIC_INFURA_ID;
+ if (!infuraKey) return { lineaSDK: null, lineaSDKContracts: null };
+
+ let l1RpcUrl;
+ let l2RpcUrl;
+ switch (networkType) {
+ case NetworkType.MAINNET:
+ l1RpcUrl = `https://mainnet.infura.io/v3/${infuraKey}`;
+ l2RpcUrl = `https://linea-mainnet.infura.io/v3/${infuraKey}`;
+ break;
+ case NetworkType.SEPOLIA:
+ l1RpcUrl = `https://sepolia.infura.io/v3/${infuraKey}`;
+ l2RpcUrl = `https://linea-sepolia.infura.io/v3/${infuraKey}`;
+ break;
+ default:
+ return { lineaSDK: null, lineaSDKContracts: null };
+ }
+
+ const sdk = new LineaSDK({
+ l1RpcUrl,
+ l2RpcUrl,
+ network: `linea-${networkType.toLowerCase()}` as Network,
+ mode: "read-only",
+ });
+
+ const newLineaSDKContracts: LineaSDKContracts = {
+ L1: sdk.getL1Contract(),
+ L2: sdk.getL2Contract(),
+ };
+
+ return { lineaSDK: sdk, lineaSDKContracts: newLineaSDKContracts };
+ }, [networkType]);
+
+ return { lineaSDK, lineaSDKContracts };
+};
+
+export default useLineaSDK;
diff --git a/bridge-ui/src/hooks/useMessageService.ts b/bridge-ui/src/hooks/useMessageService.ts
deleted file mode 100644
index d3d8c5bc..00000000
--- a/bridge-ui/src/hooks/useMessageService.ts
+++ /dev/null
@@ -1,277 +0,0 @@
-import { useState, useCallback, useMemo } from "react";
-import { readContract, simulateContract, writeContract } from "@wagmi/core";
-import { useAccount } from "wagmi";
-import { zeroAddress } from "viem";
-import log from "loglevel";
-import { LineaSDK, OnChainMessageStatus } from "@consensys/linea-sdk";
-import { L1MessageServiceContract, L2MessageServiceContract } from "@consensys/linea-sdk/dist/lib/contracts";
-import MessageService from "@/abis/MessageService.json";
-import { getChainNetworkLayer, getChainNetworkType } from "@/utils/chainsUtil";
-import { NetworkLayer, NetworkType, config, wagmiConfig } from "@/config";
-import { Transaction } from "@/models";
-import { TransactionHistory } from "@/models/history";
-import { Proof } from "@consensys/linea-sdk/dist/lib/sdk/merkleTree/types";
-import { useChainStore } from "@/stores/chainStore";
-
-interface LineaSDKContracts {
- L1: L1MessageServiceContract;
- L2: L2MessageServiceContract;
-}
-
-export interface MessageWithStatus {
- status: OnChainMessageStatus;
- messageSender: string;
- destination: string;
- fee: string;
- value: string;
- messageNonce: string;
- calldata: string;
- messageHash: string;
- proof: Proof | undefined;
-}
-
-interface ClaimMessageWithProofParams {
- proof: string[];
- messageNumber: string;
- leafIndex: number;
- from: string;
- to: string;
- fee: string;
- value: string;
- feeRecipient: string;
- merkleRoot: string;
- data: string;
-}
-
-const useMessageService = () => {
- const [transaction, setTransaction] = useState(null);
- const [isLoading, setIsLoading] = useState(false);
- const [error, setError] = useState(null);
- const [minimumFee, setMinimumFee] = useState(BigInt(0));
- const [lineaSDK, setLineaSDK] = useState(undefined);
- const [lineaSDKContracts, setLineaSDKContracts] = useState(undefined);
-
- const { messageServiceAddress, fromChain, networkType } = useChainStore((state) => ({
- messageServiceAddress: state.messageServiceAddress,
- fromChain: state.fromChain,
- networkType: state.networkType,
- }));
- const { address } = useAccount();
-
- useMemo(() => {
- let _lineaSDK;
- const infuraKey = process.env.NEXT_PUBLIC_INFURA_ID;
- switch (networkType) {
- case NetworkType.MAINNET:
- _lineaSDK = new LineaSDK({
- l1RpcUrl: `https://mainnet.infura.io/v3/${infuraKey}`,
- l2RpcUrl: `https://linea-mainnet.infura.io/v3/${infuraKey}`,
- network: "linea-mainnet",
- mode: "read-only",
- });
-
- break;
- case NetworkType.SEPOLIA:
- _lineaSDK = new LineaSDK({
- l1RpcUrl: `https://sepolia.infura.io/v3/${infuraKey}`,
- l2RpcUrl: `https://linea-sepolia.infura.io/v3/${infuraKey}`,
- network: "linea-sepolia",
- mode: "read-only",
- });
- break;
- }
- if (!_lineaSDK) {
- return;
- }
- const newLineaSDKContracts: LineaSDKContracts = {
- L1: _lineaSDK.getL1Contract(),
- L2: _lineaSDK.getL2Contract(),
- };
- setLineaSDKContracts(newLineaSDKContracts);
- setLineaSDK(_lineaSDK);
- }, [networkType]);
-
- useMemo(() => {
- const readMinimumFee = async () => {
- setError(null);
- setIsLoading(true);
-
- if (!messageServiceAddress) {
- return;
- }
-
- try {
- let fees = BigInt(0);
- if (fromChain && getChainNetworkLayer(fromChain) === NetworkLayer.L2) {
- //Get the minimum to send along the message for L2
- fees = (await readContract(wagmiConfig, {
- address: messageServiceAddress,
- abi: MessageService.abi,
- functionName: "minimumFeeInWei",
- chainId: fromChain.id,
- })) as bigint;
- }
- setMinimumFee(fees);
- } catch (error) {
- setError(error as Error);
- }
-
- setIsLoading(false);
- };
-
- readMinimumFee();
- }, [messageServiceAddress, fromChain]);
-
- const getMessagesByTransactionHash = useCallback(
- async (transactionHash: string, networkLayer: NetworkLayer) => {
- if (!lineaSDKContracts || networkLayer === NetworkLayer.UNKNOWN) {
- return;
- }
- return await lineaSDKContracts[networkLayer]?.getMessagesByTransactionHash(transactionHash);
- },
- [lineaSDKContracts],
- );
-
- const getMessagesStatusesByTransactionHash = useCallback(
- async (transactionHash: string, networkLayer: NetworkLayer) => {
- if (!lineaSDKContracts || networkLayer === NetworkLayer.UNKNOWN) {
- return;
- }
- const messages = await getMessagesByTransactionHash(transactionHash, networkLayer);
-
- const messagesWithStatuses: Array = [];
- if (messages && messages.length > 0) {
- const otherLayer = networkLayer === NetworkLayer.L1 ? NetworkLayer.L2 : NetworkLayer.L1;
-
- const promises = messages.map(async (message) => {
- const l1ClaimingService = lineaSDK?.getL1ClaimingService(
- config.networks[networkType].L1.messageServiceAddress,
- config.networks[networkType].L2.messageServiceAddress,
- );
- let status;
- // For messages to claim on L1 we check if we need to claim with the new claiming method
- // which requires the proof linked to this message
- let proof;
-
- if (otherLayer === NetworkLayer.L1) {
- // Message from L2 to L1
- status = (await l1ClaimingService?.getMessageStatus(message.messageHash)) || OnChainMessageStatus.UNKNOWN;
- if (
- status === OnChainMessageStatus.CLAIMABLE &&
- (await l1ClaimingService?.isClaimingNeedingProof(message.messageHash))
- ) {
- try {
- proof = await l1ClaimingService?.getMessageProof(message.messageHash);
- } catch (ex) {
- // We ignore the error, the proof will stay undefined, we assume
- // it's a message from the old message service
- }
- }
- } else {
- // Message from L1 to L2
- status = await lineaSDKContracts.L2.getMessageStatus(message.messageHash);
- }
-
- // Convert the BigNumbers to string for serialization issue with the storage
- const messageWithStatus: MessageWithStatus = {
- calldata: message.calldata,
- destination: message.destination,
- fee: message.fee.toString(),
- messageHash: message.messageHash,
- messageNonce: message.messageNonce.toString(),
- messageSender: message.messageSender,
- status: status,
- value: message.value.toString(),
- proof,
- };
- messagesWithStatuses.push(messageWithStatus);
- });
- await Promise.all(promises);
- }
-
- return messagesWithStatuses;
- },
- [lineaSDKContracts, getMessagesByTransactionHash, lineaSDK, networkType],
- );
-
- const writeClaimMessage = useCallback(
- async (message: MessageWithStatus, tx: TransactionHistory) => {
- setError(null);
- setIsLoading(true);
-
- // Get the right message Service address depending on the transaction
- const txNetworkLayer = getChainNetworkLayer(tx.toChain);
- const txNetworkType = getChainNetworkType(tx.toChain);
-
- if (address && txNetworkLayer && txNetworkType) {
- try {
- const { messageSender, destination, calldata, fee, messageNonce, value, proof } = message;
-
- const messageServiceAddress = config.networks[txNetworkType][txNetworkLayer].messageServiceAddress;
- if (messageServiceAddress === null) {
- return;
- }
- let writeConfig;
- if (!proof) {
- // Claiming using old message service
- writeConfig = await simulateContract(wagmiConfig, {
- address: messageServiceAddress,
- abi: MessageService.abi,
- functionName: "claimMessage",
- args: [messageSender, destination, fee, value, zeroAddress, calldata, messageNonce],
- chainId: tx.toChain.id,
- });
- } else {
- // Claiming on L1 with new message service
- const params: ClaimMessageWithProofParams = {
- data: calldata,
- fee,
- feeRecipient: zeroAddress,
- from: messageSender,
- to: destination,
- leafIndex: proof.leafIndex,
- merkleRoot: proof.root,
- messageNumber: messageNonce,
- proof: proof.proof,
- value,
- };
- writeConfig = await simulateContract(wagmiConfig, {
- address: messageServiceAddress,
- abi: MessageService.abi,
- functionName: "claimMessageWithProof",
- args: [params],
- chainId: tx.toChain.id,
- });
- }
-
- const hash = await writeContract(wagmiConfig, writeConfig.request);
-
- setTransaction({
- txHash: hash,
- chainId: tx.fromChain.id,
- name: tx.fromChain.name,
- });
- } catch (error) {
- log.error(error);
- setError(error as Error);
- setTransaction(null);
- }
- }
-
- setIsLoading(false);
- },
- [address],
- );
-
- return {
- isLoading,
- isError: error !== null,
- error,
- minimumFee,
- transaction,
- getMessagesStatusesByTransactionHash,
- writeClaimMessage,
- };
-};
-
-export default useMessageService;
diff --git a/bridge-ui/src/hooks/useMessageStatus.ts b/bridge-ui/src/hooks/useMessageStatus.ts
new file mode 100644
index 00000000..28981b9a
--- /dev/null
+++ b/bridge-ui/src/hooks/useMessageStatus.ts
@@ -0,0 +1,102 @@
+import { useCallback } from "react";
+import { OnChainMessageStatus } from "@consensys/linea-sdk";
+import useLineaSDK from "./useLineaSDK";
+import { config, NetworkLayer } from "@/config";
+import { MessageWithStatus } from "./useTransactionManagement";
+import { useChainStore } from "@/stores/chainStore";
+
+const useMessageStatus = () => {
+ const { lineaSDK, lineaSDKContracts } = useLineaSDK();
+
+ const networkType = useChainStore((state) => state.networkType);
+
+ const getMessagesByTransactionHash = useCallback(
+ async (transactionHash: string, networkLayer: NetworkLayer) => {
+ if (!lineaSDKContracts || networkLayer === NetworkLayer.UNKNOWN) {
+ return;
+ }
+ return await lineaSDKContracts[networkLayer]?.getMessagesByTransactionHash(transactionHash);
+ },
+ [lineaSDKContracts],
+ );
+
+ const getMessageStatuses = useCallback(
+ async (transactionHash: string, networkLayer: NetworkLayer) => {
+ if (!lineaSDKContracts || networkLayer === NetworkLayer.UNKNOWN) {
+ return;
+ }
+
+ const messages = await getMessagesByTransactionHash(transactionHash, networkLayer);
+
+ const messagesWithStatuses: Array = [];
+ if (messages && messages.length > 0) {
+ const otherLayer = networkLayer === NetworkLayer.L1 ? NetworkLayer.L2 : NetworkLayer.L1;
+
+ const promises = messages.map(async (message) => {
+ const l1ClaimingService = lineaSDK?.getL1ClaimingService(
+ config.networks[networkType].L1.messageServiceAddress,
+ config.networks[networkType].L2.messageServiceAddress,
+ );
+ let status: OnChainMessageStatus;
+ let claimingTransactionHash;
+ // For messages to claim on L1 we check if we need to claim with the new claiming method
+ // which requires the proof linked to this message
+ let proof;
+
+ if (otherLayer === NetworkLayer.L1) {
+ // Message from L2 to L1
+ status = (await l1ClaimingService?.getMessageStatus(message.messageHash)) || OnChainMessageStatus.UNKNOWN;
+
+ if (
+ status === OnChainMessageStatus.CLAIMABLE &&
+ (await l1ClaimingService?.isClaimingNeedingProof(message.messageHash))
+ ) {
+ try {
+ proof = await l1ClaimingService?.getMessageProof(message.messageHash);
+ } catch (ex) {
+ // We ignore the error, the proof will stay undefined, we assume
+ // it's a message from the old message service
+ }
+ }
+ if (status === OnChainMessageStatus.CLAIMED) {
+ const [messageClaimedEvent] = await lineaSDKContracts.L1.getEvents(
+ lineaSDKContracts.L1.contract.filters.MessageClaimed(message.messageHash),
+ );
+ claimingTransactionHash = messageClaimedEvent ? messageClaimedEvent.transactionHash : undefined;
+ }
+ } else {
+ // Message from L1 to L2
+ status = await lineaSDKContracts.L2.getMessageStatus(message.messageHash);
+ const [messageClaimedEvent] = await lineaSDKContracts.L2.getEvents(
+ lineaSDKContracts.L2.contract.filters.MessageClaimed(message.messageHash),
+ );
+ claimingTransactionHash = messageClaimedEvent ? messageClaimedEvent.transactionHash : undefined;
+ }
+
+ // Convert the BigNumbers to string for serialization issue with the storage
+ const messageWithStatus: MessageWithStatus = {
+ calldata: message.calldata,
+ destination: message.destination,
+ fee: message.fee.toString(),
+ messageHash: message.messageHash,
+ messageNonce: message.messageNonce.toString(),
+ messageSender: message.messageSender,
+ status: status,
+ value: message.value.toString(),
+ proof,
+ claimingTransactionHash,
+ };
+ messagesWithStatuses.push(messageWithStatus);
+ });
+ await Promise.all(promises);
+ }
+
+ return messagesWithStatuses;
+ },
+ [lineaSDKContracts, getMessagesByTransactionHash, lineaSDK, networkType],
+ );
+
+ return { getMessageStatuses };
+};
+
+export default useMessageStatus;
diff --git a/bridge-ui/src/hooks/useMinimumFee.ts b/bridge-ui/src/hooks/useMinimumFee.ts
new file mode 100644
index 00000000..a46ef5a9
--- /dev/null
+++ b/bridge-ui/src/hooks/useMinimumFee.ts
@@ -0,0 +1,47 @@
+import { useState, useEffect, useCallback } from "react";
+import { readContract } from "@wagmi/core";
+import { useChainStore } from "@/stores/chainStore";
+import { NetworkLayer, wagmiConfig } from "@/config";
+import MessageService from "@/abis/MessageService.json";
+import { getChainNetworkLayer } from "@/utils/chainsUtil";
+
+const useMinimumFee = () => {
+ const [minimumFee, setMinimumFee] = useState(BigInt(0));
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const [error, setError] = useState(null);
+
+ const { messageServiceAddress, fromChain } = useChainStore((state) => ({
+ messageServiceAddress: state.messageServiceAddress,
+ fromChain: state.fromChain,
+ }));
+
+ const fetchMinimumFee = useCallback(async () => {
+ if (!messageServiceAddress) {
+ return;
+ }
+
+ try {
+ let fees = BigInt(0);
+ if (fromChain && getChainNetworkLayer(fromChain) === NetworkLayer.L2) {
+ fees = (await readContract(wagmiConfig, {
+ address: messageServiceAddress,
+ abi: MessageService.abi,
+ functionName: "minimumFeeInWei",
+ chainId: fromChain.id,
+ })) as bigint;
+ }
+ setMinimumFee(fees);
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ } catch (err: any) {
+ setError(err);
+ }
+ }, [messageServiceAddress, fromChain]);
+
+ useEffect(() => {
+ fetchMinimumFee();
+ }, [fetchMinimumFee]);
+
+ return { minimumFee, error };
+};
+
+export default useMinimumFee;
diff --git a/bridge-ui/src/hooks/useTransactionManagement.ts b/bridge-ui/src/hooks/useTransactionManagement.ts
new file mode 100644
index 00000000..b0e2c43b
--- /dev/null
+++ b/bridge-ui/src/hooks/useTransactionManagement.ts
@@ -0,0 +1,118 @@
+import { useState, useCallback } from "react";
+import log from "loglevel";
+import { simulateContract, writeContract } from "@wagmi/core";
+import { zeroAddress } from "viem";
+import MessageService from "@/abis/MessageService.json";
+import { config, wagmiConfig } from "@/config";
+import { OnChainMessageStatus } from "@consensys/linea-sdk";
+import { Proof } from "@consensys/linea-sdk/dist/lib/sdk/merkleTree/types";
+import { TransactionHistory } from "@/models/history";
+import { getChainNetworkLayer, getChainNetworkType } from "@/utils/chainsUtil";
+import { useAccount } from "wagmi";
+import { Transaction } from "@/models";
+
+export interface MessageWithStatus {
+ status: OnChainMessageStatus;
+ messageSender: string;
+ destination: string;
+ fee: string;
+ value: string;
+ messageNonce: string;
+ calldata: string;
+ messageHash: string;
+ proof: Proof | undefined;
+ claimingTransactionHash?: string;
+}
+
+interface ClaimMessageWithProofParams {
+ proof: string[];
+ messageNumber: string;
+ leafIndex: number;
+ from: string;
+ to: string;
+ fee: string;
+ value: string;
+ feeRecipient: string;
+ merkleRoot: string;
+ data: string;
+}
+
+const useTransactionManagement = () => {
+ const { address } = useAccount();
+ const [transaction, setTransaction] = useState(null);
+ const [isLoading, setIsLoading] = useState(false);
+ const [error, setError] = useState(null);
+
+ const writeClaimMessage = useCallback(
+ async (message: MessageWithStatus, tx: TransactionHistory) => {
+ setError(null);
+ setIsLoading(true);
+
+ // Get the right message Service address depending on the transaction
+ const txNetworkLayer = getChainNetworkLayer(tx.toChain);
+ const txNetworkType = getChainNetworkType(tx.toChain);
+
+ if (address && txNetworkLayer && txNetworkType) {
+ try {
+ const { messageSender, destination, calldata, fee, messageNonce, value, proof } = message;
+
+ const messageServiceAddress = config.networks[txNetworkType][txNetworkLayer].messageServiceAddress;
+ if (messageServiceAddress === null) {
+ return;
+ }
+ let writeConfig;
+ if (!proof) {
+ // Claiming using old message service
+ writeConfig = await simulateContract(wagmiConfig, {
+ address: messageServiceAddress,
+ abi: MessageService.abi,
+ functionName: "claimMessage",
+ args: [messageSender, destination, fee, value, zeroAddress, calldata, messageNonce],
+ chainId: tx.toChain.id,
+ });
+ } else {
+ // Claiming on L1 with new message service
+ const params: ClaimMessageWithProofParams = {
+ data: calldata,
+ fee,
+ feeRecipient: zeroAddress,
+ from: messageSender,
+ to: destination,
+ leafIndex: proof.leafIndex,
+ merkleRoot: proof.root,
+ messageNumber: messageNonce,
+ proof: proof.proof,
+ value,
+ };
+ writeConfig = await simulateContract(wagmiConfig, {
+ address: messageServiceAddress,
+ abi: MessageService.abi,
+ functionName: "claimMessageWithProof",
+ args: [params],
+ chainId: tx.toChain.id,
+ });
+ }
+
+ const hash = await writeContract(wagmiConfig, writeConfig.request);
+
+ setTransaction({
+ txHash: hash,
+ chainId: tx.fromChain.id,
+ name: tx.fromChain.name,
+ });
+ } catch (error) {
+ log.error(error);
+ setError(error as Error);
+ setTransaction(null);
+ }
+ }
+
+ setIsLoading(false);
+ },
+ [address],
+ );
+
+ return { transaction, isLoading, isError: error !== null, error, writeClaimMessage };
+};
+
+export default useTransactionManagement;
diff --git a/bridge-ui/src/models/shortcut.ts b/bridge-ui/src/models/shortcut.ts
deleted file mode 100644
index ef8f41e7..00000000
--- a/bridge-ui/src/models/shortcut.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-export interface Shortcut {
- title: string;
- description?: string;
- logo: string;
- ens_name: string;
-}
diff --git a/bridge-ui/src/services/fetchERC20Image.ts b/bridge-ui/src/services/fetchERC20Image.ts
deleted file mode 100644
index ba995e19..00000000
--- a/bridge-ui/src/services/fetchERC20Image.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import axios, { AxiosResponse } from "axios";
-import log from "loglevel";
-
-interface CoinGeckoToken {
- id: string;
- symbol: string;
- name: string;
-}
-
-interface CoinGeckoTokenDetail {
- image: {
- small: string;
- };
-}
-
-async function fetchERC20Image(name: string) {
- try {
- if (!name) {
- throw new Error("Name is required");
- }
-
- const coinsResponse: AxiosResponse = await axios.get(
- "https://api.coingecko.com/api/v3/coins/list",
- );
- const coin = coinsResponse.data.find((coin: CoinGeckoToken) => coin.name === name);
-
- if (!coin) {
- throw new Error("Coin not found");
- }
-
- const coinId = coin.id;
- const coinDataResponse: AxiosResponse = await axios.get(
- `https://api.coingecko.com/api/v3/coins/${coinId}`,
- );
-
- if (!coinDataResponse.data.image.small) {
- throw new Error("Image not found");
- }
-
- const image = coinDataResponse.data.image.small;
- return image.split("?")[0];
- } catch (error) {
- log.warn(error);
- return "/images/logo/noTokenLogo.svg";
- }
-}
-
-export default fetchERC20Image;
diff --git a/bridge-ui/src/services/fetchTokenInfo.ts b/bridge-ui/src/services/fetchTokenInfo.ts
deleted file mode 100644
index cff8a4ea..00000000
--- a/bridge-ui/src/services/fetchTokenInfo.ts
+++ /dev/null
@@ -1,67 +0,0 @@
-import { Address } from "viem";
-import { GetTokenReturnType, getToken } from "@wagmi/core";
-import { sepolia, linea, mainnet, lineaSepolia, Chain } from "viem/chains";
-import log from "loglevel";
-import fetchERC20Image from "@/services/fetchERC20Image";
-import { NetworkType, TokenInfo, TokenType, wagmiConfig } from "@/config";
-
-const fetchTokenInfo = async (
- tokenAddress: Address,
- networkType: NetworkType,
- fromChain?: Chain,
-): Promise => {
- let erc20: GetTokenReturnType | undefined;
- let chainFound;
-
- if (!chainFound) {
- const chains: Chain[] = networkType === NetworkType.SEPOLIA ? [lineaSepolia, sepolia] : [linea, mainnet];
-
- // Put the fromChain arg at the begining to take it as priority
- if (fromChain) chains.unshift(fromChain);
-
- for (const chain of chains) {
- try {
- erc20 = await getToken(wagmiConfig, {
- address: tokenAddress,
- chainId: chain.id,
- });
- if (erc20.name) {
- // Found the token if no errors with fetchToken
- chainFound = chain;
- break;
- }
- } catch (err) {
- continue;
- }
- }
- }
-
- if (!erc20 || !chainFound || !erc20.name) {
- return;
- }
-
- const L1Token = chainFound.id === mainnet.id || chainFound.id === sepolia.id;
-
- // Fetch image
- const name = erc20.name;
- const image = await fetchERC20Image(name);
-
- try {
- return {
- name,
- symbol: erc20.symbol!,
- decimals: erc20.decimals,
- L1: L1Token ? tokenAddress : null,
- L2: !L1Token ? tokenAddress : null,
- image,
- type: TokenType.ERC20,
- UNKNOWN: null,
- isDefault: false,
- };
- } catch (err) {
- log.error(err);
- return;
- }
-};
-
-export default fetchTokenInfo;
diff --git a/bridge-ui/src/services/index.ts b/bridge-ui/src/services/index.ts
new file mode 100644
index 00000000..c185890e
--- /dev/null
+++ b/bridge-ui/src/services/index.ts
@@ -0,0 +1 @@
+export { fetchERC20Image, fetchTokenInfo, getTokens, USDC_TYPE, CANONICAL_BRIDGED_TYPE } from "./tokenService";
diff --git a/bridge-ui/src/services/tokenService.ts b/bridge-ui/src/services/tokenService.ts
new file mode 100644
index 00000000..a2418a0b
--- /dev/null
+++ b/bridge-ui/src/services/tokenService.ts
@@ -0,0 +1,139 @@
+import axios, { AxiosResponse } from "axios";
+import log from "loglevel";
+import { Address } from "viem";
+import { GetTokenReturnType, getToken } from "@wagmi/core";
+import { sepolia, linea, mainnet, lineaSepolia, Chain } from "viem/chains";
+import { NetworkType, TokenInfo, TokenType, wagmiConfig } from "@/config";
+import { Token } from "@/models/token";
+
+interface CoinGeckoToken {
+ id: string;
+ symbol: string;
+ name: string;
+}
+
+interface CoinGeckoTokenDetail {
+ image: {
+ small: string;
+ };
+}
+
+enum NetworkTypes {
+ MAINNET = "MAINNET",
+ SEPOLIA = "SEPOLIA",
+}
+
+export const CANONICAL_BRIDGED_TYPE = "canonical-bridge";
+export const USDC_TYPE = "USDC";
+
+export async function fetchERC20Image(name: string) {
+ try {
+ if (!name) {
+ throw new Error("Name is required");
+ }
+
+ const coinsResponse: AxiosResponse = await axios.get(
+ "https://api.coingecko.com/api/v3/coins/list",
+ );
+ const coin = coinsResponse.data.find((coin: CoinGeckoToken) => coin.name === name);
+
+ if (!coin) {
+ throw new Error("Coin not found");
+ }
+
+ const coinId = coin.id;
+ const coinDataResponse: AxiosResponse = await axios.get(
+ `https://api.coingecko.com/api/v3/coins/${coinId}`,
+ );
+
+ if (!coinDataResponse.data.image.small) {
+ throw new Error("Image not found");
+ }
+
+ const image = coinDataResponse.data.image.small;
+ return image.split("?")[0];
+ } catch (error) {
+ log.warn(error);
+ return "/images/logo/noTokenLogo.svg";
+ }
+}
+
+export async function fetchTokenInfo(
+ tokenAddress: Address,
+ networkType: NetworkType,
+ fromChain?: Chain,
+): Promise {
+ let erc20: GetTokenReturnType | undefined;
+ let chainFound;
+
+ if (!chainFound) {
+ const chains: Chain[] = networkType === NetworkType.SEPOLIA ? [lineaSepolia, sepolia] : [linea, mainnet];
+
+ // Put the fromChain arg at the begining to take it as priority
+ if (fromChain) chains.unshift(fromChain);
+
+ for (const chain of chains) {
+ try {
+ erc20 = await getToken(wagmiConfig, {
+ address: tokenAddress,
+ chainId: chain.id,
+ });
+ if (erc20.name) {
+ // Found the token if no errors with fetchToken
+ chainFound = chain;
+ break;
+ }
+ } catch (err) {
+ continue;
+ }
+ }
+ }
+
+ if (!erc20 || !chainFound || !erc20.name) {
+ return;
+ }
+
+ const L1Token = chainFound.id === mainnet.id || chainFound.id === sepolia.id;
+
+ // Fetch image
+ const name = erc20.name;
+ const image = await fetchERC20Image(name);
+
+ try {
+ return {
+ name,
+ symbol: erc20.symbol!,
+ decimals: erc20.decimals,
+ L1: L1Token ? tokenAddress : null,
+ L2: !L1Token ? tokenAddress : null,
+ image,
+ type: TokenType.ERC20,
+ UNKNOWN: null,
+ isDefault: false,
+ };
+ } catch (err) {
+ log.error(err);
+ return;
+ }
+}
+
+export async function getTokens(networkTypes: NetworkTypes): Promise {
+ try {
+ // Fetch the JSON data from the URL.
+ let url = process.env.NEXT_PUBLIC_MAINNET_TOKEN_LIST ? (process.env.NEXT_PUBLIC_MAINNET_TOKEN_LIST as string) : "";
+ if (networkTypes === NetworkTypes.SEPOLIA) {
+ url = process.env.NEXT_PUBLIC_SEPOLIA_TOKEN_LIST ? (process.env.NEXT_PUBLIC_SEPOLIA_TOKEN_LIST as string) : "";
+ }
+
+ const response = await fetch(url);
+ const data = await response.json();
+ const tokens = data.tokens;
+ const bridgedTokens = tokens.filter(
+ (token: Token) => token.tokenType.includes(CANONICAL_BRIDGED_TYPE) || token.symbol === USDC_TYPE,
+ );
+ return bridgedTokens;
+ } catch (error) {
+ log.error("Error getTokens", { error });
+ return [];
+ }
+}
diff --git a/bridge-ui/src/stores/configStore.ts b/bridge-ui/src/stores/configStore.ts
index 8f06e542..dcc76b2e 100644
--- a/bridge-ui/src/stores/configStore.ts
+++ b/bridge-ui/src/stores/configStore.ts
@@ -4,6 +4,7 @@ import { createJSONStorage, persist } from "zustand/middleware";
export type ConfigState = {
agreeToTerms: boolean;
+ rehydrated: boolean;
};
export type ConfigActions = {
@@ -14,6 +15,7 @@ export type ConfigStore = ConfigState & ConfigActions;
export const defaultInitState: ConfigState = {
agreeToTerms: false,
+ rehydrated: false,
};
export const useConfigStore = create()(
@@ -29,6 +31,11 @@ export const useConfigStore = create()(
migrate: () => {
return defaultInitState;
},
+ onRehydrateStorage: () => (state) => {
+ if (state) {
+ state.rehydrated = true;
+ }
+ },
},
),
);
diff --git a/bridge-ui/src/utils/chainsUtil.ts b/bridge-ui/src/utils/chainsUtil.ts
index ef17f045..bebaade7 100644
--- a/bridge-ui/src/utils/chainsUtil.ts
+++ b/bridge-ui/src/utils/chainsUtil.ts
@@ -14,6 +14,19 @@ export const getChainNetworkLayer = (chain: Chain) => {
return;
};
+export const getChainNetworkLayerByChainId = (chainId: number) => {
+ switch (chainId) {
+ case linea.id:
+ case lineaSepolia.id:
+ return NetworkLayer.L2;
+ case mainnet.id:
+ case sepolia.id:
+ return NetworkLayer.L1;
+ }
+
+ return;
+};
+
export const getChainNetworkType = (chain: Chain) => {
switch (chain.id) {
case linea.id:
diff --git a/bridge-ui/src/utils/constants.ts b/bridge-ui/src/utils/constants.ts
new file mode 100644
index 00000000..7ebde4fd
--- /dev/null
+++ b/bridge-ui/src/utils/constants.ts
@@ -0,0 +1,38 @@
+import BridgeIcon from "@/assets/icons/bridge.svg";
+import TransactionsIcon from "@/assets/icons/transaction.svg";
+import DocsIcon from "@/assets/icons/docs.svg";
+import FaqIcon from "@/assets/icons/faq.svg";
+
+export const MENU_ITEMS = [
+ {
+ title: "Bridge",
+ href: "/",
+ external: false,
+ Icon: BridgeIcon,
+ },
+ {
+ title: "Transactions",
+ href: "/transactions",
+ external: false,
+ Icon: TransactionsIcon,
+ },
+ {
+ title: "Docs",
+ href: "https://docs.linea.build/",
+ external: true,
+ Icon: DocsIcon,
+ },
+ {
+ title: "FAQ",
+ href: "https://support.linea.build/hc/en-us/categories/13281330249371-FAQs",
+ external: true,
+ Icon: FaqIcon,
+ },
+];
+
+export const NETWORK_ID_TO_NAME: Record = {
+ 59144: "Linea",
+ 59141: "Linea Sepolia",
+ 1: "Ethereum",
+ 11155111: "Sepolia",
+};
diff --git a/bridge-ui/src/utils/format.ts b/bridge-ui/src/utils/format.ts
index eea891fc..d0788737 100644
--- a/bridge-ui/src/utils/format.ts
+++ b/bridge-ui/src/utils/format.ts
@@ -1,3 +1,4 @@
+import { formatDate, fromUnixTime } from "date-fns";
import { Address, getAddress } from "viem";
/**
@@ -11,6 +12,17 @@ export const formatAddress = (address: string | undefined, step = 5) => {
return address.substring(0, step) + "..." + address.substring(address.length - step, address.length);
};
+/**
+ * Formats a hexadecimal string by truncating it and adding ellipsis in the middle.
+ * @param hexString - The hexadecimal string to format.
+ * @param step - The number of characters to keep at the beginning and end of the string.
+ * @returns The formatted hexadecimal string.
+ */
+export const formatHex = (hexString: string | undefined, step = 5) => {
+ if (!hexString) return "N/A";
+ return hexString.substring(0, step) + "..." + hexString.substring(hexString.length - step, hexString.length);
+};
+
/**
* Format balance
* @param balance
@@ -34,3 +46,13 @@ export const formatBalance = (balance: string | undefined, precision = 4) => {
export const safeGetAddress = (address: Address | null): string | null => {
return address ? getAddress(address) : null;
};
+
+/**
+ * Format timestamp
+ * @param timestamp
+ * @param formatStr
+ * @returns
+ */
+export const formatTimestamp = (timestamp: number, formatStr: string) => {
+ return formatDate(fromUnixTime(timestamp), formatStr);
+};
diff --git a/bridge-ui/src/utils/transactionParsers/parseERC20Events.ts b/bridge-ui/src/utils/transactionParsers/parseERC20Events.ts
index 1ce89b21..98f296e0 100644
--- a/bridge-ui/src/utils/transactionParsers/parseERC20Events.ts
+++ b/bridge-ui/src/utils/transactionParsers/parseERC20Events.ts
@@ -2,7 +2,7 @@ import { Chain, PublicClient, decodeAbiParameters, getAddress } from "viem";
import log from "loglevel";
import { NetworkTokens, NetworkType } from "@/config/config";
import { ERC20Event, ERC20V2Event } from "@/models";
-import fetchTokenInfo from "@/services/fetchTokenInfo";
+import { fetchTokenInfo } from "@/services";
import { TransactionHistory } from "@/models/history";
import { findTokenByAddress } from "./helpers";
diff --git a/bridge-ui/svg.d.ts b/bridge-ui/svg.d.ts
new file mode 100644
index 00000000..6eb3cd9d
--- /dev/null
+++ b/bridge-ui/svg.d.ts
@@ -0,0 +1,4 @@
+declare module "*.svg" {
+ import React from "react";
+ export const ReactComponent: React.FunctionComponent>;
+}
diff --git a/bridge-ui/tailwind.config.ts b/bridge-ui/tailwind.config.ts
index eb9f63da..939f5766 100644
--- a/bridge-ui/tailwind.config.ts
+++ b/bridge-ui/tailwind.config.ts
@@ -11,13 +11,12 @@ const config: Config = {
],
theme: {
extend: {
- backgroundImage: {
- hero: "url('/bridge_bg.png')",
- },
colors: {
primary: "#61DFFF",
+ secondary: "#FF62E6",
card: "#505050",
cardBg: "#1D1D1D",
+ success: "#C1FF14",
},
fontFamily: {
atypText: ["var(--font-atyp-text)"],
@@ -31,8 +30,10 @@ const config: Config = {
dark: {
...daisyuiThemes.dark,
primary: "#61DFFF",
+ secondary: "#FF62E6",
"primary-content": "#000000",
- info: "#fff",
+ info: "#61DFFF",
+ success: "#C1FF14",
},
},
],
diff --git a/bridge-ui/test/wallet-setup/metamask.setup.ts b/bridge-ui/test/wallet-setup/metamask.setup.ts
index 94f65722..925acfe8 100644
--- a/bridge-ui/test/wallet-setup/metamask.setup.ts
+++ b/bridge-ui/test/wallet-setup/metamask.setup.ts
@@ -2,8 +2,10 @@ import { MetaMask, defineWalletSetup, getExtensionId } from "@synthetixio/synpre
import { LINEA_SEPOLIA_NETWORK, METAMASK_PASSWORD, METAMASK_SEED_PHRASE, TEST_PRIVATE_KEY } from "../constants";
export default defineWalletSetup(METAMASK_PASSWORD, async (context, walletPage) => {
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
const extensionId = await getExtensionId(context, "MetaMask");
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
const metamask = new MetaMask(context, walletPage, METAMASK_PASSWORD, extensionId);
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ba848024..97e162ac 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -43,7 +43,7 @@ importers:
dependencies:
'@consensys/linea-sdk':
specifier: 0.3.0
- version: 0.3.0(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4))(utf-8-validate@5.0.10)
+ version: 0.3.0(bufferutil@4.0.8)(ts-node@10.9.2(typescript@5.5.4))(utf-8-validate@5.0.10)
'@headlessui/react':
specifier: 2.1.2
version: 2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -61,7 +61,7 @@ importers:
version: 5.0.8(@types/react@18.3.3)(@wagmi/connectors@5.1.0(@types/react@18.3.3)(@wagmi/core@2.13.0(@tanstack/query-core@5.51.9)(@types/react@18.3.3)(react@18.3.1)(typescript@5.5.4)(viem@2.18.0(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.74.3(@babel/core@7.24.9)(@babel/preset-env@7.24.8(@babel/core@7.24.9))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@4.19.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.18.0(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.0(@tanstack/query-core@5.51.9)(@types/react@18.3.3)(react@18.3.1)(typescript@5.5.4)(viem@2.18.0(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@18.3.1)(utf-8-validate@5.0.10)(viem@2.18.0(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.12.0(@tanstack/query-core@5.51.9)(@tanstack/react-query@5.51.11(react@18.3.1))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react-native@0.74.3(@babel/core@7.24.9)(@babel/preset-env@7.24.8(@babel/core@7.24.9))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@4.19.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.18.0(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8))
axios:
specifier: 1.7.2
- version: 1.7.2(debug@4.3.5)
+ version: 1.7.2
classnames:
specifier: 2.5.1
version: 2.5.1
@@ -74,9 +74,6 @@ importers:
framer-motion:
specifier: 11.3.17
version: 11.3.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
- gray-matter:
- specifier: 4.0.3
- version: 4.0.3
joi:
specifier: 17.13.3
version: 17.13.3
@@ -129,9 +126,12 @@ importers:
'@playwright/test':
specifier: 1.45.3
version: 1.45.3
+ '@svgr/webpack':
+ specifier: ^8.1.0
+ version: 8.1.0(typescript@5.5.4)
'@synthetixio/synpress':
specifier: 4.0.0-alpha.7
- version: 4.0.0-alpha.7(@playwright/test@1.45.3)(bufferutil@4.0.8)(playwright-core@1.45.3)(postcss@8.4.40)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8)
+ version: 4.0.0-alpha.7(@playwright/test@1.45.3)(bufferutil@4.0.8)(playwright-core@1.45.3)(postcss@8.4.40)(ts-node@10.9.2(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8)
'@types/fs-extra':
specifier: 11.0.4
version: 11.0.4
@@ -155,16 +155,16 @@ importers:
version: 14.2.5(eslint@9.7.0)(typescript@5.5.4)
eslint-plugin-tailwindcss:
specifier: 3.17.4
- version: 3.17.4(tailwindcss@3.4.7(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4)))
+ version: 3.17.4(tailwindcss@3.4.7(ts-node@10.9.2(typescript@5.5.4)))
postcss:
specifier: 8.4.40
version: 8.4.40
tailwind-scrollbar:
specifier: 3.1.0
- version: 3.1.0(tailwindcss@3.4.7(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4)))
+ version: 3.1.0(tailwindcss@3.4.7(ts-node@10.9.2(typescript@5.5.4)))
tailwindcss:
specifier: 3.4.7
- version: 3.4.7(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4))
+ version: 3.4.7(ts-node@10.9.2(typescript@5.5.4))
contracts:
devDependencies:
@@ -1042,12 +1042,24 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0
+ '@babel/plugin-transform-react-constant-elements@7.25.1':
+ resolution: {integrity: sha512-SLV/giH/V4SmloZ6Dt40HjTGTAIkxn33TVIHxNGNvo8ezMhrxBkzisj4op1KZYPIOHFLqhv60OHvX+YRu4xbmQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
'@babel/plugin-transform-react-display-name@7.24.7':
resolution: {integrity: sha512-H/Snz9PFxKsS1JLI4dJLtnJgCJRoo0AUm3chP6NYr+9En1JMKloheEiLIhlp5MDVznWo+H3AAC1Mc8lmUEpsgg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
+ '@babel/plugin-transform-react-jsx-development@7.24.7':
+ resolution: {integrity: sha512-QG9EnzoGn+Qar7rxuW+ZOsbWOt56FvvI93xInqsZDC5fsekx1AlIO4KIJ5M+D0p0SqSH156EpmZyXq630B8OlQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
'@babel/plugin-transform-react-jsx-self@7.24.7':
resolution: {integrity: sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw==}
engines: {node: '>=6.9.0'}
@@ -1066,6 +1078,12 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0
+ '@babel/plugin-transform-react-pure-annotations@7.24.7':
+ resolution: {integrity: sha512-PLgBVk3fzbmEjBJ/u8kFzOqS9tUeDjiaWud/rRym/yjCo/M9cASPlnrd2ZmmZpQT40fOOrvR8jh+n8jikrOhNA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
'@babel/plugin-transform-regenerator@7.24.7':
resolution: {integrity: sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==}
engines: {node: '>=6.9.0'}
@@ -1161,6 +1179,12 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0
+ '@babel/preset-react@7.24.7':
+ resolution: {integrity: sha512-AAH4lEkpmzFWrGVlHaxJB7RLH21uPQ9+He+eFLWHmF9IuFQVugz8eAsamaW0DXRrTfco5zj1wWtpdcXJUOfsag==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
'@babel/preset-typescript@7.24.7':
resolution: {integrity: sha512-SyXRe3OdWwIwalxDg5UtJnJQO+YPcTfwiIY2B0Xlddh9o7jpWLvv8X1RthIeDOxQ+O1ML5BLPCONToObyVQVuQ==}
engines: {node: '>=6.9.0'}
@@ -2838,6 +2862,84 @@ packages:
'@stablelib/x25519@1.0.3':
resolution: {integrity: sha512-KnTbKmUhPhHavzobclVJQG5kuivH+qDLpe84iRqX3CLrKp881cF160JvXJ+hjn1aMyCwYOKeIZefIH/P5cJoRw==}
+ '@svgr/babel-plugin-add-jsx-attribute@8.0.0':
+ resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@svgr/babel-plugin-remove-jsx-attribute@8.0.0':
+ resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0':
+ resolution: {integrity: sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0':
+ resolution: {integrity: sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@svgr/babel-plugin-svg-dynamic-title@8.0.0':
+ resolution: {integrity: sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@svgr/babel-plugin-svg-em-dimensions@8.0.0':
+ resolution: {integrity: sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@svgr/babel-plugin-transform-react-native-svg@8.1.0':
+ resolution: {integrity: sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@svgr/babel-plugin-transform-svg-component@8.0.0':
+ resolution: {integrity: sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==}
+ engines: {node: '>=12'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@svgr/babel-preset@8.1.0':
+ resolution: {integrity: sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@svgr/core@8.1.0':
+ resolution: {integrity: sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==}
+ engines: {node: '>=14'}
+
+ '@svgr/hast-util-to-babel-ast@8.0.0':
+ resolution: {integrity: sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==}
+ engines: {node: '>=14'}
+
+ '@svgr/plugin-jsx@8.1.0':
+ resolution: {integrity: sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@svgr/core': '*'
+
+ '@svgr/plugin-svgo@8.1.0':
+ resolution: {integrity: sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@svgr/core': '*'
+
+ '@svgr/webpack@8.1.0':
+ resolution: {integrity: sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==}
+ engines: {node: '>=14'}
+
'@swc/counter@0.1.3':
resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
@@ -2899,6 +3001,10 @@ packages:
'@tanstack/virtual-core@3.8.3':
resolution: {integrity: sha512-vd2A2TnM5lbnWZnHi9B+L2gPtkSeOtJOAw358JqokIH1+v2J7vUAzFVPwB/wrye12RFOurffXu33plm4uQ+JBQ==}
+ '@trysound/sax@0.2.0':
+ resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
+ engines: {node: '>=10.13.0'}
+
'@ts-bridge/cli@0.1.4':
resolution: {integrity: sha512-X8sgizUyAJGxwXKpCA3ovXW7ukKsaplL6CV0Q4O4TH4mS9sok0tpN7UsRsXm6CDXpe5DYZ4FWTimmlQB6QZTPw==}
engines: {node: ^18.18 || >=20}
@@ -3875,6 +3981,9 @@ packages:
resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
+ boolbase@1.0.0:
+ resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
+
bowser@2.11.0:
resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==}
@@ -4289,6 +4398,10 @@ packages:
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
engines: {node: '>= 6'}
+ commander@7.2.0:
+ resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
+ engines: {node: '>= 10'}
+
commander@8.3.0:
resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
engines: {node: '>= 12'}
@@ -4437,14 +4550,33 @@ packages:
crypt@0.0.2:
resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==}
+ css-select@5.1.0:
+ resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==}
+
css-selector-tokenizer@0.8.0:
resolution: {integrity: sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==}
+ css-tree@2.2.1:
+ resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==}
+ engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
+
+ css-tree@2.3.1:
+ resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==}
+ engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
+
+ css-what@6.1.0:
+ resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==}
+ engines: {node: '>= 6'}
+
cssesc@3.0.0:
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
engines: {node: '>=4'}
hasBin: true
+ csso@5.0.5:
+ resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==}
+ engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
+
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
@@ -4687,9 +4819,25 @@ packages:
resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
engines: {node: '>=6.0.0'}
+ dom-serializer@2.0.0:
+ resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
+
dom-walk@0.1.2:
resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==}
+ domelementtype@2.3.0:
+ resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
+
+ domhandler@5.0.3:
+ resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
+ engines: {node: '>= 4'}
+
+ domutils@3.1.0:
+ resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==}
+
+ dot-case@3.0.4:
+ resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==}
+
dotenv@16.4.5:
resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==}
engines: {node: '>=12'}
@@ -4770,6 +4918,10 @@ packages:
resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==}
engines: {node: '>=8.6'}
+ entities@4.5.0:
+ resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+ engines: {node: '>=0.12'}
+
env-paths@2.2.1:
resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
engines: {node: '>=6'}
@@ -5164,10 +5316,6 @@ packages:
ext@1.7.0:
resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==}
- extend-shallow@2.0.1:
- resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==}
- engines: {node: '>=0.10.0'}
-
extend@3.0.2:
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
@@ -5614,10 +5762,6 @@ packages:
graphemer@1.4.0:
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
- gray-matter@4.0.3:
- resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==}
- engines: {node: '>=6.0'}
-
h3@1.12.0:
resolution: {integrity: sha512-Zi/CcNeWBXDrFNlV0hUBJQR9F7a96RjMeAZweW/ZWkR9fuXrMcvKnSA63f/zZ9l0GgQOZDVHGvXivNN9PWOwhA==}
@@ -5954,10 +6098,6 @@ packages:
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
hasBin: true
- is-extendable@0.1.1:
- resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==}
- engines: {node: '>=0.10.0'}
-
is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
@@ -6610,6 +6750,9 @@ packages:
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
hasBin: true
+ lower-case@2.0.2:
+ resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==}
+
lowercase-keys@2.0.0:
resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==}
engines: {node: '>=8'}
@@ -6661,6 +6804,12 @@ packages:
md5.js@1.3.5:
resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==}
+ mdn-data@2.0.28:
+ resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==}
+
+ mdn-data@2.0.30:
+ resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
+
media-typer@0.3.0:
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
engines: {node: '>= 0.6'}
@@ -7017,6 +7166,9 @@ packages:
sass:
optional: true
+ no-case@3.0.4:
+ resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
+
nocache@3.0.4:
resolution: {integrity: sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw==}
engines: {node: '>=12.0.0'}
@@ -7124,6 +7276,9 @@ packages:
resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ nth-check@2.1.1:
+ resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
+
nullthrows@1.1.1:
resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==}
@@ -8104,10 +8259,6 @@ packages:
resolution: {integrity: sha512-TKWX8xvoGHrxVdqbYeZM9w+izTF4b9z3NhSaDkdn81btvuh+ivbIMGT/zQvDtTFWhRlThpoz6LEYTr7n8A5GcA==}
engines: {node: '>=14.0.0'}
- section-matter@1.0.0:
- resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==}
- engines: {node: '>=4'}
-
secure-json-parse@2.7.0:
resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==}
@@ -8244,6 +8395,9 @@ packages:
resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
+ snake-case@3.0.4:
+ resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==}
+
socket.io-client@4.7.5:
resolution: {integrity: sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==}
engines: {node: '>=10.0.0'}
@@ -8451,10 +8605,6 @@ packages:
resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
engines: {node: '>=12'}
- strip-bom-string@1.0.0:
- resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==}
- engines: {node: '>=0.10.0'}
-
strip-bom@3.0.0:
resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
engines: {node: '>=4'}
@@ -8531,6 +8681,14 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
+ svg-parser@2.0.4:
+ resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==}
+
+ svgo@3.3.2:
+ resolution: {integrity: sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==}
+ engines: {node: '>=14.0.0'}
+ hasBin: true
+
swarm-js@0.1.42:
resolution: {integrity: sha512-BV7c/dVlA3R6ya1lMlSSNPLYrntt0LUq4YMgy3iwpCIc6rZnS5W2wUoctarZ5pXlpKtxDDf9hNziEkcfrxdhqQ==}
@@ -9697,7 +9855,7 @@ snapshots:
'@babel/traverse': 7.24.8
'@babel/types': 7.24.9
convert-source-map: 2.0.0
- debug: 4.3.5(supports-color@8.1.1)
+ debug: 4.3.5
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
@@ -9757,7 +9915,7 @@ snapshots:
'@babel/core': 7.24.9
'@babel/helper-compilation-targets': 7.24.8
'@babel/helper-plugin-utils': 7.24.8
- debug: 4.3.5(supports-color@8.1.1)
+ debug: 4.3.5
lodash.debounce: 4.0.8
resolve: 1.22.8
transitivePeerDependencies:
@@ -10353,11 +10511,23 @@ snapshots:
'@babel/core': 7.24.9
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-transform-react-constant-elements@7.25.1(@babel/core@7.24.9)':
+ dependencies:
+ '@babel/core': 7.24.9
+ '@babel/helper-plugin-utils': 7.24.8
+
'@babel/plugin-transform-react-display-name@7.24.7(@babel/core@7.24.9)':
dependencies:
'@babel/core': 7.24.9
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-transform-react-jsx-development@7.24.7(@babel/core@7.24.9)':
+ dependencies:
+ '@babel/core': 7.24.9
+ '@babel/plugin-transform-react-jsx': 7.24.7(@babel/core@7.24.9)
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/plugin-transform-react-jsx-self@7.24.7(@babel/core@7.24.9)':
dependencies:
'@babel/core': 7.24.9
@@ -10379,6 +10549,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/plugin-transform-react-pure-annotations@7.24.7(@babel/core@7.24.9)':
+ dependencies:
+ '@babel/core': 7.24.9
+ '@babel/helper-annotate-as-pure': 7.24.7
+ '@babel/helper-plugin-utils': 7.24.8
+
'@babel/plugin-transform-regenerator@7.24.7(@babel/core@7.24.9)':
dependencies:
'@babel/core': 7.24.9
@@ -10564,6 +10740,18 @@ snapshots:
'@babel/types': 7.24.9
esutils: 2.0.3
+ '@babel/preset-react@7.24.7(@babel/core@7.24.9)':
+ dependencies:
+ '@babel/core': 7.24.9
+ '@babel/helper-plugin-utils': 7.24.8
+ '@babel/helper-validator-option': 7.24.8
+ '@babel/plugin-transform-react-display-name': 7.24.7(@babel/core@7.24.9)
+ '@babel/plugin-transform-react-jsx': 7.24.7(@babel/core@7.24.9)
+ '@babel/plugin-transform-react-jsx-development': 7.24.7(@babel/core@7.24.9)
+ '@babel/plugin-transform-react-pure-annotations': 7.24.7(@babel/core@7.24.9)
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/preset-typescript@7.24.7(@babel/core@7.24.9)':
dependencies:
'@babel/core': 7.24.9
@@ -10606,7 +10794,7 @@ snapshots:
'@babel/helper-split-export-declaration': 7.24.7
'@babel/parser': 7.24.8
'@babel/types': 7.24.9
- debug: 4.3.5(supports-color@8.1.1)
+ debug: 4.3.5
globals: 11.12.0
transitivePeerDependencies:
- supports-color
@@ -10658,7 +10846,7 @@ snapshots:
'@colors/colors@1.6.0': {}
- '@consensys/linea-sdk@0.3.0(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4))(utf-8-validate@5.0.10)':
+ '@consensys/linea-sdk@0.3.0(bufferutil@4.0.8)(ts-node@10.9.2(typescript@5.5.4))(utf-8-validate@5.0.10)':
dependencies:
better-sqlite3: 9.6.0
class-validator: 0.14.1
@@ -10666,8 +10854,8 @@ snapshots:
ethers: 6.13.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)
lru-cache: 10.4.3
pg: 8.12.0
- typeorm: 0.3.20(better-sqlite3@9.6.0)(pg@8.12.0)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4))
- typeorm-naming-strategies: 4.1.0(typeorm@0.3.20(better-sqlite3@9.6.0)(pg@8.12.0)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4)))
+ typeorm: 0.3.20(better-sqlite3@9.6.0)(pg@8.12.0)(ts-node@10.9.2(typescript@5.5.4))
+ typeorm-naming-strategies: 4.1.0(typeorm@0.3.20(better-sqlite3@9.6.0)(pg@8.12.0)(ts-node@10.9.2(typescript@5.5.4)))
winston: 3.13.1
transitivePeerDependencies:
- '@google-cloud/spanner'
@@ -10872,7 +11060,7 @@ snapshots:
'@eslint/config-array@0.17.1':
dependencies:
'@eslint/object-schema': 2.1.4
- debug: 4.3.5(supports-color@8.1.1)
+ debug: 4.3.5
minimatch: 3.1.2
transitivePeerDependencies:
- supports-color
@@ -10894,7 +11082,7 @@ snapshots:
'@eslint/eslintrc@3.1.0':
dependencies:
ajv: 6.12.6
- debug: 4.3.5(supports-color@8.1.1)
+ debug: 4.3.5
espree: 10.1.0
globals: 14.0.0
ignore: 5.3.1
@@ -11516,7 +11704,7 @@ snapshots:
dependencies:
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 3.0.4
- '@types/node': 20.12.7
+ '@types/node': 20.14.12
'@types/yargs': 15.0.19
chalk: 4.1.2
@@ -11649,7 +11837,7 @@ snapshots:
bufferutil: 4.0.8
cross-fetch: 4.0.0(encoding@0.1.13)
date-fns: 2.30.0
- debug: 4.3.5(supports-color@8.1.1)
+ debug: 4.3.5
eciesjs: 0.3.19
eventemitter2: 6.4.9
readable-stream: 3.6.2
@@ -11677,7 +11865,7 @@ snapshots:
'@types/dom-screen-wake-lock': 1.0.3
bowser: 2.11.0
cross-fetch: 4.0.0(encoding@0.1.13)
- debug: 4.3.5(supports-color@8.1.1)
+ debug: 4.3.5
eciesjs: 0.3.19
eth-rpc-errors: 4.0.3
eventemitter2: 6.4.9
@@ -11709,7 +11897,7 @@ snapshots:
dependencies:
'@ethereumjs/tx': 4.2.0
'@types/debug': 4.1.12
- debug: 4.3.5(supports-color@8.1.1)
+ debug: 4.3.5
semver: 7.6.3
superstruct: 1.0.4
transitivePeerDependencies:
@@ -11722,7 +11910,7 @@ snapshots:
'@noble/hashes': 1.4.0
'@scure/base': 1.1.7
'@types/debug': 4.1.12
- debug: 4.3.5(supports-color@8.1.1)
+ debug: 4.3.5
pony-cause: 2.1.11
semver: 7.6.3
uuid: 9.0.1
@@ -11736,7 +11924,7 @@ snapshots:
'@noble/hashes': 1.4.0
'@scure/base': 1.1.7
'@types/debug': 4.1.12
- debug: 4.3.5(supports-color@8.1.1)
+ debug: 4.3.5
pony-cause: 2.1.11
semver: 7.6.3
uuid: 9.0.1
@@ -12809,6 +12997,99 @@ snapshots:
'@stablelib/random': 1.0.2
'@stablelib/wipe': 1.0.1
+ '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.24.9)':
+ dependencies:
+ '@babel/core': 7.24.9
+
+ '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.24.9)':
+ dependencies:
+ '@babel/core': 7.24.9
+
+ '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.24.9)':
+ dependencies:
+ '@babel/core': 7.24.9
+
+ '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.24.9)':
+ dependencies:
+ '@babel/core': 7.24.9
+
+ '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.24.9)':
+ dependencies:
+ '@babel/core': 7.24.9
+
+ '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.24.9)':
+ dependencies:
+ '@babel/core': 7.24.9
+
+ '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.24.9)':
+ dependencies:
+ '@babel/core': 7.24.9
+
+ '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.24.9)':
+ dependencies:
+ '@babel/core': 7.24.9
+
+ '@svgr/babel-preset@8.1.0(@babel/core@7.24.9)':
+ dependencies:
+ '@babel/core': 7.24.9
+ '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.24.9)
+ '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.24.9)
+ '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.24.9)
+ '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.24.9)
+ '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.24.9)
+ '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.24.9)
+ '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.24.9)
+ '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.24.9)
+
+ '@svgr/core@8.1.0(typescript@5.5.4)':
+ dependencies:
+ '@babel/core': 7.24.9
+ '@svgr/babel-preset': 8.1.0(@babel/core@7.24.9)
+ camelcase: 6.3.0
+ cosmiconfig: 8.3.6(typescript@5.5.4)
+ snake-case: 3.0.4
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ '@svgr/hast-util-to-babel-ast@8.0.0':
+ dependencies:
+ '@babel/types': 7.24.9
+ entities: 4.5.0
+
+ '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.5.4))':
+ dependencies:
+ '@babel/core': 7.24.9
+ '@svgr/babel-preset': 8.1.0(@babel/core@7.24.9)
+ '@svgr/core': 8.1.0(typescript@5.5.4)
+ '@svgr/hast-util-to-babel-ast': 8.0.0
+ svg-parser: 2.0.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@svgr/plugin-svgo@8.1.0(@svgr/core@8.1.0(typescript@5.5.4))(typescript@5.5.4)':
+ dependencies:
+ '@svgr/core': 8.1.0(typescript@5.5.4)
+ cosmiconfig: 8.3.6(typescript@5.5.4)
+ deepmerge: 4.3.1
+ svgo: 3.3.2
+ transitivePeerDependencies:
+ - typescript
+
+ '@svgr/webpack@8.1.0(typescript@5.5.4)':
+ dependencies:
+ '@babel/core': 7.24.9
+ '@babel/plugin-transform-react-constant-elements': 7.25.1(@babel/core@7.24.9)
+ '@babel/preset-env': 7.24.8(@babel/core@7.24.9)
+ '@babel/preset-react': 7.24.7(@babel/core@7.24.9)
+ '@babel/preset-typescript': 7.24.7(@babel/core@7.24.9)
+ '@svgr/core': 8.1.0(typescript@5.5.4)
+ '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.5.4))
+ '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.5.4))(typescript@5.5.4)
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
'@swc/counter@0.1.3': {}
'@swc/helpers@0.5.12':
@@ -12832,7 +13113,7 @@ snapshots:
- utf-8-validate
- zod
- '@synthetixio/synpress-cache@0.0.1-alpha.7(playwright-core@1.45.3)(postcss@8.4.40)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4))(typescript@5.5.4)':
+ '@synthetixio/synpress-cache@0.0.1-alpha.7(playwright-core@1.45.3)(postcss@8.4.40)(ts-node@10.9.2(typescript@5.5.4))(typescript@5.5.4)':
dependencies:
axios: 1.6.7
chalk: 5.3.0
@@ -12843,7 +13124,7 @@ snapshots:
gradient-string: 2.0.2
playwright-core: 1.45.3
progress: 2.0.3
- tsup: 8.0.2(postcss@8.4.40)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4))(typescript@5.5.4)
+ tsup: 8.0.2(postcss@8.4.40)(ts-node@10.9.2(typescript@5.5.4))(typescript@5.5.4)
unzipper: 0.10.14
zod: 3.22.4
transitivePeerDependencies:
@@ -12859,10 +13140,10 @@ snapshots:
dependencies:
'@playwright/test': 1.45.3
- '@synthetixio/synpress-metamask@0.0.1-alpha.7(@playwright/test@1.45.3)(bufferutil@4.0.8)(playwright-core@1.45.3)(postcss@8.4.40)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)':
+ '@synthetixio/synpress-metamask@0.0.1-alpha.7(@playwright/test@1.45.3)(bufferutil@4.0.8)(playwright-core@1.45.3)(postcss@8.4.40)(ts-node@10.9.2(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)':
dependencies:
'@playwright/test': 1.45.3
- '@synthetixio/synpress-cache': 0.0.1-alpha.7(playwright-core@1.45.3)(postcss@8.4.40)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4))(typescript@5.5.4)
+ '@synthetixio/synpress-cache': 0.0.1-alpha.7(playwright-core@1.45.3)(postcss@8.4.40)(ts-node@10.9.2(typescript@5.5.4))(typescript@5.5.4)
'@synthetixio/synpress-core': 0.0.1-alpha.7(@playwright/test@1.45.3)
'@viem/anvil': 0.0.7(bufferutil@4.0.8)(utf-8-validate@5.0.10)
fs-extra: 11.2.0
@@ -12879,13 +13160,13 @@ snapshots:
- typescript
- utf-8-validate
- '@synthetixio/synpress@4.0.0-alpha.7(@playwright/test@1.45.3)(bufferutil@4.0.8)(playwright-core@1.45.3)(postcss@8.4.40)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8)':
+ '@synthetixio/synpress@4.0.0-alpha.7(@playwright/test@1.45.3)(bufferutil@4.0.8)(playwright-core@1.45.3)(postcss@8.4.40)(ts-node@10.9.2(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8)':
dependencies:
'@playwright/test': 1.45.3
'@synthetixio/ethereum-wallet-mock': 0.0.1-alpha.7(@playwright/test@1.45.3)(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.23.8)
- '@synthetixio/synpress-cache': 0.0.1-alpha.7(playwright-core@1.45.3)(postcss@8.4.40)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4))(typescript@5.5.4)
+ '@synthetixio/synpress-cache': 0.0.1-alpha.7(playwright-core@1.45.3)(postcss@8.4.40)(ts-node@10.9.2(typescript@5.5.4))(typescript@5.5.4)
'@synthetixio/synpress-core': 0.0.1-alpha.7(@playwright/test@1.45.3)
- '@synthetixio/synpress-metamask': 0.0.1-alpha.7(@playwright/test@1.45.3)(bufferutil@4.0.8)(playwright-core@1.45.3)(postcss@8.4.40)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)
+ '@synthetixio/synpress-metamask': 0.0.1-alpha.7(@playwright/test@1.45.3)(bufferutil@4.0.8)(playwright-core@1.45.3)(postcss@8.4.40)(ts-node@10.9.2(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)
transitivePeerDependencies:
- '@microsoft/api-extractor'
- '@swc/core'
@@ -12922,6 +13203,8 @@ snapshots:
'@tanstack/virtual-core@3.8.3': {}
+ '@trysound/sax@0.2.0': {}
+
'@ts-bridge/cli@0.1.4(@ts-bridge/shims@0.1.1)(typescript@5.5.4)':
dependencies:
chalk: 5.3.0
@@ -13089,7 +13372,7 @@ snapshots:
'@types/node-forge@1.3.11':
dependencies:
- '@types/node': 20.12.7
+ '@types/node': 20.14.12
'@types/node@12.20.55': {}
@@ -13211,7 +13494,7 @@ snapshots:
'@typescript-eslint/types': 7.2.0
'@typescript-eslint/typescript-estree': 7.2.0(typescript@5.5.4)
'@typescript-eslint/visitor-keys': 7.2.0
- debug: 4.3.5(supports-color@8.1.1)
+ debug: 4.3.5
eslint: 9.7.0
optionalDependencies:
typescript: 5.5.4
@@ -13261,7 +13544,7 @@ snapshots:
dependencies:
'@typescript-eslint/types': 7.2.0
'@typescript-eslint/visitor-keys': 7.2.0
- debug: 4.3.5(supports-color@8.1.1)
+ debug: 4.3.5
globby: 11.1.0
is-glob: 4.0.3
minimatch: 9.0.3
@@ -14486,7 +14769,15 @@ snapshots:
axios@1.6.7:
dependencies:
- follow-redirects: 1.15.6(debug@4.3.5)
+ follow-redirects: 1.15.6
+ form-data: 4.0.0
+ proxy-from-env: 1.1.0
+ transitivePeerDependencies:
+ - debug
+
+ axios@1.7.2:
+ dependencies:
+ follow-redirects: 1.15.6
form-data: 4.0.0
proxy-from-env: 1.1.0
transitivePeerDependencies:
@@ -14686,6 +14977,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ boolbase@1.0.0: {}
+
bowser@2.11.0: {}
boxen@5.1.2:
@@ -14955,7 +15248,7 @@ snapshots:
chrome-launcher@0.15.2:
dependencies:
- '@types/node': 20.12.7
+ '@types/node': 20.14.12
escape-string-regexp: 4.0.0
is-wsl: 2.2.0
lighthouse-logger: 1.4.2
@@ -15134,6 +15427,8 @@ snapshots:
commander@4.1.1: {}
+ commander@7.2.0: {}
+
commander@8.3.0: {}
commander@9.5.0: {}
@@ -15309,13 +15604,37 @@ snapshots:
crypt@0.0.2: {}
+ css-select@5.1.0:
+ dependencies:
+ boolbase: 1.0.0
+ css-what: 6.1.0
+ domhandler: 5.0.3
+ domutils: 3.1.0
+ nth-check: 2.1.1
+
css-selector-tokenizer@0.8.0:
dependencies:
cssesc: 3.0.0
fastparse: 1.1.2
+ css-tree@2.2.1:
+ dependencies:
+ mdn-data: 2.0.28
+ source-map-js: 1.2.0
+
+ css-tree@2.3.1:
+ dependencies:
+ mdn-data: 2.0.30
+ source-map-js: 1.2.0
+
+ css-what@6.1.0: {}
+
cssesc@3.0.0: {}
+ csso@5.0.5:
+ dependencies:
+ css-tree: 2.2.1
+
csstype@3.1.3: {}
csv-parser@3.0.0:
@@ -15386,6 +15705,10 @@ snapshots:
dependencies:
ms: 2.1.3
+ debug@4.3.5:
+ dependencies:
+ ms: 2.1.2
+
debug@4.3.5(supports-color@8.1.1):
dependencies:
ms: 2.1.2
@@ -15532,8 +15855,31 @@ snapshots:
dependencies:
esutils: 2.0.3
+ dom-serializer@2.0.0:
+ dependencies:
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+ entities: 4.5.0
+
dom-walk@0.1.2: {}
+ domelementtype@2.3.0: {}
+
+ domhandler@5.0.3:
+ dependencies:
+ domelementtype: 2.3.0
+
+ domutils@3.1.0:
+ dependencies:
+ dom-serializer: 2.0.0
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+
+ dot-case@3.0.4:
+ dependencies:
+ no-case: 3.0.4
+ tslib: 2.6.3
+
dotenv@16.4.5: {}
duplexer2@0.1.4:
@@ -15620,7 +15966,7 @@ snapshots:
engine.io-client@6.5.4(bufferutil@4.0.8)(utf-8-validate@5.0.10):
dependencies:
'@socket.io/component-emitter': 3.1.2
- debug: 4.3.5(supports-color@8.1.1)
+ debug: 4.3.5
engine.io-parser: 5.2.3
ws: 8.17.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)
xmlhttprequest-ssl: 2.0.0
@@ -15641,6 +15987,8 @@ snapshots:
ansi-colors: 4.1.3
strip-ansi: 6.0.1
+ entities@4.5.0: {}
+
env-paths@2.2.1: {}
envinfo@7.13.0: {}
@@ -15863,7 +16211,7 @@ snapshots:
eslint: 9.7.0
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.7.0)
- eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.6.0(eslint@8.57.0)(typescript@5.4.5))(eslint@9.7.0)
+ eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@9.7.0)
eslint-plugin-jsx-a11y: 6.9.0(eslint@9.7.0)
eslint-plugin-react: 7.35.0(eslint@9.7.0)
eslint-plugin-react-hooks: 4.6.2(eslint@9.7.0)
@@ -15887,11 +16235,11 @@ snapshots:
eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.7.0):
dependencies:
- debug: 4.3.5(supports-color@8.1.1)
+ debug: 4.3.5
enhanced-resolve: 5.17.1
eslint: 9.7.0
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.7.0))(eslint@9.7.0)
- eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.6.0(eslint@8.57.0)(typescript@5.4.5))(eslint@9.7.0)
+ eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@9.7.0)
fast-glob: 3.3.2
get-tsconfig: 4.7.6
is-core-module: 2.15.0
@@ -15913,17 +16261,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-module-utils@2.8.1(@typescript-eslint/parser@7.6.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@9.7.0):
- dependencies:
- debug: 3.2.7
- optionalDependencies:
- '@typescript-eslint/parser': 7.6.0(eslint@8.57.0)(typescript@5.4.5)
- eslint: 9.7.0
- eslint-import-resolver-node: 0.3.9
- transitivePeerDependencies:
- - supports-color
-
- eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.6.0(eslint@8.57.0)(typescript@5.4.5))(eslint@9.7.0):
+ eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@9.7.0):
dependencies:
array-includes: 3.1.8
array.prototype.findlastindex: 1.2.5
@@ -15933,7 +16271,7 @@ snapshots:
doctrine: 2.1.0
eslint: 9.7.0
eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.6.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@9.7.0)
+ eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.7.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.7.0))(eslint@9.7.0)
hasown: 2.0.2
is-core-module: 2.15.0
is-glob: 4.0.3
@@ -15944,7 +16282,7 @@ snapshots:
semver: 6.3.1
tsconfig-paths: 3.15.0
optionalDependencies:
- '@typescript-eslint/parser': 7.6.0(eslint@8.57.0)(typescript@5.4.5)
+ '@typescript-eslint/parser': 7.2.0(eslint@9.7.0)(typescript@5.5.4)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
@@ -16005,11 +16343,11 @@ snapshots:
string.prototype.matchall: 4.0.11
string.prototype.repeat: 1.0.0
- eslint-plugin-tailwindcss@3.17.4(tailwindcss@3.4.7(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4))):
+ eslint-plugin-tailwindcss@3.17.4(tailwindcss@3.4.7(ts-node@10.9.2(typescript@5.5.4))):
dependencies:
fast-glob: 3.3.2
postcss: 8.4.40
- tailwindcss: 3.4.7(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4))
+ tailwindcss: 3.4.7(ts-node@10.9.2(typescript@5.5.4))
eslint-scope@7.2.2:
dependencies:
@@ -16081,7 +16419,7 @@ snapshots:
ajv: 6.12.6
chalk: 4.1.2
cross-spawn: 7.0.3
- debug: 4.3.5(supports-color@8.1.1)
+ debug: 4.3.5
escape-string-regexp: 4.0.0
eslint-scope: 8.0.2
eslint-visitor-keys: 4.0.0
@@ -16453,10 +16791,6 @@ snapshots:
dependencies:
type: 2.7.3
- extend-shallow@2.0.1:
- dependencies:
- is-extendable: 0.1.1
-
extend@3.0.2: {}
extension-port-stream@3.0.0:
@@ -16614,6 +16948,8 @@ snapshots:
fn.name@1.1.0: {}
+ follow-redirects@1.15.6: {}
+
follow-redirects@1.15.6(debug@4.3.5):
optionalDependencies:
debug: 4.3.5(supports-color@8.1.1)
@@ -16969,13 +17305,6 @@ snapshots:
graphemer@1.4.0: {}
- gray-matter@4.0.3:
- dependencies:
- js-yaml: 3.14.1
- kind-of: 6.0.3
- section-matter: 1.0.0
- strip-bom-string: 1.0.0
-
h3@1.12.0:
dependencies:
cookie-es: 1.2.2
@@ -17225,7 +17554,7 @@ snapshots:
http-proxy@1.18.1:
dependencies:
eventemitter3: 4.0.7
- follow-redirects: 1.15.6(debug@4.3.5)
+ follow-redirects: 1.15.6
requires-port: 1.0.0
transitivePeerDependencies:
- debug
@@ -17407,8 +17736,6 @@ snapshots:
is-docker@3.0.0: {}
- is-extendable@0.1.1: {}
-
is-extglob@2.1.1: {}
is-finalizationregistry@1.0.2:
@@ -18250,6 +18577,10 @@ snapshots:
dependencies:
js-tokens: 4.0.0
+ lower-case@2.0.2:
+ dependencies:
+ tslib: 2.6.3
+
lowercase-keys@2.0.0: {}
lowercase-keys@3.0.0: {}
@@ -18310,6 +18641,10 @@ snapshots:
inherits: 2.0.4
safe-buffer: 5.2.1
+ mdn-data@2.0.28: {}
+
+ mdn-data@2.0.30: {}
+
media-typer@0.3.0: {}
memoize-one@5.2.1: {}
@@ -18778,6 +19113,11 @@ snapshots:
- '@babel/core'
- babel-plugin-macros
+ no-case@3.0.4:
+ dependencies:
+ lower-case: 2.0.2
+ tslib: 2.6.3
+
nocache@3.0.4: {}
node-abi@3.65.0:
@@ -18867,6 +19207,10 @@ snapshots:
dependencies:
path-key: 4.0.0
+ nth-check@2.1.1:
+ dependencies:
+ boolbase: 1.0.0
+
nullthrows@1.1.1: {}
number-to-bn@1.7.0:
@@ -19286,13 +19630,13 @@ snapshots:
camelcase-css: 2.0.1
postcss: 8.4.40
- postcss-load-config@4.0.2(postcss@8.4.40)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4)):
+ postcss-load-config@4.0.2(postcss@8.4.40)(ts-node@10.9.2(typescript@5.5.4)):
dependencies:
lilconfig: 3.1.2
yaml: 2.5.0
optionalDependencies:
postcss: 8.4.40
- ts-node: 10.9.2(@types/node@20.12.7)(typescript@5.5.4)
+ ts-node: 10.9.2(typescript@5.5.4)
postcss-nested@6.2.0(postcss@8.4.40):
dependencies:
@@ -19949,11 +20293,6 @@ snapshots:
node-addon-api: 5.1.0
node-gyp-build: 4.8.1
- section-matter@1.0.0:
- dependencies:
- extend-shallow: 2.0.1
- kind-of: 6.0.3
-
secure-json-parse@2.7.0: {}
selfsigned@2.4.1:
@@ -20140,10 +20479,15 @@ snapshots:
smart-buffer@4.2.0: {}
+ snake-case@3.0.4:
+ dependencies:
+ dot-case: 3.0.4
+ tslib: 2.6.3
+
socket.io-client@4.7.5(bufferutil@4.0.8)(utf-8-validate@5.0.10):
dependencies:
'@socket.io/component-emitter': 3.1.2
- debug: 4.3.5(supports-color@8.1.1)
+ debug: 4.3.5
engine.io-client: 6.5.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)
socket.io-parser: 4.2.4
transitivePeerDependencies:
@@ -20154,7 +20498,7 @@ snapshots:
socket.io-parser@4.2.4:
dependencies:
'@socket.io/component-emitter': 3.1.2
- debug: 4.3.5(supports-color@8.1.1)
+ debug: 4.3.5
transitivePeerDependencies:
- supports-color
@@ -20432,8 +20776,6 @@ snapshots:
dependencies:
ansi-regex: 6.0.1
- strip-bom-string@1.0.0: {}
-
strip-bom@3.0.0: {}
strip-bom@4.0.0: {}
@@ -20491,6 +20833,18 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {}
+ svg-parser@2.0.4: {}
+
+ svgo@3.3.2:
+ dependencies:
+ '@trysound/sax': 0.2.0
+ commander: 7.2.0
+ css-select: 5.1.0
+ css-tree: 2.3.1
+ css-what: 6.1.0
+ csso: 5.0.5
+ picocolors: 1.0.1
+
swarm-js@0.1.42(bufferutil@4.0.8)(utf-8-validate@5.0.10):
dependencies:
bluebird: 3.7.2
@@ -20535,11 +20889,11 @@ snapshots:
string-width: 4.2.3
strip-ansi: 6.0.1
- tailwind-scrollbar@3.1.0(tailwindcss@3.4.7(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4))):
+ tailwind-scrollbar@3.1.0(tailwindcss@3.4.7(ts-node@10.9.2(typescript@5.5.4))):
dependencies:
- tailwindcss: 3.4.7(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4))
+ tailwindcss: 3.4.7(ts-node@10.9.2(typescript@5.5.4))
- tailwindcss@3.4.7(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4)):
+ tailwindcss@3.4.7(ts-node@10.9.2(typescript@5.5.4)):
dependencies:
'@alloc/quick-lru': 5.2.0
arg: 5.0.2
@@ -20558,7 +20912,7 @@ snapshots:
postcss: 8.4.40
postcss-import: 15.1.0(postcss@8.4.40)
postcss-js: 4.0.1(postcss@8.4.40)
- postcss-load-config: 4.0.2(postcss@8.4.40)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4))
+ postcss-load-config: 4.0.2(postcss@8.4.40)(ts-node@10.9.2(typescript@5.5.4))
postcss-nested: 6.2.0(postcss@8.4.40)
postcss-selector-parser: 6.1.1
resolve: 1.22.8
@@ -20844,6 +21198,24 @@ snapshots:
v8-compile-cache-lib: 3.0.1
yn: 3.1.1
+ ts-node@10.9.2(typescript@5.5.4):
+ dependencies:
+ '@cspotcode/source-map-support': 0.8.1
+ '@tsconfig/node10': 1.0.11
+ '@tsconfig/node12': 1.0.11
+ '@tsconfig/node14': 1.0.3
+ '@tsconfig/node16': 1.0.4
+ acorn: 8.12.1
+ acorn-walk: 8.3.3
+ arg: 4.1.3
+ create-require: 1.1.1
+ diff: 4.0.2
+ make-error: 1.3.6
+ typescript: 5.5.4
+ v8-compile-cache-lib: 3.0.1
+ yn: 3.1.1
+ optional: true
+
tsconfig-paths@3.15.0:
dependencies:
'@types/json5': 0.0.29
@@ -20859,17 +21231,17 @@ snapshots:
tsort@0.0.1: {}
- tsup@8.0.2(postcss@8.4.40)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4))(typescript@5.5.4):
+ tsup@8.0.2(postcss@8.4.40)(ts-node@10.9.2(typescript@5.5.4))(typescript@5.5.4):
dependencies:
bundle-require: 4.2.1(esbuild@0.19.12)
cac: 6.7.14
chokidar: 3.6.0
- debug: 4.3.5(supports-color@8.1.1)
+ debug: 4.3.5
esbuild: 0.19.12
execa: 5.1.1
globby: 11.1.0
joycon: 3.1.1
- postcss-load-config: 4.0.2(postcss@8.4.40)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4))
+ postcss-load-config: 4.0.2(postcss@8.4.40)(ts-node@10.9.2(typescript@5.5.4))
resolve-from: 5.0.0
rollup: 4.19.0
source-map: 0.8.0-beta.0
@@ -20975,9 +21347,9 @@ snapshots:
dependencies:
typeorm: 0.3.20(better-sqlite3@9.6.0)(pg@8.11.3)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4))
- typeorm-naming-strategies@4.1.0(typeorm@0.3.20(better-sqlite3@9.6.0)(pg@8.12.0)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4))):
+ typeorm-naming-strategies@4.1.0(typeorm@0.3.20(better-sqlite3@9.6.0)(pg@8.12.0)(ts-node@10.9.2(typescript@5.5.4))):
dependencies:
- typeorm: 0.3.20(better-sqlite3@9.6.0)(pg@8.12.0)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4))
+ typeorm: 0.3.20(better-sqlite3@9.6.0)(pg@8.12.0)(ts-node@10.9.2(typescript@5.5.4))
typeorm@0.3.20(better-sqlite3@9.6.0)(pg@8.11.3)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4)):
dependencies:
@@ -21003,7 +21375,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- typeorm@0.3.20(better-sqlite3@9.6.0)(pg@8.12.0)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.5.4)):
+ typeorm@0.3.20(better-sqlite3@9.6.0)(pg@8.12.0)(ts-node@10.9.2(typescript@5.5.4)):
dependencies:
'@sqltools/formatter': 1.2.5
app-root-path: 3.1.0
@@ -21011,7 +21383,7 @@ snapshots:
chalk: 4.1.2
cli-highlight: 2.1.11
dayjs: 1.11.12
- debug: 4.3.5(supports-color@8.1.1)
+ debug: 4.3.5
dotenv: 16.4.5
glob: 10.4.5
mkdirp: 2.1.6
@@ -21023,7 +21395,7 @@ snapshots:
optionalDependencies:
better-sqlite3: 9.6.0
pg: 8.12.0
- ts-node: 10.9.2(@types/node@20.12.7)(typescript@5.5.4)
+ ts-node: 10.9.2(typescript@5.5.4)
transitivePeerDependencies:
- supports-color