Feat/bridge UI v2 (#776)

* feat: linea bridge ui refresh (#597)

* feat: linea bridge ui refresh

* feat: improve ui components

* chore: restore layout.tsx

* chore: update destination address modal

* chore: update bridge ui by feedback

chore: update bridge ui by feedback

* chore: add skeleton loader

* chore: update header nav

* feat: update new ui

* chore: fix skeleton

* fix: build issue

---------

Signed-off-by: Victorien Gauch <85494462+VGau@users.noreply.github.com>
Co-authored-by: Victorien Gauch <85494462+VGau@users.noreply.github.com>
Co-authored-by: VGau <victorien.gauch@consensys.net>

* feat: add dynamic

* fix: update lock file

* fix: change font size and all rem values

* feat: add currency state, amount, prices management

* feat: add fees calculation

* fix: remove react hook form and create a form store

* feat: add bridging feature

* fix: bridging issue

* fix: limit number of decimals for gas fees

* feat: add history

* fix: remove old code + remove dependencies

* fix: clean folders

* fix: manual claiming issue

* fix: remove warnings and optimize rerenders

* fix: adjust ui + fix network switching

* fix: update bridge ui dockerfile and github ci

* fix: update button text, input styling and history styling

* fix: add cctp deposit feature + cctp util functions and hooks

* feature toggle done for getTokenConfig (#780)

* fix: fees calculation issue (#781)

* [Feat] Bridge UI v2 - Transaction List with CCTPV2 transactions (#783)

* much of fetchCCTPBridgeEvents

* add getClaimTx

---------

Signed-off-by: kyzooghost <73516204+kyzooghost@users.noreply.github.com>

* fix: token history issue

* fix: history issue for erc20 + add confirm address modal

* fix: claiming issue

* feat: add usdc banner

* fix: update usdc banner message

* fix: update usdc banner message

* fix: update usdc banner message

* fix: update bridge ui e2e tests ci

* fix: update min storage version

---------

Signed-off-by: Victorien Gauch <85494462+VGau@users.noreply.github.com>
Signed-off-by: kyzooghost <73516204+kyzooghost@users.noreply.github.com>
Co-authored-by: viphan007 <106945122+viphan007@users.noreply.github.com>
Co-authored-by: kyzooghost <73516204+kyzooghost@users.noreply.github.com>
This commit is contained in:
Victorien Gauch
2025-03-18 11:58:12 +01:00
committed by GitHub
parent aa405ebbda
commit 8f75f73db2
336 changed files with 12135 additions and 8073 deletions

View File

@@ -38,6 +38,7 @@ jobs:
env:
NEXT_PUBLIC_WALLET_CONNECT_ID: ${{ secrets.PUBLIC_WALLET_CONNECT_ID }}
NEXT_PUBLIC_INFURA_ID: ${{ secrets.PUBLIC_BRIDGE_UI_INFURA_ID }}
NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID: ${{ secrets.PUBLIC_DYNAMIC_ENVIRONMENT_ID }}
- name: Install linux dependencies
run: |

View File

@@ -61,11 +61,13 @@ jobs:
ENV_FILE=./bridge-ui/.env.production
NEXT_PUBLIC_WALLET_CONNECT_ID=${{ secrets.PUBLIC_WALLET_CONNECT_ID }}
NEXT_PUBLIC_INFURA_ID=${{ secrets.PUBLIC_BRIDGE_UI_INFURA_ID }}
NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID=${{ secrets.PUBLIC_DYNAMIC_ENVIRONMENT_ID }}
cache-from: type=registry,ref=consensys/linea-bridge-ui:buildcache
cache-to: type=registry,ref=consensys/linea-bridge-ui:buildcache,mode=max
env:
NEXT_PUBLIC_WALLET_CONNECT_ID: ${{ secrets.PUBLIC_WALLET_CONNECT_ID }}
NEXT_PUBLIC_INFURA_ID: ${{ secrets.PUBLIC_BRIDGE_UI_INFURA_ID }}
NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID: ${{ secrets.PUBLIC_DYNAMIC_ENVIRONMENT_ID }}
test-build:
if: github.event.pull_request.head.repo.fork == true

View File

@@ -7,7 +7,6 @@
!next.config.js
!tsconfig.json
!postcss.config.js
!tailwind.config.js
!.next
!.env.production
!.env

View File

@@ -21,9 +21,9 @@ 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_WALLET_CONNECT_ID=<GITHUB_SECRET>
NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID=<GITHUB_SECRET>
NEXT_PUBLIC_INFURA_ID=<GITHUB_SECRET>
E2E_TEST_PRIVATE_KEY=<GITHUB_SECRET>
NEXT_PUBLIC_STORAGE_MIN_VERSION="3"
NEXT_PUBLIC_STORAGE_MIN_VERSION="4"

View File

@@ -21,12 +21,12 @@ 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_WALLET_CONNECT_ID=<YOUR__WALLET_CONNECT_ID>
NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID=<YOUR_DYNAMIC_ENVIRONMENT_ID>
NEXT_PUBLIC_INFURA_ID=<YOUR_INFURA_ID>
E2E_TEST_PRIVATE_KEY=<YOUR_PRIVATE_KEY>
NEXT_PUBLIC_STORAGE_MIN_VERSION="3"
NEXT_PUBLIC_STORAGE_MIN_VERSION="4"
E2E_TEST_SEED_PHRASE="test test test test test test test test test test test junk"
E2E_TEST_WALLET_PASSWORD="TestPassword!"
E2E_TEST_WALLET_PASSWORD="TestPassword!"

View File

@@ -1,19 +1,12 @@
/* eslint-env node */
module.exports = {
plugins: ["react", "@typescript-eslint"],
extends: [
"../.eslintrc.js",
"next",
"next/core-web-vitals",
"plugin:@typescript-eslint/recommended",
"plugin:tailwindcss/recommended",
],
extends: ["../.eslintrc.js", "next", "next/core-web-vitals", "plugin:@typescript-eslint/recommended"],
rules: {
"@typescript-eslint/no-unused-vars": "warn",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-duplicate-imports": "off",
"@typescript-eslint/no-var-requires": "off",
"react/react-in-jsx-scope": "off",
"tailwindcss/no-custom-classname": "off",
},
};

View File

@@ -8,8 +8,10 @@ FROM base AS builder
ARG NEXT_PUBLIC_WALLET_CONNECT_ID
ARG NEXT_PUBLIC_INFURA_ID
ARG NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID
ENV NEXT_PUBLIC_WALLET_CONNECT_ID=$NEXT_PUBLIC_WALLET_CONNECT_ID
ENV NEXT_PUBLIC_INFURA_ID=$NEXT_PUBLIC_INFURA_ID
ENV NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID=$NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID
ARG ENV_FILE
WORKDIR /app

View File

@@ -95,13 +95,13 @@ cp .env.template .env
2. Install packages:
```shell
npm i
pnpm i
```
3. Start the development server, execute:
```shell
npm run dev
pnpm dev
```
Frontend should be available at: http://localhost:3000

View File

@@ -6,26 +6,13 @@ const nextConfig = {
remotePatterns: [
{
protocol: "https",
hostname: "s2.coinmarketcap.com",
pathname: "/static/img/coins/64x64/**",
},
{
protocol: "https",
hostname: "assets.coingecko.com",
pathname: "/coins/images/**",
},
{
protocol: "https",
hostname: "coin-images.coingecko.com",
pathname: "/coins/images/**",
},
{
protocol: "https",
hostname: "storage.googleapis.com",
pathname: "/public.withstable.com/logos/**",
hostname: "**",
},
],
},
sassOptions: {
prependData: `@use 'sass:math'; @import 'src/scss/breakpoints';`,
},
webpack: (config) => {
const warning = [...(config.ignoreWarnings || []), { module: /typeorm/ }];

View File

@@ -21,48 +21,41 @@
},
"dependencies": {
"@consensys/linea-sdk": "0.3.0",
"@dynamic-labs/ethereum": "4.6.3",
"@dynamic-labs/sdk-react-core": "4.6.3",
"@dynamic-labs/wagmi-connector": "4.6.3",
"@headlessui/react": "2.1.9",
"@tanstack/react-query": "5.62.16",
"@wagmi/connectors": "5.1.15",
"@wagmi/core": "2.16.3",
"@reown/appkit": "1.6.3",
"@reown/appkit-adapter-wagmi": "1.6.3",
"clsx": "^2.1.1",
"compare-versions": "6.1.1",
"auto-zustand-selectors-hook": "3.0.1",
"clsx": "2.1.1",
"date-fns": "4.1.0",
"framer-motion": "11.11.4",
"joi": "17.13.3",
"loglevel": "1.9.2",
"next": "14.2.15",
"next": "14.2.24",
"next-seo": "6.6.0",
"pino-pretty": "11.2.2",
"react": "18.3.1",
"react-device-detect": "2.2.3",
"react-dom": "18.3.1",
"react-hook-form": "7.53.0",
"react-icons": "5.3.0",
"react-toastify": "10.0.5",
"sass": "1.83.3",
"sharp": "0.33.5",
"swiper": "11.1.14",
"tailwind-merge": "^2.5.3",
"viem": "2.22.4",
"wagmi": "2.14.6",
"zod": "3.24.2",
"zustand": "4.5.4"
},
"devDependencies": {
"@playwright/test": "1.45.3",
"@svgr/webpack": "^8.1.0",
"@svgr/webpack": "8.1.0",
"@synthetixio/synpress": "4.0.0-alpha.7",
"@types/fs-extra": "11.0.4",
"@types/react": "18.3.11",
"@types/react-dom": "18.3.0",
"autoprefixer": "10.4.20",
"daisyui": "4.12.12",
"dotenv": "16.4.5",
"eslint-config-next": "14.2.15",
"eslint-plugin-tailwindcss": "3.17.4",
"postcss": "8.4.47",
"tailwind-scrollbar": "3.1.0",
"tailwindcss": "3.4.13"
"postcss": "8.4.47"
}
}

View File

@@ -1,7 +1,6 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

View File

@@ -0,0 +1,196 @@
<svg width="300" height="445" viewBox="0 0 300 445" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1395_87120)">
<path
d="M76.3234 5.09393C77.7299 5.09393 78.87 3.95378 78.87 2.54733C78.87 1.14088 77.7299 0.000732422 76.3234 0.000732422C74.917 0.000732422 73.7769 1.14088 73.7769 2.54733C73.7769 3.95378 74.917 5.09393 76.3234 5.09393Z"
fill="#190066" />
<path d="M20.3586 27.9827H10.1796V38.1616H20.3586V27.9827Z" fill="#F8F7F2" />
<path d="M50.8877 27.9827H40.7087V38.1616H50.8877V27.9827Z" fill="#F8F7F2" />
<path d="M81.4168 27.9827H71.2379V38.1616H81.4168V27.9827Z" fill="#F8F7F2" />
<path d="M142.476 27.9827H132.297V38.1616H142.476V27.9827Z" fill="#F8F7F2" />
<path d="M173.005 27.9827H162.826V38.1616H173.005V27.9827Z" fill="#190066" />
<path d="M203.535 27.9827H193.356V38.1616H203.535V27.9827Z" fill="#190066" />
<path d="M20.3586 58.5095H10.1796V68.6885H20.3586V58.5095Z" fill="#F8F7F2" />
<path d="M50.8877 58.5095H40.7087V68.6885H50.8877V58.5095Z" fill="#190066" />
<path d="M111.947 58.5095H101.768V68.6885H111.947V58.5095Z" fill="#190066" />
<path d="M142.476 58.5095H132.297V68.6885H142.476V58.5095Z" fill="#190066" />
<path d="M203.535 58.5095H193.356V68.6885H203.535V58.5095Z" fill="#190066" />
<path d="M20.3586 89.0366H10.1796V99.2156H20.3586V89.0366Z" fill="#190066" />
<path d="M50.8877 89.0366H40.7087V99.2156H50.8877V89.0366Z" fill="#F8F7F2" />
<path d="M81.4168 89.0366H71.2379V99.2156H81.4168V89.0366Z" fill="#F8F7F2" />
<path d="M111.947 89.0366H101.768V99.2156H111.947V89.0366Z" fill="#F8F7F2" />
<path d="M142.476 89.0366H132.297V99.2156H142.476V89.0366Z" fill="#190066" />
<path d="M50.8877 119.57H40.7087V129.749H50.8877V119.57Z" fill="#F8F7F2" />
<path d="M142.476 119.57H132.297V129.749H142.476V119.57Z" fill="#190066" />
<path d="M173.005 119.57H162.826V129.749H173.005V119.57Z" fill="#190066" />
<path
d="M198.442 127.207C199.848 127.207 200.989 126.067 200.989 124.661C200.989 123.254 199.848 122.114 198.442 122.114C197.036 122.114 195.895 123.254 195.895 124.661C195.895 126.067 197.036 127.207 198.442 127.207Z"
fill="#190066" />
<path d="M61.0594 139.929H30.53V170.458H61.0594V139.929Z" fill="#190066" />
<path d="M91.5888 139.929H61.0594V170.458H91.5888V139.929Z" fill="#190066" />
<path d="M203.535 150.097H193.356V160.276H203.535V150.097Z" fill="#190066" />
<path d="M0.000841141 170.462H-30.5286V200.991H0.000841141V170.462Z" fill="#6119EF" />
<path d="M30.5295 170.462H0.00012207V200.991H30.5295V170.462Z" fill="#190066" />
<path d="M61.0594 170.462H30.53V200.991H61.0594V170.462Z" fill="#6119EF" />
<path d="M91.5888 170.462H61.0594V200.991H91.5888V170.462Z" fill="#6119EF" />
<path d="M122.118 170.462H91.5883V200.991H122.118V170.462Z" fill="#190066" />
<path d="M152.647 170.462H122.118V200.991H152.647V170.462Z" fill="#190066" />
<path d="M234.064 180.629H223.885V190.808H234.064V180.629Z" fill="#190066" />
<path d="M91.5888 200.989H61.0594V231.518H91.5888V200.989Z" fill="#61DFFF" />
<path d="M183.177 200.989H152.648V231.518H183.177V200.989Z" fill="#190066" />
<path d="M213.707 200.989H183.177V231.518H213.707V200.989Z" fill="#6119EF" />
<path d="M264.593 211.163H254.414V221.342H264.593V211.163Z" fill="#190066" />
<path d="M61.0594 231.522H30.53V262.051H61.0594V231.522Z" fill="#61DFFF" />
<path d="M91.5888 231.522H61.0594V262.051H91.5888V231.522Z" fill="#6119EF" />
<path d="M122.118 231.522H91.5883V262.051H122.118V231.522Z" fill="#61DFFF" />
<path d="M152.647 231.522H122.118V262.051H152.647V231.522Z" fill="#190066" />
<path d="M183.177 231.522H152.648V262.051H183.177V231.522Z" fill="#6119EF" />
<path d="M213.707 231.522H183.177V262.051H213.707V231.522Z" fill="#190066" />
<path d="M234.064 241.683H223.885V251.862H234.064V241.683Z" fill="#F8F7F2" />
<path d="M264.593 241.683H254.414V251.862H264.593V241.683Z" fill="#F8F7F2" />
<path d="M61.0594 262.049H30.53V292.578H61.0594V262.049Z" fill="#190066" />
<path d="M91.5888 262.049H61.0594V292.578H91.5888V262.049Z" fill="#61DFFF" />
<path d="M122.118 262.049H91.5883V292.578H122.118V262.049Z" fill="#61DFFF" />
<path d="M152.647 262.049H122.118V292.578H152.647V262.049Z" fill="#6119EF" />
<path d="M183.177 262.049H152.648V292.578H183.177V262.049Z" fill="#F8F7F2" />
<path d="M234.064 272.216H223.885V282.395H234.064V272.216Z" fill="#190066" />
<path d="M264.593 272.216H254.414V282.395H264.593V272.216Z" fill="#190066" />
<path d="M20.3586 302.743H10.1796V312.922H20.3586V302.743Z" fill="#190066" />
<path d="M50.8877 302.743H40.7087V312.922H50.8877V302.743Z" fill="#190066" />
<path d="M264.593 302.743H254.414V312.922H264.593V302.743Z" fill="#190066" />
<path d="M295.123 302.743H284.944V312.922H295.123V302.743Z" fill="#190066" />
<path d="M20.3586 333.276H10.1796V343.455H20.3586V333.276Z" fill="#F8F7F2" />
<path d="M50.8877 333.276H40.7087V343.455H50.8877V333.276Z" fill="#F8F7F2" />
<path d="M81.4168 333.276H71.2379V343.455H81.4168V333.276Z" fill="#F8F7F2" />
<path d="M173.005 333.276H162.826V343.455H173.005V333.276Z" fill="#F8F7F2" />
<path d="M203.535 333.276H193.356V343.455H203.535V333.276Z" fill="#F8F7F2" />
<path d="M234.064 333.276H223.885V343.455H234.064V333.276Z" fill="#190066" />
<path d="M264.593 333.276H254.414V343.455H264.593V333.276Z" fill="#190066" />
<path d="M20.3586 363.809H10.1796V373.988H20.3586V363.809Z" fill="#F8F7F2" />
<path d="M50.8877 363.809H40.7087V373.988H50.8877V363.809Z" fill="#190066" />
<path d="M81.4168 363.809H71.2379V373.988H81.4168V363.809Z" fill="#F8F7F2" />
<path d="M111.947 363.809H101.768V373.988H111.947V363.809Z" fill="#F8F7F2" />
<path d="M142.476 363.809H132.297V373.988H142.476V363.809Z" fill="#190066" />
<path d="M173.005 363.809H162.826V373.988H173.005V363.809Z" fill="#F8F7F2" />
<path d="M203.535 363.809H193.356V373.988H203.535V363.809Z" fill="#F8F7F2" />
<path d="M234.064 363.809H223.885V373.988H234.064V363.809Z" fill="#F8F7F2" />
<path d="M264.593 363.809H254.414V373.988H264.593V363.809Z" fill="#190066" />
<path d="M20.3586 394.336H10.1796V404.515H20.3586V394.336Z" fill="#F8F7F2" />
<path d="M50.8877 394.336H40.7087V404.515H50.8877V394.336Z" fill="#190066" />
<path d="M81.4168 394.336H71.2379V404.515H81.4168V394.336Z" fill="#F8F7F2" />
<path d="M111.947 394.336H101.768V404.515H111.947V394.336Z" fill="#190066" />
<path d="M142.476 394.336H132.297V404.515H142.476V394.336Z" fill="#F8F7F2" />
<path d="M173.005 394.336H162.826V404.515H173.005V394.336Z" fill="#190066" />
<path d="M203.535 394.336H193.356V404.515H203.535V394.336Z" fill="#F8F7F2" />
<path d="M234.064 394.336H223.885V404.515H234.064V394.336Z" fill="#F8F7F2" />
<path d="M20.3586 424.863H10.1796V435.042H20.3586V424.863Z" fill="#F8F7F2" />
<path d="M50.8877 424.863H40.7087V435.042H50.8877V424.863Z" fill="#190066" />
<path d="M111.947 424.863H101.768V435.042H111.947V424.863Z" fill="#190066" />
<path d="M142.476 424.863H132.297V435.042H142.476V424.863Z" fill="#190066" />
<path d="M173.005 424.863H162.826V435.042H173.005V424.863Z" fill="#190066" />
<path d="M203.535 424.863H193.356V435.042H203.535V424.863Z" fill="#190066" />
<path
d="M-15.2643 139.928C-6.83389 139.928 0.000352859 133.094 0.000352859 124.664C0.000352859 116.233 -6.83389 109.399 -15.2643 109.399C-23.6948 109.399 -30.5291 116.233 -30.5291 124.664C-30.5291 133.094 -23.6948 139.928 -15.2643 139.928Z"
fill="#FFF068" />
<path
d="M15.2648 139.928C23.6953 139.928 30.5295 133.094 30.5295 124.664C30.5295 116.233 23.6953 109.399 15.2648 109.399C6.83436 109.399 0.00012207 116.233 0.00012207 124.664C0.00012207 133.094 6.83436 139.928 15.2648 139.928Z"
fill="white" />
<path
d="M76.3236 139.928C84.7541 139.928 91.5883 133.094 91.5883 124.664C91.5883 116.233 84.7541 109.399 76.3236 109.399C67.8931 109.399 61.0589 116.233 61.0589 124.664C61.0589 133.094 67.8931 139.928 76.3236 139.928Z"
fill="white" />
<path
d="M15.2648 148.123C19.1666 148.123 22.3387 151.295 22.3387 155.197C22.3387 159.098 19.1666 162.27 15.2648 162.27C11.363 162.27 8.19094 159.098 8.19094 155.197C8.19094 151.295 11.363 148.123 15.2648 148.123ZM15.2648 139.932C6.83573 139.932 0.00012207 146.767 0.00012207 155.197C0.00012207 163.626 6.83573 170.461 15.2648 170.461C23.6939 170.461 30.5295 163.626 30.5295 155.197C30.5295 146.767 23.6939 139.932 15.2648 139.932Z"
fill="#6119EF" />
<path
d="M106.853 148.123C110.755 148.123 113.927 151.295 113.927 155.197C113.927 159.098 110.755 162.27 106.853 162.27C102.951 162.27 99.7793 159.098 99.7793 155.197C99.7793 151.295 102.951 148.123 106.853 148.123ZM106.853 139.932C98.4241 139.932 91.5885 146.767 91.5885 155.197C91.5885 163.626 98.4241 170.461 106.853 170.461C115.282 170.461 122.118 163.626 122.118 155.197C122.118 146.767 115.282 139.932 106.853 139.932Z"
fill="#6119EF" />
<path
d="M167.912 200.988C176.342 200.988 183.176 194.154 183.176 185.723C183.176 177.293 176.342 170.459 167.912 170.459C159.481 170.459 152.647 177.293 152.647 185.723C152.647 194.154 159.481 200.988 167.912 200.988Z"
fill="#6119EF" />
<path
d="M198.441 200.988C206.871 200.988 213.706 194.154 213.706 185.723C213.706 177.293 206.871 170.459 198.441 170.459C190.01 170.459 183.176 177.293 183.176 185.723C183.176 194.154 190.01 200.988 198.441 200.988Z"
fill="white" />
<path
d="M-15.2643 231.515C-6.83389 231.515 0.000352859 224.681 0.000352859 216.25C0.000352859 207.82 -6.83389 200.986 -15.2643 200.986C-23.6948 200.986 -30.5291 207.82 -30.5291 216.25C-30.5291 224.681 -23.6948 231.515 -15.2643 231.515Z"
fill="white" />
<path
d="M15.2648 231.515C23.6953 231.515 30.5295 224.681 30.5295 216.25C30.5295 207.82 23.6953 200.986 15.2648 200.986C6.83436 200.986 0.00012207 207.82 0.00012207 216.25C0.00012207 224.681 6.83436 231.515 15.2648 231.515Z"
fill="white" />
<path
d="M228.971 231.515C237.401 231.515 244.235 224.681 244.235 216.25C244.235 207.82 237.401 200.986 228.971 200.986C220.54 200.986 213.706 207.82 213.706 216.25C213.706 224.681 220.54 231.515 228.971 231.515Z"
fill="#61DFFF" />
<path
d="M-15.2643 292.575C-6.83389 292.575 0.000352859 285.741 0.000352859 277.31C0.000352859 268.88 -6.83389 262.046 -15.2643 262.046C-23.6948 262.046 -30.5291 268.88 -30.5291 277.31C-30.5291 285.741 -23.6948 292.575 -15.2643 292.575Z"
fill="#FCD6FF" />
<path
d="M15.2648 270.236C19.1666 270.236 22.3387 273.409 22.3387 277.31C22.3387 281.212 19.1666 284.384 15.2648 284.384C11.363 284.384 8.19094 281.212 8.19094 277.31C8.19094 273.409 11.363 270.236 15.2648 270.236ZM15.2648 262.046C6.83573 262.046 0.00012207 268.881 0.00012207 277.31C0.00012207 285.739 6.83573 292.575 15.2648 292.575C23.6939 292.575 30.5295 285.739 30.5295 277.31C30.5295 268.881 23.6939 262.046 15.2648 262.046Z"
fill="#6119EF" />
<path
d="M198.441 270.236C202.343 270.236 205.515 273.409 205.515 277.31C205.515 281.212 202.343 284.384 198.441 284.384C194.539 284.384 191.367 281.212 191.367 277.31C191.367 273.409 194.539 270.236 198.441 270.236ZM198.441 262.046C190.012 262.046 183.176 268.881 183.176 277.31C183.176 285.739 190.012 292.575 198.441 292.575C206.87 292.575 213.706 285.739 213.706 277.31C213.706 268.881 206.87 262.046 198.441 262.046Z"
fill="#6119EF" />
<path
d="M76.3236 323.108C84.7541 323.108 91.5883 316.274 91.5883 307.843C91.5883 299.413 84.7541 292.579 76.3236 292.579C67.8931 292.579 61.0589 299.413 61.0589 307.843C61.0589 316.274 67.8931 323.108 76.3236 323.108Z"
fill="white" />
<path
d="M106.853 323.108C115.284 323.108 122.118 316.274 122.118 307.843C122.118 299.413 115.284 292.579 106.853 292.579C98.4227 292.579 91.5885 299.413 91.5885 307.843C91.5885 316.274 98.4227 323.108 106.853 323.108Z"
fill="white" />
<path
d="M137.382 323.108C145.812 323.108 152.647 316.274 152.647 307.843C152.647 299.413 145.812 292.579 137.382 292.579C128.951 292.579 122.117 299.413 122.117 307.843C122.117 316.274 128.951 323.108 137.382 323.108Z"
fill="white" />
<path
d="M198.441 323.108C206.871 323.108 213.706 316.274 213.706 307.843C213.706 299.413 206.871 292.579 198.441 292.579C190.01 292.579 183.176 299.413 183.176 307.843C183.176 316.274 190.01 323.108 198.441 323.108Z"
fill="#61DFFF" />
<path
d="M228.971 323.108C237.401 323.108 244.235 316.274 244.235 307.843C244.235 299.413 237.401 292.579 228.971 292.579C220.54 292.579 213.706 299.413 213.706 307.843C213.706 316.274 220.54 323.108 228.971 323.108Z"
fill="#6119EF" />
<path
d="M106.853 353.635C115.284 353.635 122.118 346.801 122.118 338.37C122.118 329.94 115.284 323.106 106.853 323.106C98.4227 323.106 91.5885 329.94 91.5885 338.37C91.5885 346.801 98.4227 353.635 106.853 353.635Z"
fill="white" />
<path
d="M76.3236 445.222C84.7541 445.222 91.5883 438.388 91.5883 429.957C91.5883 421.527 84.7541 414.692 76.3236 414.692C67.8931 414.692 61.0589 421.527 61.0589 429.957C61.0589 438.388 67.8931 445.222 76.3236 445.222Z"
fill="#6119EF" />
<path
d="M106.853 40.7002C111.068 40.7002 114.485 37.2831 114.485 33.0679C114.485 28.8527 111.068 25.4355 106.853 25.4355C102.638 25.4355 99.2206 28.8527 99.2206 33.0679C99.2206 37.2831 102.638 40.7002 106.853 40.7002Z"
fill="#61DFFF" />
<path
d="M167.911 71.233C172.127 71.233 175.544 67.8158 175.544 63.6006C175.544 59.3854 172.127 55.9683 167.911 55.9683C163.696 55.9683 160.279 59.3854 160.279 63.6006C160.279 67.8158 163.696 71.233 167.911 71.233Z"
fill="#6119EF" />
<path
d="M167.911 101.76C172.127 101.76 175.544 98.3427 175.544 94.1275C175.544 89.9122 172.127 86.4951 167.911 86.4951C163.696 86.4951 160.279 89.9122 160.279 94.1275C160.279 98.3427 163.696 101.76 167.911 101.76Z"
fill="#190066" />
<path
d="M228.971 101.76C233.186 101.76 236.603 98.3427 236.603 94.1275C236.603 89.9122 233.186 86.4951 228.971 86.4951C224.756 86.4951 221.338 89.9122 221.338 94.1275C221.338 98.3427 224.756 101.76 228.971 101.76Z"
fill="#61DFFF" />
<path
d="M259.5 101.76C263.715 101.76 267.132 98.3427 267.132 94.1275C267.132 89.9122 263.715 86.4951 259.5 86.4951C255.285 86.4951 251.868 89.9122 251.868 94.1275C251.868 98.3427 255.285 101.76 259.5 101.76Z"
fill="#6119EF" />
<path
d="M259.5 132.287C263.715 132.287 267.132 128.87 267.132 124.655C267.132 120.439 263.715 117.022 259.5 117.022C255.285 117.022 251.868 120.439 251.868 124.655C251.868 128.87 255.285 132.287 259.5 132.287Z"
fill="#6119EF" />
<path
d="M290.029 132.287C294.244 132.287 297.661 128.87 297.661 124.655C297.661 120.439 294.244 117.022 290.029 117.022C285.814 117.022 282.397 120.439 282.397 124.655C282.397 128.87 285.814 132.287 290.029 132.287Z"
fill="#61DFFF" />
<path
d="M290.029 162.814C294.244 162.814 297.661 159.397 297.661 155.182C297.661 150.966 294.244 147.549 290.029 147.549C285.814 147.549 282.397 150.966 282.397 155.182C282.397 159.397 285.814 162.814 290.029 162.814Z"
fill="#6119EF" />
<path
d="M259.5 193.347C263.715 193.347 267.132 189.93 267.132 185.715C267.132 181.5 263.715 178.083 259.5 178.083C255.285 178.083 251.868 181.5 251.868 185.715C251.868 189.93 255.285 193.347 259.5 193.347Z"
fill="#6119EF" />
<path
d="M290.029 254.407C294.244 254.407 297.661 250.99 297.661 246.774C297.661 242.559 294.244 239.142 290.029 239.142C285.814 239.142 282.397 242.559 282.397 246.774C282.397 250.99 285.814 254.407 290.029 254.407Z"
fill="white" />
<path
d="M290.029 284.94C294.244 284.94 297.661 281.523 297.661 277.307C297.661 273.092 294.244 269.675 290.029 269.675C285.814 269.675 282.397 273.092 282.397 277.307C282.397 281.523 285.814 284.94 290.029 284.94Z"
fill="#6119EF" />
<path
d="M290.029 345.994C294.244 345.994 297.661 342.577 297.661 338.361C297.661 334.146 294.244 330.729 290.029 330.729C285.814 330.729 282.397 334.146 282.397 338.361C282.397 342.577 285.814 345.994 290.029 345.994Z"
fill="white" />
<path
d="M228.971 437.586C233.186 437.586 236.603 434.169 236.603 429.954C236.603 425.739 233.186 422.322 228.971 422.322C224.756 422.322 221.338 425.739 221.338 429.954C221.338 434.169 224.756 437.586 228.971 437.586Z"
fill="#190066" />
</g>
<defs>
<clipPath id="clip0_1395_87120">
<rect width="1244.07" height="1177.93" fill="white" transform="translate(-519)" />
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,61 @@
<svg width="428" height="359" viewBox="0 0 428 359" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M417.245 256.962V246.783H407.066V256.962H417.245Z" fill="#F8F7F2"/>
<path d="M417.245 287.491V277.312H407.066V287.491H417.245Z" fill="#190066"/>
<path d="M427.406 358.721V328.192H396.877V358.721H427.406Z" fill="#61DFFF"/>
<path d="M386.712 226.432V216.253H376.533V226.432H386.712Z" fill="#190066"/>
<path d="M386.712 256.962V246.783H376.533V256.962H386.712Z" fill="#F8F7F2"/>
<path d="M396.88 358.721V328.192H366.351V358.721H396.88Z" fill="#190066"/>
<path d="M356.186 256.962V246.783H346.007V256.962H356.186Z" fill="#190066"/>
<path d="M356.186 287.491V277.312H346.007V287.491H356.186Z" fill="#190066"/>
<path d="M356.186 318.021V307.842H346.007V318.021H356.186Z" fill="#190066"/>
<path d="M356.186 348.55V338.371H346.007V348.55H356.186Z" fill="#190066"/>
<path d="M325.653 226.432V216.253H315.474V226.432H325.653Z" fill="#190066"/>
<path d="M325.653 256.962V246.783H315.474V256.962H325.653Z" fill="#F8F7F2"/>
<path d="M325.653 287.491V277.312H315.474V287.491H325.653Z" fill="#190066"/>
<path d="M325.653 318.021V307.842H315.474V318.021H325.653Z" fill="#F8F7F2"/>
<path d="M325.653 348.55V338.371H315.474V348.55H325.653Z" fill="#F8F7F2"/>
<path d="M295.119 256.962V246.783H284.94V256.962H295.119Z" fill="#F8F7F2"/>
<path d="M295.119 287.491V277.312H284.94V287.491H295.119Z" fill="#F8F7F2"/>
<path d="M295.119 318.021V307.842H284.94V318.021H295.119Z" fill="#F8F7F2"/>
<path d="M295.119 348.55V338.371H284.94V348.55H295.119Z" fill="#190066"/>
<path d="M264.592 226.432V216.253H254.413V226.432H264.592Z" fill="#F8F7F2"/>
<path d="M264.592 256.962V246.783H254.413V256.962H264.592Z" fill="#190066"/>
<path d="M264.592 287.491V277.312H254.413V287.491H264.592Z" fill="#190066"/>
<path d="M264.592 318.021V307.842H254.413V318.021H264.592Z" fill="#F8F7F2"/>
<path d="M264.592 348.55V338.371H254.413V348.55H264.592Z" fill="#190066"/>
<path d="M234.066 256.962V246.783H223.887V256.962H234.066Z" fill="#190066"/>
<path d="M234.066 287.491V277.312H223.887V287.491H234.066Z" fill="#F8F7F2"/>
<path d="M234.066 318.021V307.842H223.887V318.021H234.066Z" fill="#F8F7F2"/>
<path d="M234.066 348.55V338.371H223.887V348.55H234.066Z" fill="#190066"/>
<path d="M203.538 287.491V277.312H193.359V287.491H203.538Z" fill="#F8F7F2"/>
<path d="M203.538 318.021V307.842H193.359V318.021H203.538Z" fill="#F8F7F2"/>
<path d="M173.005 287.491V277.312H162.826V287.491H173.005Z" fill="#190066"/>
<path d="M173.005 318.021V307.842H162.826V318.021H173.005Z" fill="#F8F7F2"/>
<path d="M173.005 348.55V338.371H162.826V348.55H173.005Z" fill="#190066"/>
<path d="M142.472 287.491V277.312H132.293V287.491H142.472Z" fill="#F8F7F2"/>
<path d="M142.472 318.021V307.842H132.293V318.021H142.472Z" fill="#F8F7F2"/>
<path d="M142.472 348.55V338.371H132.293V348.55H142.472Z" fill="#190066"/>
<path d="M111.946 318.021V307.842H101.767V318.021H111.946Z" fill="#F8F7F2"/>
<path d="M111.946 348.55V338.371H101.767V348.55H111.946Z" fill="#190066"/>
<path d="M81.4183 318.021V307.842H71.2393V318.021H81.4183Z" fill="#190066"/>
<path d="M81.4183 348.55V338.371H71.2393V348.55H81.4183Z" fill="#F8F7F2"/>
<path d="M50.8918 348.55V338.371H40.7129V348.55H50.8918Z" fill="#190066"/>
<path d="M20.3586 43.2561V33.0771H10.1797V43.2561H20.3586Z" fill="#F8F7F2"/>
<path d="M20.3586 318.021V307.842H10.1797V318.021H20.3586Z" fill="#190066"/>
<path d="M366.354 282.398C366.354 290.828 373.188 297.662 381.618 297.662C390.049 297.662 396.883 290.828 396.883 282.398C396.883 273.967 390.049 267.133 381.618 267.133C373.188 267.133 366.354 273.967 366.354 282.398Z" fill="#190066"/>
<path d="M388.691 312.927C388.691 316.829 385.519 320.001 381.618 320.001C377.716 320.001 374.545 316.829 374.545 312.927C374.545 309.025 377.716 305.853 381.618 305.853C385.519 305.853 388.691 309.025 388.691 312.927ZM396.883 312.927C396.883 304.498 390.047 297.662 381.618 297.662C373.188 297.662 366.354 304.498 366.354 312.927C366.354 321.356 373.188 328.192 381.618 328.192C390.047 328.192 396.883 321.356 396.883 312.927Z" fill="#6119EF"/>
<path d="M183.18 343.456C183.18 351.887 190.014 358.721 198.445 358.721C206.875 358.721 213.709 351.887 213.709 343.456C213.709 335.026 206.875 328.192 198.445 328.192C190.014 328.192 183.18 335.026 183.18 343.456Z" fill="white"/>
<path d="M0 343.456C0 351.887 6.83424 358.721 15.2647 358.721C23.6951 358.721 30.5294 351.887 30.5294 343.456C30.5294 335.026 23.6951 328.192 15.2647 328.192C6.83424 328.192 0 335.026 0 343.456Z" fill="white"/>
<path d="M403.999 251.294C403.999 255.51 407.416 258.927 411.631 258.927C415.847 258.927 419.264 255.51 419.264 251.294C419.264 247.079 415.847 243.662 411.631 243.662C407.416 243.662 403.999 247.079 403.999 251.294Z" fill="#190066"/>
<path d="M404.521 190.809C404.521 195.025 407.938 198.442 412.154 198.442C416.369 198.442 419.786 195.025 419.786 190.809C419.786 186.594 416.369 183.177 412.154 183.177C407.938 183.177 404.521 186.594 404.521 190.809Z" fill="#F8F7F2"/>
<path d="M373.988 160.279C373.988 164.495 377.405 167.912 381.62 167.912C385.836 167.912 389.253 164.495 389.253 160.279C389.253 156.064 385.836 152.647 381.62 152.647C377.405 152.647 373.988 156.064 373.988 160.279Z" fill="#61DFFF"/>
<path d="M373.988 190.809C373.988 195.025 377.405 198.442 381.62 198.442C385.836 198.442 389.253 195.025 389.253 190.809C389.253 186.594 385.836 183.177 381.62 183.177C377.405 183.177 373.988 186.594 373.988 190.809Z" fill="#6119EF"/>
<path d="M343.468 160.279C343.468 164.495 346.885 167.912 351.101 167.912C355.316 167.912 358.733 164.495 358.733 160.279C358.733 156.064 355.316 152.647 351.101 152.647C346.885 152.647 343.468 156.064 343.468 160.279Z" fill="#6119EF"/>
<path d="M312.935 160.279C312.935 164.495 316.352 167.912 320.568 167.912C324.783 167.912 328.2 164.495 328.2 160.279C328.2 156.064 324.783 152.647 320.568 152.647C316.352 152.647 312.935 156.064 312.935 160.279Z" fill="#190066"/>
<path d="M312.935 190.809C312.935 195.025 316.352 198.442 320.568 198.442C324.783 198.442 328.2 195.025 328.2 190.809C328.2 186.594 324.783 183.177 320.568 183.177C316.352 183.177 312.935 186.594 312.935 190.809Z" fill="#190066"/>
<path d="M282.407 190.809C282.407 195.025 285.824 198.442 290.04 198.442C294.255 198.442 297.672 195.025 297.672 190.809C297.672 186.594 294.255 183.177 290.04 183.177C285.824 183.177 282.407 186.594 282.407 190.809Z" fill="#190066"/>
<path d="M190.82 251.868C190.82 256.083 194.238 259.5 198.453 259.5C202.668 259.5 206.085 256.083 206.085 251.868C206.085 247.652 202.668 244.235 198.453 244.235C194.238 244.235 190.82 247.652 190.82 251.868Z" fill="#6119EF"/>
<path d="M99.2285 282.397C99.2285 286.612 102.646 290.029 106.861 290.029C111.076 290.029 114.494 286.612 114.494 282.397C114.494 278.182 111.076 274.765 106.861 274.765C102.646 274.765 99.2285 278.182 99.2285 282.397Z" fill="#6119EF"/>
<path d="M68.6953 282.397C68.6953 286.612 72.1124 290.029 76.3276 290.029C80.5429 290.029 83.96 286.612 83.96 282.397C83.96 278.182 80.5429 274.765 76.3276 274.765C72.1124 274.765 68.6953 278.182 68.6953 282.397Z" fill="#61DFFF"/>
<path d="M7.6416 7.632C7.6416 11.847 11.0588 15.264 15.274 15.264C19.4892 15.264 22.9063 11.847 22.9063 7.632C22.9063 3.417 19.4892 0 15.274 0C11.0588 0 7.6416 3.417 7.6416 7.632Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@@ -0,0 +1,221 @@
<svg width="610" height="842" viewBox="0 0 610 842" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1395_86446)">
<path d="M595.323 5.09393C596.73 5.09393 597.87 3.95378 597.87 2.54733C597.87 1.14088 596.73 0.000732422 595.323 0.000732422C593.917 0.000732422 592.777 1.14088 592.777 2.54733C592.777 3.95378 593.917 5.09393 595.323 5.09393Z" fill="#190066"/>
<path d="M478.3 27.9827H468.121V38.1616H478.3V27.9827Z" fill="#F8F7F2"/>
<path d="M508.829 27.9827H498.65V38.1616H508.829V27.9827Z" fill="#F8F7F2"/>
<path d="M539.359 27.9827H529.18V38.1616H539.359V27.9827Z" fill="#F8F7F2"/>
<path d="M569.888 27.9827H559.709V38.1616H569.888V27.9827Z" fill="#F8F7F2"/>
<path d="M600.417 27.9827H590.238V38.1616H600.417V27.9827Z" fill="#F8F7F2"/>
<path d="M447.77 58.5095H437.591V68.6885H447.77V58.5095Z" fill="#F8F7F2"/>
<path d="M478.3 58.5095H468.121V68.6885H478.3V58.5095Z" fill="#190066"/>
<path d="M508.829 58.5095H498.65V68.6885H508.829V58.5095Z" fill="#F8F7F2"/>
<path d="M539.359 58.5095H529.18V68.6885H539.359V58.5095Z" fill="#F8F7F2"/>
<path d="M569.888 58.5095H559.709V68.6885H569.888V58.5095Z" fill="#190066"/>
<path d="M417.241 89.0366H407.062V99.2156H417.241V89.0366Z" fill="#F8F7F2"/>
<path d="M447.77 89.0366H437.591V99.2156H447.77V89.0366Z" fill="#F8F7F2"/>
<path d="M478.3 89.0366H468.121V99.2156H478.3V89.0366Z" fill="#190066"/>
<path d="M508.829 89.0366H498.65V99.2156H508.829V89.0366Z" fill="#F8F7F2"/>
<path d="M539.359 89.0366H529.18V99.2156H539.359V89.0366Z" fill="#190066"/>
<path d="M569.888 89.0366H559.709V99.2156H569.888V89.0366Z" fill="#F8F7F2"/>
<path d="M600.417 89.0366H590.238V99.2156H600.417V89.0366Z" fill="#F8F7F2"/>
<path d="M386.711 119.57H376.532V129.749H386.711V119.57Z" fill="#190066"/>
<path d="M417.241 119.57H407.062V129.749H417.241V119.57Z" fill="#F8F7F2"/>
<path d="M447.77 119.57H437.591V129.749H447.77V119.57Z" fill="#F8F7F2"/>
<path d="M478.3 119.57H468.121V129.749H478.3V119.57Z" fill="#F8F7F2"/>
<path d="M569.888 119.57H559.709V129.749H569.888V119.57Z" fill="#F8F7F2"/>
<path d="M386.711 150.097H376.532V160.276H386.711V150.097Z" fill="#F8F7F2"/>
<path d="M417.241 150.097H407.062V160.276H417.241V150.097Z" fill="#190066"/>
<path d="M447.77 150.097H437.591V160.276H447.77V150.097Z" fill="#190066"/>
<path d="M478.3 150.097H468.121V160.276H478.3V150.097Z" fill="#190066"/>
<path d="M508.829 150.097H498.65V160.276H508.829V150.097Z" fill="#190066"/>
<path d="M580.059 139.929H549.53V170.458H580.059V139.929Z" fill="#6119EF"/>
<path d="M610.589 139.929H580.059V170.458H610.589V139.929Z" fill="#6119EF"/>
<path d="M386.711 180.629H376.532V190.808H386.711V180.629Z" fill="#F8F7F2"/>
<path d="M519.001 170.462H488.471V200.991H519.001V170.462Z" fill="#6119EF"/>
<path d="M549.53 170.462H519V200.991H549.53V170.462Z" fill="#190066"/>
<path d="M580.059 170.462H549.53V200.991H580.059V170.462Z" fill="white"/>
<path d="M610.589 170.462H580.059V200.991H610.589V170.462Z" fill="#190066"/>
<path d="M478.3 211.163H468.121V221.342H478.3V211.163Z" fill="#190066"/>
<path d="M610.589 200.989H580.059V231.518H610.589V200.989Z" fill="#61DFFF"/>
<path d="M478.3 241.683H468.121V251.862H478.3V241.683Z" fill="#F8F7F2"/>
<path d="M508.829 241.683H498.65V251.862H508.829V241.683Z" fill="#190066"/>
<path d="M580.059 231.522H549.53V262.051H580.059V231.522Z" fill="#61DFFF"/>
<path d="M610.589 231.522H580.059V262.051H610.589V231.522Z" fill="white"/>
<path d="M447.77 272.216H437.591V282.395H447.77V272.216Z" fill="#190066"/>
<path d="M478.3 272.216H468.121V282.395H478.3V272.216Z" fill="#F8F7F2"/>
<path d="M580.059 262.049H549.53V292.578H580.059V262.049Z" fill="#190066"/>
<path d="M610.589 262.049H580.059V292.578H610.589V262.049Z" fill="#61DFFF"/>
<path d="M478.3 302.743H468.121V312.922H478.3V302.743Z" fill="#190066"/>
<path d="M508.829 302.743H498.65V312.922H508.829V302.743Z" fill="#190066"/>
<path d="M539.359 302.743H529.18V312.922H539.359V302.743Z" fill="#190066"/>
<path d="M569.888 302.743H559.709V312.922H569.888V302.743Z" fill="#190066"/>
<path d="M447.77 333.276H437.591V343.455H447.77V333.276Z" fill="#190066"/>
<path d="M478.3 333.276H468.121V343.455H478.3V333.276Z" fill="#F8F7F2"/>
<path d="M508.829 333.276H498.65V343.455H508.829V333.276Z" fill="#190066"/>
<path d="M539.359 333.276H529.18V343.455H539.359V333.276Z" fill="#F8F7F2"/>
<path d="M569.888 333.276H559.709V343.455H569.888V333.276Z" fill="#F8F7F2"/>
<path d="M600.417 333.276H590.238V343.455H600.417V333.276Z" fill="#F8F7F2"/>
<path d="M478.3 363.809H468.121V373.988H478.3V363.809Z" fill="#F8F7F2"/>
<path d="M508.829 363.809H498.65V373.988H508.829V363.809Z" fill="#F8F7F2"/>
<path d="M539.359 363.809H529.18V373.988H539.359V363.809Z" fill="#F8F7F2"/>
<path d="M569.888 363.809H559.709V373.988H569.888V363.809Z" fill="#190066"/>
<path d="M600.417 363.809H590.238V373.988H600.417V363.809Z" fill="#F8F7F2"/>
<path d="M447.77 394.336H437.591V404.515H447.77V394.336Z" fill="#F8F7F2"/>
<path d="M478.3 394.336H468.121V404.515H478.3V394.336Z" fill="#190066"/>
<path d="M508.829 394.336H498.65V404.515H508.829V394.336Z" fill="#190066"/>
<path d="M539.359 394.336H529.18V404.515H539.359V394.336Z" fill="#F8F7F2"/>
<path d="M569.888 394.336H559.709V404.515H569.888V394.336Z" fill="#190066"/>
<path d="M600.417 394.336H590.238V404.515H600.417V394.336Z" fill="#F8F7F2"/>
<path d="M478.3 424.863H468.121V435.042H478.3V424.863Z" fill="#190066"/>
<path d="M508.829 424.863H498.65V435.042H508.829V424.863Z" fill="#F8F7F2"/>
<path d="M539.359 424.863H529.18V435.042H539.359V424.863Z" fill="#F8F7F2"/>
<path d="M569.888 424.863H559.709V435.042H569.888V424.863Z" fill="#190066"/>
<path d="M508.829 455.39H498.65V465.569H508.829V455.39Z" fill="#F8F7F2"/>
<path d="M539.359 455.39H529.18V465.569H539.359V455.39Z" fill="#F8F7F2"/>
<path d="M508.829 485.923H498.65V496.102H508.829V485.923Z" fill="#190066"/>
<path d="M539.359 485.923H529.18V496.102H539.359V485.923Z" fill="#F8F7F2"/>
<path d="M569.888 485.923H559.709V496.102H569.888V485.923Z" fill="#190066"/>
<path d="M508.829 516.456H498.65V526.635H508.829V516.456Z" fill="#F8F7F2"/>
<path d="M539.359 516.456H529.18V526.635H539.359V516.456Z" fill="#F8F7F2"/>
<path d="M569.888 516.456H559.709V526.635H569.888V516.456Z" fill="#190066"/>
<path d="M600.417 516.456H590.238V526.635H600.417V516.456Z" fill="#F8F7F2"/>
<path d="M539.359 546.983H529.18V557.162H539.359V546.983Z" fill="#F8F7F2"/>
<path d="M569.888 546.983H559.709V557.162H569.888V546.983Z" fill="#190066"/>
<path d="M600.417 546.983H590.238V557.162H600.417V546.983Z" fill="#190066"/>
<path d="M539.359 577.51H529.18V587.689H539.359V577.51Z" fill="#190066"/>
<path d="M569.888 577.51H559.709V587.689H569.888V577.51Z" fill="#F8F7F2"/>
<path d="M600.417 577.51H590.238V587.689H600.417V577.51Z" fill="#F8F7F2"/>
<path d="M569.888 608.037H559.709V618.216H569.888V608.037Z" fill="#190066"/>
<path d="M600.417 608.037H590.238V618.216H600.417V608.037Z" fill="#190066"/>
<path d="M264.594 638.57H254.415V648.749H264.594V638.57Z" fill="#F8F7F2"/>
<path d="M539.359 638.57H529.18V648.749H539.359V638.57Z" fill="#190066"/>
<path d="M600.417 638.57H590.238V648.749H600.417V638.57Z" fill="#190066"/>
<path d="M203.535 669.097H193.356V679.276H203.535V669.097Z" fill="#F8F7F2"/>
<path d="M234.064 669.097H223.885V679.276H234.064V669.097Z" fill="#F8F7F2"/>
<path d="M264.594 669.097H254.415V679.276H264.594V669.097Z" fill="#F8F7F2"/>
<path d="M295.123 669.097H284.944V679.276H295.123V669.097Z" fill="#F8F7F2"/>
<path d="M325.652 669.097H315.473V679.276H325.652V669.097Z" fill="#F8F7F2"/>
<path d="M356.182 669.097H346.003V679.276H356.182V669.097Z" fill="#F8F7F2"/>
<path d="M386.711 669.097H376.532V679.276H386.711V669.097Z" fill="#F8F7F2"/>
<path d="M417.241 669.097H407.062V679.276H417.241V669.097Z" fill="#F8F7F2"/>
<path d="M539.359 669.097H529.18V679.276H539.359V669.097Z" fill="#F8F7F2"/>
<path d="M569.888 669.097H559.709V679.276H569.888V669.097Z" fill="#F8F7F2"/>
<path d="M203.535 699.63H193.356V709.809H203.535V699.63Z" fill="#190066"/>
<path d="M234.064 699.63H223.885V709.809H234.064V699.63Z" fill="#F8F7F2"/>
<path d="M264.594 699.63H254.415V709.809H264.594V699.63Z" fill="#F8F7F2"/>
<path d="M295.123 699.63H284.944V709.809H295.123V699.63Z" fill="#F8F7F2"/>
<path d="M356.182 699.63H346.003V709.809H356.182V699.63Z" fill="#F8F7F2"/>
<path d="M386.711 699.63H376.532V709.809H386.711V699.63Z" fill="#F8F7F2"/>
<path d="M417.241 699.63H407.062V709.809H417.241V699.63Z" fill="#190066"/>
<path d="M447.77 699.63H437.591V709.809H447.77V699.63Z" fill="#F8F7F2"/>
<path d="M478.3 699.63H468.121V709.809H478.3V699.63Z" fill="#F8F7F2"/>
<path d="M508.829 699.63H498.65V709.809H508.829V699.63Z" fill="#190066"/>
<path d="M539.359 699.63H529.18V709.809H539.359V699.63Z" fill="#F8F7F2"/>
<path d="M610.589 689.462H580.059V719.991H610.589V689.462Z" fill="#190066"/>
<path d="M264.594 730.162H254.415V740.341H264.594V730.162Z" fill="#F8F7F2"/>
<path d="M295.123 730.162H284.944V740.341H295.123V730.162Z" fill="#F8F7F2"/>
<path d="M325.652 730.162H315.473V740.341H325.652V730.162Z" fill="#F8F7F2"/>
<path d="M356.182 730.162H346.003V740.341H356.182V730.162Z" fill="#190066"/>
<path d="M417.241 730.162H407.062V740.341H417.241V730.162Z" fill="#F8F7F2"/>
<path d="M447.77 730.162H437.591V740.341H447.77V730.162Z" fill="#F8F7F2"/>
<path d="M478.3 730.162H468.121V740.341H478.3V730.162Z" fill="#F8F7F2"/>
<path d="M508.829 730.162H498.65V740.341H508.829V730.162Z" fill="#F8F7F2"/>
<path d="M539.359 730.162H529.18V740.341H539.359V730.162Z" fill="#190066"/>
<path d="M569.888 730.162H559.709V740.341H569.888V730.162Z" fill="#F8F7F2"/>
<path d="M610.589 719.989H580.059V750.518H610.589V719.989Z" fill="#190066"/>
<path d="M173.005 760.684H162.826V770.863H173.005V760.684Z" fill="#F8F7F2"/>
<path d="M203.535 760.684H193.356V770.863H203.535V760.684Z" fill="#F8F7F2"/>
<path d="M234.064 760.684H223.885V770.863H234.064V760.684Z" fill="#190066"/>
<path d="M264.594 760.684H254.415V770.863H264.594V760.684Z" fill="#190066"/>
<path d="M295.123 760.684H284.944V770.863H295.123V760.684Z" fill="#F8F7F2"/>
<path d="M325.652 760.684H315.473V770.863H325.652V760.684Z" fill="#190066"/>
<path d="M356.182 760.684H346.003V770.863H356.182V760.684Z" fill="#190066"/>
<path d="M386.711 760.684H376.532V770.863H386.711V760.684Z" fill="#190066"/>
<path d="M447.77 760.684H437.591V770.863H447.77V760.684Z" fill="#190066"/>
<path d="M478.3 760.684H468.121V770.863H478.3V760.684Z" fill="#F8F7F2"/>
<path d="M508.829 760.684H498.65V770.863H508.829V760.684Z" fill="#F8F7F2"/>
<path d="M539.359 760.684H529.18V770.863H539.359V760.684Z" fill="#F8F7F2"/>
<path d="M580.059 750.522H549.53V781.051H580.059V750.522Z" fill="#190066"/>
<path d="M610.589 750.522H580.059V781.051H610.589V750.522Z" fill="#61DFFF"/>
<path d="M81.4169 791.216H71.2379V801.395H81.4169V791.216Z" fill="#190066"/>
<path d="M111.946 791.216H101.767V801.395H111.946V791.216Z" fill="#F8F7F2"/>
<path d="M142.476 791.216H132.297V801.395H142.476V791.216Z" fill="#190066"/>
<path d="M173.005 791.216H162.826V801.395H173.005V791.216Z" fill="#190066"/>
<path d="M203.535 791.216H193.356V801.395H203.535V791.216Z" fill="#F8F7F2"/>
<path d="M234.064 791.216H223.885V801.395H234.064V791.216Z" fill="#F8F7F2"/>
<path d="M264.594 791.216H254.415V801.395H264.594V791.216Z" fill="#190066"/>
<path d="M295.123 791.216H284.944V801.395H295.123V791.216Z" fill="#F8F7F2"/>
<path d="M325.652 791.216H315.473V801.395H325.652V791.216Z" fill="#190066"/>
<path d="M356.182 791.216H346.003V801.395H356.182V791.216Z" fill="#F8F7F2"/>
<path d="M386.711 791.216H376.532V801.395H386.711V791.216Z" fill="#190066"/>
<path d="M457.941 781.049H427.412V811.578H457.941V781.049Z" fill="#190066"/>
<path d="M478.3 791.216H468.121V801.395H478.3V791.216Z" fill="#F8F7F2"/>
<path d="M508.829 791.216H498.65V801.395H508.829V791.216Z" fill="#190066"/>
<path d="M539.359 791.216H529.18V801.395H539.359V791.216Z" fill="#F8F7F2"/>
<path d="M580.059 781.049H549.53V811.578H580.059V781.049Z" fill="#190066"/>
<path d="M610.589 781.049H580.059V811.578H610.589V781.049Z" fill="#6119EF"/>
<path d="M81.4169 821.743H71.2379V831.922H81.4169V821.743Z" fill="#F8F7F2"/>
<path d="M111.946 821.743H101.767V831.922H111.946V821.743Z" fill="#190066"/>
<path d="M142.476 821.743H132.297V831.922H142.476V821.743Z" fill="#190066"/>
<path d="M173.005 821.743H162.826V831.922H173.005V821.743Z" fill="#190066"/>
<path d="M203.535 821.743H193.356V831.922H203.535V821.743Z" fill="#F8F7F2"/>
<path d="M234.064 821.743H223.885V831.922H234.064V821.743Z" fill="#190066"/>
<path d="M264.594 821.743H254.415V831.922H264.594V821.743Z" fill="#F8F7F2"/>
<path d="M295.123 821.743H284.944V831.922H295.123V821.743Z" fill="#F8F7F2"/>
<path d="M325.652 821.743H315.473V831.922H325.652V821.743Z" fill="#190066"/>
<path d="M356.182 821.743H346.003V831.922H356.182V821.743Z" fill="#F8F7F2"/>
<path d="M427.412 811.576H396.883V842.105H427.412V811.576Z" fill="#6119EF"/>
<path d="M457.941 811.576H427.412V842.105H457.941V811.576Z" fill="#6119EF"/>
<path d="M478.3 821.743H468.121V831.922H478.3V821.743Z" fill="#F8F7F2"/>
<path d="M508.829 821.743H498.65V831.922H508.829V821.743Z" fill="#190066"/>
<path d="M539.359 821.743H529.18V831.922H539.359V821.743Z" fill="#F8F7F2"/>
<path d="M580.059 811.576H549.53V842.105H580.059V811.576Z" fill="#6119EF"/>
<path d="M610.589 811.576H580.059V842.105H610.589V811.576Z" fill="#190066"/>
<path d="M503.736 139.928C512.166 139.928 519 133.094 519 124.664C519 116.233 512.166 109.399 503.736 109.399C495.305 109.399 488.471 116.233 488.471 124.664C488.471 133.094 495.305 139.928 503.736 139.928Z" fill="#61DFFF"/>
<path d="M534.265 139.928C542.695 139.928 549.53 133.094 549.53 124.664C549.53 116.233 542.695 109.399 534.265 109.399C525.834 109.399 519 116.233 519 124.664C519 133.094 525.834 139.928 534.265 139.928Z" fill="white"/>
<path d="M595.324 139.928C603.754 139.928 610.588 133.094 610.588 124.664C610.588 116.233 603.754 109.399 595.324 109.399C586.893 109.399 580.059 116.233 580.059 124.664C580.059 133.094 586.893 139.928 595.324 139.928Z" fill="#190066"/>
<path d="M534.265 148.123C538.167 148.123 541.339 151.295 541.339 155.197C541.339 159.098 538.167 162.27 534.265 162.27C530.363 162.27 527.191 159.098 527.191 155.197C527.191 151.295 530.363 148.123 534.265 148.123ZM534.265 139.932C525.836 139.932 519 146.767 519 155.197C519 163.626 525.836 170.461 534.265 170.461C542.694 170.461 549.53 163.626 549.53 155.197C549.53 146.767 542.694 139.932 534.265 139.932Z" fill="#6119EF"/>
<path d="M503.736 231.515C512.166 231.515 519 224.681 519 216.25C519 207.82 512.166 200.986 503.736 200.986C495.305 200.986 488.471 207.82 488.471 216.25C488.471 224.681 495.305 231.515 503.736 231.515Z" fill="#190066"/>
<path d="M534.265 231.515C542.695 231.515 549.53 224.681 549.53 216.25C549.53 207.82 542.695 200.986 534.265 200.986C525.834 200.986 519 207.82 519 216.25C519 224.681 525.834 231.515 534.265 231.515Z" fill="white"/>
<path d="M503.736 292.575C512.166 292.575 519 285.741 519 277.31C519 268.88 512.166 262.046 503.736 262.046C495.305 262.046 488.471 268.88 488.471 277.31C488.471 285.741 495.305 292.575 503.736 292.575Z" fill="#190066"/>
<path d="M534.265 270.236C538.167 270.236 541.339 273.409 541.339 277.31C541.339 281.212 538.167 284.384 534.265 284.384C530.363 284.384 527.191 281.212 527.191 277.31C527.191 273.409 530.363 270.236 534.265 270.236ZM534.265 262.046C525.836 262.046 519 268.881 519 277.31C519 285.739 525.836 292.575 534.265 292.575C542.694 292.575 549.53 285.739 549.53 277.31C549.53 268.881 542.694 262.046 534.265 262.046Z" fill="#6119EF"/>
<path d="M595.324 323.108C603.754 323.108 610.588 316.274 610.588 307.843C610.588 299.413 603.754 292.579 595.324 292.579C586.893 292.579 580.059 299.413 580.059 307.843C580.059 316.274 586.893 323.108 595.324 323.108Z" fill="white"/>
<path d="M595.324 445.222C603.754 445.222 610.588 438.388 610.588 429.957C610.588 421.527 603.754 414.692 595.324 414.692C586.893 414.692 580.059 421.527 580.059 429.957C580.059 438.388 586.893 445.222 595.324 445.222Z" fill="#190066"/>
<path d="M564.794 475.749C573.225 475.749 580.059 468.915 580.059 460.484C580.059 452.054 573.225 445.219 564.794 445.219C556.364 445.219 549.53 452.054 549.53 460.484C549.53 468.915 556.364 475.749 564.794 475.749Z" fill="white"/>
<path d="M595.324 475.749C603.754 475.749 610.588 468.915 610.588 460.484C610.588 452.054 603.754 445.219 595.324 445.219C586.893 445.219 580.059 452.054 580.059 460.484C580.059 468.915 586.893 475.749 595.324 475.749Z" fill="white"/>
<path d="M595.324 506.282C603.754 506.282 610.588 499.447 610.588 491.017C610.588 482.586 603.754 475.752 595.324 475.752C586.893 475.752 580.059 482.586 580.059 491.017C580.059 499.447 586.893 506.282 595.324 506.282Z" fill="#61DFFF"/>
<path d="M564.794 658.928C573.225 658.928 580.059 652.094 580.059 643.664C580.059 635.233 573.225 628.399 564.794 628.399C556.364 628.399 549.53 635.233 549.53 643.664C549.53 652.094 556.364 658.928 564.794 658.928Z" fill="white"/>
<path d="M595.324 689.461C603.754 689.461 610.588 682.627 610.588 674.197C610.588 665.766 603.754 658.932 595.324 658.932C586.893 658.932 580.059 665.766 580.059 674.197C580.059 682.627 586.893 689.461 595.324 689.461Z" fill="#61DFFF"/>
<path d="M381.618 842.108C390.048 842.108 396.882 835.274 396.882 826.843C396.882 818.413 390.048 811.579 381.618 811.579C373.187 811.579 366.353 818.413 366.353 826.843C366.353 835.274 373.187 842.108 381.618 842.108Z" fill="white"/>
<path d="M412.147 193.347C416.363 193.347 419.78 189.93 419.78 185.715C419.78 181.5 416.363 178.083 412.147 178.083C407.932 178.083 404.515 181.5 404.515 185.715C404.515 189.93 407.932 193.347 412.147 193.347Z" fill="#190066"/>
<path d="M442.676 193.347C446.891 193.347 450.308 189.93 450.308 185.715C450.308 181.5 446.891 178.083 442.676 178.083C438.461 178.083 435.043 181.5 435.043 185.715C435.043 189.93 438.461 193.347 442.676 193.347Z" fill="#6119EF"/>
<path d="M473.206 193.347C477.421 193.347 480.838 189.93 480.838 185.715C480.838 181.5 477.421 178.083 473.206 178.083C468.99 178.083 465.573 181.5 465.573 185.715C465.573 189.93 468.99 193.347 473.206 193.347Z" fill="#190066"/>
<path d="M442.676 223.88C446.891 223.88 450.308 220.463 450.308 216.248C450.308 212.032 446.891 208.615 442.676 208.615C438.461 208.615 435.043 212.032 435.043 216.248C435.043 220.463 438.461 223.88 442.676 223.88Z" fill="#F8F7F2"/>
<path d="M381.617 254.407C385.833 254.407 389.25 250.99 389.25 246.774C389.25 242.559 385.833 239.142 381.617 239.142C377.402 239.142 373.985 242.559 373.985 246.774C373.985 250.99 377.402 254.407 381.617 254.407Z" fill="#190066"/>
<path d="M412.147 254.407C416.363 254.407 419.78 250.99 419.78 246.774C419.78 242.559 416.363 239.142 412.147 239.142C407.932 239.142 404.515 242.559 404.515 246.774C404.515 250.99 407.932 254.407 412.147 254.407Z" fill="#F8F7F2"/>
<path d="M381.617 284.94C385.833 284.94 389.25 281.523 389.25 277.307C389.25 273.092 385.833 269.675 381.617 269.675C377.402 269.675 373.985 273.092 373.985 277.307C373.985 281.523 377.402 284.94 381.617 284.94Z" fill="#61DFFF"/>
<path d="M412.147 284.94C416.363 284.94 419.78 281.523 419.78 277.307C419.78 273.092 416.363 269.675 412.147 269.675C407.932 269.675 404.515 273.092 404.515 277.307C404.515 281.523 407.932 284.94 412.147 284.94Z" fill="#6119EF"/>
<path d="M381.617 315.461C385.833 315.461 389.25 312.044 389.25 307.828C389.25 303.613 385.833 300.196 381.617 300.196C377.402 300.196 373.985 303.613 373.985 307.828C373.985 312.044 377.402 315.461 381.617 315.461Z" fill="#6119EF"/>
<path d="M381.617 345.994C385.833 345.994 389.25 342.577 389.25 338.361C389.25 334.146 385.833 330.729 381.617 330.729C377.402 330.729 373.985 334.146 373.985 338.361C373.985 342.577 377.402 345.994 381.617 345.994Z" fill="#190066"/>
<path d="M412.147 345.994C416.363 345.994 419.78 342.577 419.78 338.361C419.78 334.146 416.363 330.729 412.147 330.729C407.932 330.729 404.515 334.146 404.515 338.361C404.515 342.577 407.932 345.994 412.147 345.994Z" fill="#190066"/>
<path d="M412.147 376.521C416.363 376.521 419.78 373.103 419.78 368.888C419.78 364.673 416.363 361.256 412.147 361.256C407.932 361.256 404.515 364.673 404.515 368.888C404.515 373.103 407.932 376.521 412.147 376.521Z" fill="#190066"/>
<path d="M473.206 468.107C477.421 468.107 480.838 464.69 480.838 460.475C480.838 456.26 477.421 452.843 473.206 452.843C468.99 452.843 465.573 456.26 465.573 460.475C465.573 464.69 468.99 468.107 473.206 468.107Z" fill="#6119EF"/>
<path d="M503.735 559.7C507.95 559.7 511.367 556.283 511.367 552.068C511.367 547.853 507.95 544.436 503.735 544.436C499.52 544.436 496.103 547.853 496.103 552.068C496.103 556.283 499.52 559.7 503.735 559.7Z" fill="#6119EF"/>
<path d="M503.735 590.233C507.95 590.233 511.367 586.816 511.367 582.601C511.367 578.386 507.95 574.969 503.735 574.969C499.52 574.969 496.103 578.386 496.103 582.601C496.103 586.816 499.52 590.233 503.735 590.233Z" fill="#61DFFF"/>
<path d="M228.97 651.287C233.185 651.287 236.602 647.87 236.602 643.655C236.602 639.44 233.185 636.022 228.97 636.022C224.755 636.022 221.338 639.44 221.338 643.655C221.338 647.87 224.755 651.287 228.97 651.287Z" fill="white"/>
<path d="M442.676 681.814C446.891 681.814 450.308 678.397 450.308 674.182C450.308 669.967 446.891 666.55 442.676 666.55C438.461 666.55 435.043 669.967 435.043 674.182C435.043 678.397 438.461 681.814 442.676 681.814Z" fill="#190066"/>
<path d="M503.735 681.814C507.95 681.814 511.367 678.397 511.367 674.182C511.367 669.967 507.95 666.55 503.735 666.55C499.52 666.55 496.103 669.967 496.103 674.182C496.103 678.397 499.52 681.814 503.735 681.814Z" fill="#190066"/>
<path d="M167.912 712.347C172.127 712.347 175.544 708.93 175.544 704.715C175.544 700.499 172.127 697.082 167.912 697.082C163.696 697.082 160.279 700.499 160.279 704.715C160.279 708.93 163.696 712.347 167.912 712.347Z" fill="#6119EF"/>
<path d="M320.559 712.347C324.774 712.347 328.191 708.93 328.191 704.715C328.191 700.499 324.774 697.082 320.559 697.082C316.344 697.082 312.927 700.499 312.927 704.715C312.927 708.93 316.344 712.347 320.559 712.347Z" fill="#6119EF"/>
<path d="M198.441 742.88C202.657 742.88 206.074 739.463 206.074 735.248C206.074 731.032 202.657 727.615 198.441 727.615C194.226 727.615 190.809 731.032 190.809 735.248C190.809 739.463 194.226 742.88 198.441 742.88Z" fill="#61DFFF"/>
<path d="M228.97 742.88C233.185 742.88 236.602 739.463 236.602 735.248C236.602 731.032 233.185 727.615 228.97 727.615C224.755 727.615 221.338 731.032 221.338 735.248C221.338 739.463 224.755 742.88 228.97 742.88Z" fill="#F8F7F2"/>
<path d="M381.617 742.88C385.833 742.88 389.25 739.463 389.25 735.248C389.25 731.032 385.833 727.615 381.617 727.615C377.402 727.615 373.985 731.032 373.985 735.248C373.985 739.463 377.402 742.88 381.617 742.88Z" fill="#F8F7F2"/>
<path d="M137.382 773.407C141.597 773.407 145.015 769.99 145.015 765.775C145.015 761.56 141.597 758.142 137.382 758.142C133.167 758.142 129.75 761.56 129.75 765.775C129.75 769.99 133.167 773.407 137.382 773.407Z" fill="#6119EF"/>
<path d="M412.147 803.94C416.363 803.94 419.78 800.523 419.78 796.307C419.78 792.092 416.363 788.675 412.147 788.675C407.932 788.675 404.515 792.092 404.515 796.307C404.515 800.523 407.932 803.94 412.147 803.94Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_1395_86446">
<rect width="1244.07" height="1177.93" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -0,0 +1,8 @@
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g>
<path d="M16 0H0V16H16V0Z" fill="#6CF9D8" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M3.47576 4.20983L4.25541 3.43018L7.37104 6.54585C7.18199 6.6264 7.00484 6.7438 6.85059 6.89805C6.73114 7.0175 6.63374 7.1507 6.55849 7.29255L3.47576 4.20983ZM6.49979 8.71985L3.42969 11.79L4.20931 12.5696L7.24819 9.5307C7.10519 9.45525 6.97094 9.3573 6.85059 9.237C6.69724 9.08365 6.58029 8.9077 6.49979 8.71985ZM8.79504 9.5291L11.7919 12.526L12.5716 11.7464L9.54174 8.7165C9.46119 8.9056 9.34374 9.08275 9.18949 9.237C9.07004 9.35645 8.93684 9.4538 8.79504 9.5291ZM9.48324 7.29565L12.5255 4.2534L11.7458 3.47378L8.67239 6.54725C8.86019 6.6278 9.03619 6.7447 9.18949 6.89805C9.30984 7.0184 9.40774 7.15265 9.48324 7.29565Z"
fill="#2D2E33" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 847 B

View File

@@ -0,0 +1,6 @@
<svg width="21" height="17" viewBox="0 0 21 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M17.6167 1.42787C16.2829 0.801883 14.8568 0.34692 13.3658 0.0878906C13.1827 0.419979 12.9688 0.866636 12.8213 1.22197C11.2364 0.98287 9.66598 0.98287 8.11022 1.22197C7.96277 0.866636 7.74395 0.419979 7.55925 0.0878906C6.06667 0.34692 4.63892 0.803543 3.30517 1.43119C0.614973 5.50923 -0.114296 9.48602 0.250339 13.4063C2.03462 14.743 3.76379 15.5549 5.4638 16.0862C5.88353 15.5068 6.25789 14.8908 6.5804 14.2415C5.96619 14.0074 5.37791 13.7185 4.82204 13.383C4.96951 13.2735 5.11375 13.1589 5.25312 13.041C8.64344 14.6317 12.327 14.6317 15.6768 13.041C15.8178 13.1589 15.962 13.2735 16.1079 13.383C15.5504 13.7201 14.9605 14.009 14.3463 14.2432C14.6688 14.8908 15.0415 15.5084 15.4629 16.0879C17.1645 15.5566 18.8953 14.7446 20.6796 13.4063C21.1074 8.86168 19.9487 4.92143 17.6167 1.42787ZM7.04226 10.9954C6.02453 10.9954 5.18992 10.0422 5.18992 8.88162C5.18992 7.72095 6.0067 6.76621 7.04226 6.76621C8.07783 6.76621 8.91241 7.71928 8.89459 8.88162C8.89625 10.0422 8.07783 10.9954 7.04226 10.9954ZM13.8877 10.9954C12.8699 10.9954 12.0353 10.0422 12.0353 8.88162C12.0353 7.72095 12.8521 6.76621 13.8877 6.76621C14.9232 6.76621 15.7578 7.71928 15.74 8.88162C15.74 10.0422 14.9232 10.9954 13.8877 10.9954Z"
fill="#121212" />
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,5 @@
<svg width="752" height="689" viewBox="0 0 752 689" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M128.889 0H613.333V688.889H542.222V373.333H541.525C533.665 286.121 460.369 217.777 371.111 217.777C281.853 217.777 208.557 286.121 200.697 373.333H200V688.889H128.889V0Z" fill="black"/>
<path d="M0 97.777L28.889 195.555H53.333V591.111C41.06 591.111 31.111 601.06 31.111 613.333V640H26.667C14.394 640 4.44398 649.949 4.44398 662.222V688.889H253.333V662.222C253.333 649.949 243.384 640 231.111 640H226.667V613.333C226.667 601.06 216.717 591.111 204.444 591.111H177.778V97.777H0Z" fill="black"/>
<path d="M546.667 591.111C534.394 591.111 524.444 601.06 524.444 613.333V640H520C507.727 640 497.778 649.949 497.778 662.222V688.889H746.667V662.222C746.667 649.949 736.717 640 724.444 640H720V613.333C720 601.06 710.051 591.111 697.778 591.111V195.555H722.222L751.111 97.777H573.333V591.111H546.667Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 925 B

View File

@@ -0,0 +1,6 @@
<svg width="21" height="19" viewBox="0 0 21 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M16.1831 0.0878906H19.2366L12.5655 7.71251L20.4135 18.0879H14.2686L9.45569 11.7953L3.94862 18.0879H0.893231L8.02862 9.93251L0.5 0.0878906H6.80092L11.1514 5.83958L16.1831 0.0878906ZM15.1114 16.2602H16.8034L5.88154 1.81958H4.06585L15.1114 16.2602Z"
fill="#121212" />
</svg>

After

Width:  |  Height:  |  Size: 389 B

View File

@@ -0,0 +1,6 @@
<svg width="24" height="17" viewBox="0 0 24 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M22.9362 3.54026C22.9362 3.54026 22.714 1.97181 22.0298 1.28311C21.1634 0.376699 20.1947 0.372256 19.7504 0.318937C16.5691 0.0878905 11.7926 0.0878906 11.7926 0.0878906H11.7837C11.7837 0.0878906 7.0073 0.0878905 3.82596 0.318937C3.38164 0.372256 2.41302 0.376699 1.54659 1.28311C0.862338 1.97181 0.644621 3.54026 0.644621 3.54026C0.644621 3.54026 0.413574 5.3842 0.413574 7.22369V8.94765C0.413574 10.7871 0.640178 12.6311 0.640178 12.6311C0.640178 12.6311 0.862338 14.1995 1.54215 14.8882C2.40858 15.7946 3.54604 15.7635 4.05256 15.8613C5.87428 16.0346 11.7882 16.0879 11.7882 16.0879C11.7882 16.0879 16.5691 16.079 19.7504 15.8524C20.1947 15.7991 21.1634 15.7946 22.0298 14.8882C22.714 14.1995 22.9362 12.6311 22.9362 12.6311C22.9362 12.6311 23.1628 10.7916 23.1628 8.94765V7.22369C23.1628 5.3842 22.9362 3.54026 22.9362 3.54026ZM9.43773 11.0404V4.64662L15.5827 7.85462L9.43773 11.0404Z"
fill="#121212" />
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,9 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="20" cy="20" r="20" fill="#61dfff" />
<g>
<path d="M27.7733 28.6429H11.6667V11.6719H15.3519V25.3539H27.7733V28.6411V28.6429Z" fill="#121212" />
<path
d="M27.7733 14.9593C29.5888 14.9593 31.0606 13.4875 31.0606 11.672C31.0606 9.85652 29.5888 8.38477 27.7733 8.38477C25.9578 8.38477 24.4861 9.85652 24.4861 11.672C24.4861 13.4875 25.9578 14.9593 27.7733 14.9593Z"
fill="#121212" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 524 B

View File

@@ -0,0 +1,20 @@
<svg viewBox="0 0 32 33" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1455_914)">
<path
d="M32 16.5C32 7.66346 24.8365 0.5 16 0.5C7.16346 0.5 0 7.66346 0 16.5C0 25.3365 7.16346 32.5 16 32.5C24.8365 32.5 32 25.3365 32 16.5Z"
fill="url(#paint0_linear_1455_914)" />
</g>
<defs>
<linearGradient id="paint0_linear_1455_914" x1="31.12" y1="40.109" x2="-3.77644" y2="4.43737"
gradientUnits="userSpaceOnUse">
<stop offset="0.1562" stop-color="#F8CECE" />
<stop offset="0.3958" stop-color="#ED7878" />
<stop offset="0.7292" stop-color="#E7781D" />
<stop offset="0.9063" stop-color="#D36E09" />
<stop offset="1" stop-color="#D76E04" />
</linearGradient>
<clipPath id="clip0_1455_914">
<rect width="32" height="32" fill="white" transform="translate(0 0.5)" />
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 885 B

View File

@@ -1,159 +0,0 @@
[
{
"constant": true,
"inputs": [],
"name": "name",
"outputs": [{ "name": "", "type": "string" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "name": "_spender", "type": "address" },
{ "name": "_value", "type": "uint256" }
],
"name": "approve",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "totalSupply",
"outputs": [{ "name": "", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "name": "_from", "type": "address" },
{ "name": "_to", "type": "address" },
{ "name": "_value", "type": "uint256" }
],
"name": "transferFrom",
"outputs": [{ "name": "success", "type": "bool" }],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "decimals",
"outputs": [{ "name": "", "type": "uint8" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [{ "name": "_value", "type": "uint256" }],
"name": "burn",
"outputs": [{ "name": "success", "type": "bool" }],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [{ "name": "", "type": "address" }],
"name": "balanceOf",
"outputs": [{ "name": "", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "name": "_from", "type": "address" },
{ "name": "_value", "type": "uint256" }
],
"name": "burnFrom",
"outputs": [{ "name": "success", "type": "bool" }],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "symbol",
"outputs": [{ "name": "", "type": "string" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "name": "_to", "type": "address" },
{ "name": "_value", "type": "uint256" }
],
"name": "transfer",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "name": "_spender", "type": "address" },
{ "name": "_value", "type": "uint256" },
{ "name": "_extraData", "type": "bytes" }
],
"name": "approveAndCall",
"outputs": [{ "name": "success", "type": "bool" }],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{ "name": "", "type": "address" },
{ "name": "", "type": "address" }
],
"name": "allowance",
"outputs": [{ "name": "", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{ "name": "initialSupply", "type": "uint256" },
{ "name": "tokenName", "type": "string" },
{ "name": "tokenSymbol", "type": "string" }
],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "name": "from", "type": "address" },
{ "indexed": true, "name": "to", "type": "address" },
{ "indexed": false, "name": "value", "type": "uint256" }
],
"name": "Transfer",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "name": "from", "type": "address" },
{ "indexed": false, "name": "value", "type": "uint256" }
],
"name": "Burn",
"type": "event"
}
]

