mirror of
https://github.com/vacp2p/linea-monorepo.git
synced 2026-01-08 15:13:50 -05:00
Feat: add lifi and small fixes (#798)
* fix: add gitattribute rules for woff2 files * feat: add lifi widget + fixes minor issues * fix: remove unused packages + clean constants declaration and config * fix: update dockerfile and github ci workflows * fix: env variable naming issue * fix: bridge mode alt value issue + remove button component
This commit is contained in:
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -24,3 +24,5 @@ prover/prover-assets/**/**/**/*.bin binary
|
|||||||
prover/prover-assets/**/**/**/**/*.bin binary
|
prover/prover-assets/**/**/**/**/*.bin binary
|
||||||
prover/prover-assets/kzgsrs/* binary
|
prover/prover-assets/kzgsrs/* binary
|
||||||
|
|
||||||
|
*.woff2 binary
|
||||||
|
|
||||||
|
|||||||
1
.github/workflows/bridge-ui-e2e-tests.yml
vendored
1
.github/workflows/bridge-ui-e2e-tests.yml
vendored
@@ -39,6 +39,7 @@ jobs:
|
|||||||
NEXT_PUBLIC_WALLET_CONNECT_ID: ${{ secrets.PUBLIC_WALLET_CONNECT_ID }}
|
NEXT_PUBLIC_WALLET_CONNECT_ID: ${{ secrets.PUBLIC_WALLET_CONNECT_ID }}
|
||||||
NEXT_PUBLIC_INFURA_ID: ${{ secrets.PUBLIC_BRIDGE_UI_INFURA_ID }}
|
NEXT_PUBLIC_INFURA_ID: ${{ secrets.PUBLIC_BRIDGE_UI_INFURA_ID }}
|
||||||
NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID: ${{ secrets.PUBLIC_DYNAMIC_ENVIRONMENT_ID }}
|
NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID: ${{ secrets.PUBLIC_DYNAMIC_ENVIRONMENT_ID }}
|
||||||
|
NEXT_PUBLIC_LIFI_API_KEY: ${{ secrets.PUBLIC_LIFI_API_KEY }}
|
||||||
|
|
||||||
- name: Install linux dependencies
|
- name: Install linux dependencies
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
9
.github/workflows/bridge-ui-publish.yml
vendored
9
.github/workflows/bridge-ui-publish.yml
vendored
@@ -56,18 +56,19 @@ jobs:
|
|||||||
file: ./bridge-ui/Dockerfile
|
file: ./bridge-ui/Dockerfile
|
||||||
push: ${{ env.DOCKERHUB_USERNAME != '' && env.DOCKERHUB_TOKEN != '' }}
|
push: ${{ env.DOCKERHUB_USERNAME != '' && env.DOCKERHUB_TOKEN != '' }}
|
||||||
tags: consensys/linea-bridge-ui:${{ env.DOCKER_TAG }}
|
tags: consensys/linea-bridge-ui:${{ env.DOCKER_TAG }}
|
||||||
# Env file dedicated for dev
|
|
||||||
build-args: |
|
build-args: |
|
||||||
ENV_FILE=./bridge-ui/.env.production
|
ENV_FILE=./bridge-ui/.env.production
|
||||||
NEXT_PUBLIC_WALLET_CONNECT_ID=${{ secrets.PUBLIC_WALLET_CONNECT_ID }}
|
NEXT_PUBLIC_WALLET_CONNECT_ID=${{ env.NEXT_PUBLIC_WALLET_CONNECT_ID }}
|
||||||
NEXT_PUBLIC_INFURA_ID=${{ secrets.PUBLIC_BRIDGE_UI_INFURA_ID }}
|
NEXT_PUBLIC_INFURA_ID=${{ env.NEXT_PUBLIC_INFURA_ID }}
|
||||||
NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID=${{ secrets.PUBLIC_DYNAMIC_ENVIRONMENT_ID }}
|
NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID=${{ env.NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID }}
|
||||||
|
NEXT_PUBLIC_LIFI_API_KEY=${{ env.NEXT_PUBLIC_LIFI_API_KEY }}
|
||||||
cache-from: type=registry,ref=consensys/linea-bridge-ui:buildcache
|
cache-from: type=registry,ref=consensys/linea-bridge-ui:buildcache
|
||||||
cache-to: type=registry,ref=consensys/linea-bridge-ui:buildcache,mode=max
|
cache-to: type=registry,ref=consensys/linea-bridge-ui:buildcache,mode=max
|
||||||
env:
|
env:
|
||||||
NEXT_PUBLIC_WALLET_CONNECT_ID: ${{ secrets.PUBLIC_WALLET_CONNECT_ID }}
|
NEXT_PUBLIC_WALLET_CONNECT_ID: ${{ secrets.PUBLIC_WALLET_CONNECT_ID }}
|
||||||
NEXT_PUBLIC_INFURA_ID: ${{ secrets.PUBLIC_BRIDGE_UI_INFURA_ID }}
|
NEXT_PUBLIC_INFURA_ID: ${{ secrets.PUBLIC_BRIDGE_UI_INFURA_ID }}
|
||||||
NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID: ${{ secrets.PUBLIC_DYNAMIC_ENVIRONMENT_ID }}
|
NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID: ${{ secrets.PUBLIC_DYNAMIC_ENVIRONMENT_ID }}
|
||||||
|
NEXT_PUBLIC_LIFI_API_KEY: ${{ secrets.PUBLIC_LIFI_API_KEY }}
|
||||||
|
|
||||||
test-build:
|
test-build:
|
||||||
if: github.event.pull_request.head.repo.fork == true
|
if: github.event.pull_request.head.repo.fork == true
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ NEXT_PUBLIC_MAINNET_LINEA_USDC_BRIDGE=0xA2Ee6Fce4ACB62D95448729cDb781e3BEb62504A
|
|||||||
NEXT_PUBLIC_MAINNET_GAS_ESTIMATED=100000
|
NEXT_PUBLIC_MAINNET_GAS_ESTIMATED=100000
|
||||||
NEXT_PUBLIC_MAINNET_DEFAULT_GAS_LIMIT_SURPLUS=6000
|
NEXT_PUBLIC_MAINNET_DEFAULT_GAS_LIMIT_SURPLUS=6000
|
||||||
NEXT_PUBLIC_MAINNET_PROFIT_MARGIN=2
|
NEXT_PUBLIC_MAINNET_PROFIT_MARGIN=2
|
||||||
MAINNET_TOKEN_LIST=https://raw.githubusercontent.com/Consensys/linea-token-list/main/json/linea-mainnet-token-shortlist.json
|
NEXT_PUBLIC_MAINNET_TOKEN_LIST=https://raw.githubusercontent.com/Consensys/linea-token-list/main/json/linea-mainnet-token-shortlist.json
|
||||||
|
|
||||||
NEXT_PUBLIC_SEPOLIA_L1_TOKEN_BRIDGE=0x5A0a48389BB0f12E5e017116c1105da97E129142
|
NEXT_PUBLIC_SEPOLIA_L1_TOKEN_BRIDGE=0x5A0a48389BB0f12E5e017116c1105da97E129142
|
||||||
NEXT_PUBLIC_SEPOLIA_LINEA_TOKEN_BRIDGE=0x93DcAdf238932e6e6a85852caC89cBd71798F463
|
NEXT_PUBLIC_SEPOLIA_LINEA_TOKEN_BRIDGE=0x93DcAdf238932e6e6a85852caC89cBd71798F463
|
||||||
@@ -18,7 +18,7 @@ NEXT_PUBLIC_SEPOLIA_LINEA_USDC_BRIDGE=0x39fd5cF710314341d35f9Dca20c1daa059Acb843
|
|||||||
NEXT_PUBLIC_SEPOLIA_GAS_ESTIMATED=100000
|
NEXT_PUBLIC_SEPOLIA_GAS_ESTIMATED=100000
|
||||||
NEXT_PUBLIC_SEPOLIA_DEFAULT_GAS_LIMIT_SURPLUS=6000
|
NEXT_PUBLIC_SEPOLIA_DEFAULT_GAS_LIMIT_SURPLUS=6000
|
||||||
NEXT_PUBLIC_SEPOLIA_PROFIT_MARGIN=2
|
NEXT_PUBLIC_SEPOLIA_PROFIT_MARGIN=2
|
||||||
SEPOLIA_TOKEN_LIST=https://raw.githubusercontent.com/Consensys/linea-token-list/main/json/linea-sepolia-token-shortlist.json
|
NEXT_PUBLIC_SEPOLIA_TOKEN_LIST=https://raw.githubusercontent.com/Consensys/linea-token-list/main/json/linea-sepolia-token-shortlist.json
|
||||||
|
|
||||||
NEXT_PUBLIC_WALLET_CONNECT_ID=<GITHUB_SECRET>
|
NEXT_PUBLIC_WALLET_CONNECT_ID=<GITHUB_SECRET>
|
||||||
NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID=<GITHUB_SECRET>
|
NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID=<GITHUB_SECRET>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ NEXT_PUBLIC_MAINNET_LINEA_USDC_BRIDGE=0xA2Ee6Fce4ACB62D95448729cDb781e3BEb62504A
|
|||||||
NEXT_PUBLIC_MAINNET_GAS_ESTIMATED=100000
|
NEXT_PUBLIC_MAINNET_GAS_ESTIMATED=100000
|
||||||
NEXT_PUBLIC_MAINNET_DEFAULT_GAS_LIMIT_SURPLUS=6000
|
NEXT_PUBLIC_MAINNET_DEFAULT_GAS_LIMIT_SURPLUS=6000
|
||||||
NEXT_PUBLIC_MAINNET_PROFIT_MARGIN=2
|
NEXT_PUBLIC_MAINNET_PROFIT_MARGIN=2
|
||||||
MAINNET_TOKEN_LIST=https://raw.githubusercontent.com/Consensys/linea-token-list/main/json/linea-mainnet-token-shortlist.json
|
NEXT_PUBLIC_MAINNET_TOKEN_LIST=https://raw.githubusercontent.com/Consensys/linea-token-list/main/json/linea-mainnet-token-shortlist.json
|
||||||
|
|
||||||
NEXT_PUBLIC_SEPOLIA_L1_TOKEN_BRIDGE=0x5A0a48389BB0f12E5e017116c1105da97E129142
|
NEXT_PUBLIC_SEPOLIA_L1_TOKEN_BRIDGE=0x5A0a48389BB0f12E5e017116c1105da97E129142
|
||||||
NEXT_PUBLIC_SEPOLIA_LINEA_TOKEN_BRIDGE=0x93DcAdf238932e6e6a85852caC89cBd71798F463
|
NEXT_PUBLIC_SEPOLIA_LINEA_TOKEN_BRIDGE=0x93DcAdf238932e6e6a85852caC89cBd71798F463
|
||||||
@@ -18,7 +18,7 @@ NEXT_PUBLIC_SEPOLIA_LINEA_USDC_BRIDGE=0x39fd5cF710314341d35f9Dca20c1daa059Acb843
|
|||||||
NEXT_PUBLIC_SEPOLIA_GAS_ESTIMATED=100000
|
NEXT_PUBLIC_SEPOLIA_GAS_ESTIMATED=100000
|
||||||
NEXT_PUBLIC_SEPOLIA_DEFAULT_GAS_LIMIT_SURPLUS=6000
|
NEXT_PUBLIC_SEPOLIA_DEFAULT_GAS_LIMIT_SURPLUS=6000
|
||||||
NEXT_PUBLIC_SEPOLIA_PROFIT_MARGIN=2
|
NEXT_PUBLIC_SEPOLIA_PROFIT_MARGIN=2
|
||||||
SEPOLIA_TOKEN_LIST=https://raw.githubusercontent.com/Consensys/linea-token-list/main/json/linea-sepolia-token-shortlist.json
|
NEXT_PUBLIC_SEPOLIA_TOKEN_LIST=https://raw.githubusercontent.com/Consensys/linea-token-list/main/json/linea-sepolia-token-shortlist.json
|
||||||
|
|
||||||
NEXT_PUBLIC_WALLET_CONNECT_ID=<YOUR__WALLET_CONNECT_ID>
|
NEXT_PUBLIC_WALLET_CONNECT_ID=<YOUR__WALLET_CONNECT_ID>
|
||||||
NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID=<YOUR_DYNAMIC_ENVIRONMENT_ID>
|
NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID=<YOUR_DYNAMIC_ENVIRONMENT_ID>
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ FROM base AS builder
|
|||||||
ARG NEXT_PUBLIC_WALLET_CONNECT_ID
|
ARG NEXT_PUBLIC_WALLET_CONNECT_ID
|
||||||
ARG NEXT_PUBLIC_INFURA_ID
|
ARG NEXT_PUBLIC_INFURA_ID
|
||||||
ARG NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID
|
ARG NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID
|
||||||
|
ARG NEXT_PUBLIC_LIFI_API_KEY
|
||||||
ENV NEXT_PUBLIC_WALLET_CONNECT_ID=$NEXT_PUBLIC_WALLET_CONNECT_ID
|
ENV NEXT_PUBLIC_WALLET_CONNECT_ID=$NEXT_PUBLIC_WALLET_CONNECT_ID
|
||||||
ENV NEXT_PUBLIC_INFURA_ID=$NEXT_PUBLIC_INFURA_ID
|
ENV NEXT_PUBLIC_INFURA_ID=$NEXT_PUBLIC_INFURA_ID
|
||||||
ENV NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID=$NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID
|
ENV NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID=$NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID
|
||||||
|
ENV NEXT_PUBLIC_LIFI_API_KEY=$NEXT_PUBLIC_LIFI_API_KEY
|
||||||
ARG ENV_FILE
|
ARG ENV_FILE
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|||||||
@@ -21,13 +21,14 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@consensys/linea-sdk": "0.3.0",
|
"@consensys/linea-sdk": "0.3.0",
|
||||||
"@dynamic-labs/ethereum": "4.6.3",
|
"@dynamic-labs/ethereum": "4.9.5",
|
||||||
"@dynamic-labs/sdk-react-core": "4.6.3",
|
"@dynamic-labs/sdk-react-core": "4.9.5",
|
||||||
"@dynamic-labs/wagmi-connector": "4.6.3",
|
"@dynamic-labs/wagmi-connector": "4.9.5",
|
||||||
"@headlessui/react": "2.1.9",
|
"@headlessui/react": "2.1.9",
|
||||||
"@tanstack/react-query": "5.62.16",
|
"@lifi/widget": "3.18.1",
|
||||||
"@wagmi/connectors": "5.1.15",
|
"@tanstack/react-query": "5.69.0",
|
||||||
"@wagmi/core": "2.16.3",
|
"@wagmi/connectors": "5.7.11",
|
||||||
|
"@wagmi/core": "2.16.7",
|
||||||
"auto-zustand-selectors-hook": "3.0.1",
|
"auto-zustand-selectors-hook": "3.0.1",
|
||||||
"clsx": "2.1.1",
|
"clsx": "2.1.1",
|
||||||
"date-fns": "4.1.0",
|
"date-fns": "4.1.0",
|
||||||
@@ -35,14 +36,14 @@
|
|||||||
"loglevel": "1.9.2",
|
"loglevel": "1.9.2",
|
||||||
"next": "14.2.24",
|
"next": "14.2.24",
|
||||||
"next-seo": "6.6.0",
|
"next-seo": "6.6.0",
|
||||||
"pino-pretty": "11.2.2",
|
"pino-pretty": "13.0.0",
|
||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
"react-dom": "18.3.1",
|
"react-dom": "18.3.1",
|
||||||
"react-icons": "5.3.0",
|
"react-icons": "5.5.0",
|
||||||
"sass": "1.83.3",
|
"sass": "1.86.0",
|
||||||
"sharp": "0.33.5",
|
"sharp": "0.33.5",
|
||||||
"viem": "2.22.4",
|
"viem": "2.23.13",
|
||||||
"wagmi": "2.14.6",
|
"wagmi": "2.14.15",
|
||||||
"zod": "3.24.2",
|
"zod": "3.24.2",
|
||||||
"zustand": "4.5.4"
|
"zustand": "4.5.4"
|
||||||
},
|
},
|
||||||
@@ -53,9 +54,9 @@
|
|||||||
"@types/fs-extra": "11.0.4",
|
"@types/fs-extra": "11.0.4",
|
||||||
"@types/react": "18.3.11",
|
"@types/react": "18.3.11",
|
||||||
"@types/react-dom": "18.3.0",
|
"@types/react-dom": "18.3.0",
|
||||||
"autoprefixer": "10.4.20",
|
"autoprefixer": "10.4.21",
|
||||||
"dotenv": "16.4.5",
|
"dotenv": "16.4.7",
|
||||||
"eslint-config-next": "14.2.15",
|
"eslint-config-next": "14.2.15",
|
||||||
"postcss": "8.4.47"
|
"postcss": "8.5.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
@@ -68,7 +68,12 @@ export default function FaqPage() {
|
|||||||
|
|
||||||
<ul className={styles["list"]}>
|
<ul className={styles["list"]}>
|
||||||
{faqList.map((faq, index) => (
|
{faqList.map((faq, index) => (
|
||||||
<FaqItem key={index} data={faq} isOpen={openIndex === index} onToggle={() => handleToggle(index)} />
|
<FaqItem
|
||||||
|
key={`faq-item-${index}`}
|
||||||
|
data={faq}
|
||||||
|
isOpen={openIndex === index}
|
||||||
|
onToggle={() => handleToggle(index)}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ const metadata: Metadata = {
|
|||||||
|
|
||||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<html lang="en" data-theme="v2">
|
<html lang="en" data-theme="v2" className={clsx(atypFont.variable, atypTextFont.variable)}>
|
||||||
<title>{metadata.title?.toString()}</title>
|
<title>{metadata.title?.toString()}</title>
|
||||||
<meta name="description" content={metadata.description?.toString()} key="desc" />
|
<meta name="description" content={metadata.description?.toString()} key="desc" />
|
||||||
|
|
||||||
<body className={clsx(atypFont.variable, atypTextFont.variable, atypFont.className, atypTextFont.className)}>
|
<body>
|
||||||
<noscript dangerouslySetInnerHTML={{ __html: gtmNoScript }} />
|
<noscript dangerouslySetInnerHTML={{ __html: gtmNoScript }} />
|
||||||
|
|
||||||
<Providers>
|
<Providers>
|
||||||
@@ -45,8 +45,8 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
|||||||
</svg>
|
</svg>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
<Script id="usabilla" dangerouslySetInnerHTML={{ __html: usabillaBeScript }} />
|
<Script id="usabilla" dangerouslySetInnerHTML={{ __html: usabillaBeScript }} strategy="lazyOnload" />
|
||||||
<Script id="gtm" dangerouslySetInnerHTML={{ __html: gtmScript }} />
|
<Script id="gtm" dangerouslySetInnerHTML={{ __html: gtmScript }} strategy="lazyOnload" />
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
10
bridge-ui/src/app/native-bridge/page.module.scss
Normal file
10
bridge-ui/src/app/native-bridge/page.module.scss
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
.content-wrapper {
|
||||||
|
max-width: 29.25rem;
|
||||||
|
margin: 0 auto 3.75rem;
|
||||||
|
|
||||||
|
width: calc(100% - 3rem);
|
||||||
|
|
||||||
|
@include bp("tablet") {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
21
bridge-ui/src/app/native-bridge/page.tsx
Normal file
21
bridge-ui/src/app/native-bridge/page.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import InternalNav from "@/components/internal-nav";
|
||||||
|
import BridgeLayout from "@/components/bridge/bridge-layout";
|
||||||
|
import styles from "./page.module.scss";
|
||||||
|
import TopBanner from "@/components/top-banner";
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<TopBanner
|
||||||
|
text="Bridging USDC (USDC.e) is temporarily disabled until March 26. Learn more in our announcement here."
|
||||||
|
href="https://x.com/LineaBuild/status/1901347758230958528"
|
||||||
|
/>
|
||||||
|
<section className={styles["content-wrapper"]}>
|
||||||
|
<InternalNav />
|
||||||
|
<BridgeLayout />
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,21 +1,14 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import InternalNav from "@/components/internal-nav";
|
import InternalNav from "@/components/internal-nav";
|
||||||
import BridgeLayout from "@/components/bridge/bridge-layout";
|
|
||||||
import styles from "./page.module.scss";
|
import styles from "./page.module.scss";
|
||||||
import TopBanner from "@/components/top-banner";
|
import { Widget } from "@/components/lifi/widget";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
<>
|
<section className={styles["content-wrapper"]}>
|
||||||
<TopBanner
|
<InternalNav />
|
||||||
text="Bridging USDC (USDC.e) is temporarily disabled until March 26. Learn more in our announcement here."
|
<Widget />
|
||||||
href="https://x.com/LineaBuild/status/1901347758230958528"
|
</section>
|
||||||
/>
|
|
||||||
<section className={styles["content-wrapper"]}>
|
|
||||||
<InternalNav />
|
|
||||||
<BridgeLayout />
|
|
||||||
</section>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,81 +3,26 @@ import localFont from "next/font/local";
|
|||||||
const atypFont = localFont({
|
const atypFont = localFont({
|
||||||
display: "swap",
|
display: "swap",
|
||||||
src: [
|
src: [
|
||||||
{
|
|
||||||
path: "../../../public/fonts/AtypDisplay-Light-subset.woff2",
|
|
||||||
weight: "300",
|
|
||||||
style: "normal",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "../../../public/fonts/AtypDisplay-Light.woff2",
|
|
||||||
weight: "300",
|
|
||||||
style: "normal",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "../../../public/fonts/AtypDisplay-LightItalic.woff2",
|
|
||||||
weight: "300",
|
|
||||||
style: "italic",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "../../../public/fonts/AtypDisplay-Regular-subset.woff2",
|
path: "../../../public/fonts/AtypDisplay-Regular-subset.woff2",
|
||||||
weight: "400",
|
weight: "400",
|
||||||
style: "normal",
|
style: "normal",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "../../../public/fonts/AtypDisplay-Regular.woff2",
|
|
||||||
weight: "400",
|
|
||||||
style: "normal",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "../../../public/fonts/AtypDisplay-Italic.woff2",
|
|
||||||
weight: "400",
|
|
||||||
style: "italic",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "../../../public/fonts/AtypDisplay-Medium-subset.woff2",
|
path: "../../../public/fonts/AtypDisplay-Medium-subset.woff2",
|
||||||
weight: "500",
|
weight: "500",
|
||||||
style: "normal",
|
style: "normal",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "../../../public/fonts/AtypDisplay-Medium.woff2",
|
|
||||||
weight: "500",
|
|
||||||
style: "normal",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "../../../public/fonts/AtypDisplay-MediumItalic.woff2",
|
|
||||||
weight: "500",
|
|
||||||
style: "italic",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "../../../public/fonts/AtypDisplay-Semibold-subset.woff2",
|
path: "../../../public/fonts/AtypDisplay-Semibold-subset.woff2",
|
||||||
weight: "600",
|
weight: "600",
|
||||||
style: "normal",
|
style: "normal",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "../../../public/fonts/AtypDisplay-Semibold.woff2",
|
|
||||||
weight: "600",
|
|
||||||
style: "normal",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "../../../public/fonts/AtypDisplay-SemiboldItalic.woff2",
|
|
||||||
weight: "600",
|
|
||||||
style: "italic",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "../../../public/fonts/AtypDisplay-Bold-subset.woff2",
|
path: "../../../public/fonts/AtypDisplay-Bold-subset.woff2",
|
||||||
weight: "700",
|
weight: "700",
|
||||||
style: "normal",
|
style: "normal",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "../../../public/fonts/AtypDisplay-Bold.woff2",
|
|
||||||
weight: "700",
|
|
||||||
style: "normal",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "../../../public/fonts/AtypDisplay-BoldItalic.woff2",
|
|
||||||
weight: "700",
|
|
||||||
style: "italic",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
variable: "--font-atyp",
|
variable: "--font-atyp",
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,86 +3,26 @@ import localFont from "next/font/local";
|
|||||||
const atypTextFont = localFont({
|
const atypTextFont = localFont({
|
||||||
display: "swap",
|
display: "swap",
|
||||||
src: [
|
src: [
|
||||||
{
|
|
||||||
path: "../../../public/fonts/AtypText-Light-subset.woff2",
|
|
||||||
weight: "300",
|
|
||||||
style: "normal",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "../../../public/fonts/AtypText-LightItalic.woff2",
|
|
||||||
weight: "300",
|
|
||||||
style: "italic",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "../../../public/fonts/AtypText-Light.woff2",
|
|
||||||
weight: "300",
|
|
||||||
style: "normal",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "../../../public/fonts/AtypText-Regular-subset.woff2",
|
path: "../../../public/fonts/AtypText-Regular-subset.woff2",
|
||||||
weight: "400",
|
weight: "400",
|
||||||
style: "normal",
|
style: "normal",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "../../../public/fonts/AtypText-Regular.woff2",
|
|
||||||
weight: "400",
|
|
||||||
style: "normal",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "../../../public/fonts/AtypText-Italic-subset.woff2",
|
|
||||||
weight: "400",
|
|
||||||
style: "italic",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "../../../public/fonts/AtypText-Italic.woff2",
|
|
||||||
weight: "400",
|
|
||||||
style: "italic",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "../../../public/fonts/AtypText-Medium-subset.woff2",
|
path: "../../../public/fonts/AtypText-Medium-subset.woff2",
|
||||||
weight: "500",
|
weight: "500",
|
||||||
style: "normal",
|
style: "normal",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "../../../public/fonts/AtypText-Medium.woff2",
|
|
||||||
weight: "500",
|
|
||||||
style: "normal",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "../../../public/fonts/AtypText-MediumItalic.woff2",
|
|
||||||
weight: "500",
|
|
||||||
style: "italic",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "../../../public/fonts/AtypText-Semibold-subset.woff2",
|
path: "../../../public/fonts/AtypText-Semibold-subset.woff2",
|
||||||
weight: "600",
|
weight: "600",
|
||||||
style: "normal",
|
style: "normal",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "../../../public/fonts/AtypText-Semibold.woff2",
|
|
||||||
weight: "600",
|
|
||||||
style: "normal",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "../../../public/fonts/AtypText-SemiboldItalic.woff2",
|
|
||||||
weight: "600",
|
|
||||||
style: "italic",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "../../../public/fonts/AtypText-Bold-subset.woff2",
|
path: "../../../public/fonts/AtypText-Bold-subset.woff2",
|
||||||
weight: "700",
|
weight: "700",
|
||||||
style: "normal",
|
style: "normal",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "../../../public/fonts/AtypText-Bold.woff2",
|
|
||||||
weight: "700",
|
|
||||||
style: "normal",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "../../../public/fonts/AtypText-BoldItalic.woff2",
|
|
||||||
weight: "700",
|
|
||||||
style: "italic",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
variable: "--font-atyp-text",
|
variable: "--font-atyp-text",
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,20 +1,17 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useDynamicContext, useIsLoggedIn } from "@/lib/dynamic";
|
import { useDynamicContext } from "@/lib/dynamic";
|
||||||
import { useAccount } from "wagmi";
|
import { useAccount } from "wagmi";
|
||||||
import Bridge from "../form";
|
import Bridge from "../form";
|
||||||
import TransactionHistory from "../transaction-history";
|
import TransactionHistory from "../transaction-history";
|
||||||
import { supportedChainIds } from "@/lib/wagmi";
|
|
||||||
import { useTokens } from "@/hooks";
|
import { useTokens } from "@/hooks";
|
||||||
import { useChainStore, FormStoreProvider, FormState, useNativeBridgeNavigationStore } from "@/stores";
|
import { useChainStore, FormStoreProvider, FormState, useNativeBridgeNavigationStore } from "@/stores";
|
||||||
import { ChainLayer } from "@/types";
|
import { ChainLayer } from "@/types";
|
||||||
import WrongNetwork from "../wrong-network";
|
|
||||||
import BridgeSkeleton from "./skeleton";
|
import BridgeSkeleton from "./skeleton";
|
||||||
|
|
||||||
export default function BridgeLayout() {
|
export default function BridgeLayout() {
|
||||||
const isTransactionHistoryOpen = useNativeBridgeNavigationStore.useIsTransactionHistoryOpen();
|
const isTransactionHistoryOpen = useNativeBridgeNavigationStore.useIsTransactionHistoryOpen();
|
||||||
const { chain, address } = useAccount();
|
const { address } = useAccount();
|
||||||
const isLoggedIn = useIsLoggedIn();
|
|
||||||
const { sdkHasLoaded } = useDynamicContext();
|
const { sdkHasLoaded } = useDynamicContext();
|
||||||
const tokens = useTokens();
|
const tokens = useTokens();
|
||||||
const fromChain = useChainStore.useFromChain();
|
const fromChain = useChainStore.useFromChain();
|
||||||
@@ -23,10 +20,6 @@ export default function BridgeLayout() {
|
|||||||
return <BridgeSkeleton />;
|
return <BridgeSkeleton />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLoggedIn && (!chain?.id || !supportedChainIds.includes(chain.id))) {
|
|
||||||
return <WrongNetwork />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isTransactionHistoryOpen) {
|
if (isTransactionHistoryOpen) {
|
||||||
return <TransactionHistory />;
|
return <TransactionHistory />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
column-gap: 0.5rem;
|
column-gap: 0.5rem;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
cursor: pointer;
|
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,12 +12,12 @@ export default function BridgeMode() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<button type="button" className={styles.button}>
|
<div className={styles.button}>
|
||||||
<div className={styles["selected-label"]}>
|
<div className={styles["selected-label"]}>
|
||||||
<Image src={logoSrc} width={16} height={16} alt="{label}" />
|
<Image src={logoSrc} width={16} height={16} alt={label} />
|
||||||
<span>{label}</span>
|
<span>{label}</span>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export default function CurrencyDropdown({ disabled }: Props) {
|
|||||||
{isOpen && (
|
{isOpen && (
|
||||||
<div ref={dropdownRef} className={styles.dropdown}>
|
<div ref={dropdownRef} className={styles.dropdown}>
|
||||||
{supportedCurrencies.map((option) => (
|
{supportedCurrencies.map((option) => (
|
||||||
<div key={option.value} onClick={() => handleSelect(option)} className={styles.option}>
|
<div key={`currency-${option.value}`} onClick={() => handleSelect(option)} className={styles.option}>
|
||||||
<span className={styles.flag}>{option.flag}</span>
|
<span className={styles.flag}>{option.flag}</span>
|
||||||
{option.label}
|
{option.label}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export function DestinationAddress() {
|
|||||||
<div className={styles["destination-address"]}>
|
<div className={styles["destination-address"]}>
|
||||||
<div className={styles["headline"]}>
|
<div className={styles["headline"]}>
|
||||||
<p className={styles.title}>Send to wallet</p>
|
<p className={styles.title}>Send to wallet</p>
|
||||||
{address !== inputValue && !error && (
|
{address !== inputValue && !error && isAddress(inputValue) && (
|
||||||
<Link
|
<Link
|
||||||
href={`${toChain.blockExplorers?.default.url ?? ""}/address/${inputValue}`}
|
href={`${toChain.blockExplorers?.default.url ?? ""}/address/${inputValue}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import Modal from "@/components/modal";
|
import Modal from "@/components/modal";
|
||||||
|
|
||||||
import styles from "./manual-claim.module.scss";
|
import styles from "./manual-claim.module.scss";
|
||||||
import Link from "next/link";
|
|
||||||
import Button from "@/components/ui/button";
|
import Button from "@/components/ui/button";
|
||||||
|
import { useNativeBridgeNavigationStore } from "@/stores";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isModalOpen: boolean;
|
isModalOpen: boolean;
|
||||||
@@ -10,12 +10,24 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function ManualClaim({ isModalOpen, onCloseModal }: Props) {
|
export default function ManualClaim({ isModalOpen, onCloseModal }: Props) {
|
||||||
|
const setIsTransactionHistoryOpen = useNativeBridgeNavigationStore.useSetIsTransactionHistoryOpen();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal title="Manual claim on destination" isOpen={isModalOpen} onClose={onCloseModal}>
|
<Modal title="Manual claim on destination" isOpen={isModalOpen} onClose={onCloseModal}>
|
||||||
<div className={styles["modal-inner"]}>
|
<div className={styles["modal-inner"]}>
|
||||||
<p className={styles["text"]}>
|
<p className={styles["text"]}>
|
||||||
You will need to claim your transaction on the destination chain with an additional transaction that requires
|
You will need to claim your transaction on the destination chain with an additional transaction that requires
|
||||||
ETH on the destination chain. This can be done on the <Link href="/transactions">Transaction page</Link>.
|
ETH on the destination chain. This can be done on the{" "}
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
onClick={() => {
|
||||||
|
setIsTransactionHistoryOpen(true);
|
||||||
|
onCloseModal();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Transaction page
|
||||||
|
</Button>
|
||||||
|
.
|
||||||
</p>
|
</p>
|
||||||
<Button fullWidth onClick={onCloseModal}>
|
<Button fullWidth onClick={onCloseModal}>
|
||||||
OK
|
OK
|
||||||
|
|||||||
@@ -11,7 +11,8 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
|
|
||||||
a {
|
button {
|
||||||
|
font-size: 1rem;
|
||||||
color: var(--v2-color-indigo);
|
color: var(--v2-color-indigo);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export default function SelectNetwork({ isModalOpen, onCloseModal, onClick, netw
|
|||||||
filteredNetworks.map((network, index: number) => {
|
filteredNetworks.map((network, index: number) => {
|
||||||
return (
|
return (
|
||||||
<NetworkDetails
|
<NetworkDetails
|
||||||
key={index}
|
key={`select-network-${index}`}
|
||||||
name={network.name}
|
name={network.name}
|
||||||
onClickNetwork={() => {
|
onClickNetwork={() => {
|
||||||
onClick(network);
|
onClick(network);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export default function ListTransaction({ transactions }: Props) {
|
|||||||
<>
|
<>
|
||||||
<ul className={styles["list"]}>
|
<ul className={styles["list"]}>
|
||||||
{transactions.map((item, index) => (
|
{transactions.map((item, index) => (
|
||||||
<Transaction key={`${item.bridgingTx}-${index}`} onClick={handleClickTransaction} {...item} />
|
<Transaction key={`transaction-${item.bridgingTx}-${index}`} onClick={handleClickTransaction} {...item} />
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
<TransactionDetails
|
<TransactionDetails
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import styles from "./skeleton-loader.module.scss";
|
|||||||
export default function SkeletonLoader() {
|
export default function SkeletonLoader() {
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
{Array.from({ length: 3 }).map((_, i) => (
|
{Array.from({ length: 3 }).map((_, index) => (
|
||||||
<div key={i} className={styles.group}>
|
<div key={`skeleton-item-${index}`} className={styles.group}>
|
||||||
<div className={styles["skeleton-item"]}>
|
<div className={styles["skeleton-item"]}>
|
||||||
{Array.from({ length: 2 }).map((_, i) => (
|
{Array.from({ length: 2 }).map((_, i) => (
|
||||||
<div key={i} className={styles["skeleton-group"]}>
|
<div key={`skeleton-group-${i}`} className={styles["skeleton-group"]}>
|
||||||
<div className={clsx(styles.skeleton, "pulsating")} />
|
<div className={clsx(styles.skeleton, "pulsating")} />
|
||||||
<div className={clsx(styles.skeleton, "pulsating")} />
|
<div className={clsx(styles.skeleton, "pulsating")} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -244,6 +244,22 @@ export const MENUS = [
|
|||||||
url: "https://linea.build/assets",
|
url: "https://linea.build/assets",
|
||||||
external: true,
|
external: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
__id: "01wv6FiyW8WmBwVSiELs0L",
|
||||||
|
__typename: "link",
|
||||||
|
name: "Privacy Policy",
|
||||||
|
label: "Privacy Policy",
|
||||||
|
url: "https://consensys.io/privacy-notice",
|
||||||
|
external: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
__id: "01wv6FiyW8WmBwVSiELs0L",
|
||||||
|
__typename: "link",
|
||||||
|
name: "Terms of service",
|
||||||
|
label: "Terms of service",
|
||||||
|
url: "https://linea.build/terms-of-service",
|
||||||
|
external: true,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
submenusRight: {
|
submenusRight: {
|
||||||
__id: "SQOTqw3aK6kNo3oUGGh7W",
|
__id: "SQOTqw3aK6kNo3oUGGh7W",
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export const DesktopNavigation = ({ menus, theme = Theme.default }: Props) => {
|
|||||||
<nav className={styles["nav-wrapper"]}>
|
<nav className={styles["nav-wrapper"]}>
|
||||||
<ul className={`${styles.navigation} ${styles[theme]}`}>
|
<ul className={`${styles.navigation} ${styles[theme]}`}>
|
||||||
{menus.map((menu, index) => (
|
{menus.map((menu, index) => (
|
||||||
<MenuItem key={index} menu={filterMobileOnly(menu)} />
|
<MenuItem key={`menu-item-${index}`} menu={filterMobileOnly(menu)} />
|
||||||
))}
|
))}
|
||||||
<li>
|
<li>
|
||||||
<HeaderConnect />
|
<HeaderConnect />
|
||||||
@@ -87,8 +87,8 @@ function MenuItem({ menu }: MenuItemProps) {
|
|||||||
|
|
||||||
{menu.submenusLeft && (
|
{menu.submenusLeft && (
|
||||||
<ul className={styles.submenu}>
|
<ul className={styles.submenu}>
|
||||||
{menu.submenusLeft.map((submenu, key) => (
|
{menu.submenusLeft.map((submenu, index) => (
|
||||||
<li className={styles.submenuItem} key={key}>
|
<li className={styles.submenuItem} key={`${menu.name}-submenuleft-{${index}`}>
|
||||||
<Link href={submenu.url as string} target={submenu.external ? "_blank" : "_self"}>
|
<Link href={submenu.url as string} target={submenu.external ? "_blank" : "_self"}>
|
||||||
{submenu.label}
|
{submenu.label}
|
||||||
{submenu.external && (
|
{submenu.external && (
|
||||||
@@ -102,7 +102,7 @@ function MenuItem({ menu }: MenuItemProps) {
|
|||||||
{menu.submenusRight && (
|
{menu.submenusRight && (
|
||||||
<ul className={styles.right}>
|
<ul className={styles.right}>
|
||||||
{menu.submenusRight?.submenusLeft?.map((submenu, subIndex) => (
|
{menu.submenusRight?.submenusLeft?.map((submenu, subIndex) => (
|
||||||
<li className={styles.submenuItem} key={subIndex}>
|
<li className={styles.submenuItem} key={`${menu.name}-submenuright-submenuleft-${subIndex}`}>
|
||||||
<Link
|
<Link
|
||||||
href={submenu.url as string}
|
href={submenu.url as string}
|
||||||
target={submenu.external ? "_blank" : "_self"}
|
target={submenu.external ? "_blank" : "_self"}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ export const MobileNavigation = ({ menus, theme = "default" }: Props) => {
|
|||||||
{menus.map((menu, index) => (
|
{menus.map((menu, index) => (
|
||||||
<li
|
<li
|
||||||
className={clsx(styles.menuItem, { [styles.active]: activeMenu === index })}
|
className={clsx(styles.menuItem, { [styles.active]: activeMenu === index })}
|
||||||
key={index}
|
key={`mobile-menu-item-${index}`}
|
||||||
onClick={() => handleToggleMenu(menu, index)}
|
onClick={() => handleToggleMenu(menu, index)}
|
||||||
>
|
>
|
||||||
{menu.url ? (
|
{menu.url ? (
|
||||||
@@ -107,7 +107,7 @@ export const MobileNavigation = ({ menus, theme = "default" }: Props) => {
|
|||||||
{menu.submenusLeft.map((submenu, subIndex) => (
|
{menu.submenusLeft.map((submenu, subIndex) => (
|
||||||
<li
|
<li
|
||||||
className={styles.submenuItem}
|
className={styles.submenuItem}
|
||||||
key={subIndex}
|
key={`mobile-submenuleft-item-${subIndex}`}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
handleToggleMenu(submenu, -1);
|
handleToggleMenu(submenu, -1);
|
||||||
@@ -128,7 +128,10 @@ export const MobileNavigation = ({ menus, theme = "default" }: Props) => {
|
|||||||
{menu.submenusRight && (
|
{menu.submenusRight && (
|
||||||
<ul className={`${styles.submenu} ${styles.right}`}>
|
<ul className={`${styles.submenu} ${styles.right}`}>
|
||||||
{menu.submenusRight?.submenusLeft?.map((submenu, subIndex) => (
|
{menu.submenusRight?.submenusLeft?.map((submenu, subIndex) => (
|
||||||
<li className={styles.submenuItem} key={subIndex}>
|
<li
|
||||||
|
className={styles.submenuItem}
|
||||||
|
key={`mobile-submenuright-submenuleft-item-${subIndex}`}
|
||||||
|
>
|
||||||
<Link
|
<Link
|
||||||
href={submenu.url as string}
|
href={submenu.url as string}
|
||||||
target={submenu.external ? "_blank" : "_self"}
|
target={submenu.external ? "_blank" : "_self"}
|
||||||
|
|||||||
@@ -6,9 +6,13 @@ import NavItem from "./item";
|
|||||||
|
|
||||||
const NavData = [
|
const NavData = [
|
||||||
{
|
{
|
||||||
title: "Native Bridge",
|
title: "All Bridges",
|
||||||
href: "/",
|
href: "/",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Native Bridge",
|
||||||
|
href: "/native-bridge",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function InternalNav() {
|
export default function InternalNav() {
|
||||||
@@ -18,7 +22,7 @@ export default function InternalNav() {
|
|||||||
<div className={styles["wrapper"]}>
|
<div className={styles["wrapper"]}>
|
||||||
<div className={styles["list-nav"]}>
|
<div className={styles["list-nav"]}>
|
||||||
{NavData.map((item, index) => (
|
{NavData.map((item, index) => (
|
||||||
<NavItem key={index} {...item} active={pathnane === item.href} />
|
<NavItem key={`internal-nav-item-${index}`} {...item} active={pathnane === item.href} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -45,8 +45,9 @@ export function Layout({ children }: { children: React.ReactNode }) {
|
|||||||
src={"/images/illustration/illustration-mobile.svg"}
|
src={"/images/illustration/illustration-mobile.svg"}
|
||||||
role="presentation"
|
role="presentation"
|
||||||
alt="illustration mobile"
|
alt="illustration mobile"
|
||||||
width={428}
|
width={0}
|
||||||
height={359}
|
height={0}
|
||||||
|
style={{ width: "100%", height: "auto", objectFit: "cover" }}
|
||||||
priority
|
priority
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -69,6 +70,7 @@ export function Layout({ children }: { children: React.ReactNode }) {
|
|||||||
alt="illustration left"
|
alt="illustration left"
|
||||||
width={300}
|
width={300}
|
||||||
height={445}
|
height={445}
|
||||||
|
priority
|
||||||
/>
|
/>
|
||||||
<Image
|
<Image
|
||||||
className="right-illustration"
|
className="right-illustration"
|
||||||
@@ -77,14 +79,17 @@ export function Layout({ children }: { children: React.ReactNode }) {
|
|||||||
alt="illustration right"
|
alt="illustration right"
|
||||||
width={610}
|
width={610}
|
||||||
height={842}
|
height={842}
|
||||||
|
priority
|
||||||
/>
|
/>
|
||||||
<Image
|
<Image
|
||||||
className={clsx("mobile-illustration", { hidden: pathname === "/faq" })}
|
className={clsx("mobile-illustration", { hidden: pathname === "/faq" })}
|
||||||
src={"/images/illustration/illustration-mobile.svg"}
|
src={"/images/illustration/illustration-mobile.svg"}
|
||||||
role="presentation"
|
role="presentation"
|
||||||
alt="illustration mobile"
|
alt="illustration mobile"
|
||||||
width={428}
|
width={0}
|
||||||
height={359}
|
height={0}
|
||||||
|
style={{ width: "100%", height: "auto", objectFit: "cover" }}
|
||||||
|
priority
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Web3Provider } from "@/contexts/web3.context";
|
import { Web3Provider } from "@/contexts/web3.context";
|
||||||
|
import { QueryProvider } from "@/contexts/query.context";
|
||||||
import { TokenStoreProvider } from "@/stores";
|
import { TokenStoreProvider } from "@/stores";
|
||||||
import { getTokenConfig } from "@/services/tokenService";
|
import { getTokenConfig } from "@/services/tokenService";
|
||||||
|
|
||||||
@@ -16,8 +17,10 @@ export async function Providers({ children }: ProvidersProps) {
|
|||||||
const tokensStoreInitialState = await getTokenStoreInitialState();
|
const tokensStoreInitialState = await getTokenStoreInitialState();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Web3Provider>
|
<QueryProvider>
|
||||||
<TokenStoreProvider initialState={tokensStoreInitialState}>{children}</TokenStoreProvider>
|
<Web3Provider>
|
||||||
</Web3Provider>
|
<TokenStoreProvider initialState={tokensStoreInitialState}>{children}</TokenStoreProvider>
|
||||||
|
</Web3Provider>
|
||||||
|
</QueryProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
11
bridge-ui/src/components/lifi/client-only/index.tsx
Normal file
11
bridge-ui/src/components/lifi/client-only/index.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { type PropsWithChildren } from "react";
|
||||||
|
import { useHydrated } from "@/hooks";
|
||||||
|
|
||||||
|
interface ClientOnlyProps extends PropsWithChildren {
|
||||||
|
fallback?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ClientOnly({ children, fallback = null }: ClientOnlyProps) {
|
||||||
|
const hydrated = useHydrated();
|
||||||
|
return hydrated ? children : fallback;
|
||||||
|
}
|
||||||
171
bridge-ui/src/components/lifi/widget/index.tsx
Normal file
171
bridge-ui/src/components/lifi/widget/index.tsx
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useDynamicContext } from "@dynamic-labs/sdk-react-core";
|
||||||
|
import { ChainId, LiFiWidget, WidgetSkeleton, type WidgetConfig } from "@/lib/lifi";
|
||||||
|
import { ClientOnly } from "../client-only";
|
||||||
|
import atypTextFont from "@/assets/fonts/atypText";
|
||||||
|
import { CHAINS_RPC_URLS } from "@/constants";
|
||||||
|
import { config } from "@/config";
|
||||||
|
|
||||||
|
const widgetConfig: Partial<WidgetConfig> = {
|
||||||
|
variant: "compact",
|
||||||
|
subvariant: "default",
|
||||||
|
appearance: "light",
|
||||||
|
integrator: "Linea",
|
||||||
|
theme: {
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: "#6119ef",
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: "#6119ef",
|
||||||
|
},
|
||||||
|
background: {
|
||||||
|
default: "#ffffff",
|
||||||
|
paper: "#f8f7f2",
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
primary: "#121212",
|
||||||
|
secondary: "#525252",
|
||||||
|
},
|
||||||
|
grey: {
|
||||||
|
200: "#f5f5f5",
|
||||||
|
300: "#f1f1f1",
|
||||||
|
700: "#525252",
|
||||||
|
800: "#222222",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
shape: {
|
||||||
|
borderRadius: 10,
|
||||||
|
borderRadiusSecondary: 30,
|
||||||
|
borderRadiusTertiary: 24,
|
||||||
|
},
|
||||||
|
typography: {
|
||||||
|
fontFamily: atypTextFont.style.fontFamily,
|
||||||
|
body1: {
|
||||||
|
fontSize: "0.875rem",
|
||||||
|
},
|
||||||
|
body2: {
|
||||||
|
fontSize: "0.875rem",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
borderRadius: "0.625rem",
|
||||||
|
maxHeight: "80vh",
|
||||||
|
maxWidth: "29.25rem",
|
||||||
|
minWidth: "none",
|
||||||
|
fontSize: "0.875rem",
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
MuiButton: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
fontSize: "0.875rem",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiIconButton: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
fontSize: "0.875rem",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiAppBar: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
display: "flex",
|
||||||
|
fontSize: "0.875rem",
|
||||||
|
justifyContent: "flex-end",
|
||||||
|
["p"]: {
|
||||||
|
visibility: "hidden",
|
||||||
|
fontSize: "0.875rem",
|
||||||
|
},
|
||||||
|
["p:before"]: {
|
||||||
|
content: '""',
|
||||||
|
visibility: "visible",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiCard: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
fontSize: "0.875rem",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultProps: {
|
||||||
|
variant: "elevation",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiInputCard: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
hiddenUI: ["appearance", "language"],
|
||||||
|
sdkConfig: {
|
||||||
|
rpcUrls: {
|
||||||
|
[ChainId.ETH]: [CHAINS_RPC_URLS[ChainId.ETH]],
|
||||||
|
[ChainId.LNA]: [CHAINS_RPC_URLS[ChainId.LNA]],
|
||||||
|
[ChainId.ARB]: [CHAINS_RPC_URLS[ChainId.ARB]],
|
||||||
|
[ChainId.AVA]: [CHAINS_RPC_URLS[ChainId.AVA]],
|
||||||
|
[ChainId.BAS]: [CHAINS_RPC_URLS[ChainId.BAS]],
|
||||||
|
[ChainId.BLS]: [CHAINS_RPC_URLS[ChainId.BLS]],
|
||||||
|
[ChainId.BSC]: [CHAINS_RPC_URLS[ChainId.BSC]],
|
||||||
|
[ChainId.CEL]: [CHAINS_RPC_URLS[ChainId.CEL]],
|
||||||
|
[ChainId.MNT]: [CHAINS_RPC_URLS[ChainId.MNT]],
|
||||||
|
[ChainId.OPT]: [CHAINS_RPC_URLS[ChainId.OPT]],
|
||||||
|
[ChainId.POL]: [CHAINS_RPC_URLS[ChainId.POL]],
|
||||||
|
[ChainId.SCL]: [CHAINS_RPC_URLS[ChainId.SCL]],
|
||||||
|
[ChainId.ERA]: [CHAINS_RPC_URLS[ChainId.ERA]],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
chains: {
|
||||||
|
deny: [
|
||||||
|
ChainId.BTC,
|
||||||
|
ChainId.SOL,
|
||||||
|
ChainId.PZE,
|
||||||
|
ChainId.MOR,
|
||||||
|
ChainId.FUS,
|
||||||
|
ChainId.BOB,
|
||||||
|
ChainId.MAM,
|
||||||
|
ChainId.LSK,
|
||||||
|
ChainId.UNI,
|
||||||
|
ChainId.IMX,
|
||||||
|
ChainId.GRA,
|
||||||
|
ChainId.TAI,
|
||||||
|
ChainId.SOE,
|
||||||
|
ChainId.FRA,
|
||||||
|
ChainId.ABS,
|
||||||
|
ChainId.RSK,
|
||||||
|
ChainId.WCC,
|
||||||
|
ChainId.BER,
|
||||||
|
ChainId.KAI,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
bridges: {
|
||||||
|
allow: ["stargateV2", "stargateV2Bus", "across", "hop", "squid", "relay"],
|
||||||
|
},
|
||||||
|
apiKey: config.lifiApiKey,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function Widget() {
|
||||||
|
const { setShowAuthFlow } = useDynamicContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<ClientOnly fallback={<WidgetSkeleton config={widgetConfig} />}>
|
||||||
|
<LiFiWidget
|
||||||
|
config={{
|
||||||
|
...widgetConfig,
|
||||||
|
walletConfig: {
|
||||||
|
onConnect() {
|
||||||
|
setShowAuthFlow(true);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
integrator="linea"
|
||||||
|
/>
|
||||||
|
</ClientOnly>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import UnionIcon from "@/assets/icons/union.svg";
|
import UnionIcon from "@/assets/icons/union.svg";
|
||||||
import LeftIllustration from "./illustration/left.svg";
|
|
||||||
import RightIllustration from "./illustration/right.svg";
|
|
||||||
import CloseIcon from "@/assets/icons/x-circle.svg";
|
import CloseIcon from "@/assets/icons/x-circle.svg";
|
||||||
import styles from "./top-banner.module.scss";
|
import styles from "./top-banner.module.scss";
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
text: string;
|
text: string;
|
||||||
@@ -22,7 +21,16 @@ export default function TopBanner({ text, href }: Props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles["banner-wrapper"]}>
|
<div className={styles["banner-wrapper"]}>
|
||||||
<LeftIllustration className={styles["left-illustration"]} />
|
<Image
|
||||||
|
className={styles["left-illustration"]}
|
||||||
|
src={"/images/illustration/banner/left.svg"}
|
||||||
|
role="presentation"
|
||||||
|
alt="banner illustration left"
|
||||||
|
width={0}
|
||||||
|
height={0}
|
||||||
|
style={{ width: "56px", height: "100%" }}
|
||||||
|
priority
|
||||||
|
/>
|
||||||
<div className={styles["banner"]}>
|
<div className={styles["banner"]}>
|
||||||
<Link href={href} target="_blank" rel="noopener noreferrer" className={styles["inner"]} passHref>
|
<Link href={href} target="_blank" rel="noopener noreferrer" className={styles["inner"]} passHref>
|
||||||
<span>{text}</span>
|
<span>{text}</span>
|
||||||
@@ -30,7 +38,16 @@ export default function TopBanner({ text, href }: Props) {
|
|||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<CloseIcon onClick={handleClose} className={styles["close-icon"]} />
|
<CloseIcon onClick={handleClose} className={styles["close-icon"]} />
|
||||||
<RightIllustration className={styles["right-illustration"]} />
|
<Image
|
||||||
|
className={styles["right-illustration"]}
|
||||||
|
src={"/images/illustration/banner/right.svg"}
|
||||||
|
role="presentation"
|
||||||
|
alt="banner illustration right"
|
||||||
|
width={0}
|
||||||
|
height={0}
|
||||||
|
style={{ width: "221px", height: "100%" }}
|
||||||
|
priority
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,12 +23,19 @@ const chainConfigSchema = z.object({
|
|||||||
export const configSchema = z
|
export const configSchema = z
|
||||||
.object({
|
.object({
|
||||||
chains: z.record(z.string().regex(/^\d+$/), chainConfigSchema),
|
chains: z.record(z.string().regex(/^\d+$/), chainConfigSchema),
|
||||||
walletConnectId: z.string(),
|
walletConnectId: z.string().nonempty(),
|
||||||
storage: z.object({
|
storage: z.object({
|
||||||
minVersion: z.number().positive().int(),
|
minVersion: z.number().positive().int(),
|
||||||
}),
|
}),
|
||||||
// Feature toggle for CCTPV2 for USDC transfers
|
// Feature toggle for CCTPV2 for USDC transfers
|
||||||
isCctpEnabled: z.boolean(),
|
isCctpEnabled: z.boolean(),
|
||||||
|
infuraApiKey: z.string().nonempty(),
|
||||||
|
dynamicEnvironmentId: z.string().nonempty(),
|
||||||
|
lifiApiKey: z.string().nonempty(),
|
||||||
|
tokenListUrls: z.object({
|
||||||
|
mainnet: z.string().trim().url(),
|
||||||
|
sepolia: z.string().trim().url(),
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
.strict();
|
.strict();
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,13 @@ export const config: Config = {
|
|||||||
minVersion: process.env.NEXT_PUBLIC_STORAGE_MIN_VERSION ? parseInt(process.env.NEXT_PUBLIC_STORAGE_MIN_VERSION) : 1,
|
minVersion: process.env.NEXT_PUBLIC_STORAGE_MIN_VERSION ? parseInt(process.env.NEXT_PUBLIC_STORAGE_MIN_VERSION) : 1,
|
||||||
},
|
},
|
||||||
isCctpEnabled: process.env.NEXT_PUBLIC_IS_CCTP_ENABLED === "true",
|
isCctpEnabled: process.env.NEXT_PUBLIC_IS_CCTP_ENABLED === "true",
|
||||||
|
infuraApiKey: process.env.NEXT_PUBLIC_INFURA_ID ?? "",
|
||||||
|
dynamicEnvironmentId: process.env.NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID ?? "",
|
||||||
|
lifiApiKey: process.env.NEXT_PUBLIC_LIFI_API_KEY ?? "",
|
||||||
|
tokenListUrls: {
|
||||||
|
mainnet: process.env.NEXT_PUBLIC_MAINNET_TOKEN_LIST ?? "",
|
||||||
|
sepolia: process.env.NEXT_PUBLIC_SEPOLIA_TOKEN_LIST ?? "",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function getConfiguration(): Promise<Config> {
|
export async function getConfiguration(): Promise<Config> {
|
||||||
|
|||||||
8
bridge-ui/src/constants/cctp.ts
Normal file
8
bridge-ui/src/constants/cctp.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// Current fast transfer route fee for mainnet
|
||||||
|
export const CCTP_TRANSFER_MAX_FEE_FALLBACK = 100n;
|
||||||
|
// 1000 Fast transfer, 2000 Standard transfer
|
||||||
|
export const CCTP_MIN_FINALITY_THRESHOLD = 1000;
|
||||||
|
// https://developers.circle.com/stablecoins/message-format, add 2 for '0x' prefix
|
||||||
|
export const CCTP_V2_MESSAGE_HEADER_LENGTH = 298;
|
||||||
|
export const CCTP_V2_EXPIRATION_BLOCK_OFFSET = 2 + 344 * 2;
|
||||||
|
export const CCTP_V2_EXPIRATION_BLOCK_LENGTH = 64;
|
||||||
85
bridge-ui/src/constants/chains.ts
Normal file
85
bridge-ui/src/constants/chains.ts
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import { config } from "@/config";
|
||||||
|
import {
|
||||||
|
arbitrum,
|
||||||
|
aurora,
|
||||||
|
avalanche,
|
||||||
|
base,
|
||||||
|
blast,
|
||||||
|
bsc,
|
||||||
|
celo,
|
||||||
|
cronos,
|
||||||
|
fantom,
|
||||||
|
gnosis,
|
||||||
|
ink,
|
||||||
|
linea,
|
||||||
|
lineaSepolia,
|
||||||
|
mainnet,
|
||||||
|
mantle,
|
||||||
|
mode,
|
||||||
|
moonbeam,
|
||||||
|
optimism,
|
||||||
|
polygon,
|
||||||
|
scroll,
|
||||||
|
sei,
|
||||||
|
sepolia,
|
||||||
|
sonic,
|
||||||
|
zksync,
|
||||||
|
} from "viem/chains";
|
||||||
|
|
||||||
|
export const CHAINS = [
|
||||||
|
mainnet,
|
||||||
|
sepolia,
|
||||||
|
linea,
|
||||||
|
lineaSepolia,
|
||||||
|
arbitrum,
|
||||||
|
aurora,
|
||||||
|
avalanche,
|
||||||
|
base,
|
||||||
|
blast,
|
||||||
|
bsc,
|
||||||
|
celo,
|
||||||
|
cronos,
|
||||||
|
fantom,
|
||||||
|
gnosis,
|
||||||
|
ink,
|
||||||
|
mantle,
|
||||||
|
mode,
|
||||||
|
moonbeam,
|
||||||
|
optimism,
|
||||||
|
polygon,
|
||||||
|
scroll,
|
||||||
|
sei,
|
||||||
|
sonic,
|
||||||
|
zksync,
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export const CHAINS_IDS = CHAINS.map((chain) => chain.id);
|
||||||
|
|
||||||
|
export const CHAINS_RPC_URLS: Record<(typeof CHAINS_IDS)[number], string> = {
|
||||||
|
[mainnet.id]: `https://mainnet.infura.io/v3/${config.infuraApiKey}`,
|
||||||
|
[sepolia.id]: `https://sepolia.infura.io/v3/${config.infuraApiKey}`,
|
||||||
|
[linea.id]: `https://linea-mainnet.infura.io/v3/${config.infuraApiKey}`,
|
||||||
|
[lineaSepolia.id]: `https://linea-sepolia.infura.io/v3/${config.infuraApiKey}`,
|
||||||
|
[arbitrum.id]: `https://arbitrum-mainnet.infura.io/v3/${config.infuraApiKey}`,
|
||||||
|
[aurora.id]: `https://mainnet.aurora.dev`,
|
||||||
|
[avalanche.id]: `https://avalanche-mainnet.infura.io/v3/${config.infuraApiKey}`,
|
||||||
|
[base.id]: `https://base-mainnet.infura.io/v3/${config.infuraApiKey}`,
|
||||||
|
[blast.id]: `https://blast-mainnet.infura.io/v3/${config.infuraApiKey}`,
|
||||||
|
[bsc.id]: `https://bsc-mainnet.infura.io/v3/${config.infuraApiKey}`,
|
||||||
|
[celo.id]: `https://celo-mainnet.infura.io/v3/${config.infuraApiKey}`,
|
||||||
|
[cronos.id]: `https://evm.cronos.org`,
|
||||||
|
[fantom.id]: `https://rpc.ankr.com/fantom`,
|
||||||
|
[gnosis.id]: `https://rpc.gnosischain.com`,
|
||||||
|
[ink.id]: `https://rpc-gel.inkonchain.com`,
|
||||||
|
[mantle.id]: `https://mantle-mainnet.infura.io/v3/${config.infuraApiKey}`,
|
||||||
|
[mode.id]: `https://mainnet.mode.network`,
|
||||||
|
[moonbeam.id]: `https://rpc.testnet.moonbeam.network`,
|
||||||
|
[optimism.id]: `https://optimism-mainnet.infura.io/v3/${config.infuraApiKey}`,
|
||||||
|
[polygon.id]: `https://polygon-mainnet.infura.io/v3/${config.infuraApiKey}`,
|
||||||
|
[scroll.id]: `https://scroll-mainnet.infura.io/v3/${config.infuraApiKey}`,
|
||||||
|
[sei.id]: `https://evm-rpc.sei-apis.com`,
|
||||||
|
[sonic.id]: `https://rpc.soniclabs.com`,
|
||||||
|
[zksync.id]: `https://zksync-mainnet.infura.io/v3/${config.infuraApiKey}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const NATIVE_BRIDGE_SUPPORTED_CHAIN_IDS = [mainnet.id, linea.id, lineaSepolia.id, sepolia.id] as const;
|
||||||
4
bridge-ui/src/constants/index.ts
Normal file
4
bridge-ui/src/constants/index.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export * from "./chains";
|
||||||
|
export * from "./tokens";
|
||||||
|
export * from "./message";
|
||||||
|
export * from "./cctp";
|
||||||
1
bridge-ui/src/constants/message.ts
Normal file
1
bridge-ui/src/constants/message.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const INBOX_L1L2_MESSAGE_STATUS_MAPPING_SLOT = 176n;
|
||||||
1
bridge-ui/src/constants/tokens.ts
Normal file
1
bridge-ui/src/constants/tokens.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const USDC_SYMBOL = "USDC";
|
||||||
14
bridge-ui/src/contexts/query.context.tsx
Normal file
14
bridge-ui/src/contexts/query.context.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { ReactNode } from "react";
|
||||||
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
type Web3ProviderProps = {
|
||||||
|
children: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
|
export function QueryProvider({ children }: Web3ProviderProps) {
|
||||||
|
return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>;
|
||||||
|
}
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { DynamicWagmiConnector, EthereumWalletConnectors, DynamicContextProvider } from "@/lib/dynamic";
|
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
import { config } from "@/lib/wagmi";
|
|
||||||
import { WagmiProvider } from "wagmi";
|
import { WagmiProvider } from "wagmi";
|
||||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
import { DynamicWagmiConnector, EthereumWalletConnectors, DynamicContextProvider } from "@/lib/dynamic";
|
||||||
|
import { config as wagmiConfig } from "@/lib/wagmi";
|
||||||
const queryClient = new QueryClient();
|
import { config } from "@/config";
|
||||||
|
|
||||||
type Web3ProviderProps = {
|
type Web3ProviderProps = {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@@ -65,17 +63,15 @@ export function Web3Provider({ children }: Web3ProviderProps) {
|
|||||||
return (
|
return (
|
||||||
<DynamicContextProvider
|
<DynamicContextProvider
|
||||||
settings={{
|
settings={{
|
||||||
environmentId: process.env.NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID!,
|
environmentId: config.dynamicEnvironmentId,
|
||||||
walletConnectors: [EthereumWalletConnectors],
|
walletConnectors: [EthereumWalletConnectors],
|
||||||
mobileExperience: "redirect",
|
mobileExperience: "redirect",
|
||||||
appName: "Linea Bridge",
|
appName: "Linea Bridge",
|
||||||
cssOverrides,
|
cssOverrides,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<WagmiProvider config={config}>
|
<WagmiProvider config={wagmiConfig}>
|
||||||
<QueryClientProvider client={queryClient}>
|
<DynamicWagmiConnector>{children}</DynamicWagmiConnector>
|
||||||
<DynamicWagmiConnector>{children}</DynamicWagmiConnector>
|
|
||||||
</QueryClientProvider>
|
|
||||||
</WagmiProvider>
|
</WagmiProvider>
|
||||||
</DynamicContextProvider>
|
</DynamicContextProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useEstimateFeesPerGas, useWatchBlockNumber } from "wagmi";
|
import { useEstimateFeesPerGas, useWatchBlockNumber } from "wagmi";
|
||||||
import { SupportedChainId } from "@/lib/wagmi";
|
import { SupportedChainIds } from "@/types";
|
||||||
|
|
||||||
const useFeeData = (chainId: SupportedChainId) => {
|
const useFeeData = (chainId: SupportedChainIds) => {
|
||||||
const { data, refetch } = useEstimateFeesPerGas({ chainId, type: "eip1559" });
|
const { data, refetch } = useEstimateFeesPerGas({ chainId, type: "eip1559" });
|
||||||
|
|
||||||
useWatchBlockNumber({
|
useWatchBlockNumber({
|
||||||
|
|||||||
@@ -16,3 +16,4 @@ export { default as useTokenBalance } from "./useTokenBalance";
|
|||||||
export { default as useTokenPrices } from "./useTokenPrices";
|
export { default as useTokenPrices } from "./useTokenPrices";
|
||||||
export { default as useTokens } from "./useTokens";
|
export { default as useTokens } from "./useTokens";
|
||||||
export { default as useTransactionHistory } from "./useTransactionHistory";
|
export { default as useTransactionHistory } from "./useTransactionHistory";
|
||||||
|
export { default as useHydrated } from "./useHydrated";
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { useChainStore } from "@/stores";
|
import { useChainStore } from "@/stores";
|
||||||
import { getCctpFee } from "@/services/cctp";
|
import { getCctpFee } from "@/services/cctp";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { CCTP_TRANSFER_MAX_FEE_FALLBACK } from "@/utils/cctp";
|
import { CCTP_TRANSFER_MAX_FEE_FALLBACK } from "@/constants";
|
||||||
|
|
||||||
const useCctpSrcDomain = () => {
|
const useCctpSrcDomain = () => {
|
||||||
const fromChain = useChainStore.useFromChain();
|
const fromChain = useChainStore.useFromChain();
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { encodeFunctionData, padHex, zeroHash } from "viem";
|
|||||||
import { useFormStore, useChainStore } from "@/stores";
|
import { useFormStore, useChainStore } from "@/stores";
|
||||||
import { isCctp } from "@/utils/tokens";
|
import { isCctp } from "@/utils/tokens";
|
||||||
import { useCctpFee, useCctpDestinationDomain } from "./useCctpUtilHooks";
|
import { useCctpFee, useCctpDestinationDomain } from "./useCctpUtilHooks";
|
||||||
import { CCTP_MIN_FINALITY_THRESHOLD } from "@/utils";
|
import { CCTP_MIN_FINALITY_THRESHOLD } from "@/constants";
|
||||||
|
|
||||||
type UseDepositForBurnTxArgs = {
|
type UseDepositForBurnTxArgs = {
|
||||||
allowance?: bigint;
|
allowance?: bigint;
|
||||||
|
|||||||
15
bridge-ui/src/hooks/useHydrated.ts
Normal file
15
bridge-ui/src/hooks/useHydrated.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { useSyncExternalStore } from "react";
|
||||||
|
|
||||||
|
function subscribe() {
|
||||||
|
return () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const useHydrated = () => {
|
||||||
|
return useSyncExternalStore(
|
||||||
|
subscribe,
|
||||||
|
() => true,
|
||||||
|
() => false,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useHydrated;
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { LineaSDK, Network } from "@consensys/linea-sdk";
|
import { LineaSDK, Network } from "@consensys/linea-sdk";
|
||||||
|
import { linea, lineaSepolia, mainnet, sepolia } from "viem/chains";
|
||||||
import { L1MessageServiceContract, L2MessageServiceContract } from "@consensys/linea-sdk/dist/lib/contracts";
|
import { L1MessageServiceContract, L2MessageServiceContract } from "@consensys/linea-sdk/dist/lib/contracts";
|
||||||
import { useChainStore } from "@/stores";
|
import { useChainStore } from "@/stores";
|
||||||
|
import { CHAINS_RPC_URLS } from "@/constants";
|
||||||
|
|
||||||
export interface LineaSDKContracts {
|
export interface LineaSDKContracts {
|
||||||
L1: L1MessageServiceContract;
|
L1: L1MessageServiceContract;
|
||||||
@@ -15,11 +17,11 @@ const useLineaSDK = () => {
|
|||||||
let l1RpcUrl;
|
let l1RpcUrl;
|
||||||
let l2RpcUrl;
|
let l2RpcUrl;
|
||||||
if (fromChain.testnet) {
|
if (fromChain.testnet) {
|
||||||
l1RpcUrl = `https://sepolia.infura.io/v3/${process.env.NEXT_PUBLIC_INFURA_ID}`;
|
l1RpcUrl = CHAINS_RPC_URLS[sepolia.id];
|
||||||
l2RpcUrl = `https://linea-sepolia.infura.io/v3/${process.env.NEXT_PUBLIC_INFURA_ID}`;
|
l2RpcUrl = CHAINS_RPC_URLS[lineaSepolia.id];
|
||||||
} else {
|
} else {
|
||||||
l1RpcUrl = `https://mainnet.infura.io/v3/${process.env.NEXT_PUBLIC_INFURA_ID}`;
|
l1RpcUrl = CHAINS_RPC_URLS[mainnet.id];
|
||||||
l2RpcUrl = `https://linea-mainnet.infura.io/v3/${process.env.NEXT_PUBLIC_INFURA_ID}`;
|
l2RpcUrl = CHAINS_RPC_URLS[linea.id];
|
||||||
}
|
}
|
||||||
|
|
||||||
const sdk = new LineaSDK({
|
const sdk = new LineaSDK({
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useMemo } from "react";
|
|||||||
import { useChainStore, useTokenStore } from "@/stores";
|
import { useChainStore, useTokenStore } from "@/stores";
|
||||||
import { ChainLayer, Token } from "@/types";
|
import { ChainLayer, Token } from "@/types";
|
||||||
import { config } from "@/config";
|
import { config } from "@/config";
|
||||||
import { USDC_SYMBOL } from "@/utils";
|
import { USDC_SYMBOL } from "@/constants";
|
||||||
|
|
||||||
const useTokens = (): Token[] => {
|
const useTokens = (): Token[] => {
|
||||||
const tokensList = useTokenStore((state) => state.tokensList);
|
const tokensList = useTokenStore((state) => state.tokensList);
|
||||||
|
|||||||
3
bridge-ui/src/lib/lifi.ts
Normal file
3
bridge-ui/src/lib/lifi.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
export * from "@lifi/widget";
|
||||||
@@ -1,22 +1,26 @@
|
|||||||
import { http, createConfig } from "wagmi";
|
import { http, createConfig } from "wagmi";
|
||||||
import { linea, lineaSepolia, mainnet, sepolia } from "wagmi/chains";
|
import { CHAINS, CHAINS_IDS, CHAINS_RPC_URLS } from "@/constants";
|
||||||
|
|
||||||
export const chains = [mainnet, linea, lineaSepolia, sepolia] as const;
|
|
||||||
export const supportedChainIds = [mainnet.id, linea.id, lineaSepolia.id, sepolia.id] as const;
|
|
||||||
|
|
||||||
export type SupportedChainId = (typeof supportedChainIds)[number];
|
|
||||||
|
|
||||||
export const config = createConfig({
|
export const config = createConfig({
|
||||||
chains,
|
chains: CHAINS,
|
||||||
multiInjectedProviderDiscovery: false,
|
multiInjectedProviderDiscovery: false,
|
||||||
transports: {
|
transports: generateWagmiTransports(CHAINS_IDS),
|
||||||
[mainnet.id]: http(`https://mainnet.infura.io/v3/${process.env.NEXT_PUBLIC_INFURA_ID}`, { batch: true }),
|
|
||||||
[sepolia.id]: http(`https://sepolia.infura.io/v3/${process.env.NEXT_PUBLIC_INFURA_ID}`, { batch: true }),
|
|
||||||
[linea.id]: http(`https://linea-mainnet.infura.io/v3/${process.env.NEXT_PUBLIC_INFURA_ID}`, { batch: true }),
|
|
||||||
[lineaSepolia.id]: http(`https://linea-sepolia.infura.io/v3/${process.env.NEXT_PUBLIC_INFURA_ID}`, { batch: true }),
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function generateWagmiTransports(chainIds: (typeof CHAINS_IDS)[number][]) {
|
||||||
|
return chainIds.reduce(
|
||||||
|
(acc, chainId) => {
|
||||||
|
acc[chainId] = generateWagmiTransport(chainId);
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Record<(typeof chainIds)[number], ReturnType<typeof generateWagmiTransport>>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateWagmiTransport(chainId: (typeof CHAINS_IDS)[number]) {
|
||||||
|
return http(CHAINS_RPC_URLS[chainId], { batch: true });
|
||||||
|
}
|
||||||
|
|
||||||
declare module "wagmi" {
|
declare module "wagmi" {
|
||||||
interface Register {
|
interface Register {
|
||||||
config: typeof config;
|
config: typeof config;
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ enum NetworkTypes {
|
|||||||
export async function getTokens(networkTypes: NetworkTypes): Promise<GithubTokenListToken[]> {
|
export async function getTokens(networkTypes: NetworkTypes): Promise<GithubTokenListToken[]> {
|
||||||
try {
|
try {
|
||||||
// Fetch the JSON data from the URL.
|
// Fetch the JSON data from the URL.
|
||||||
let url = process.env.MAINNET_TOKEN_LIST ? (process.env.MAINNET_TOKEN_LIST as string) : "";
|
let url = config.tokenListUrls.mainnet;
|
||||||
if (networkTypes === NetworkTypes.SEPOLIA) {
|
if (networkTypes === NetworkTypes.SEPOLIA) {
|
||||||
url = process.env.SEPOLIA_TOKEN_LIST ? (process.env.SEPOLIA_TOKEN_LIST as string) : "";
|
url = config.tokenListUrls.sepolia;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import { SupportedChainId } from "@/lib/wagmi";
|
import { NATIVE_BRIDGE_SUPPORTED_CHAIN_IDS } from "@/constants";
|
||||||
import { Address } from "viem";
|
import { Address } from "viem";
|
||||||
|
|
||||||
|
export type SupportedChainIds = (typeof NATIVE_BRIDGE_SUPPORTED_CHAIN_IDS)[number];
|
||||||
|
|
||||||
export enum ChainLayer {
|
export enum ChainLayer {
|
||||||
L1 = "L1",
|
L1 = "L1",
|
||||||
L2 = "L2",
|
L2 = "L2",
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Chain = {
|
export type Chain = {
|
||||||
id: SupportedChainId;
|
id: SupportedChainIds;
|
||||||
name: string;
|
name: string;
|
||||||
iconPath: string;
|
iconPath: string;
|
||||||
nativeCurrency: { name: string; symbol: string; decimals: number };
|
nativeCurrency: { name: string; symbol: string; decimals: number };
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export { type LinkBlock, type AssetType, Theme } from "./ui";
|
export { type LinkBlock, type AssetType, Theme } from "./ui";
|
||||||
export { type Chain, ChainLayer } from "./chain";
|
export { type Chain, ChainLayer, type SupportedChainIds } from "./chain";
|
||||||
export { type TransactionType, TransactionStatus } from "./transaction";
|
export { type TransactionType, TransactionStatus } from "./transaction";
|
||||||
export { type Token, type GithubTokenListToken, type NetworkTokens } from "./token";
|
export { type Token, type GithubTokenListToken, type NetworkTokens } from "./token";
|
||||||
export { BridgeProvider } from "./providers";
|
export { BridgeProvider } from "./providers";
|
||||||
|
|||||||
@@ -4,15 +4,11 @@ import { GetPublicClientReturnType } from "@wagmi/core";
|
|||||||
import { fetchCctpAttestationByTxHash, reattestCctpV2PreFinalityMessage } from "@/services/cctp";
|
import { fetchCctpAttestationByTxHash, reattestCctpV2PreFinalityMessage } from "@/services/cctp";
|
||||||
import { getPublicClient } from "@wagmi/core";
|
import { getPublicClient } from "@wagmi/core";
|
||||||
import { config as wagmiConfig } from "@/lib/wagmi";
|
import { config as wagmiConfig } from "@/lib/wagmi";
|
||||||
|
import {
|
||||||
// Current fast transfer route fee for mainnet
|
CCTP_V2_EXPIRATION_BLOCK_LENGTH,
|
||||||
export const CCTP_TRANSFER_MAX_FEE_FALLBACK = 100n;
|
CCTP_V2_EXPIRATION_BLOCK_OFFSET,
|
||||||
// 1000 Fast transfer, 2000 Standard transfer
|
CCTP_V2_MESSAGE_HEADER_LENGTH,
|
||||||
export const CCTP_MIN_FINALITY_THRESHOLD = 1000;
|
} from "@/constants";
|
||||||
// https://developers.circle.com/stablecoins/message-format, add 2 for '0x' prefix
|
|
||||||
const CCTP_V2_MESSAGE_HEADER_LENGTH = 298;
|
|
||||||
const CCTP_V2_EXPIRATION_BLOCK_OFFSET = 2 + 344 * 2;
|
|
||||||
const CCTP_V2_EXPIRATION_BLOCK_LENGTH = 64;
|
|
||||||
|
|
||||||
const isCctpNonceUsed = async (
|
const isCctpNonceUsed = async (
|
||||||
client: GetPublicClientReturnType,
|
client: GetPublicClientReturnType,
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
import { Address } from "viem";
|
import { Address } from "viem";
|
||||||
import { linea, mainnet, Chain as ViemChain, sepolia, lineaSepolia } from "viem/chains";
|
import { linea, mainnet, Chain as ViemChain, sepolia, lineaSepolia } from "viem/chains";
|
||||||
import { SupportedChainId } from "@/lib/wagmi";
|
|
||||||
import { config } from "@/config";
|
import { config } from "@/config";
|
||||||
import { Chain, ChainLayer } from "@/types";
|
import { Chain, ChainLayer, SupportedChainIds } from "@/types";
|
||||||
|
|
||||||
export const generateChain = (chain: ViemChain): Chain => {
|
export const generateChain = (chain: ViemChain): Chain => {
|
||||||
return {
|
return {
|
||||||
id: chain.id as SupportedChainId,
|
id: chain.id as SupportedChainIds,
|
||||||
name: chain.id !== lineaSepolia.id ? chain.name : "Linea Sepolia",
|
name: chain.id !== lineaSepolia.id ? chain.name : "Linea Sepolia",
|
||||||
iconPath: config.chains[chain.id].iconPath,
|
iconPath: config.chains[chain.id].iconPath,
|
||||||
nativeCurrency: chain.nativeCurrency,
|
nativeCurrency: chain.nativeCurrency,
|
||||||
blockExplorers: chain.blockExplorers,
|
blockExplorers: chain.blockExplorers,
|
||||||
// Possibly the wrong assumption to fallback to 'false', but fallback to 'true' makes the app crash mysteriously
|
|
||||||
testnet: Boolean(chain.testnet),
|
testnet: Boolean(chain.testnet),
|
||||||
layer: getChainNetworkLayer(chain.id),
|
layer: getChainNetworkLayer(chain.id),
|
||||||
messageServiceAddress: config.chains[chain.id].messageServiceAddress as Address,
|
messageServiceAddress: config.chains[chain.id].messageServiceAddress as Address,
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import { BridgeTransaction } from "@/types";
|
import { BridgeTransaction, SupportedChainIds } from "@/types";
|
||||||
import { SupportedChainId } from "@/lib/wagmi";
|
|
||||||
|
|
||||||
export function getCompleteTxStoreKeyForTx(transaction: BridgeTransaction): string {
|
export function getCompleteTxStoreKeyForTx(transaction: BridgeTransaction): string {
|
||||||
return getCompleteTxStoreKey(transaction.fromChain.id, transaction.bridgingTx);
|
return getCompleteTxStoreKey(transaction.fromChain.id, transaction.bridgingTx);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCompleteTxStoreKey(fromChainId: SupportedChainId, bridgingTxHash: string): string {
|
export function getCompleteTxStoreKey(fromChainId: SupportedChainIds, bridgingTxHash: string): string {
|
||||||
return `${fromChainId}-${bridgingTxHash}`;
|
return `${fromChainId}-${bridgingTxHash}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,6 @@ export { estimateEthGasFee, estimateERC20GasFee } from "./fees";
|
|||||||
export { formatAddress, formatBalance, formatHex, formatTimestamp, safeGetAddress } from "./format";
|
export { formatAddress, formatBalance, formatHex, formatTimestamp, safeGetAddress } from "./format";
|
||||||
export { fetchTransactionsHistory } from "./history";
|
export { fetchTransactionsHistory } from "./history";
|
||||||
export { computeMessageHash, computeMessageStorageSlot, isCctpV2BridgeMessage, isNativeBridgeMessage } from "./message";
|
export { computeMessageHash, computeMessageStorageSlot, isCctpV2BridgeMessage, isNativeBridgeMessage } from "./message";
|
||||||
export { isEth, isCctp, USDC_SYMBOL } from "./tokens";
|
export { isEth, isCctp } from "./tokens";
|
||||||
export { isEmptyObject } from "./utils";
|
export { isEmptyObject } from "./utils";
|
||||||
export {
|
export { getCctpTransactionStatus, getCctpMessageByTxHash } from "./cctp";
|
||||||
CCTP_TRANSFER_MAX_FEE_FALLBACK,
|
|
||||||
CCTP_MIN_FINALITY_THRESHOLD,
|
|
||||||
getCctpTransactionStatus,
|
|
||||||
getCctpMessageByTxHash,
|
|
||||||
} from "./cctp";
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { keccak256, encodeAbiParameters, Address } from "viem";
|
import { keccak256, encodeAbiParameters, Address } from "viem";
|
||||||
import { CctpV2BridgeMessage, NativeBridgeMessage } from "@/types";
|
import { CctpV2BridgeMessage, NativeBridgeMessage } from "@/types";
|
||||||
|
import { INBOX_L1L2_MESSAGE_STATUS_MAPPING_SLOT } from "@/constants";
|
||||||
const INBOX_L1L2_MESSAGE_STATUS_MAPPING_SLOT = 176n;
|
|
||||||
|
|
||||||
export function computeMessageHash(
|
export function computeMessageHash(
|
||||||
from: Address,
|
from: Address,
|
||||||
|
|||||||
@@ -19,5 +19,3 @@ export const isCctp = (token: Token) => {
|
|||||||
isAddress(token.L2)
|
isAddress(token.L2)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const USDC_SYMBOL = "USDC";
|
|
||||||
|
|||||||
3556
pnpm-lock.yaml
generated
3556
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user