diff --git a/packages/bridge-ui/.default.env b/packages/bridge-ui/.default.env index 8a7396838e4..17535bdac75 100644 --- a/packages/bridge-ui/.default.env +++ b/packages/bridge-ui/.default.env @@ -2,4 +2,8 @@ VITE_NODE_ENV=dev VITE_L1_RPC_URL="" VITE_L2_RPC_URL="" VITE_TAIKO_BRIDGE_ADDRESS="" -VITE_MAINNET_BRIDGE_ADDRESS="" \ No newline at end of file +VITE_MAINNET_BRIDGE_ADDRESS="" +VITE_TEST_ERC20_ADDRESS_MAINNET="" +VITE_MAINNET_TOKEN_VAULT_ADDRESS="" +VITE_TAIKO_TOKEN_VAULT_ADDRESS="" +VITE_TEST_ERC20_ADDRESS_MAINNET="" diff --git a/packages/bridge-ui/jest.config.js b/packages/bridge-ui/jest.config.js index 258bf0d4476..472d18cf5ec 100644 --- a/packages/bridge-ui/jest.config.js +++ b/packages/bridge-ui/jest.config.js @@ -39,9 +39,9 @@ export default { ], coverageThreshold: { global: { - statements: 100, - branches: 97, - functions: 100, + statements: 98.45, + branches: 86, + functions: 96, lines: 100, }, }, diff --git a/packages/bridge-ui/src/App.svelte b/packages/bridge-ui/src/App.svelte index 50d2498a485..46150a98d3a 100644 --- a/packages/bridge-ui/src/App.svelte +++ b/packages/bridge-ui/src/App.svelte @@ -2,28 +2,35 @@ import { wrap } from "svelte-spa-router/wrap"; import QueryProvider from "./components/providers/QueryProvider.svelte"; import Router from "svelte-spa-router"; - import { SvelteToast, toast } from "@zerodevx/svelte-toast"; + import { SvelteToast } from "@zerodevx/svelte-toast"; import type { SvelteToastOptions } from "@zerodevx/svelte-toast"; import Home from "./pages/home/Home.svelte"; import { setupI18n } from "./i18n"; import { BridgeType } from "./domain/bridge"; import ETHBridge from "./eth/bridge"; - import { bridges, chainIdToBridgeAddress } from "./store/bridge"; + import { bridges, chainIdToTokenVaultAddress } from "./store/bridge"; import ERC20Bridge from "./erc20/bridge"; - import { pendingTransactions } from "./store/transactions"; + import { + pendingTransactions, + transactioner, + transactions, + } from "./store/transactions"; import Navbar from "./components/Navbar.svelte"; import { signer } from "./store/signer"; import type { Transactioner } from "./domain/transactions"; - import { RelayerService } from "./relayer/service"; setupI18n({ withLocale: "en" }); - import { CHAIN_MAINNET, CHAIN_TKO } from "./domain/chain"; + import { chains, CHAIN_MAINNET, CHAIN_TKO } from "./domain/chain"; import SwitchEthereumChainModal from "./components/modals/SwitchEthereumChainModal.svelte"; import { ProofService } from "./proof/service"; import { ethers } from "ethers"; import type { Prover } from "./domain/proof"; import { successToast } from "./utils/toast"; + import { StorageService } from "./storage/service"; + import { MessageStatus } from "./domain/message"; + import BridgeABI from "./constants/abi/Bridge"; + import { providers } from "./store/providers"; const providerMap: Map = new Map< number, @@ -38,6 +45,8 @@ new ethers.providers.JsonRpcProvider(import.meta.env.VITE_L2_RPC_URL) ); + providers.set(providerMap); + const prover: Prover = new ProofService(providerMap); const ethBridge = new ETHBridge(prover); @@ -49,26 +58,78 @@ return store; }); - chainIdToBridgeAddress.update((store) => { - store.set(CHAIN_TKO.id, import.meta.env.VITE_TAIKO_BRIDGE_ADDRESS); - store.set(CHAIN_MAINNET.id, import.meta.env.VITE_MAINNET_BRIDGE_ADDRESS); + chainIdToTokenVaultAddress.update((store) => { + store.set(CHAIN_TKO.id, import.meta.env.VITE_TAIKO_TOKEN_VAULT_ADDRESS); + store.set( + CHAIN_MAINNET.id, + import.meta.env.VITE_MAINNET_TOKEN_VAULT_ADDRESS + ); return store; }); - const relayerURL = import.meta.env.VITE_RELAYER_URL; + // const relayerURL = import.meta.env.VITE_RELAYER_URL; + + const storageTransactioner: Transactioner = new StorageService( + window.localStorage, + providerMap + ); + + transactioner.set(storageTransactioner); + + signer.subscribe(async (store) => { + if (store) { + const txs = await $transactioner.GetAllByAddress( + await store.getAddress() + ); - const transactioner: Transactioner = new RelayerService(relayerURL); + transactions.set(txs); + } + return store; + }); pendingTransactions.subscribe((store) => { store.forEach(async (tx) => { - await $signer.provider.waitForTransaction(tx.hash, 3); + await $signer.provider.waitForTransaction(tx.hash, 1); successToast("Transaction completed!"); const s = store; s.pop(); pendingTransactions.set(s); + + transactions.set( + await $transactioner.GetAllByAddress(await $signer.getAddress()) + ); }); }); + transactions.subscribe((store) => { + if (store) { + store.forEach(async (tx) => { + if (tx.interval) clearInterval(tx.interval); + + if (tx.status === MessageStatus.New) { + const provider = providerMap.get(tx.toChainId); + + const interval = setInterval(async () => { + tx.interval = interval; + const contract = new ethers.Contract( + chains[tx.toChainId].bridgeAddress, + BridgeABI, + provider + ); + + const messageStatus: MessageStatus = + await contract.getMessageStatus(tx.signal); + + if (messageStatus === MessageStatus.Done) { + successToast("Bridge message processed successfully"); + clearInterval(tx.interval); + } + }, 30 * 1000); + } + }); + } + }); + const toastOptions: SvelteToastOptions = { dismissable: false, duration: 4000, @@ -87,7 +148,7 @@
- +
diff --git a/packages/bridge-ui/src/app.css b/packages/bridge-ui/src/app.css index 8e836461a0e..210ffb08e8e 100644 --- a/packages/bridge-ui/src/app.css +++ b/packages/bridge-ui/src/app.css @@ -50,4 +50,8 @@ .taiko-banner { background-image: url('assets/taiko-banner.svg'); background-repeat: no-repeat; -} \ No newline at end of file +} + +.dropdown-content.address-dropdown-content { + border-radius: 6px; +} diff --git a/packages/bridge-ui/src/components/AddressDropdown.svelte b/packages/bridge-ui/src/components/AddressDropdown.svelte index 0842b2646a4..81d3e735be1 100644 --- a/packages/bridge-ui/src/components/AddressDropdown.svelte +++ b/packages/bridge-ui/src/components/AddressDropdown.svelte @@ -7,19 +7,32 @@ import { pendingTransactions } from "../store/transactions"; import ChevDown from "./icons/ChevDown.svelte"; import { getAddressAvatarFromIdenticon } from "../utils/addressAvatar"; - import type { BridgeTransaction } from "../domain/transactions"; import { LottiePlayer } from "@lottiefiles/svelte-lottie-player"; - import type { Signer } from "ethers"; + import { ethers, Signer } from "ethers"; import { errorToast } from "../utils/toast"; - - export let transactions: BridgeTransaction[] = []; + import CopyIcon from "./icons/Copy.svelte"; + import DisconnectIcon from "./icons/Disconnect.svelte"; + import { slide } from "svelte/transition"; + import { fromChain } from "../store/chain"; + import { truncateString } from "../utils/truncateString"; let address: string; let addressAvatarImgData: string; + let tokenBalance: string = ""; + onMount(async () => { - setAddress($signer); + await setAddress($signer); }); + $: getUserBalance($signer); + + async function getUserBalance(signer) { + if (signer) { + const userBalance = await signer.getBalance("latest"); + tokenBalance = ethers.utils.formatEther(userBalance); + } + } + $: setAddress($signer).catch((e) => console.error(e)); async function setAddress(signer: Signer) { @@ -78,26 +91,41 @@
diff --git a/packages/bridge-ui/src/components/Loader.svelte b/packages/bridge-ui/src/components/Loader.svelte new file mode 100644 index 00000000000..a70c8626cac --- /dev/null +++ b/packages/bridge-ui/src/components/Loader.svelte @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/packages/bridge-ui/src/components/Navbar.svelte b/packages/bridge-ui/src/components/Navbar.svelte index 44bfadc31f4..803c4aeea6d 100644 --- a/packages/bridge-ui/src/components/Navbar.svelte +++ b/packages/bridge-ui/src/components/Navbar.svelte @@ -3,26 +3,7 @@ import Logo from "./icons/Logo.svelte"; import { signer } from "../store/signer"; import AddressDropdown from "./AddressDropdown.svelte"; - import type { - BridgeTransaction, - Transactioner, - } from "../domain/transactions"; - import type { Signer } from "ethers"; - import { fromChain } from "../store/chain"; import ChainDropdown from "./ChainDropdown.svelte"; - import type { Chain } from "../domain/chain"; - - export let transactioner: Transactioner; - let transactions: BridgeTransaction[]; - $: getTransactions($signer, $fromChain); - - async function getTransactions(signer: Signer, chain: Chain) { - if (!signer || !chain) return; - transactions = await transactioner.GetAllByAddress( - await signer.getAddress(), - chain.id - ); - }