View File

@@ -0,0 +1,325 @@
{
"abi": [
{
"inputs": [
{ "internalType": "uint32", "name": "_localDomain", "type": "uint32" },
{ "internalType": "uint32", "name": "_version", "type": "uint32" }
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [{ "indexed": true, "internalType": "address", "name": "attester", "type": "address" }],
"name": "AttesterDisabled",
"type": "event"
},
{
"anonymous": false,
"inputs": [{ "indexed": true, "internalType": "address", "name": "attester", "type": "address" }],
"name": "AttesterEnabled",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "internalType": "address", "name": "previousAttesterManager", "type": "address" },
{ "indexed": true, "internalType": "address", "name": "newAttesterManager", "type": "address" }
],
"name": "AttesterManagerUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [{ "indexed": false, "internalType": "uint64", "name": "version", "type": "uint64" }],
"name": "Initialized",
"type": "event"
},
{
"anonymous": false,
"inputs": [{ "indexed": false, "internalType": "uint256", "name": "newMaxMessageBodySize", "type": "uint256" }],
"name": "MaxMessageBodySizeUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "internalType": "address", "name": "caller", "type": "address" },
{ "indexed": false, "internalType": "uint32", "name": "sourceDomain", "type": "uint32" },
{ "indexed": true, "internalType": "bytes32", "name": "nonce", "type": "bytes32" },
{ "indexed": false, "internalType": "bytes32", "name": "sender", "type": "bytes32" },
{ "indexed": true, "internalType": "uint32", "name": "finalityThresholdExecuted", "type": "uint32" },
{ "indexed": false, "internalType": "bytes", "name": "messageBody", "type": "bytes" }
],
"name": "MessageReceived",
"type": "event"
},
{
"anonymous": false,
"inputs": [{ "indexed": false, "internalType": "bytes", "name": "message", "type": "bytes" }],
"name": "MessageSent",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" },
{ "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" }
],
"name": "OwnershipTransferStarted",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" },
{ "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" }
],
"name": "OwnershipTransferred",
"type": "event"
},
{ "anonymous": false, "inputs": [], "name": "Pause", "type": "event" },
{
"anonymous": false,
"inputs": [{ "indexed": true, "internalType": "address", "name": "newAddress", "type": "address" }],
"name": "PauserChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [{ "indexed": true, "internalType": "address", "name": "newRescuer", "type": "address" }],
"name": "RescuerChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": false, "internalType": "uint256", "name": "oldSignatureThreshold", "type": "uint256" },
{ "indexed": false, "internalType": "uint256", "name": "newSignatureThreshold", "type": "uint256" }
],
"name": "SignatureThresholdUpdated",
"type": "event"
},
{ "anonymous": false, "inputs": [], "name": "Unpause", "type": "event" },
{
"inputs": [],
"name": "NONCE_USED",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{ "inputs": [], "name": "acceptOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" },
{
"inputs": [],
"name": "attesterManager",
"outputs": [{ "internalType": "address", "name": "", "type": "address" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [{ "internalType": "address", "name": "attester", "type": "address" }],
"name": "disableAttester",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [{ "internalType": "address", "name": "newAttester", "type": "address" }],
"name": "enableAttester",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [{ "internalType": "uint256", "name": "index", "type": "uint256" }],
"name": "getEnabledAttester",
"outputs": [{ "internalType": "address", "name": "", "type": "address" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getNumEnabledAttesters",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "owner_", "type": "address" },
{ "internalType": "address", "name": "pauser_", "type": "address" },
{ "internalType": "address", "name": "rescuer_", "type": "address" },
{ "internalType": "address", "name": "attesterManager_", "type": "address" },
{ "internalType": "address[]", "name": "attesters_", "type": "address[]" },
{ "internalType": "uint256", "name": "signatureThreshold_", "type": "uint256" },
{ "internalType": "uint256", "name": "maxMessageBodySize_", "type": "uint256" }
],
"name": "initialize",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "initializedVersion",
"outputs": [{ "internalType": "uint64", "name": "", "type": "uint64" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [{ "internalType": "address", "name": "attester", "type": "address" }],
"name": "isEnabledAttester",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "localDomain",
"outputs": [{ "internalType": "uint32", "name": "", "type": "uint32" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "maxMessageBodySize",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [{ "internalType": "address", "name": "", "type": "address" }],
"stateMutability": "view",
"type": "function"
},
{ "inputs": [], "name": "pause", "outputs": [], "stateMutability": "nonpayable", "type": "function" },
{
"inputs": [],
"name": "paused",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "pauser",
"outputs": [{ "internalType": "address", "name": "", "type": "address" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "pendingOwner",
"outputs": [{ "internalType": "address", "name": "", "type": "address" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{ "internalType": "bytes", "name": "message", "type": "bytes" },
{ "internalType": "bytes", "name": "attestation", "type": "bytes" }
],
"name": "receiveMessage",
"outputs": [{ "internalType": "bool", "name": "success", "type": "bool" }],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "contract IERC20", "name": "tokenContract", "type": "address" },
{ "internalType": "address", "name": "to", "type": "address" },
{ "internalType": "uint256", "name": "amount", "type": "uint256" }
],
"name": "rescueERC20",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "rescuer",
"outputs": [{ "internalType": "address", "name": "", "type": "address" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{ "internalType": "uint32", "name": "destinationDomain", "type": "uint32" },
{ "internalType": "bytes32", "name": "recipient", "type": "bytes32" },
{ "internalType": "bytes32", "name": "destinationCaller", "type": "bytes32" },
{ "internalType": "uint32", "name": "minFinalityThreshold", "type": "uint32" },
{ "internalType": "bytes", "name": "messageBody", "type": "bytes" }
],
"name": "sendMessage",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [{ "internalType": "uint256", "name": "newMaxMessageBodySize", "type": "uint256" }],
"name": "setMaxMessageBodySize",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [{ "internalType": "uint256", "name": "newSignatureThreshold", "type": "uint256" }],
"name": "setSignatureThreshold",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "signatureThreshold",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [{ "internalType": "address", "name": "newOwner", "type": "address" }],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{ "inputs": [], "name": "unpause", "outputs": [], "stateMutability": "nonpayable", "type": "function" },
{
"inputs": [{ "internalType": "address", "name": "newAttesterManager", "type": "address" }],
"name": "updateAttesterManager",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [{ "internalType": "address", "name": "_newPauser", "type": "address" }],
"name": "updatePauser",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [{ "internalType": "address", "name": "newRescuer", "type": "address" }],
"name": "updateRescuer",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }],
"name": "usedNonces",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "version",
"outputs": [{ "internalType": "uint32", "name": "", "type": "uint32" }],
"stateMutability": "view",
"type": "function"
}
]
}

View File

@@ -0,0 +1,91 @@
.content-wrapper {
max-width: 29.25rem;
margin: 3rem auto 1.5rem;
width: calc(100% - 3rem);
@include bp("tablet") {
margin-top: 3.75rem;
width: 100%;
}
}
.title {
font-size: 2rem;
margin-top: 1.5rem;
margin-bottom: 1.5rem;
}
.home-link {
text-decoration: none;
color: var(--v2-color-indigo);
font-size: 0.875rem;
&:hover {
text-decoration: underline;
}
svg {
width: 0.5625rem;
height: 0.5625rem;
margin-right: 0.5rem;
}
}
ul.list {
display: flex;
flex-direction: column;
row-gap: 1.5rem;
}
li.item {
border-radius: 0.625rem;
background-color: var(--v2-color-white);
padding: 1rem;
.item-title {
font-size: 1rem;
font-weight: 500;
line-height: 1.4;
}
.button {
svg {
width: 1rem;
height: 1rem;
color: var(--v2-color-black);
}
}
.content {
font-size: 0.875rem;
line-height: 1.4;
a {
color: var(--v2-color-indigo);
&:hover {
color: var(--v2-color-navy);
}
}
ul {
list-style-type: disc;
padding-left: 1.25rem;
&:not(:last-child) {
margin-bottom: 0.625rem;
}
}
p:not(:last-child) {
margin-bottom: 0.625rem;
}
}
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
}

View File

@@ -1,214 +1,144 @@
"use client";
import { useState } from "react";
import { motion } from "framer-motion";
import styles from "./page.module.scss";
import Link from "next/link";
import { Collapse } from "@/components/ui";
import ArrowLeftIcon from "@/assets/icons/arrow-left.svg";
import PlusIcon from "@/assets/icons/plus.svg";
const faqList = [
{
title: "Which address do the funds go to?",
content: `<p>By default, your bridged funds are sent to the same address they were originally sent from.</p>`,
},
{
title: "Can I send the funds to a different address?",
content: `<p>Yes — find the 'To different address' button beneath the field that displays the amount of ETH or tokens you will receive on the target chain. You can then enter the address you want the funds to be bridged to. Only send funds to addresses on Ethereum Mainnet or Linea Mainnet. You may lose funds otherwise, and they will be irretrievable (see this <a rel="noopener noreferrer" target="_blank" href="https://support.metamask.io/managing-my-tokens/moving-your-tokens/funds-sent-on-wrong-network/#non-evm-tokens-and-networks">MetaMask Support article</a> for an explanation). This means you cannot bridge to Bitcoin or Solana, for example, using the Linea Bridge.</p>`,
},
{
title: "I need to claim a transaction. What do I do?",
content: `<div><p>If you chose manual claiming, you need to return to the Linea Bridge and make an additional transaction. It typically takes around 20 minutes (two Ethereum epochs) before you can claim a deposit; withdrawals from Linea can take much longer, and vary in duration.</p><p>To claim, head to the 'Transactions' tab and locate the bridge transaction you want to claim; it should be marked as 'Ready to claim'. Click on it, and then on the 'Claim' button to initiate the transaction.</p></div>`,
},
{
title: "What fees will I pay?",
content: `<div><p>Linea does not charge fees for using the bridge. However, you will need to pay gas fees for transactions. Gas fees vary according to network activity, so we cannot accurately predict how much you'll pay. The bulk of the cost will be for interacting with Ethereum Mainnet; Linea's gas fees are considerably cheaper.</p><p>If you use manual claiming, you pay gas fees for the bridge transaction and then gas fees for the claim transaction.</p><p>For automatic claiming, you pay gas fees for your initial bridge transaction and then a fee to cover the gas costs of the postman executing the claim on your behalf.</p></div>`,
},
{
title: "Why can I only select Linea Mainnet and Ethereum Mainnet?",
content: `<div><p>The Linea Bridge is designed to transact between layer 1 (Ethereum Mainnet) and layer 2 (Linea Mainnet), rather than for bridging to any other chains.</p><p>If you want to bridge to other EVM-compatible networks, consider using the <a rel="noopener noreferrer" target="_blank" href="https://portfolio.metamask.io/bridge">MetaMask Portfolio bridge</a>.</p></div>`,
},
{
title: "Why can't I see the token I want to bridge?",
content: `<p>The tokens available to select on the Linea Bridge are sourced from a curated list defined <a rel="noopener noreferrer" target="_blank" href="https://github.com/Consensys/linea-token-list/blob/main/json/linea-mainnet-token-shortlist.json">here</a> and maintained by the Linea team. This practice ensures users always bridge to the correct token—rather than variants, preventing <a rel="noopener noreferrer" target="_blank" href="https://support.linea.build/bridging/bridging-to-the-correct-erc-20-token-on-linea">liquidity fragmentation</a> and mitigates the risk of funds loss.</p>`,
},
{
title: "Why can't I see my bridged tokens in my wallet?",
content: `<div><p>First, check whether your funds are ready to claim. To see claimable funds, go to the “Transactions” tab of the Linea Bridge app.</p><p>If claiming isn't the issue, head to Lineascan and see if the transaction is pending, and in the queue:</p><ul class="list-disc pl-8"><li><a rel="noopener noreferrer" target="_blank" href="https://lineascan.build/txsDeposits">Here</a> for L1 -&gt; L2 transactions (deposits)</li><li><a rel="noopener noreferrer" target="_blank" href="https://lineascan.build/txsExit">Here</a> for L2 -&gt; L1 transactions (exit transactions)</li></ul><p>If the transaction is still pending, just wait for it to be confirmed, and your funds will be available to claim or will be in your wallet (depending on the claiming method you chose). If the transaction is missing, or it has been confirmed and you still don&amp;pos;t see your tokens, contact support by clicking the button in the bottom-left of the Linea Bridge app, or head to the <a rel="noopener noreferrer" target="_blank" href="https://support.linea.build/">Linea help center</a>.</p></div>`,
},
{
title: "How long does the bridging take?",
content: `<div><p>This depends on the direction of your bridge:</p><ul><li>Deposit (L1 -&gt; L2): Approximately 20 minutes.</li><li>Withdrawal (L2 -&gt; L1): Between 8 and 32 hours. The L2 transaction must be finalized on Ethereum Mainnet before you can claim your funds.</li></ul></div>`,
},
{
title: "Can I speed up my transaction?",
content: `<div><p>Yes, although it's not a method we recommend for beginners. <a rel="noopener noreferrer" target="_blank" href="https://support.linea.build/bridging/can-i-speed-up-bridge-transfers-on-the-linea-bridge">View the guide on the MetaMask Help Center</a>.</p><p>Note that this only speeds up your submission of the bridge transaction. It does not actually speed up the bridging process itself that you initiate with this transaction — you cannot speed up the 8-32 hour waiting time for a withdrawal.</p></div>`,
},
{
title: "Where can I access support?",
content: `<div><p>Click the “Contact support” button in the bottom-left corner of the Linea Bridge app.</p><p>Alternatively, head to the Linea Help Center and click the “Contact” button on the homepage.</p><p>You can also get advice and support on our moderated <a rel="noopener noreferrer" target="_blank" href="https://discord.gg/linea">Discord</a>.</p></div>`,
},
];
export default function FaqPage() {
const [openIndex, setOpenIndex] = useState<number | null>(null);
const handleToggle = (index: number) => {
setOpenIndex(openIndex === index ? null : index);
};
return (
<div className="m-auto min-w-min max-w-5xl">
<h1 className="mb-6 text-4xl md:hidden">FAQ</h1>
<div className="flex flex-col gap-5">
<Collapse title="Which address do the funds go to?">
<p>By default, your bridged funds are sent to the same address they were originally sent from.</p>
</Collapse>
<>
<div className={styles["content-wrapper"]}>
<Link className={styles["home-link"]} href="/">
<ArrowLeftIcon />
<span>Bridge</span>
</Link>
<h1 className={styles["title"]}>FAQ</h1>
<Collapse title="Can I send the funds to a different address?">
<p>
Yes find the &apos;To different address&apos; button beneath the field that displays the amount of ETH or
tokens you will receive on the target chain. You can then enter the address you want the funds to be bridged
to. Only send funds to addresses on Ethereum Mainnet or Linea Mainnet. You may lose funds otherwise, and
they will be irretrievable (see this{" "}
<Link
href="https://support.metamask.io/managing-my-tokens/moving-your-tokens/funds-sent-on-wrong-network/#non-evm-tokens-and-networks"
rel="noopener noreferrer"
passHref
target="_blank"
className="link link-secondary"
>
MetaMask Support article
</Link>{" "}
for an explanation). This means you cannot bridge to Bitcoin or Solana, for example, using the Linea Bridge.
</p>
</Collapse>
<Collapse title="I need to claim a transaction. What do I do?">
<p>
If you chose manual claiming, you need to return to the Linea Bridge and make an additional transaction. It
typically takes around 20 minutes (two Ethereum epochs) before you can claim a deposit; withdrawals from
Linea can take much longer, and vary in duration.
</p>
<p>
To claim, head to the &apos;Transactions&apos; tab and locate the bridge transaction you want to claim; it
should be marked as &apos;Ready to claim&apos;. Click on it, and then on the &apos;Claim&apos; button to
initiate the transaction.
</p>
</Collapse>
<Collapse title="What fees will I pay?">
<p>
Linea does not charge fees for using the bridge. However, you will need to pay gas fees for transactions.
Gas fees vary according to network activity, so we cannot accurately predict how much you&apos;ll pay. The
bulk of the cost will be for interacting with Ethereum Mainnet; Linea&apos;s gas fees are considerably
cheaper.
</p>
<p>
If you use manual claiming, you pay gas fees for the bridge transaction and then gas fees for the claim
transaction.
</p>
<p>
For automatic claiming, you pay gas fees for your initial bridge transaction and then a fee to cover the gas
costs of the postman executing the claim on your behalf.
</p>
</Collapse>
<Collapse title="Why can I only select Linea Mainnet and Ethereum Mainnet?">
<p>
The Linea Bridge is designed to transact between layer 1 (Ethereum Mainnet) and layer 2 (Linea Mainnet),
rather than for bridging to any other chains.
</p>
<p>
If you want to bridge to other EVM-compatible networks, consider using the{" "}
<Link
href="https://portfolio.metamask.io/bridge"
rel="noopener noreferrer"
passHref
target="_blank"
className="link link-secondary"
>
MetaMask Portfolio bridge
</Link>
.
</p>
</Collapse>
<Collapse title="Why can't I see the token I want to bridge?">
<p>
The tokens available to select on the Linea Bridge are sourced from a curated list defined{" "}
<Link
href="https://github.com/Consensys/linea-token-list/blob/main/json/linea-mainnet-token-shortlist.json"
rel="noopener noreferrer"
passHref
target="_blank"
className="link link-secondary"
>
here
</Link>{" "}
and maintained by the Linea team. This practice ensures users always bridge to the correct tokenrather than
variants, preventing{" "}
<Link
href="https://support.linea.build/bridging/bridging-to-the-correct-erc-20-token-on-linea"
rel="noopener noreferrer"
passHref
target="_blank"
className="link link-secondary"
>
liquidity fragmentation
</Link>{" "}
and mitigates the risk of funds loss.
</p>
</Collapse>
<Collapse title="Why can't I see my bridged tokens in my wallet?">
<p>
First, check whether your funds are ready to claim. To see claimable funds, go to the Transactions tab of
the Linea Bridge app.
</p>
<p>
If claiming isn&apos;t the issue, head to Lineascan and see if the transaction is pending, and in the queue:
</p>
<ul className="list-disc pl-8">
<li>
<Link
href="https://lineascan.build/txsDeposits"
rel="noopener noreferrer"
passHref
target="_blank"
className="link link-secondary"
>
Here
</Link>{" "}
for L1 -&gt; L2 transactions (deposits)
</li>
<li>
<Link
href="https://lineascan.build/txsExit"
rel="noopener noreferrer"
passHref
target="_blank"
className="link link-secondary"
>
Here
</Link>{" "}
for L2 -&gt; L1 transactions (exit transactions)
</li>
</ul>
<p>
If the transaction is still pending, just wait for it to be confirmed, and your funds will be available to
claim or will be in your wallet (depending on the claiming method you chose). If the transaction is missing,
or it has been confirmed and you still don&pos;t see your tokens, contact support by clicking the button in
the bottom-left of the Linea Bridge app, or head to the{" "}
<Link
href="https://support.linea.build/"
rel="noopener noreferrer"
passHref
target="_blank"
className="link link-secondary"
>
Linea help center
</Link>
.
</p>
</Collapse>
<Collapse title="How long does the bridging take?">
<p>This depends on the direction of your bridge:</p>
<ul className="list-disc pl-8">
<li>Deposit (L1 -&gt; L2): Approximately 20 minutes.</li>
<li>
Withdrawal (L2 -&gt; L1): Between 8 and 32 hours. The L2 transaction must be finalized on Ethereum Mainnet
before you can claim your funds.
</li>
</ul>
</Collapse>
<Collapse title="Can I speed up my transaction?">
<p>
Yes, although it&apos;s not a method we recommend for beginners.{" "}
<Link
href="https://support.linea.build/bridging/can-i-speed-up-bridge-transfers-on-the-linea-bridge"
rel="noopener noreferrer"
passHref
target="_blank"
className="link link-secondary"
>
View the guide on the MetaMask Help Center
</Link>
.
</p>
<p>
Note that this only speeds up your submission of the bridge transaction. It does not actually speed up the
bridging process itself that you initiate with this transaction you cannot speed up the 8-32 hour waiting
time for a withdrawal.
</p>
</Collapse>
<Collapse title="Where can I access support?">
<p>Click the Contact support button in the bottom-left corner of the Linea Bridge app.</p>
<p>Alternatively, head to the Linea Help Center and click the Contact button on the homepage.</p>
<p>
You can also get advice and support on our moderated{" "}
<Link
href="https://discord.gg/linea"
rel="noopener noreferrer"
passHref
target="_blank"
className="link link-secondary"
>
Discord
</Link>
.
</p>
</Collapse>
<ul className={styles["list"]}>
{faqList.map((faq, index) => (
<FaqItem key={index} data={faq} isOpen={openIndex === index} onToggle={() => handleToggle(index)} />
))}
</ul>
</div>
</div>
</>
);
}
type FaqItemProps = {
data: {
title: string;
content: string;
};
isOpen: boolean;
onToggle: () => void;
};
function FaqItem({ data, isOpen, onToggle }: FaqItemProps) {
const { title, content } = data;
return (
<li className={styles["item"]}>
<motion.div
className={styles["header"]}
onClick={onToggle}
role="button"
tabIndex={0}
aria-expanded={isOpen}
onKeyDown={(e) => e.key === "Enter" && onToggle()}
animate={{
marginBottom: isOpen ? "1.6rem" : "0rem",
}}
transition={{
marginBottom: {
duration: 0.3,
ease: "easeInOut",
},
}}
>
<h2 className={styles["item-title"]}>{title}</h2>
<motion.div
animate={{ rotate: isOpen ? 225 : 0 }}
transition={{ duration: 0.3, ease: "easeInOut" }}
className={styles["button"]}
>
<PlusIcon />
</motion.div>
</motion.div>
<motion.div
className={styles["content"]}
initial={false}
animate={{
height: isOpen ? "auto" : 0,
opacity: isOpen ? 1 : 0,
}}
transition={{
duration: 0.3,
}}
style={{ overflow: "hidden" }}
>
<motion.div
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: isOpen ? 1 : 0, y: isOpen ? 0 : -10 }}
transition={{
duration: 0.3,
ease: "easeInOut",
delay: isOpen ? 0.1 : 0,
}}
dangerouslySetInnerHTML={{ __html: content }}
/>
</motion.div>
</li>
);
}

View File

@@ -1,70 +1,8 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
/* toastify */
--toastify-color-success: #7adffd;
--toastify-color-progress-success: #7adffd;
--background-color: #F8F7F2;
--yellow: #FFF068;
--background-color: #f8f7f2;
}
body {
background: var(--background-color);
}
@layer components {
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active{
-webkit-box-shadow: 0 0 0 30px var(--background-color) inset !important;
}
.container {
@apply mx-auto px-4;
max-width: 1280px;
}
.btn-custom {
@apply min-h-[2.5rem] h-[2.5rem] px-6;
}
.divider:not(:empty) {
gap: 0;
}
.menu-horizontal > li:not(.menu-title) > details > ul {
padding: 0;
}
:where(.menu-horizontal > li:not(.menu-title) > details > ul) {
border-radius: 0;
}
.tooltip-top::after {
border-color: var(--yellow) transparent transparent transparent;
}
.tooltip-right::after {
border-color: transparent var(--yellow) transparent transparent;
}
.tooltip-bottom::after {
border-color: transparent transparent var(--yellow) transparent;
}
.tooltip-left::after {
border-color: transparent transparent transparent var(--yellow);
}
.tooltip:before {
border: 1px solid var(--yellow);
background-color: var(--yellow);
color: #525252;
border-radius: 8px;
padding: 8px;
}
}

View File

@@ -1,14 +1,14 @@
import { Metadata } from "next";
import Script from "next/script";
import clsx from "clsx";
import usabillaBeScript from "@/scripts/usabilla";
import { gtmScript, gtmNoScript } from "@/scripts/gtm";
import { Providers } from "@/components/layouts/Providers";
import { Layout } from "@/components/layouts/Layout";
import { cn } from "@/utils/cn";
import atypFont from "@/assets/fonts/atyp";
import atypTextFont from "@/assets/fonts/atypText";
import "react-toastify/dist/ReactToastify.css";
import "./globals.css";
import "../scss/app.scss";
const metadata: Metadata = {
title: "Linea Bridge",
@@ -21,16 +21,28 @@ const metadata: Metadata = {
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" data-theme="dark">
<html lang="en" data-theme="v2">
<title>{metadata.title?.toString()}</title>
<meta name="description" content={metadata.description?.toString()} key="desc" />
<body className={cn(atypFont.variable, atypTextFont.variable, atypFont.className, atypTextFont.className)}>
<body className={clsx(atypFont.variable, atypTextFont.variable, atypFont.className, atypTextFont.className)}>
<noscript dangerouslySetInnerHTML={{ __html: gtmNoScript }} />
<Providers>
<Layout>{children}</Layout>
</Providers>
<svg style={{ display: "none" }} viewBox="0 0 9 9" xmlns="http://www.w3.org/2000/svg">
<defs>
<symbol id="icon-new-tab" viewBox="0 0 9 9" fill="none">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M6.95232 0.500055L8.4888 2.03654V6.57541L7.31527 6.57541V2.52263L7.3056 2.51296L1.31858 8.49998L0.48877 7.67016L6.47579 1.68315L6.46622 1.67358L2.37962 1.67356L2.37963 0.500031L6.95232 0.500055Z"
fill="currentColor"
/>
</symbol>
</defs>
</svg>
</body>
<Script id="usabilla" dangerouslySetInnerHTML={{ __html: usabillaBeScript }} />

View 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%;
}
}

View File

@@ -1,10 +1,21 @@
import BridgeLayout from "@/components/bridge/BridgeLayout";
"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 (
<div className="min-w-min max-w-lg md:m-auto">
<h1 className="mb-6 text-4xl font-bold md:hidden">Bridge</h1>
<BridgeLayout />
</div>
<>
<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>
</>
);
}

View File

@@ -1,27 +0,0 @@
"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 (
<div className="m-auto min-w-min max-w-5xl">
<div className="flex min-h-80 flex-col items-center justify-center gap-8 rounded-lg bg-cardBg p-4">
<span>Please connect your wallet.</span>
<ConnectButton />
</div>
</div>
);
}
return (
<div className="m-auto min-w-min max-w-5xl">
<h1 className="mb-6 text-4xl md:hidden">Transactions</h1>
<Transactions />
</div>
);
}

View File

@@ -0,0 +1,4 @@
<svg viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 5L1 5" stroke="currentColor" stroke-width="0.920112" stroke-linejoin="round" />
<path d="M5.5 9.5L1 5L5.5 0.5" stroke="currentColor" stroke-width="0.920112" stroke-linejoin="round" />
</svg>

After

Width:  |  Height:  |  Size: 280 B

View File

@@ -0,0 +1,5 @@
<svg viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.15256e-07 5.20386L9.08949 5.20386" stroke="currentColor" stroke-width="0.52203" stroke-linejoin="round" />
<path d="M4.54475 0.999934L9.08949 5.20382L4.54475 9.40771" stroke="currentColor" stroke-width="0.52203"
stroke-linejoin="round" />
</svg>

After

Width:  |  Height:  |  Size: 338 B

View File

@@ -0,0 +1,7 @@
<svg viewBox="0 0 12 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<g>
<path
d="M5.99999 1.25C6.3328 1.25 6.63983 1.42578 6.80858 1.71406L11.8711 10.3391C12.0422 10.6297 12.0422 10.9883 11.8758 11.2789C11.7094 11.5695 11.3976 11.75 11.0625 11.75H0.937489C0.602333 11.75 0.290614 11.5695 0.124208 11.2789C-0.0421981 10.9883 -0.0398543 10.6273 0.128896 10.3391L5.1914 1.71406C5.36015 1.42578 5.66718 1.25 5.99999 1.25ZM5.99999 4.25C5.68827 4.25 5.43749 4.50078 5.43749 4.8125V7.4375C5.43749 7.74922 5.68827 8 5.99999 8C6.31171 8 6.56249 7.74922 6.56249 7.4375V4.8125C6.56249 4.50078 6.31171 4.25 5.99999 4.25ZM6.74999 9.5C6.74999 9.30109 6.67097 9.11032 6.53032 8.96967C6.38967 8.82902 6.1989 8.75 5.99999 8.75C5.80108 8.75 5.61031 8.82902 5.46966 8.96967C5.32901 9.11032 5.24999 9.30109 5.24999 9.5C5.24999 9.69891 5.32901 9.88968 5.46966 10.0303C5.61031 10.171 5.80108 10.25 5.99999 10.25C6.1989 10.25 6.38967 10.171 6.53032 10.0303C6.67097 9.88968 6.74999 9.69891 6.74999 9.5Z"
fill="currentColor" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,22 +1,10 @@
<svg width="30" height="30" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M18 21C19.6569 21 21 19.6569 21 18C21 16.3431 19.6569 15 18 15C16.3431 15 15 16.3431 15 18C15 19.6569 16.3431 21 18 21Z"
stroke="currentcolor"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M6 9C7.65685 9 9 7.65685 9 6C9 4.34315 7.65685 3 6 3C4.34315 3 3 4.34315 3 6C3 7.65685 4.34315 9 6 9Z"
stroke="currentcolor"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M13 6H16C16.5304 6 17.0391 6.21071 17.4142 6.58579C17.7893 6.96086 18 7.46957 18 8V15"
stroke="currentcolor"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path d="M6 9V21" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" />
</svg>
d="M12 14C13.1046 14 14 13.1046 14 12C14 10.8954 13.1046 10 12 10C10.8954 10 10 10.8954 10 12C10 13.1046 10.8954 14 12 14Z"
stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" />
<path d="M4 6C5.10457 6 6 5.10457 6 4C6 2.89543 5.10457 2 4 2C2.89543 2 2 2.89543 2 4C2 5.10457 2.89543 6 4 6Z"
stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" />
<path d="M8.66669 4H10.6667C11.0203 4 11.3594 4.14048 11.6095 4.39052C11.8595 4.64057 12 4.97971 12 5.33333V10"
stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" />
<path d="M4 6V14" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" />
</svg>

Before

Width:  |  Height:  |  Size: 810 B

After

Width:  |  Height:  |  Size: 855 B

View File

@@ -0,0 +1,4 @@
<svg viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 4.10376L5.5 8.60376L10 4.10376" stroke="#121212" stroke-width="0.75" stroke-linecap="round"
stroke-linejoin="round" />
</svg>

After

Width:  |  Height:  |  Size: 216 B

View File

@@ -0,0 +1,7 @@
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M2.66659 2H13.3333C13.6869 2 14.026 2.14048 14.2761 2.39052C14.5261 2.64057 14.6666 2.97971 14.6666 3.33333V7.33333C14.6666 9.10144 13.9642 10.7971 12.714 12.0474C11.4637 13.2976 9.76803 14 7.99992 14C7.12444 14 6.25753 13.8276 5.4487 13.4925C4.63986 13.1575 3.90493 12.6664 3.28587 12.0474C2.03563 10.7971 1.33325 9.10144 1.33325 7.33333V3.33333C1.33325 2.97971 1.47373 2.64057 1.72378 2.39052C1.97382 2.14048 2.31296 2 2.66659 2Z"
stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" />
<path d="M5.33325 6.66669L7.99992 9.33335L10.6666 6.66669" stroke="currentColor" stroke-width="1.33333"
stroke-linecap="round" stroke-linejoin="round" />
</svg>

After

Width:  |  Height:  |  Size: 786 B

View File

@@ -0,0 +1,4 @@
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.3337 4L6.00033 11.3333L2.66699 8" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round"
stroke-linejoin="round" />
</svg>

After

Width:  |  Height:  |  Size: 227 B

View File

@@ -0,0 +1,7 @@
<svg viewBox="0 0 12 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<g>
<path
d="M6 0.499939C7.5913 0.499939 9.11742 1.13208 10.2426 2.2573C11.3679 3.38252 12 4.90864 12 6.49994C12 8.09124 11.3679 9.61736 10.2426 10.7426C9.11742 11.8678 7.5913 12.4999 6 12.4999C4.4087 12.4999 2.88258 11.8678 1.75736 10.7426C0.632141 9.61736 0 8.09124 0 6.49994C0 4.90864 0.632141 3.38252 1.75736 2.2573C2.88258 1.13208 4.4087 0.499939 6 0.499939ZM5.4375 3.31244V6.49994C5.4375 6.68744 5.53125 6.86322 5.68828 6.96869L7.93828 8.46869C8.19609 8.64213 8.54531 8.57181 8.71875 8.31166C8.89219 8.0515 8.82187 7.70463 8.56172 7.53119L6.5625 6.19994V3.31244C6.5625 3.00072 6.31172 2.74994 6 2.74994C5.68828 2.74994 5.4375 3.00072 5.4375 3.31244Z"
fill="currentColor" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 784 B

View File

@@ -0,0 +1,8 @@
<svg viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M3.2591 13.5478C3.08132 13.5478 2.9628 13.4885 2.84428 13.37C2.60724 13.133 2.60724 12.7774 2.84428 12.5404L12.3258 3.05888C12.5628 2.82184 12.9184 2.82184 13.1554 3.05888C13.3924 3.29592 13.3924 3.65147 13.1554 3.88851L3.67391 13.37C3.55539 13.4885 3.43687 13.5478 3.2591 13.5478Z"
fill="currentColor" />
<path
d="M12.7406 13.5478C12.5628 13.5478 12.4443 13.4885 12.3258 13.37L2.84428 3.88851C2.60724 3.65147 2.60724 3.29592 2.84428 3.05888C3.08132 2.82184 3.43687 2.82184 3.67391 3.05888L13.1554 12.5404C13.3924 12.7774 13.3924 13.133 13.1554 13.37C13.0369 13.4885 12.9184 13.5478 12.7406 13.5478Z"
fill="currentColor" />
</svg>

After

Width:  |  Height:  |  Size: 732 B

View File

@@ -0,0 +1,7 @@
<svg viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<g>
<path
d="M0 16.75C0 17.0938 0.28125 17.375 0.625 17.375H3.1875C3.47656 18.8008 4.73828 19.875 6.25 19.875C7.76172 19.875 9.02344 18.8008 9.3125 17.375H19.375C19.7188 17.375 20 17.0938 20 16.75C20 16.4062 19.7188 16.125 19.375 16.125H9.3125C9.02344 14.6992 7.76172 13.625 6.25 13.625C4.73828 13.625 3.47656 14.6992 3.1875 16.125H0.625C0.28125 16.125 0 16.4062 0 16.75ZM4.375 16.75C4.375 16.2527 4.57254 15.7758 4.92417 15.4242C5.27581 15.0725 5.75272 14.875 6.25 14.875C6.74728 14.875 7.22419 15.0725 7.57583 15.4242C7.92746 15.7758 8.125 16.2527 8.125 16.75C8.125 17.2473 7.92746 17.7242 7.57583 18.0758C7.22419 18.4275 6.74728 18.625 6.25 18.625C5.75272 18.625 5.27581 18.4275 4.92417 18.0758C4.57254 17.7242 4.375 17.2473 4.375 16.75ZM11.875 10.5C11.875 10.2538 11.9235 10.01 12.0177 9.78247C12.112 9.55498 12.2501 9.34828 12.4242 9.17417C12.5983 9.00006 12.805 8.86195 13.0325 8.76773C13.26 8.6735 13.5038 8.625 13.75 8.625C13.9962 8.625 14.24 8.6735 14.4675 8.76773C14.695 8.86195 14.9017 9.00006 15.0758 9.17417C15.2499 9.34828 15.388 9.55498 15.4823 9.78247C15.5765 10.01 15.625 10.2538 15.625 10.5C15.625 10.7462 15.5765 10.99 15.4823 11.2175C15.388 11.445 15.2499 11.6517 15.0758 11.8258C14.9017 11.9999 14.695 12.138 14.4675 12.2323C14.24 12.3265 13.9962 12.375 13.75 12.375C13.5038 12.375 13.26 12.3265 13.0325 12.2323C12.805 12.138 12.5983 11.9999 12.4242 11.8258C12.2501 11.6517 12.112 11.445 12.0177 11.2175C11.9235 10.99 11.875 10.7462 11.875 10.5ZM13.75 7.375C12.2383 7.375 10.9766 8.44922 10.6875 9.875H0.625C0.28125 9.875 0 10.1562 0 10.5C0 10.8438 0.28125 11.125 0.625 11.125H10.6875C10.9766 12.5508 12.2383 13.625 13.75 13.625C15.2617 13.625 16.5234 12.5508 16.8125 11.125H19.375C19.7188 11.125 20 10.8438 20 10.5C20 10.1562 19.7188 9.875 19.375 9.875H16.8125C16.5234 8.44922 15.2617 7.375 13.75 7.375ZM7.5 6.125C7.00272 6.125 6.52581 5.92746 6.17417 5.57583C5.82254 5.22419 5.625 4.74728 5.625 4.25C5.625 3.75272 5.82254 3.27581 6.17417 2.92417C6.52581 2.57254 7.00272 2.375 7.5 2.375C7.99728 2.375 8.47419 2.57254 8.82583 2.92417C9.17746 3.27581 9.375 3.75272 9.375 4.25C9.375 4.74728 9.17746 5.22419 8.82583 5.57583C8.47419 5.92746 7.99728 6.125 7.5 6.125ZM10.5625 3.625C10.2734 2.19922 9.01172 1.125 7.5 1.125C5.98828 1.125 4.72656 2.19922 4.4375 3.625H0.625C0.28125 3.625 0 3.90625 0 4.25C0 4.59375 0.28125 4.875 0.625 4.875H4.4375C4.72656 6.30078 5.98828 7.375 7.5 7.375C9.01172 7.375 10.2734 6.30078 10.5625 4.875H19.375C19.7188 4.875 20 4.59375 20 4.25C20 3.90625 19.7188 3.625 19.375 3.625H10.5625Z"
fill="currentColor" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,10 @@
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g>
<path
d="M13.3333 6H7.33333C6.59695 6 6 6.59695 6 7.33333V13.3333C6 14.0697 6.59695 14.6667 7.33333 14.6667H13.3333C14.0697 14.6667 14.6667 14.0697 14.6667 13.3333V7.33333C14.6667 6.59695 14.0697 6 13.3333 6Z"
stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" />
<path
d="M3.33325 9.99992H2.66659C2.31296 9.99992 1.97382 9.85944 1.72378 9.60939C1.47373 9.35935 1.33325 9.02021 1.33325 8.66659V2.66659C1.33325 2.31296 1.47373 1.97382 1.72378 1.72378C1.97382 1.47373 2.31296 1.33325 2.66659 1.33325H8.66659C9.02021 1.33325 9.35935 1.47373 9.60939 1.72378C9.85944 1.97382 9.99992 2.31296 9.99992 2.66659V3.33325"
stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 871 B

View File

@@ -1,13 +1,13 @@
<svg width="30" height="30" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M14 2H6C5.46957 2 4.96086 2.21071 4.58579 2.58579C4.21071 2.96086 4 3.46957 4 4V20C4 20.5304 4.21071 21.0391 4.58579 21.4142C4.96086 21.7893 5.46957 22 6 22H18C18.5304 22 19.0391 21.7893 19.4142 21.4142C19.7893 21.0391 20 20.5304 20 20V8L14 2Z"
stroke="currentcolor"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path d="M14 2V8H20" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" />
<path d="M16 13H8" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" />
<path d="M16 17H8" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" />
<path d="M10 9H9H8" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" />
</svg>
d="M9.33335 1.33325H4.00002C3.6464 1.33325 3.30726 1.47373 3.05721 1.72378C2.80716 1.97382 2.66669 2.31296 2.66669 2.66659V13.3333C2.66669 13.6869 2.80716 14.026 3.05721 14.2761C3.30726 14.5261 3.6464 14.6666 4.00002 14.6666H12C12.3536 14.6666 12.6928 14.5261 12.9428 14.2761C13.1929 14.026 13.3334 13.6869 13.3334 13.3333V5.33325L9.33335 1.33325Z"
stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" />
<path d="M9.33331 1.33325V5.33325H13.3333" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round"
stroke-linejoin="round" />
<path d="M10.6666 8.66675H5.33331" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round"
stroke-linejoin="round" />
<path d="M10.6666 11.3333H5.33331" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round"
stroke-linejoin="round" />
<path d="M6.66665 6H5.99998H5.33331" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round"
stroke-linejoin="round" />
</svg>

Before

Width:  |  Height:  |  Size: 825 B

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,15 +1,17 @@
<svg width="30" height="30" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z"
stroke="currentcolor"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M9.08997 8.99996C9.32507 8.33163 9.78912 7.76807 10.3999 7.40909C11.0107 7.05012 11.7289 6.9189 12.4271 7.03867C13.1254 7.15844 13.7588 7.52148 14.215 8.06349C14.6713 8.60549 14.921 9.29148 14.92 9.99996C14.92 12 11.92 13 11.92 13"
stroke="currentcolor"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path d="M12 17H12.01" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" />
</svg>
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1373_4410)">
<path
d="M7.99998 14.6666C11.6819 14.6666 14.6666 11.6818 14.6666 7.99992C14.6666 4.31802 11.6819 1.33325 7.99998 1.33325C4.31808 1.33325 1.33331 4.31802 1.33331 7.99992C1.33331 11.6818 4.31808 14.6666 7.99998 14.6666Z"
stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" />
<path
d="M6.06 5.99989C6.21673 5.55434 6.5261 5.17863 6.9333 4.93931C7.3405 4.7 7.81926 4.61252 8.28478 4.69237C8.7503 4.77222 9.17254 5.01424 9.47672 5.37558C9.78089 5.73691 9.94737 6.19424 9.94666 6.66656C9.94666 7.99989 7.94666 8.66656 7.94666 8.66656"
stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" />
<path d="M8 11.3333H8.00667" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round"
stroke-linejoin="round" />
</g>
<defs>
<clipPath id="clip0_1373_4410">
<rect width="16" height="16" fill="white" />
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 749 B

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,5 @@
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M13.7712 6.23904L12.6549 5.66072C12.4484 5.55663 12.1916 5.63759 12.0912 5.85157C11.9851 6.07132 12.0688 6.33157 12.2753 6.43566L13.1628 6.89253V9.66265L11.0698 9.66843V3.73494C11.0698 2.57831 10.3219 2 9.39535 2H4.93023C4.00372 2 3.25581 2.57831 3.25581 3.73494V13.1325H2.4186C2.18977 13.1325 2 13.3292 2 13.5663C2 13.8034 2.18977 14 2.4186 14H11.907C12.1358 14 12.3256 13.8034 12.3256 13.5663C12.3256 13.3292 12.1358 13.1325 11.907 13.1325H11.0698V10.5359L13.5814 10.5301C13.8158 10.5301 14 10.3335 14 10.0964V6.62651C14 6.46458 13.9107 6.31422 13.7712 6.23904ZM4.65116 4.82795C4.65116 4.0241 5.12558 3.73494 5.70605 3.73494H8.62512C9.2 3.73494 9.67442 4.0241 9.67442 4.82795V5.53928C9.67442 6.33735 9.2 6.62651 8.61953 6.62651H5.70605C5.12558 6.62651 4.65116 6.33735 4.65116 5.53349V4.82795ZM4.93023 7.92771H6.60465C6.83349 7.92771 7.02326 8.12434 7.02326 8.36145C7.02326 8.59855 6.83349 8.79518 6.60465 8.79518H4.93023C4.7014 8.79518 4.51163 8.59855 4.51163 8.36145C4.51163 8.12434 4.7014 7.92771 4.93023 7.92771Z"
fill="currentColor" />
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,6 @@
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 3.33325V12.6666" stroke="currentColor" strokeWidth="1.33333" strokeLinecap="round"
strokeLinejoin="round" />
<path d="M3.33398 8H12.6673" stroke="currentColor" strokeWidth="1.33333" strokeLinecap="round"
strokeLinejoin="round" />
</svg>

After

Width:  |  Height:  |  Size: 333 B

View File

@@ -0,0 +1,6 @@
<svg viewBox="0 0 12 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M5.5002 9.71452C7.70946 9.71452 9.50041 7.92357 9.50041 5.71431C9.50041 3.50506 7.70946 1.71411 5.5002 1.71411C3.29095 1.71411 1.5 3.50506 1.5 5.71431C1.5 7.92357 3.29095 9.71452 5.5002 9.71452Z"
stroke="#525252" stroke-linecap="round" stroke-linejoin="round" />
<path d="M10.5003 10.7142L8.3252 8.53906" stroke="#525252" stroke-linecap="round" stroke-linejoin="round" />
</svg>

After

Width:  |  Height:  |  Size: 472 B

View File

@@ -0,0 +1,5 @@
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M15.1015 5.30222C15.1953 5.55716 15.1162 5.84139 14.914 6.02307L13.6452 7.17759C13.6774 7.4208 13.695 7.66987 13.695 7.92188C13.695 8.17388 13.6774 8.42295 13.6452 8.66616L14.914 9.82068C15.1162 10.0024 15.1953 10.2866 15.1015 10.5415C14.9726 10.8902 14.8173 11.2243 14.6385 11.5466L14.5008 11.784C14.3074 12.1063 14.0906 12.411 13.8532 12.6982C13.6803 12.9092 13.3932 12.9795 13.1353 12.8975L11.5032 12.3788C11.1105 12.6806 10.6768 12.9326 10.2138 13.1231L9.84756 14.7963C9.78895 15.0629 9.58384 15.2739 9.31425 15.3179C8.90988 15.3852 8.49378 15.4204 8.06889 15.4204C7.644 15.4204 7.22791 15.3852 6.82353 15.3179C6.55395 15.2739 6.34883 15.0629 6.29022 14.7963L5.92394 13.1231C5.46096 12.9326 5.02728 12.6806 4.63463 12.3788L3.0054 12.9004C2.74754 12.9824 2.46038 12.9092 2.28749 12.7011C2.05014 12.414 1.8333 12.1092 1.6399 11.7869L1.50218 11.5495C1.32343 11.2272 1.16813 10.8932 1.0392 10.5445C0.945431 10.2895 1.02455 10.0053 1.22674 9.82361L2.49554 8.66909C2.46331 8.42295 2.44572 8.17388 2.44572 7.92188C2.44572 7.66987 2.46331 7.4208 2.49554 7.17759L1.22674 6.02307C1.02455 5.84139 0.945431 5.55716 1.0392 5.30222C1.16813 4.95352 1.32343 4.61947 1.50218 4.29714L1.6399 4.05979C1.8333 3.73746 2.05014 3.43272 2.28749 3.14555C2.46038 2.93457 2.74754 2.86425 3.0054 2.94629L4.63756 3.46495C5.03021 3.16313 5.46389 2.91113 5.92687 2.72066L6.29316 1.04749C6.35176 0.780832 6.55688 0.569853 6.82646 0.5259C7.23084 0.455573 7.64693 0.42041 8.07182 0.42041C8.49671 0.42041 8.91281 0.455573 9.31718 0.522969C9.58677 0.566923 9.79189 0.777902 9.85049 1.04456L10.2168 2.71773C10.6798 2.9082 11.1134 3.1602 11.5061 3.46202L13.1382 2.94336C13.3961 2.86132 13.6833 2.93457 13.8562 3.14262C14.0935 3.42979 14.3103 3.73453 14.5037 4.05686L14.6415 4.29421C14.8202 4.61654 14.9755 4.95059 15.1044 5.29929L15.1015 5.30222ZM8.07182 10.2661C8.69354 10.2661 9.2898 10.0191 9.72943 9.57948C10.1691 9.13986 10.416 8.5436 10.416 7.92188C10.416 7.30015 10.1691 6.70389 9.72943 6.26427C9.2898 5.82465 8.69354 5.57767 8.07182 5.57767C7.4501 5.57767 6.85384 5.82465 6.41422 6.26427C5.97459 6.70389 5.72761 7.30015 5.72761 7.92188C5.72761 8.5436 5.97459 9.13986 6.41422 9.57948C6.85384 10.0191 7.4501 10.2661 8.07182 10.2661Z"
fill="currentColor" />
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,13 @@
<svg viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="20" fill="currentColor" fill-opacity="0.1" />
<path d="M24.166 10.8333L27.4993 14.1666L24.166 17.4999" stroke="currentColor" stroke-width="1.66667"
stroke-linecap="round" stroke-linejoin="round" />
<path
d="M12.5 19.1667V17.5001C12.5 16.616 12.8512 15.7682 13.4763 15.1431C14.1014 14.5179 14.9493 14.1667 15.8333 14.1667H27.5"
stroke="currentColor" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" />
<path d="M15.8333 29.1667L12.5 25.8333L15.8333 22.5" stroke="currentColor" stroke-width="1.66667"
stroke-linecap="round" stroke-linejoin="round" />
<path
d="M27.5 20.8333V22.4999C27.5 23.384 27.1488 24.2318 26.5237 24.8569C25.8986 25.4821 25.0507 25.8333 24.1667 25.8333H12.5"
stroke="currentColor" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" />
</svg>

After

Width:  |  Height:  |  Size: 940 B

View File

@@ -0,0 +1,4 @@
<svg stroke="currentColor" stroke-width="0" fill="currentColor" viewBox="0 0 24 24" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg">
<path d="M19.5 3.5 18 2l-1.5 1.5L15 2l-1.5 1.5L12 2l-1.5 1.5L9 2 7.5 3.5 6 2v14H3v3c0 1.66 1.34 3 3 3h12c1.66 0 3-1.34 3-3V2zM19 19c0 .55-.45 1-1 1s-1-.45-1-1v-3H8V5h11z"></path>
<path d="M9 7h6v2H9zm7 0h2v2h-2zm-7 3h6v2H9zm7 0h2v2h-2z"></path>
</svg>

After

Width:  |  Height:  |  Size: 396 B

View File

@@ -1,27 +1,11 @@
<svg width="30" height="30" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.3333 0.666748L14 3.33341L11.3333 6.00008" stroke="currentColor" stroke-width="1.33333"
stroke-linecap="round" stroke-linejoin="round" />
<path d="M2 7.33325V5.99992C2 5.29267 2.28095 4.6144 2.78105 4.1143C3.28115 3.6142 3.95942 3.33325 4.66667 3.33325H14"
stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" />
<path d="M4.66667 15.3333L2 12.6667L4.66667 10" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round"
stroke-linejoin="round" />
<path
d="M17 1L21 5L17 9"
stroke="currentcolor"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M3 11V9C3 7.93913 3.42143 6.92172 4.17157 6.17157C4.92172 5.42143 5.93913 5 7 5H21"
stroke="currentcolor"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M7 23L3 19L7 15"
stroke="currentcolor"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M21 13V15C21 16.0609 20.5786 17.0783 19.8284 17.8284C19.0783 18.5786 18.0609 19 17 19H3"
stroke="currentcolor"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
d="M14 8.66675V10.0001C14 10.7073 13.719 11.3856 13.219 11.8857C12.7189 12.3858 12.0406 12.6667 11.3333 12.6667H2"
stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" />
</svg>

Before

Width:  |  Height:  |  Size: 715 B

After

Width:  |  Height:  |  Size: 830 B

View File

@@ -0,0 +1,5 @@
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M6.46451 -0.000707992L8.00124 1.53602V6.07562H6.82752V2.02219L6.81792 2.01259L0.829945 8.00057L0 7.17062L5.98797 1.18265L5.97834 1.17301L1.89108 1.17299L1.89108 -0.000732422L6.46451 -0.000707992Z"
fill="currentColor" />
</svg>

After

Width:  |  Height:  |  Size: 377 B

View File

@@ -0,0 +1,4 @@
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 24 24" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg">
<path fill="none" d="M0 0h24v24H0z"></path>
<path d="M18 4H6C3.79 4 2 5.79 2 8v8c0 2.21 1.79 4 4 4h12c2.21 0 4-1.79 4-4V8c0-2.21-1.79-4-4-4zm-1.86 9.77c-.24.2-.57.28-.88.2L4.15 11.25C4.45 10.52 5.16 10 6 10h12c.67 0 1.26.34 1.63.84l-3.49 2.93zM6 6h12c1.1 0 2 .9 2 2v.55c-.59-.34-1.27-.55-2-.55H6c-.73 0-1.41.21-2 .55V8c0-1.1.9-2 2-2z"></path>
</svg>

After

Width:  |  Height:  |  Size: 494 B

View File

@@ -0,0 +1,9 @@
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g>
<path
d="M8.00065 14.6666C11.6825 14.6666 14.6673 11.6818 14.6673 7.99992C14.6673 4.31802 11.6825 1.33325 8.00065 1.33325C4.31875 1.33325 1.33398 4.31802 1.33398 7.99992C1.33398 11.6818 4.31875 14.6666 8.00065 14.6666Z"
stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" />
<path d="M10 6L6 10" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" />
<path d="M6 6L10 10" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 663 B

View File

@@ -0,0 +1,34 @@
<svg viewBox="0 0 172 31" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M23.3528 26.155H19.6505V8.13879H23.3528V26.155Z" fill="currentColor" />
<path
d="M36.7305 7.78516C38.7743 7.78516 40.444 8.50179 41.7376 9.93504C43.0294 11.3683 43.6772 13.1906 43.6772 15.4V26.1532H39.9749V15.964C39.9749 14.5773 39.5282 13.4307 38.6347 12.5261C37.7413 11.6215 36.6375 11.1691 35.3215 11.1691C33.8398 11.1691 32.6299 11.6215 31.6899 12.5261C30.7499 13.4307 30.279 14.5773 30.279 15.964V26.1532H26.5767V8.13882H30.279V12.7923C30.7723 11.2417 31.5894 10.0188 32.7304 9.12535C33.8696 8.23189 35.2042 7.78516 36.7324 7.78516H36.7305Z"
fill="currentColor" />
<path
d="M55.2196 7.78516C58.1103 7.78516 60.4612 8.82008 62.2705 10.8881C64.0797 12.9579 64.8206 15.4596 64.4911 18.3987H49.5778C49.8365 19.8562 50.5066 21.0382 51.588 21.9428C52.6695 22.8474 53.9725 23.2997 55.5007 23.2997C56.6752 23.2997 57.7269 23.0112 58.6557 22.436C59.5845 21.8609 60.3067 21.0791 60.8242 20.0907L63.9271 21.4663C63.1286 22.9945 61.9876 24.2155 60.5078 25.1332C59.028 26.0508 57.3229 26.5087 55.3946 26.5087C52.6918 26.5087 50.4228 25.6153 48.5894 23.8302C46.7559 22.0452 45.8383 19.8227 45.8383 17.1665C45.8383 14.5103 46.7373 12.2841 48.5354 10.4842C50.3335 8.68606 52.5597 7.78702 55.2177 7.78702L55.2196 7.78516ZM55.2196 10.9942C53.8552 10.9942 52.6751 11.4111 51.6755 12.2469C50.676 13.0808 50.0115 14.1678 49.6839 15.508H60.7553C60.4501 14.1678 59.7967 13.0808 58.799 12.2469C57.7995 11.413 56.6063 10.9942 55.2196 10.9942Z"
fill="currentColor" />
<path
d="M74.7268 7.78516C76.8897 7.78516 78.6524 8.34915 80.0149 9.47715C81.3775 10.6051 82.0606 12.2283 82.0606 14.3428V26.1551H78.3583V21.3602C77.2527 24.7925 75.032 26.5087 71.6946 26.5087C70.1422 26.5087 68.8504 26.0453 67.8155 25.1164C66.7806 24.1876 66.265 22.9721 66.265 21.4681C66.265 19.7054 66.8867 18.382 68.1338 17.5015C69.379 16.6211 70.9668 16.0739 72.8933 15.8617L76.3834 15.508C77.6994 15.3907 78.3583 14.8025 78.3583 13.7453C78.3583 12.876 78.0233 12.1817 77.3532 11.6661C76.6831 11.1487 75.8082 10.8899 74.7268 10.8899C73.6453 10.8899 72.6867 11.1729 71.9235 11.7369C71.1604 12.3009 70.6727 13.1236 70.4605 14.205L66.7936 13.3227C67.123 11.6308 68.0035 10.285 69.4386 9.28543C70.8719 8.28773 72.6346 7.78702 74.7268 7.78702V7.78516ZM72.8933 23.5101C74.4215 23.5101 75.7133 22.9982 76.7706 21.9763C77.8278 20.9544 78.3564 19.7612 78.3564 18.3969V17.4103C78.0754 17.8813 77.3923 18.1735 76.3108 18.2908L72.8915 18.7133C71.998 18.8324 71.2869 19.0949 70.7583 19.5062C70.2297 19.9176 69.9654 20.476 69.9654 21.1815C69.9654 21.8869 70.2297 22.4509 70.7583 22.8735C71.2869 23.296 71.998 23.5082 72.8915 23.5082L72.8933 23.5101Z"
fill="currentColor" />
<path d="M17.1005 26.155H0V8.13879H3.91261V22.665H17.1005V26.155Z" fill="currentColor" />
<path
d="M85.5507 7.78509C87.4782 7.78509 89.0408 6.22253 89.0408 4.29501C89.0408 2.36749 87.4782 0.804932 85.5507 0.804932C83.6232 0.804932 82.0606 2.36749 82.0606 4.29501C82.0606 6.22253 83.6232 7.78509 85.5507 7.78509Z"
fill="currentColor" />
<path
d="M164.964 13.4761C166.836 13.4761 168.372 14.1401 169.572 15.4681C170.788 16.7801 171.324 18.4041 171.18 20.3401H160.5C160.628 21.5561 161.116 22.5561 161.964 23.3401C162.828 24.1081 163.876 24.4921 165.108 24.4921C166.052 24.4921 166.892 24.2601 167.628 23.7961C168.364 23.3321 168.924 22.7001 169.308 21.9001L170.844 22.5481C170.332 23.6201 169.572 24.4841 168.564 25.1401C167.556 25.7801 166.396 26.1001 165.084 26.1001C163.276 26.1001 161.756 25.5001 160.524 24.3001C159.308 23.0841 158.7 21.5801 158.7 19.7881C158.7 17.9961 159.3 16.5001 160.5 15.3001C161.7 14.0841 163.188 13.4761 164.964 13.4761ZM164.964 15.0841C163.86 15.0841 162.9 15.4361 162.084 16.1401C161.268 16.8441 160.756 17.7481 160.548 18.8521H169.356C169.18 17.7641 168.684 16.8681 167.868 16.1641C167.068 15.4441 166.1 15.0841 164.964 15.0841Z"
fill="currentColor" />
<path
d="M154.315 17.1961V13.7161H156.115V24.5881C156.115 26.3801 155.499 27.8761 154.267 29.0761C153.051 30.2921 151.539 30.9001 149.731 30.9001C148.435 30.9001 147.283 30.5721 146.275 29.9161C145.267 29.2761 144.507 28.4201 143.995 27.3481L145.627 26.5801C146.011 27.3961 146.563 28.0441 147.283 28.5241C148.003 29.0041 148.819 29.2441 149.731 29.2441C151.027 29.2441 152.115 28.7961 152.995 27.9001C153.875 27.0201 154.315 25.9161 154.315 24.5881V22.2361C153.899 23.3721 153.235 24.2761 152.323 24.9481C151.411 25.6201 150.355 25.9561 149.155 25.9561C147.507 25.9561 146.131 25.3641 145.027 24.1801C143.939 22.9801 143.395 21.4921 143.395 19.7161C143.395 17.9401 143.939 16.4601 145.027 15.2761C146.131 14.0761 147.507 13.4761 149.155 13.4761C150.355 13.4761 151.411 13.8121 152.323 14.4841C153.235 15.1561 153.899 16.0601 154.315 17.1961ZM149.683 24.3001C151.027 24.3001 152.131 23.8681 152.995 23.0041C153.875 22.1241 154.315 21.0281 154.315 19.7161C154.315 18.4201 153.875 17.3401 152.995 16.4761C152.131 15.5961 151.027 15.1561 149.683 15.1561C148.435 15.1561 147.379 15.5961 146.515 16.4761C145.667 17.3401 145.243 18.4201 145.243 19.7161C145.243 21.0121 145.667 22.1001 146.515 22.9801C147.379 23.8601 148.435 24.3001 149.683 24.3001Z"
fill="currentColor" />
<path
d="M139.01 17.244V8.09998H140.81V25.86H139.01V22.332C138.594 23.484 137.93 24.404 137.018 25.092C136.106 25.764 135.05 26.1 133.85 26.1C132.202 26.1 130.826 25.5 129.722 24.3C128.634 23.1 128.09 21.596 128.09 19.788C128.09 17.996 128.634 16.5 129.722 15.3C130.826 14.084 132.202 13.476 133.85 13.476C135.05 13.476 136.106 13.82 137.018 14.508C137.93 15.18 138.594 16.092 139.01 17.244ZM134.354 24.42C135.698 24.42 136.81 23.98 137.69 23.1C138.57 22.22 139.01 21.116 139.01 19.788C139.01 18.46 138.57 17.356 137.69 16.476C136.81 15.596 135.698 15.156 134.354 15.156C133.122 15.156 132.074 15.604 131.21 16.5C130.362 17.38 129.938 18.476 129.938 19.788C129.938 21.1 130.362 22.204 131.21 23.1C132.074 23.98 133.122 24.42 134.354 24.42Z"
fill="currentColor" />
<path
d="M123.431 10.1159C123.431 9.77994 123.543 9.49994 123.767 9.27594C123.991 9.05194 124.271 8.93994 124.607 8.93994C124.927 8.93994 125.199 9.05194 125.423 9.27594C125.663 9.49994 125.783 9.77994 125.783 10.1159C125.783 10.4359 125.663 10.7079 125.423 10.9319C125.199 11.1559 124.927 11.2679 124.607 11.2679C124.271 11.2679 123.991 11.1559 123.767 10.9319C123.543 10.7079 123.431 10.4359 123.431 10.1159ZM125.495 25.8599H123.695V13.7159H125.495V25.8599Z"
fill="currentColor" />
<path
d="M116.73 13.7159V16.9799C117.146 15.5879 117.786 14.6199 118.65 14.0759C119.53 13.5319 120.538 13.3639 121.674 13.5719V15.3239C120.314 15.0199 119.146 15.2279 118.17 15.9479C117.21 16.6519 116.73 17.6439 116.73 18.9239V25.8599H114.93V13.7159H116.73Z"
fill="currentColor" />
<path
d="M108.016 17.3881C109.296 17.5001 110.32 17.9241 111.088 18.6601C111.872 19.3801 112.264 20.3081 112.264 21.4441C112.264 22.6921 111.808 23.7401 110.896 24.5881C110 25.4361 108.848 25.8601 107.44 25.8601H100V9.06006H106.72C108.128 9.06006 109.28 9.48406 110.176 10.3321C111.088 11.1801 111.544 12.2281 111.544 13.4761C111.544 14.5001 111.224 15.3561 110.584 16.0441C109.96 16.7321 109.104 17.1801 108.016 17.3881ZM106.6 10.7161H101.848V16.6441H106.6C107.528 16.6441 108.288 16.3641 108.88 15.8041C109.472 15.2281 109.768 14.5161 109.768 13.6681C109.768 12.8361 109.472 12.1401 108.88 11.5801C108.288 11.0041 107.528 10.7161 106.6 10.7161ZM101.848 24.2041H107.224C108.136 24.2041 108.888 23.9161 109.48 23.3401C110.072 22.7641 110.368 22.0601 110.368 21.2281C110.368 20.3801 110.072 19.6761 109.48 19.1161C108.888 18.5561 108.136 18.2761 107.224 18.2761H101.848V24.2041Z"
fill="currentColor" />
</svg>

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@@ -1,24 +0,0 @@
import { useAppKit } from "@reown/appkit/react";
import { cn } from "@/utils/cn";
import { Button } from "./ui";
type ConnectButtonProps = {
fullWidth?: boolean;
};
export default function ConnectButton({ fullWidth }: ConnectButtonProps) {
const { open } = useAppKit();
return (
<Button
id="wallet-connect-btn"
variant="primary"
size="md"
className={cn("text-lg font-normal", {
"w-full": fullWidth,
})}
onClick={() => open()}
>
Connect Wallet
</Button>
);
}

View File

@@ -1,54 +0,0 @@
import Image from "next/image";
import Link from "next/link";
import { MdCallMade } from "react-icons/md";
import { cn } from "@/utils/cn";
export type DropdownItemProps = {
title: string;
onClick?: () => void;
iconPath?: string;
externalLink?: string;
className?: string;
};
export default function DropdownItem({ title, iconPath, onClick, externalLink, className }: DropdownItemProps) {
if (externalLink) {
return (
<li key={`dropdown-item-${title}`} className="w-full">
<Link
href={externalLink}
passHref
target="_blank"
rel="noopener noreferrer"
className={cn(
"btn btn-md flex justify-start font-normal rounded-none border-none bg-backgroundColor hover:bg-primary-light",
className,
)}
>
{iconPath && (
<Image src={iconPath} alt={title} width={18} height={18} style={{ width: "18px", height: "auto" }} />
)}
{title}
<MdCallMade />
</Link>
</li>
);
}
return (
<li key={`dropdown-item-${title}`} className="w-full">
<button
className={cn(
"btn btn-md flex justify-start font-normal border-none bg-backgroundColor hover:bg-primary-light rounded-none",
className,
)}
onClick={onClick}
>
{iconPath && (
<Image src={iconPath} alt={title} width={18} height={18} style={{ width: "18px", height: "auto" }} />
)}
{title}
</button>
</li>
);
}

View File

@@ -1,36 +0,0 @@
import { useQueryClient } from "@tanstack/react-query";
import { useEffect } from "react";
import { useBlockNumber } from "wagmi";
import { formatBalance } from "@/utils/format";
import { useFormContext } from "react-hook-form";
import { useChainStore } from "@/stores/chainStore";
import { useTokenBalance } from "@/hooks/useTokenBalance";
export function Balance() {
// Context
const { token, networkLayer } = useChainStore((state) => ({
token: state.token,
networkLayer: state.networkLayer,
}));
const tokenAddress = token?.[networkLayer];
// Wagmi
const queryClient = useQueryClient();
const { data: blockNumber } = useBlockNumber({ watch: true });
const { balance, queryKey } = useTokenBalance(tokenAddress, token?.decimals);
// Form
const { setValue } = useFormContext();
useEffect(() => {
setValue("balance", balance);
}, [balance, setValue, token?.decimals]);
useEffect(() => {
if (blockNumber && blockNumber % 5n === 0n) {
queryClient.invalidateQueries({ queryKey });
}
}, [blockNumber, queryClient, queryKey]);
return <span className="label-text ml-1">{balance && `Balance: ${formatBalance(balance)} ${token?.symbol}`}</span>;
}

View File

@@ -1,202 +0,0 @@
"use client";
import { useContext, useEffect, useMemo, useState } from "react";
import { useAccount, useWaitForTransactionReceipt } from "wagmi";
import { BridgeExternal } from "./BridgeExternal";
import { FromChain } from "./FromChain";
import { Balance } from "./Balance";
import { Amount } from "./form/Amount";
import SwapIcon from "@/assets/icons/swap.svg";
import { ToChain } from "./ToChain";
import { ReceivedAmount } from "./ReceivedAmount";
import { Recipient } from "./form/Recipient";
import { ClaimingType } from "./form/ClaimingType";
import { Fees } from "./fees";
import { Submit } from "./form/Submit";
import { useFormContext } from "react-hook-form";
import { BridgeForm, Transaction } from "@/models";
import { useChainStore } from "@/stores/chainStore";
import { NetworkLayer, TokenType } from "@/config";
import { useBridge, useSwitchNetwork, useFetchHistory } from "@/hooks";
import TokenList from "./TokenList";
import { toast } from "react-toastify";
import { ERC20Stepper } from "./ERC20Stepper";
import ConnectButton from "../ConnectButton";
import { cn } from "@/utils/cn";
import { useReceivedAmount } from "@/hooks/useReceivedAmount";
import { ModalContext } from "@/contexts/modal.context";
import TransactionConfirmationModal from "./modals/TransactionConfirmationModal";
const Bridge = () => {
const [waitingTransaction, setWaitingTransaction] = useState<Transaction | undefined>();
const { handleShow, handleClose } = useContext(ModalContext);
const fromChain = useChainStore((state) => state.fromChain);
const token = useChainStore((state) => state.token);
const networkLayer = useChainStore((state) => state.networkLayer);
const switchChainInStore = useChainStore((state) => state.switchChain);
const { handleSubmit, watch, reset, setValue } = useFormContext<BridgeForm>();
const [amount, bridgingAllowed, claim] = watch(["amount", "bridgingAllowed", "claim"]);
const enoughAllowance = useMemo(() => bridgingAllowed || token?.type === TokenType.ETH, [bridgingAllowed, token]);
const { totalReceived, fees } = useReceivedAmount({
amount,
enoughAllowance,
claim,
});
const { fetchHistory } = useFetchHistory();
const { isConnected, address } = useAccount();
const {
isLoading: isWaitingLoading,
isSuccess: isWaitingSuccess,
isError: isWaitingError,
} = useWaitForTransactionReceipt({
hash: waitingTransaction?.txHash,
chainId: waitingTransaction?.chainId,
});
const { switchChain } = useSwitchNetwork(fromChain?.id);
const { hash, isLoading, bridge, error: bridgeError } = useBridge();
// Set tx hash to trigger useWaitForTransaction
useEffect(() => {
if (hash) {
setWaitingTransaction({
txHash: hash,
chainId: fromChain?.id,
name: fromChain?.name,
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [hash]);
useEffect(() => {
setValue("gasFees", fees.transactionFeeInWei);
}, [fees.transactionFeeInWei, setValue]);
// Clear tx waiting when changing account
useEffect(() => {
setWaitingTransaction(undefined);
}, [address]);
useEffect(() => {
if (isWaitingSuccess && waitingTransaction) {
handleShow(<TransactionConfirmationModal handleClose={handleClose} />, {
showCloseButton: true,
});
setWaitingTransaction(undefined);
fetchHistory();
reset();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isWaitingSuccess, waitingTransaction]);
useEffect(() => {
if (isWaitingError) {
toast.error("Token bridging failed.");
setWaitingTransaction(undefined);
}
}, [isWaitingError]);
useEffect(() => {
if (bridgeError?.displayInToast) {
toast.error(
<>
{bridgeError.message}
<a href={bridgeError.link} target="_blank" className="ml-1 underline">
here
</a>
</>,
{
autoClose: false,
draggable: false,
closeOnClick: false,
style: { width: 500, marginLeft: -90 },
},
);
}
}, [bridgeError]);
// Click on approve
const onSubmit = async (data: BridgeForm) => {
if (isLoading || isWaitingLoading) return;
await switchChain();
bridge(data.amount, data.minFees, data.recipient);
};
return (
<>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="min-w-min max-w-lg rounded-lg bg-cardBg p-6 shadow-lg sm:p-4">
<div
className={cn({
"opacity-40 pointer-events-none": !isConnected,
})}
>
<FromChain />
<div className="mb-8">
<div className="grid grid-flow-col items-center gap-2 rounded-lg bg-backgroundColor p-3">
<div className="grid grid-flow-row gap-2">
<TokenList />
<Balance />
</div>
<div className="grid grid-flow-row">
<Amount />
</div>
</div>
</div>
<div className="divider my-6 flex justify-center">
<button
className="btn btn-circle w-fit border-none bg-transparent shadow-none transition-transform duration-200 hover:border-none hover:bg-transparent hover:shadow-none"
onClick={(e) => {
e.preventDefault();
e.currentTarget.classList.toggle("rotate-180");
switchChainInStore();
reset();
}}
>
<SwapIcon />
</button>
</div>
<ToChain />
<div className="mb-4">
<ReceivedAmount receivedAmount={totalReceived} />
</div>
<div className="mb-4">
<Recipient />
</div>
<div className="mb-7">
<ClaimingType />
</div>
<div className="mb-7">{isConnected && <Fees totalReceived={totalReceived} fees={fees} />}</div>
<div className="text-center">
{isConnected && <Submit isLoading={isLoading} isWaitingLoading={isWaitingLoading} />}
</div>
<div className="mt-4">{isConnected && <BridgeExternal />}</div>
</div>
{!isConnected && <ConnectButton fullWidth />}
</div>
</form>
{isConnected && token && networkLayer !== NetworkLayer.UNKNOWN && token[networkLayer] && (
<div className="mt-4 px-2">
<ERC20Stepper />
</div>
)}
</>
);
};
export default Bridge;

View File

@@ -1,17 +0,0 @@
import Link from "next/link";
import { MdArrowOutward } from "react-icons/md";
export function BridgeExternal() {
return (
<Link
href="https://linea.build/apps?types=bridge"
passHref
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-center gap-2 p-2"
>
<span>Bridge using Third-Party bridges</span>
<MdArrowOutward className="text-secondary" />
</Link>
);
}

View File

@@ -1,67 +0,0 @@
"use client";
import { useAccount } from "wagmi";
import { MdWarning } from "react-icons/md";
import Link from "next/link";
import Bridge from "../bridge/Bridge";
import { BridgeExternal } from "./BridgeExternal";
import { useTokenStore } from "@/stores/tokenStoreProvider";
import { FormProvider, useForm } from "react-hook-form";
import { BridgeForm } from "@/models";
import { useChainStore } from "@/stores/chainStore";
import { TokenType } from "@/config";
export default function BridgeLayout() {
const { isConnected } = useAccount();
const configContextValue = useTokenStore((state) => state.tokensList);
const token = useChainStore((state) => state.token);
const methods = useForm<BridgeForm>({
defaultValues: {
token: configContextValue?.UNKNOWN[0],
claim: token?.type === TokenType.ETH ? "auto" : "manual",
amount: "",
minFees: 0n,
gasFees: 0n,
bridgingAllowed: false,
balance: "0",
},
});
return (
<>
{!isConnected && (
<div className="mb-4 min-w-min max-w-lg rounded-lg bg-cardBg p-2 shadow-lg">
<BridgeExternal />
</div>
)}
{isConnected && token?.type === TokenType.USDC && (
<div className="mb-4 min-w-min max-w-lg rounded-lg bg-warning p-2 text-warning-content shadow-lg">
<div className="flex flex-col items-center justify-center gap-2 text-center">
<MdWarning className="text-lg" />
<p>
The Linea Mainnet USDC bridge will be paused on Sunday 16th of March 20:00 UTC for an upgrade and will
remain paused until CCTP V2 integration is complete.{" "}
<Link
href="https://www.circle.com/blog/linea-to-become-the-first-bridged-usdc-standard-blockchain-to-upgrade-to-native-usdc"
target="_blank"
rel="noopener noreferrer"
passHref
className="link"
>
See more here.
</Link>{" "}
All pending messages will be automatically claimed.
</p>
<p>To bridge USDC between Linea and Ethereum, you can use alternative bridge providers.</p>
<p>Linea Sepolia (Testnet) is currently being upgraded to support CCTP V2.</p>
</div>
</div>
)}
<FormProvider {...methods}>
<Bridge />
</FormProvider>
</>
);
}

View File

@@ -1,26 +0,0 @@
import { useChainStore } from "@/stores/chainStore";
import { useFormContext } from "react-hook-form";
import { Stepper } from "../ui";
import { parseUnits } from "viem";
const STEPS = ["Approve", "Bridge"];
export function ERC20Stepper() {
const token = useChainStore((state) => state.token);
const { watch } = useFormContext();
const watchAmount = watch("amount");
const watchAllowance = watch("allowance");
const isETHTransfer = token && token.symbol === "ETH";
const isApproved =
!isETHTransfer &&
watchAmount &&
watchAmount > 0 &&
watchAllowance &&
token?.decimals &&
watchAllowance >= parseUnits(watchAmount, token?.decimals);
return <Stepper steps={STEPS} activeStep={isApproved ? 1 : 0} />;
}

View File

@@ -1,10 +0,0 @@
import FromChainDropdown from "./dropdowns/FromChainDropdown";
export function FromChain() {
return (
<div className="mb-4 flex items-center justify-between">
<span>From this network</span>
<FromChainDropdown />
</div>
);
}

View File

@@ -1,54 +0,0 @@
import { useAccount } from "wagmi";
import { PiApproximateEqualsBold } from "react-icons/pi";
import { useChainStore } from "@/stores/chainStore";
import { formatBalance } from "@/utils/format";
import useTokenPrices from "@/hooks/useTokenPrices";
import { zeroAddress } from "viem";
import { NetworkType } from "@/config";
type ReceivedAmountProps = {
receivedAmount?: string;
};
export function ReceivedAmount({ receivedAmount }: ReceivedAmountProps) {
const { isConnected } = useAccount();
const { token, fromChain, tokenAddress, networkType } = useChainStore((state) => ({
token: state.token,
fromChain: state.fromChain,
tokenAddress: state.token?.[state.networkLayer] || zeroAddress,
networkType: state.networkType,
}));
const { data: tokenPrices } = useTokenPrices([tokenAddress], fromChain?.id);
return (
<div className="flex min-h-20 flex-col gap-2 rounded-lg bg-backgroundColor p-3">
{isConnected && (
<>
<span className="text-2xl font-semibold">
{(parseFloat(receivedAmount || "0") > 0 && formatBalance(receivedAmount)) || 0} {token?.symbol}
</span>
{networkType === NetworkType.MAINNET && (
<span className="label-text flex items-center">
{receivedAmount &&
parseFloat(receivedAmount) > 0 &&
tokenPrices?.[tokenAddress.toLowerCase()]?.usd &&
tokenPrices?.[tokenAddress.toLowerCase()]?.usd > 0 ? (
<>
<PiApproximateEqualsBold />
{(Number(receivedAmount) * tokenPrices?.[tokenAddress.toLowerCase()]?.usd).toLocaleString("en-US", {
style: "currency",
currency: "USD",
maximumFractionDigits: 4,
})}
</>
) : (
""
)}
</span>
)}
</>
)}
</div>
);
}

View File

@@ -1,10 +0,0 @@
import ToChainDropdown from "./dropdowns/ToChainDropdown";
export function ToChain() {
return (
<div className="mb-4 flex items-center justify-between">
<span>To this network</span>
<ToChainDropdown />
</div>
);
}

View File

@@ -1,40 +0,0 @@
import Image from "next/image";
import { MdKeyboardArrowDown } from "react-icons/md";
import { ModalContext } from "@/contexts/modal.context";
import { useChainStore } from "@/stores/chainStore";
import { useContext } from "react";
import { useAccount } from "wagmi";
import TokenModal from "./modals/TokenModal";
import { useFormContext } from "react-hook-form";
import { Button } from "../ui";
export default function TokenList() {
const token = useChainStore((state) => state.token);
const { isConnected } = useAccount();
const { handleShow } = useContext(ModalContext);
const { setValue, clearErrors } = useFormContext();
return (
<div className="dropdown">
{token && (
<Button
id="token-select-btn"
type="button"
variant="outline"
className="border-none bg-cardBg px-2 py-1 font-normal hover:bg-cardBg hover:text-card"
disabled={!isConnected}
onClick={() =>
handleShow(<TokenModal setValue={setValue} clearErrors={clearErrors} />, {
showCloseButton: false,
})
}
>
<Image src={token.image} alt={token.name} width={25} height={25} className="rounded-full" />
{token.symbol}
<MdKeyboardArrowDown size={20} />
</Button>
)}
</div>
);
}

View File

@@ -0,0 +1,27 @@
.amount {
display: flex;
flex-direction: column;
gap: 0.25rem;
max-width: calc(100% - 8.75rem);
input {
font-family: var(--font-atyp);
background-color: var(--v2-color-smoke);
font-size: 2rem;
}
.calculated-value {
font-size: 0.75rem;
color: var(--v2-color-darker-gray);
font-family: var(--font-atyp-text);
min-height: 0.75rem;
}
}
.title {
color: var(--v2-color-black);
font-size: 0.875rem;
font-weight: 500;
font-family: var(--font-atyp-text);
margin-bottom: 0.5rem;
}

View File

@@ -0,0 +1,117 @@
import { ChangeEvent, useEffect, useState } from "react";
import { useAccount } from "wagmi";
import { formatUnits, parseUnits } from "viem";
import { useIsLoggedIn } from "@/lib/dynamic";
import { useTokenPrices } from "@/hooks";
import { useChainStore, useConfigStore, useFormStore } from "@/stores";
import styles from "./amount.module.scss";
const AMOUNT_REGEX = /^[0-9]*[.,]?[0-9]*$/;
const MAX_AMOUNT_CHAR = 20;
export function Amount() {
const currency = useConfigStore((state) => state.currency);
const fromChain = useChainStore.useFromChain();
const { address } = useAccount();
const isLoggedIn = useIsLoggedIn();
const amount = useFormStore((state) => state.amount);
const token = useFormStore((state) => state.token);
const setAmount = useFormStore((state) => state.setAmount);
const tokenAddress = token[fromChain.layer];
const { data: tokenPrices } = useTokenPrices([tokenAddress], fromChain.id);
const [inputValue, setInputValue] = useState(amount ? formatUnits(amount, token.decimals) : "");
useEffect(() => {
setInputValue(amount ? formatUnits(amount, token.decimals) : "");
}, [amount, token.decimals]);
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
const { key } = event;
const allowedKeys = ["Backspace", "Tab", "ArrowLeft", "ArrowRight", "Delete"];
const decimalSeparators = [".", ","];
// If the key pressed is a decimal separator, allow it only if none is already present.
if (decimalSeparators.includes(key)) {
if (decimalSeparators.some((sep) => inputValue.includes(sep))) {
event.preventDefault();
}
return;
}
// Otherwise, allow digits and allowed control keys.
if (!(/[0-9]/.test(key) || allowedKeys.includes(key))) {
event.preventDefault();
}
};
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
let newValue = e.target.value;
newValue = newValue.replace(/[,;]/g, ".");
if (newValue.length > MAX_AMOUNT_CHAR) {
newValue = newValue.substring(0, MAX_AMOUNT_CHAR);
}
if (newValue.length > 1 && newValue[0] === "0" && newValue[1] !== ".") {
newValue = newValue.replace(/^0+/, "");
if (newValue === "") newValue = "0";
}
if (!AMOUNT_REGEX.test(newValue)) {
return;
}
setInputValue(newValue);
if (newValue.endsWith(".")) {
return;
}
try {
const parsed = parseUnits(newValue, token.decimals);
setAmount(parsed);
} catch (error) {
console.error("Error parsing amount:", error);
}
};
useEffect(() => {
setAmount(0n);
}, [address, setAmount]);
const formattedAmount = amount ? formatUnits(amount, token.decimals) : "";
const tokenPrice = tokenPrices?.[tokenAddress.toLowerCase()];
const calculatedValue =
tokenPrice && tokenPrice > 0
? (Number(formattedAmount) * tokenPrice).toLocaleString("en-US", {
style: "currency",
currency: currency.label,
maximumFractionDigits: 4,
})
: "";
return (
<div className={styles["amount"]}>
<p className={styles.title}>Send</p>
<input
disabled={!isLoggedIn}
id="amount-input"
type="text"
autoCorrect="off"
autoComplete="off"
spellCheck="false"
inputMode="decimal"
value={inputValue}
onKeyDown={handleKeyDown}
onChange={handleChange}
pattern={AMOUNT_REGEX.source}
placeholder="0"
/>
{!fromChain?.testnet && <span className={styles["calculated-value"]}>{calculatedValue}</span>}
</div>
);
}

View File

@@ -0,0 +1,8 @@
.balance {
display: flex;
align-items: center;
column-gap: 0.25rem;
font-size: 0.75rem;
color: var(--v2-color-darker-gray);
font-family: var(--font-atyp-text);
}

View File

@@ -0,0 +1,36 @@
import { useQueryClient } from "@tanstack/react-query";
import { useEffect } from "react";
import { useBlockNumber } from "wagmi";
import { formatBalance } from "@/utils";
import { useTokenBalance, useSelectedToken } from "@/hooks";
import styles from "./balance.module.scss";
import { useFormStore } from "@/stores";
export function Balance() {
// Context
const token = useSelectedToken();
// Wagmi
const queryClient = useQueryClient();
const { data: blockNumber } = useBlockNumber({ watch: true });
const { balance, queryKey } = useTokenBalance(token);
// Form
const setBalance = useFormStore((state) => state.setBalance);
useEffect(() => {
setBalance(balance);
}, [balance, setBalance, token?.decimals]);
useEffect(() => {
if (blockNumber && blockNumber % 5n === 0n) {
queryClient.invalidateQueries({ queryKey });
}
}, [blockNumber, queryClient, queryKey]);
return (
<div className={styles.balance}>
<span>{balance && `${formatBalance(balance.toString())} ${token?.symbol}`} available</span>
</div>
);
}

View File

@@ -0,0 +1,50 @@
"use client";
import { useDynamicContext, useIsLoggedIn } from "@/lib/dynamic";
import { useAccount } from "wagmi";
import Bridge from "../form";
import TransactionHistory from "../transaction-history";
import { supportedChainIds } from "@/lib/wagmi";
import { useTokens } from "@/hooks";
import { useChainStore, FormStoreProvider, FormState, useNativeBridgeNavigationStore } from "@/stores";
import { ChainLayer } from "@/types";
import WrongNetwork from "../wrong-network";
import BridgeSkeleton from "./skeleton";
export default function BridgeLayout() {
const isTransactionHistoryOpen = useNativeBridgeNavigationStore.useIsTransactionHistoryOpen();
const { chain, address } = useAccount();
const isLoggedIn = useIsLoggedIn();
const { sdkHasLoaded } = useDynamicContext();
const tokens = useTokens();
const fromChain = useChainStore.useFromChain();
if (!sdkHasLoaded) {
return <BridgeSkeleton />;
}
if (isLoggedIn && (!chain?.id || !supportedChainIds.includes(chain.id))) {
return <WrongNetwork />;
}
if (isTransactionHistoryOpen) {
return <TransactionHistory />;
}
const initialFormState: FormState = {
token: tokens[0],
claim: fromChain?.layer === ChainLayer.L1 ? "auto" : "manual",
amount: null,
minimumFees: 0n,
gasFees: 0n,
bridgingFees: 0n,
balance: 0n,
recipient: address || "0x",
};
return (
<FormStoreProvider initialState={initialFormState}>
<Bridge />
</FormStoreProvider>
);
}

View File

@@ -0,0 +1,20 @@
import clsx from "clsx";
import styles from "./skeleton.module.scss";
export default function BridgeSkeleton() {
return (
<div className={styles.wrapper}>
<div className={styles.headline}>
<div className={clsx(styles["action"], "pulsating")} />
</div>
<div className={styles.exchange}>
<div className={clsx(styles["from"], "pulsating")} />
<div className={clsx(styles["to"], "pulsating")} />
</div>
<div className={clsx(styles["amount-wrapper"], "pulsating")} />
<div className={styles["connect-btn-wrapper"]}>
<div className={clsx(styles["connect-btn"], "pulsating")} />
</div>
</div>
);
}

View File

@@ -0,0 +1,74 @@
.wrapper {
background-color: var(--v2-color-white);
border-radius: 0.625rem;
padding: 1.5rem;
}
.headline {
display: flex;
justify-content: flex-end;
align-items: center;
margin-bottom: 1rem;
}
.action {
display: flex;
gap: 1rem;
width: 4rem;
height: 2rem;
background-color: var(--v2-color-smoke);
border-radius: 0.625rem;
}
.exchange {
position: relative;
display: flex;
gap: 0.5rem;
}
.from {
background-color: var(--v2-color-smoke);
border-radius: 0.625rem;
padding: 1rem;
width: 50%;
height: 5rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.to {
background-color: var(--v2-color-smoke);
border-radius: 0.625rem;
padding: 1rem;
display: flex;
width: 50%;
height: 5rem;
flex-direction: column;
gap: 0.5rem;
}
.amount-wrapper {
background-color: var(--v2-color-smoke);
border-radius: 0.625rem;
padding: 1rem;
margin-top: 0.625rem;
transition: box-shadow 0.25s ease-in-out;
display: flex;
justify-content: space-between;
align-items: center;
height: 6rem;
}
.connect-btn-wrapper {
display: flex;
margin-top: 1rem;
justify-content: center;
}
.connect-btn {
width: 100%;
height: 2.5rem;
background-color: var(--v2-color-smoke);
border-radius: 1.875rem;
}

View File

@@ -0,0 +1,21 @@
.logo-wrapper {
position: relative;
width: 2.5rem;
height: 2.5rem;
}
.big {
width: 2.5rem;
height: 2.5rem;
border-radius: 50%;
}
.small {
position: absolute;
width: 1rem;
height: 1rem;
bottom: 0;
right: 0;
border-radius: 50%;
border: 1px solid var(--v2-color-smoke);
}

View File

@@ -0,0 +1,18 @@
import Image from "next/image";
import styles from "./bridge-two-logo.module.scss";
type Props = {
src1: string;
alt1: string;
src2: string;
alt2: string;
};
export default function BridgeTwoLogo({ src1, src2, alt1, alt2 }: Props) {
return (
<div className={styles["logo-wrapper"]}>
<Image className={styles.big} src={src1} width="40" height="40" alt={alt1} />
<Image className={styles.small} src={src2} width="16" height="16" alt={alt2} />
</div>
);
}

View File

@@ -0,0 +1,63 @@
.wrapper {
margin-top: 0.625rem;
background-color: var(--v2-color-smoke);
border-radius: 0.625rem;
padding: 1rem;
transition: box-shadow 0.25s ease-in-out;
&:hover {
box-shadow: inset 0 0 0 0.125rem var(--v2-color-icterine);
}
}
.setting {
cursor: pointer;
color: var(--v2-color-indigo);
display: flex;
align-items: center;
svg {
width: 1rem;
height: 1rem;
}
}
.top {
display: flex;
justify-content: space-between;
align-items: center;
font-family: var(--font-atyp-text);
.title {
color: var(--v2-color-black);
font-size: 0.875rem;
font-weight: 500;
}
.config {
display: flex;
align-items: center;
gap: 0.5rem;
}
}
.result {
display: flex;
gap: 0.5rem;
margin: 1rem 0;
}
.bottom {
font-family: var(--font-atyp-text);
font-size: 0.75rem;
margin-top: 0.5rem;
button {
font-size: inherit;
color: var(--v2-color-indigo);
cursor: pointer;
&:hover {
text-decoration: underline;
}
}
}

View File

@@ -0,0 +1,14 @@
.time {
display: flex;
gap: 0.25rem;
align-items: center;
cursor: pointer;
color: var(--v2-color-darker-gray);
font-size: inherit;
font-weight: 500;
span {
color: var(--v2-color-black);
height: 0.75rem;
}
}

View File

@@ -0,0 +1,28 @@
import { useState } from "react";
import EstimatedTimeModal from "../../modal/estimated-time";
import ClockIcon from "@/assets/icons/clock.svg";
import styles from "./estimated-time.module.scss";
import { useChainStore } from "@/stores";
import { ChainLayer } from "@/types";
export default function EstimatedTime() {
const fromChain = useChainStore.useFromChain();
const [showEstimatedTimeModal, setShowEstimatedTimeModal] = useState<boolean>(false);
const estimatedTime = fromChain.layer === ChainLayer.L1 ? "~ 20 mins" : "~ 8-32 hours";
const estimatedTimeType = fromChain.layer === ChainLayer.L1 ? "deposit" : "withdraw";
return (
<>
<button type="button" className={styles.time} onClick={() => setShowEstimatedTimeModal(true)}>
<ClockIcon />
<span>{estimatedTime}</span>
</button>
<EstimatedTimeModal
type={estimatedTimeType}
isModalOpen={showEstimatedTimeModal}
onCloseModal={() => setShowEstimatedTimeModal(false)}
/>
</>
);
}

View File

@@ -0,0 +1,12 @@
.estimate {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
font-size: 0.75rem;
font-family: var(--font-atyp-text);
svg {
width: 0.75rem;
height: 0.75rem;
}
}

View File

@@ -0,0 +1,20 @@
import styles from "./fees.module.scss";
import ManualClaim from "../manual-claim";
import EstimatedTime from "../estimated-time";
import WithFees from "./with-fees";
import { useFormStore, useChainStore } from "@/stores";
export default function Fees() {
const fromChain = useChainStore.useFromChain();
const claim = useFormStore((state) => state.claim);
return (
<>
<div className={styles.estimate}>
<WithFees iconPath={fromChain.iconPath} />
<EstimatedTime />
{claim === "manual" && <ManualClaim />}
</div>
</>
);
}

View File

@@ -0,0 +1,47 @@
import Image from "next/image";
import { formatEther } from "viem";
import styles from "./with-fees.module.scss";
import { useState } from "react";
import GasFees from "../../../modal/gas-fees";
import { useFees } from "@/hooks";
import { useConfigStore } from "@/stores";
type Props = {
iconPath: string;
};
export default function WithFees({ iconPath }: Props) {
const [showGasFeesModal, setShowGasFeesModal] = useState<boolean>(false);
const currency = useConfigStore.useCurrency();
const { total, fees, isLoading } = useFees();
if (isLoading) {
return null;
}
return (
<>
{total && (
<button
type="button"
className={styles["gas-fees"]}
onClick={() => {
setShowGasFeesModal(true);
}}
>
<Image src={iconPath} width={12} height={12} alt="fee-chain-icon" />
<p className={styles["estimate-crypto"]}>{`${Number(formatEther(total.fees)).toFixed(8)} ETH`}</p>
{total.fiatValue && (
<p className={styles["estimate-amount"]}>{`(${total.fiatValue.toLocaleString("en-US", {
style: "currency",
currency: currency.label,
maximumFractionDigits: 2,
})})`}</p>
)}
</button>
)}
<GasFees isModalOpen={showGasFeesModal} onCloseModal={() => setShowGasFeesModal(false)} fees={fees} />
</>
);
}

View File

@@ -0,0 +1,25 @@
.gas-fees {
display: flex;
gap: 0.25rem;
align-items: center;
color: var(--v2-color-darker-gray);
cursor: pointer;
font-size: inherit;
img {
width: 0.75rem;
height: 0.75rem;
border-radius: 50%;
}
.estimate-crypto {
height: 0.75rem;
font-weight: 500;
}
.estimate-amount {
height: 0.75rem;
color: var(--v2-color-darker-gray);
font-weight: 500;
}
}

View File

@@ -0,0 +1,68 @@
import BridgeTwoLogo from "@/components/bridge/bridge-two-logo";
import styles from "./claiming.module.scss";
import SettingIcon from "@/assets/icons/setting.svg";
import { useEffect, useState } from "react";
import AdvancedSettings from "@/components/bridge/modal/advanced-settings";
import Skeleton from "@/components/bridge/claiming/skeleton";
import ReceivedAmount from "./received-amount";
import Fees from "./fees";
import { useFormStore, useChainStore } from "@/stores";
export default function Claiming() {
const fromChain = useChainStore.useFromChain();
const toChain = useChainStore.useToChain();
const [loading, setLoading] = useState<boolean>(false);
const [showAdvancedSettingsModal, setShowAdvancedSettingsModal] = useState<boolean>(false);
const amount = useFormStore((state) => state.amount);
const balance = useFormStore((state) => state.balance);
const originChainBalanceTooLow = amount && balance < amount;
useEffect(() => {
setLoading(true);
const timeout = setTimeout(() => {
setLoading(false);
}, 1000);
return () => clearTimeout(timeout);
}, [amount]);
if (!amount || amount <= 0n) return null;
if (originChainBalanceTooLow) return null;
return (
<div className={styles["wrapper"]}>
<div className={styles.top}>
<p className={styles.title}>Receive</p>
<div className={styles.config}>
<button className={styles.setting} type="button" onClick={() => setShowAdvancedSettingsModal(true)}>
<SettingIcon />
</button>
</div>
</div>
{loading ? (
<Skeleton />
) : (
<div className={styles.content}>
<div className={styles.result}>
<BridgeTwoLogo
src1={fromChain?.iconPath ?? ""}
src2={toChain?.iconPath ?? ""}
alt1={fromChain?.nativeCurrency.symbol ?? ""}
alt2={toChain?.nativeCurrency.symbol ?? ""}
/>
<ReceivedAmount />
</div>
<Fees />
</div>
)}
<AdvancedSettings
isModalOpen={showAdvancedSettingsModal}
onCloseModal={() => setShowAdvancedSettingsModal(false)}
/>
</div>
);
}

View File

@@ -0,0 +1,18 @@
import { useState } from "react";
import styles from "./manual-claim.module.scss";
import AttentionIcon from "@/assets/icons/attention.svg";
import ManualClaimModal from "@/components/bridge/modal/manual-claim";
export default function ManualClaim() {
const [showManualClaimModal, setShowManualClaimModal] = useState<boolean>(false);
return (
<>
<button type="button" className={styles.manual} onClick={() => setShowManualClaimModal(true)}>
<AttentionIcon />
<span>Manual</span>
</button>
<ManualClaimModal isModalOpen={showManualClaimModal} onCloseModal={() => setShowManualClaimModal(false)} />
</>
);
}

View File

@@ -0,0 +1,12 @@
.manual {
color: var(--v2-color-dark-red);
display: flex;
align-items: center;
gap: 0.25rem;
cursor: pointer;
font-size: inherit;
span {
height: 0.75rem;
}
}

View File

@@ -0,0 +1,33 @@
import { formatUnits } from "viem";
import styles from "./received-amount.module.scss";
import { useTokenPrices } from "@/hooks";
import { useConfigStore, useChainStore, useFormStore } from "@/stores";
export default function ReceivedAmount() {
const fromChain = useChainStore.useFromChain();
const currency = useConfigStore.useCurrency();
const amount = useFormStore((state) => state.amount);
const token = useFormStore((state) => state.token);
const { data: tokenPrices } = useTokenPrices([token[fromChain.layer]], fromChain.id);
return (
<div className={styles.value}>
<p className={styles.crypto}>
{formatUnits(amount || 0n, token.decimals)} {token.symbol}
</p>
{tokenPrices?.[token[fromChain.layer].toLowerCase()] &&
tokenPrices?.[token[fromChain.layer].toLowerCase()] > 0 && (
<p className={styles.amount}>
{(
Number(formatUnits(amount || 0n, token.decimals)) * tokenPrices?.[token[fromChain.layer].toLowerCase()]
).toLocaleString("en-US", {
style: "currency",
currency: currency.label,
maximumFractionDigits: 4,
})}
</p>
)}
</div>
);
}

View File

@@ -0,0 +1,12 @@
.value {
.crypto {
font-size: 1.5rem;
font-weight: 500;
margin-bottom: 0.25rem;
}
.amount {
font-size: 0.75rem;
color: var(--v2-color-darker-gray);
font-family: var(--font-atyp-text);
}
}

View File

@@ -0,0 +1,18 @@
import clsx from "clsx";
import styles from "./skeleton.module.scss";
export default function Skeleton() {
return (
<div className={styles.wrapper}>
<div className={styles.result}>
<div className={clsx(styles["logo-wrapper"])}>
<div className={clsx(styles.big, "pulsating")} />
<div className={clsx(styles.small, "pulsating")} />
</div>
<div className={clsx(styles.value, "pulsating")} />
</div>
<div className={clsx(styles.estimate, "pulsating")} />
<div className={clsx(styles.bottom, "pulsating")} />
</div>
);
}

View File

@@ -0,0 +1,53 @@
.wrapper {
display: flex;
flex-direction: column;
}
.logo-wrapper {
position: relative;
width: 2.5rem;
height: 2.5rem;
}
.big {
width: 2.5rem;
height: 2.5rem;
border-radius: 50%;
}
.small {
position: absolute;
width: 1rem;
height: 1rem;
bottom: 0;
right: 0;
border-radius: 50%;
border: 1px solid var(--v2-color-smoke);
}
.result {
display: flex;
gap: 0.5rem;
margin: 1rem 0;
}
.value {
min-width: 4rem;
min-height: 2.5rem;
border-radius: 0.375rem;
}
.estimate {
min-width: 4rem;
min-height: 1.0625rem;
border-radius: 0.375rem;
}
.bottom {
min-height: 1.0625rem;
min-width: 7.5rem;
border-radius: 0.375rem;
margin-top: 0.5rem;
display: inline-block;
align-self: flex-start;
}

View File

@@ -0,0 +1,63 @@
.container {
position: relative;
font-family: var(--font-atyp-text);
}
.button {
width: 100%;
padding: 0.375rem 0.5rem 0.25rem;
border-radius: 6.25rem;
background-color: var(--v2-color-smoke);
display: flex;
column-gap: 0.5rem;
justify-content: space-between;
align-items: center;
cursor: pointer;
font-size: 0.875rem;
&:disabled {
cursor: not-allowed;
}
}
.selected-label {
display: flex;
align-items: center;
line-height: 1;
}
.flag {
font-size: 0.875rem;
margin-right: 0.375rem;
}
.dropdown {
position: absolute;
top: 100%;
left: 0;
z-index: 10;
background: var(--v2-color-white);
border-radius: 0.25rem;
box-shadow: 0 0.1875rem 0.5rem 0 rgba(18, 18, 18, 0.2);
width: 100%;
margin-top: 0.25rem;
overflow: hidden;
}
.option {
padding: 0.375rem 0.5rem 0.25rem;
font-size: 0.875rem;
display: flex;
align-items: center;
cursor: pointer;
transition: background-color 0.2s ease-in-out;
&:hover {
background-color: var(--v2-color-light-pink);
}
}
.caret {
width: 0.75rem;
height: 0.75rem;
}

View File

@@ -0,0 +1,67 @@
import React, { useState, useEffect, useRef } from "react";
import CaretDownIcon from "@/assets/icons/caret-down.svg";
import styles from "./currency-dropdown.module.scss";
import { CurrencyOption, useConfigStore } from "@/stores";
type Props = {
disabled?: boolean;
};
export default function CurrencyDropdown({ disabled }: Props) {
const supportedCurrencies = useConfigStore.useSupportedCurrencies();
const currency = useConfigStore.useCurrency();
const setCurrency = useConfigStore.useSetCurrency();
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
const buttonRef = useRef<HTMLButtonElement>(null);
const handleSelect = (option: CurrencyOption) => {
setCurrency(option);
setIsOpen(false);
};
const toggleDropdown = () => {
setIsOpen((prev) => !prev);
};
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
dropdownRef.current &&
!dropdownRef.current.contains(event.target as Node) &&
buttonRef.current &&
!buttonRef.current.contains(event.target as Node)
) {
setIsOpen(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);
return (
<div className={styles.container}>
<button ref={buttonRef} type="button" className={styles.button} onClick={toggleDropdown} disabled={disabled}>
<div className={styles["selected-label"]}>
<span className={styles.flag}>{currency.flag}</span>
<span>{currency.label}</span>
</div>
<CaretDownIcon className={styles.caret} />
</button>
{isOpen && (
<div ref={dropdownRef} className={styles.dropdown}>
{supportedCurrencies.map((option) => (
<div key={option.value} onClick={() => handleSelect(option)} className={styles.option}>
<span className={styles.flag}>{option.flag}</span>
{option.label}
</div>
))}
</div>
)}
</div>
);
}

View File

@@ -0,0 +1,89 @@
.destination-address {
display: flex;
flex-direction: column;
gap: 0.25rem;
width: 100%;
input {
font-family: var(--font-atyp);
background-color: var(--v2-color-smoke);
margin-top: 0.25rem;
border-radius: 0.625rem;
box-shadow: 0 0 0 1px var(--v2-color-silver);
width: 100%;
padding: 0.75rem 2.625rem 0.75rem 1rem;
font-size: 0.875rem;
color: var(--v2-color-black);
&.error {
box-shadow: inset 0 0 0 0.125rem var(--v2-color-red);
}
}
}
.headline {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
.title {
color: var(--v2-color-black);
font-size: 0.875rem;
font-weight: 500;
font-family: var(--font-atyp-text);
}
a {
display: flex;
color: var(--v2-color-indigo);
font-size: 0.875rem;
gap: 0.2rem;
font-weight: 500;
svg {
width: 0.75rem;
height: 0.75rem;
color: var(--v2-color-indigo);
transform: rotate(-45deg);
}
}
}
.input-container {
position: relative;
}
.message-text {
font-family: var(--font-atyp-text);
font-size: 0.75rem;
margin-top: 0.25rem;
line-height: 1.4;
&.success {
color: var(--v2-color-green);
}
&.error {
color: var(--v2-color-red);
}
&.warning {
color: var(--v2-color-dark-red);
}
}
.reset {
width: 1rem;
height: 1rem;
color: var(--v2-color-darker-gray);
position: absolute;
right: 1rem;
top: calc(50% - 0.5rem);
cursor: pointer;
visibility: hidden;
&.show {
visibility: visible;
}
}

View File

@@ -0,0 +1,99 @@
import { ChangeEvent, useEffect, useState } from "react";
import { useAccount } from "wagmi";
import { isAddress } from "viem";
import clsx from "clsx";
import Link from "next/link";
import styles from "./destination-address.module.scss";
import XCircleIcon from "@/assets/icons/x-circle.svg";
import { useChainStore, useFormStore } from "@/stores";
import { ChainLayer } from "@/types";
import ArrowRightIcon from "@/assets/icons/arrow-right.svg";
export function DestinationAddress() {
const { address } = useAccount();
const toChain = useChainStore.useToChain();
const recipient = useFormStore((state) => state.recipient);
const setRecipient = useFormStore((state) => state.setRecipient);
const [inputValue, setInputValue] = useState(recipient);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
if (inputValue && !isAddress(inputValue)) {
setError("Invalid address");
} else {
setError(null);
}
}, [inputValue]);
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
setInputValue(() => e.target.value as `0x${string}`);
if (isAddress(e.target.value)) {
setRecipient(e.target.value);
}
};
const handleResetInput = () => {
if (address) {
setInputValue(address);
setRecipient(address);
}
};
return (
<div className={styles["destination-address"]}>
<div className={styles["headline"]}>
<p className={styles.title}>Send to wallet</p>
{address !== inputValue && !error && (
<Link
href={`${toChain.blockExplorers?.default.url ?? ""}/address/${inputValue}`}
target="_blank"
rel="noopenner noreferrer"
>
VIEW ON {toChain.layer === ChainLayer.L1 ? "ETHERSCAN" : "LINEASCAN"}
<ArrowRightIcon />
</Link>
)}
</div>
<div className={styles["input-container"]}>
<input
type="text"
id="address"
required
maxLength={42}
value={inputValue}
pattern="^0x[a-fA-F0-9]{40}$"
onChange={handleChange}
className={clsx(styles.input, {
[styles["error"]]: error,
})}
/>
<button
type="button"
className={clsx(styles.reset, {
[styles["show"]]: inputValue !== address,
})}
onClick={handleResetInput}
>
<XCircleIcon />
</button>
</div>
<p
className={clsx(styles["message-text"], {
[styles["warning"]]: inputValue !== address,
[styles["success"]]: inputValue === address,
[styles["error"]]: error,
})}
>
{error
? error.toString()
: address !== inputValue
? "Editing the destination address can result in loss of your funds. Make sure you control this address."
: "This is your connected address"}
</p>
</div>
);
}

View File

@@ -1,75 +0,0 @@
"use client";
import Image from "next/image";
import { NetworkType } from "@/config";
import { useChainStore } from "@/stores/chainStore";
import { useEffect, useRef } from "react";
import DropdownItem from "@/components/DropdownItem";
import { getChainLogoPath } from "@/utils/chainsUtil";
import { useFormContext } from "react-hook-form";
export default function FromChainDropdown() {
const { networkType, fromChain, toChain, switchChain } = useChainStore((state) => ({
networkType: state.networkType,
fromChain: state.fromChain,
toChain: state.toChain,
switchChain: state.switchChain,
}));
const { reset } = useFormContext();
const detailsRef = useRef<HTMLDetailsElement>(null);
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
if (detailsRef.current && !detailsRef.current.contains(e.target as Node)) {
detailsRef.current.removeAttribute("open");
}
};
document.addEventListener("click", handleClickOutside);
return () => {
document.removeEventListener("click", handleClickOutside);
};
}, []);
const switchNetworkHandler = async () => {
switchChain();
reset();
};
if (networkType == NetworkType.SEPOLIA || networkType == NetworkType.MAINNET) {
return (
<details className="dropdown relative" ref={detailsRef}>
<summary className="flex cursor-pointer items-center gap-2 rounded-full bg-backgroundColor p-2 px-3">
{fromChain && (
<Image
src={getChainLogoPath(fromChain.id)}
alt="MetaMask"
width={0}
height={0}
style={{ width: "18px", height: "auto" }}
/>
)}
<span className="hidden md:block">
{fromChain?.name === "Linea Sepolia Testnet" ? "Linea Sepolia" : fromChain?.name}
</span>
<svg
className="size-4 text-card transition-transform"
fill="none"
stroke="black"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="3" d="M19 9l-7 7-7-7"></path>
</svg>
</summary>
<ul className="menu dropdown-content absolute right-0 z-10 mt-2 min-w-max bg-backgroundColor p-0 shadow">
<DropdownItem
title={toChain?.name ? (toChain?.name === "Linea Sepolia Testnet" ? "Linea Sepolia" : toChain?.name) : ""}
iconPath={toChain && getChainLogoPath(toChain.id)}
onClick={switchNetworkHandler}
/>
</ul>
</details>
);
}
}

View File

@@ -1,78 +0,0 @@
"use client";
import Image from "next/image";
import { NetworkType } from "@/config";
import { useChainStore } from "@/stores/chainStore";
import { useEffect, useRef } from "react";
import DropdownItem from "@/components/DropdownItem";
import { getChainLogoPath } from "@/utils/chainsUtil";
import { useFormContext } from "react-hook-form";
export default function ToChainDropdown() {
const { networkType, fromChain, toChain, switchChain } = useChainStore((state) => ({
networkType: state.networkType,
fromChain: state.fromChain,
toChain: state.toChain,
switchChain: state.switchChain,
}));
const { reset } = useFormContext();
const detailsRef = useRef<HTMLDetailsElement>(null);
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
if (detailsRef.current && !detailsRef.current.contains(e.target as Node)) {
detailsRef.current.removeAttribute("open");
}
};
document.addEventListener("click", handleClickOutside);
return () => {
document.removeEventListener("click", handleClickOutside);
};
}, []);
const switchNetworkHandler = async () => {
switchChain();
reset();
};
if (networkType == NetworkType.SEPOLIA || networkType == NetworkType.MAINNET) {
return (
<details className="dropdown relative" ref={detailsRef}>
<summary className="flex cursor-pointer items-center gap-2 rounded-full bg-backgroundColor p-2 px-3">
{toChain && (
<Image
src={getChainLogoPath(toChain.id)}
alt="MetaMask"
width={0}
height={0}
style={{ width: "18px", height: "auto" }}
/>
)}
<span className="hidden md:block">
{toChain?.name === "Linea Sepolia Testnet" ? "Linea Sepolia" : toChain?.name}
</span>
<svg
className="size-4 text-card transition-transform"
fill="none"
stroke="black"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="3" d="M19 9l-7 7-7-7"></path>
</svg>
</summary>
<ul className="menu dropdown-content absolute right-0 z-10 mt-2 min-w-max bg-backgroundColor p-0 shadow">
<DropdownItem
title={
fromChain?.name ? (fromChain?.name === "Linea Sepolia Testnet" ? "Linea Sepolia" : fromChain?.name) : ""
}
iconPath={fromChain && getChainLogoPath(fromChain.id)}
onClick={switchNetworkHandler}
/>
</ul>
</details>
);
}
}

View File

@@ -0,0 +1,28 @@
.faq-help {
text-align: center;
font-size: 0.875rem;
margin-top: 1.5rem;
font-family: var(--font-atyp-text);
&.is-mobile {
@include bp("tablet") {
display: none;
}
}
&.is-desktop {
display: none;
@include bp("tablet") {
display: block;
}
}
a {
color: var(--v2-color-indigo);
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}

View File

@@ -0,0 +1,20 @@
import Link from "next/link";
import styles from "./faq-help.module.scss";
import clsx from "clsx";
type Props = {
isMobile?: boolean;
};
export default function FaqHelp({ isMobile }: Props) {
return (
<div
className={clsx(styles["faq-help"], {
[styles["is-mobile"]]: isMobile,
[styles["is-desktop"]]: !isMobile,
})}
>
Need help? <Link href="/faq">Check our FAQ</Link>
</div>
);
}

View File

@@ -1,24 +0,0 @@
import { BsDashLg } from "react-icons/bs";
import { MdInfo } from "react-icons/md";
import { Tooltip } from "@/components/ui";
interface FeeLineProps {
label: string;
value: string | undefined;
tooltip?: string;
tooltipClassName?: string;
}
export const FeeLine: React.FC<FeeLineProps> = ({ label, value, tooltip, tooltipClassName }) => (
<div className="flex justify-between">
<div className="flex items-center gap-2">
<span>{label}:</span>
{tooltip && (
<Tooltip text={tooltip} className={tooltipClassName}>
<MdInfo className="text-icon" />
</Tooltip>
)}
</div>
<span>{value ? value : <BsDashLg />}</span>
</div>
);

View File

@@ -1,109 +0,0 @@
import { useEffect } from "react";
import { useAccount, useBalance } from "wagmi";
import { formatEther, zeroAddress } from "viem";
import { useFormContext } from "react-hook-form";
import { NetworkLayer, NetworkType } from "@/config";
import { useChainStore } from "@/stores/chainStore";
import { formatBalance } from "@/utils/format";
import { FeeLine } from "./FeeLine";
import useTokenPrices from "@/hooks/useTokenPrices";
type FeesProps = {
totalReceived: string;
fees: {
total: bigint;
bridgingFeeInWei: bigint;
transactionFeeInWei: bigint;
};
};
export function Fees({ totalReceived, fees: { total, bridgingFeeInWei, transactionFeeInWei } }: FeesProps) {
// Context
const { token, networkLayer, fromChain, networkType } = useChainStore((state) => ({
token: state.token,
networkType: state.networkType,
networkLayer: state.networkLayer,
fromChain: state.fromChain,
}));
// Form
const {
watch,
setError,
clearErrors,
setValue,
formState: { errors },
} = useFormContext();
const [amount, claim] = watch(["amount", "claim"]);
// Wagmi
const { address, isConnected } = useAccount();
const { data: ethBalance } = useBalance({
address,
chainId: fromChain?.id,
});
// Hooks
const { data: ethPrice } = useTokenPrices([zeroAddress], fromChain?.id);
useEffect(() => {
if (ethBalance && total && total > 0 && ethBalance.value <= total) {
setError("minFees", {
type: "custom",
message: "Execution fees exceed ETH balance",
});
} else {
clearErrors("minFees");
}
}, [setError, clearErrors, ethBalance, total]);
useEffect(() => {
setValue("minFees", bridgingFeeInWei);
}, [bridgingFeeInWei, setValue]);
const estimatedTime = networkLayer === NetworkLayer.L1 ? "20 mins" : "8 hrs to 32 hrs";
return (
<div className="flex flex-col gap-2 text-sm">
<FeeLine
label="Estimated Time"
value={amount && !errors.amount?.message && estimatedTime}
tooltipClassName="z-[100]"
tooltip={
networkLayer === NetworkLayer.L1
? "Linea has a 20 minutes delay on deposits as a security measure."
: "Linea has a minimum 8 hour delay on withdrawals as a security measure. Withdrawals can take up to 32 hours to complete"
}
/>
<FeeLine
label="Estimated Total Fee"
value={
isConnected &&
amount &&
!errors.amount?.message &&
(networkType === NetworkType.MAINNET && ethPrice && ethPrice?.[zeroAddress]
? `${(Number(formatEther(total)) * ethPrice[zeroAddress].usd).toLocaleString("en-US", {
style: "currency",
currency: "USD",
maximumFractionDigits: 4,
})}`
: `${formatBalance(formatEther(total), 8)} ETH`)
}
tooltipClassName="before:whitespace-pre-wrap before:content-[attr(data-tip)] text-left z-[100]"
tooltip={
claim === "auto"
? `Bridging transaction fee: ${formatBalance(formatEther(transactionFeeInWei), 8)} ETH\nAutomatic claiming Fee: ${formatBalance(formatEther(bridgingFeeInWei), 8)} ETH`
: `Bridging transaction fee: ${formatBalance(formatEther(transactionFeeInWei), 8)} ETH`
}
/>
<FeeLine
label="Total Received"
value={
!errors.amount?.message && totalReceived && totalReceived !== "0"
? `${formatBalance(totalReceived)} ${token?.symbol}`
: undefined
}
/>
</div>
);
}

View File

@@ -1,2 +0,0 @@
export { FeeLine } from "./FeeLine";
export { Fees } from "./Fees";

View File

@@ -1,142 +0,0 @@
import { ChangeEvent, useCallback, useEffect } from "react";
import { useAccount } from "wagmi";
import { useFormContext } from "react-hook-form";
import { parseUnits, zeroAddress } from "viem";
import { PiApproximateEqualsBold } from "react-icons/pi";
import { NetworkType, TokenType } from "@/config";
import useTokenPrices from "@/hooks/useTokenPrices";
import { useChainStore } from "@/stores/chainStore";
const AMOUNT_REGEX = "^[0-9]*[.,]?[0-9]*$";
const MAX_AMOUNT_CHAR = 20;
export function Amount() {
const token = useChainStore((state) => state.token);
const fromChain = useChainStore((state) => state.fromChain);
const tokenAddress = useChainStore((state) => state.token?.[state.networkLayer] || zeroAddress);
const networkType = useChainStore((state) => state.networkType);
const { address } = useAccount();
const { setValue, getValues, setError, clearErrors, trigger, watch } = useFormContext();
const watchBalance = watch("balance");
const [amount, gasFees, minFees] = getValues(["amount", "gasFees", "minFees"]);
const { data: tokenPrices } = useTokenPrices([tokenAddress], fromChain?.id);
const compareAmountBalance = useCallback(
(_amount: string) => {
if (!token) {
return;
}
const amountToCompare =
token.type === TokenType.ETH
? parseUnits(_amount, token.decimals) + gasFees + minFees
: parseUnits(_amount, token.decimals);
const balanceToCompare = parseUnits(watchBalance, token.decimals);
if (amountToCompare > balanceToCompare) {
setError("amount", {
type: "custom",
message: "Not enough funds (Incl fees)",
});
} else {
clearErrors("amount");
}
},
[token, gasFees, minFees, clearErrors, setError, watchBalance],
);
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
const { key } = event;
// Allow control keys, numeric keys, decimal point (if not already present), +, -, and arrow keys
const allowedKeys = ["Backspace", "Tab", "ArrowLeft", "ArrowRight", "Delete"];
if (/[0-9]/.test(key) && !amount.includes(".") && amount[0] === "0") {
event.preventDefault();
return;
}
if (
!(
/[0-9]/.test(key) ||
allowedKeys.includes(key) ||
(key === "." && !amount.includes(".")) ||
(key === "," && !amount.includes(","))
)
) {
event.preventDefault();
}
};
const checkAmountHandler = (e: ChangeEvent<HTMLInputElement>) => {
// Replace minus
const amount = e.target.value.replace(/,/g, ".");
if (!token) {
return;
}
if (new RegExp(AMOUNT_REGEX).test(amount) || amount === "") {
// Limit max char
if (amount.length > MAX_AMOUNT_CHAR) {
setValue("amount", amount.substring(0, MAX_AMOUNT_CHAR));
} else {
setValue("amount", amount);
}
}
compareAmountBalance(amount);
};
useEffect(() => {
if (amount) {
trigger(["amount"]);
compareAmountBalance(amount);
}
}, [amount, trigger, compareAmountBalance]);
// Detect when changing account
useEffect(() => {
setValue("amount", "");
clearErrors("amount");
}, [address, setValue, clearErrors]);
return (
<>
<input
id="amount-input"
type="text"
autoCorrect="off"
autoComplete="off"
spellCheck="false"
inputMode="decimal"
value={amount}
onKeyDown={handleKeyDown}
onChange={checkAmountHandler}
pattern={AMOUNT_REGEX}
placeholder="Enter amount"
className="input input-md w-full border-0 bg-inherit p-0 text-right text-lg font-medium placeholder:text-right placeholder:text-inherit focus:border-0 focus:outline-none md:text-3xl"
/>
{networkType === NetworkType.MAINNET && (
<span className="label-text flex items-center justify-end">
{amount &&
tokenPrices?.[tokenAddress.toLowerCase()]?.usd &&
tokenPrices?.[tokenAddress.toLowerCase()]?.usd > 0 ? (
<>
<PiApproximateEqualsBold />
{(Number(amount) * tokenPrices?.[tokenAddress.toLowerCase()]?.usd).toLocaleString("en-US", {
style: "currency",
currency: "USD",
maximumFractionDigits: 4,
})}
</>
) : (
""
)}
</span>
)}
</>
);
}

Some files were not shown because too many files have changed in this diff Show More