From 9732cc084ed99196a4cd2ccedf9187af4d289ed6 Mon Sep 17 00:00:00 2001 From: shadab-taiko <108871478+shadab-taiko@users.noreply.github.com> Date: Sat, 4 Mar 2023 07:30:38 +0530 Subject: [PATCH] feat(bridge-ui): fetch transactions from relayer api (#13244) --- packages/bridge-ui/README.md | 12 +- packages/bridge-ui/jest.config.js | 4 +- packages/bridge-ui/src/App.svelte | 54 +++-- .../src/components/TransactionDetail.svelte | 4 +- .../src/components/form/BridgeForm.svelte | 3 +- packages/bridge-ui/src/domain/chain.ts | 6 +- packages/bridge-ui/src/domain/relayerApi.ts | 16 ++ packages/bridge-ui/src/domain/transactions.ts | 8 +- packages/bridge-ui/src/relayer-api/service.ts | 192 ++++++++++++++++++ .../bridge-ui/src/storage/service.spec.ts | 81 +------- packages/bridge-ui/src/storage/service.ts | 12 +- packages/bridge-ui/src/store/relayerApi.ts | 7 + 12 files changed, 299 insertions(+), 100 deletions(-) create mode 100644 packages/bridge-ui/src/domain/relayerApi.ts create mode 100644 packages/bridge-ui/src/relayer-api/service.ts create mode 100644 packages/bridge-ui/src/store/relayerApi.ts diff --git a/packages/bridge-ui/README.md b/packages/bridge-ui/README.md index 876a1df3eb4..e611dba3128 100644 --- a/packages/bridge-ui/README.md +++ b/packages/bridge-ui/README.md @@ -16,20 +16,22 @@ You can use the following values in the `.env` file to spin up the Bridge UI loc VITE_NODE_ENV=dev VITE_L1_RPC_URL="https://l1rpc.internal.taiko.xyz/" VITE_L2_RPC_URL="https://l2rpc.internal.taiko.xyz/" +VITE_L1_EXPLORER_URL="https://l1explorer.internal.taiko.xyz/" +VITE_L2_EXPLORER_URL="https://l2explorer.internal.taiko.xyz/" VITE_RELAYER_URL="https://relayer.internal.taiko.xyz/" -VITE_TEST_ERC20_ADDRESS_MAINNET="0x1B5Ccd66cc2408A0084047720167F6234Dc5498A" +VITE_TEST_ERC20_ADDRESS_MAINNET="0x3435A6180fBB1BAEc87bDC49915282BfBC328C70" VITE_TEST_ERC20_SYMBOL_MAINNET="BULL" VITE_TEST_ERC20_NAME_MAINNET="Bull Token" VITE_MAINNET_CHAIN_ID=31336 VITE_TAIKO_CHAIN_ID=167001 VITE_MAINNET_CHAIN_NAME="Ethereum A2" VITE_TAIKO_CHAIN_NAME="Taiko A2" -VITE_MAINNET_TOKEN_VAULT_ADDRESS="0xAE4C9bD0f7AE5398Df05043079596E2BF0079CE9" +VITE_MAINNET_TOKEN_VAULT_ADDRESS="0x5E506e2E0EaD3Ff9d93859A5879cAA02582f77c3" VITE_TAIKO_TOKEN_VAULT_ADDRESS="0x0000777700000000000000000000000000000002" -VITE_MAINNET_HEADER_SYNC_ADDRESS="0xa6421A7f48498cee3aEb6428a8A2DD5fAA3AcE2f" +VITE_MAINNET_HEADER_SYNC_ADDRESS="0x9b557777Be33A8A2fE6aF93E017A0d139B439E5D" VITE_TAIKO_HEADER_SYNC_ADDRESS="0x0000777700000000000000000000000000000001" -VITE_MAINNET_BRIDGE_ADDRESS="0x0237443359aB0b11EcDC41A7aF1C90226a88c70f" +VITE_MAINNET_BRIDGE_ADDRESS="0xAE4C9bD0f7AE5398Df05043079596E2BF0079CE9" VITE_TAIKO_BRIDGE_ADDRESS="0x0000777700000000000000000000000000000004" -VITE_MAINNET_SIGNAL_SERVICE_ADDRESS="0x403cc7802725928652a3d116Bb1781005e2e76d3" +VITE_MAINNET_SIGNAL_SERVICE_ADDRESS="0x162A36c9821eadeCFF9669A3940b7f72d055Cd1c" VITE_TAIKO_SIGNAL_SERVICE_ADDRESS="0x0000777700000000000000000000000000000007" ``` \ No newline at end of file diff --git a/packages/bridge-ui/jest.config.js b/packages/bridge-ui/jest.config.js index 8a0c0adcfea..3b0b7ddd5c1 100644 --- a/packages/bridge-ui/jest.config.js +++ b/packages/bridge-ui/jest.config.js @@ -39,9 +39,9 @@ export default { ], coverageThreshold: { global: { - statements: 95, + statements: 94, branches: 72, - functions: 89, + functions: 88, lines: 95, }, }, diff --git a/packages/bridge-ui/src/App.svelte b/packages/bridge-ui/src/App.svelte index 41fe60d2384..cca26a6823c 100644 --- a/packages/bridge-ui/src/App.svelte +++ b/packages/bridge-ui/src/App.svelte @@ -27,7 +27,7 @@ } from "./store/transactions"; import Navbar from "./components/Navbar.svelte"; import { signer } from "./store/signer"; - import type { Transactioner } from "./domain/transactions"; + import type { BridgeTransaction, Transactioner } from "./domain/transactions"; import { wagmiClient } from "./store/wagmi"; setupI18n({ withLocale: "en" }); @@ -53,6 +53,9 @@ import type { TokenService } from "./domain/token"; import { CustomTokenService } from "./storage/customTokenService"; import { userTokens, tokenService } from "./store/userToken"; + import RelayerAPIService from "./relayer-api/service"; + import type { RelayerAPI } from "./domain/relayerApi"; + import { relayerApi, relayerBlockInfoMap } from "./store/relayerApi"; const providerMap: Map = new Map< number, @@ -106,8 +109,6 @@ ], }); - providers.set(providerMap); - const prover: Prover = new ProofService(providerMap); const ethBridge = new ETHBridge(prover); @@ -133,6 +134,8 @@ providerMap ); + const relayerApiService: RelayerAPI = new RelayerAPIService(providerMap, import.meta.env.VITE_RELAYER_URL); + const tokenStore: TokenService = new CustomTokenService( window.localStorage, ); @@ -140,15 +143,38 @@ tokenService.set(tokenStore); transactioner.set(storageTransactioner); + relayerApi.set(relayerApiService); signer.subscribe(async (store) => { if (store) { const userAddress = await store.getAddress(); - const txs = await $transactioner.GetAllByAddress( + + const apiTxs = await $relayerApi.GetAllByAddress( userAddress ); - transactions.set(txs); + const blockInfoMap = await $relayerApi.GetBlockInfo(); + relayerBlockInfoMap.set(blockInfoMap) + + const txs = await $transactioner.GetAllByAddress( + userAddress, + ); + + // const hashToApiTxsMap = new Map(apiTxs.map((tx) => { + // return [tx.hash, tx]; + // })) + + const updatedStorageTxs: BridgeTransaction[] = txs.filter((tx) => { + const blockInfo = blockInfoMap.get(tx.fromChainId); + if(blockInfo?.latestProcessedBlock >= tx.receipt.blockNumber) { + return false; + } + return true; + }); + + $transactioner.UpdateStorageByAddress(userAddress, updatedStorageTxs); + + transactions.set([...updatedStorageTxs, ...apiTxs]); const tokens = await $tokenService.GetTokens(userAddress) userTokens.set(tokens); @@ -160,10 +186,13 @@ store.forEach(async (tx) => { await $signer.provider.waitForTransaction(tx.hash, 1); successToast("Transaction completed!"); + + // TODO: Fix, .pop() removes the last tx but the confirmed tx is not necessarily the last one in the pendingTransactions array. const s = store; s.pop(); pendingTransactions.set(s); + // TODO: Do we need this? transactions.set( await $transactioner.GetAllByAddress(await $signer.getAddress()) ); @@ -175,24 +204,23 @@ transactions.subscribe((store) => { if (store) { store.forEach(async (tx) => { - const txInterval = transactionToIntervalMap.get(tx.ethersTx.hash); + const txInterval = transactionToIntervalMap.get(tx.hash); if (txInterval) { clearInterval(txInterval); - transactionToIntervalMap.delete(tx.ethersTx.hash); + transactionToIntervalMap.delete(tx.hash); } if (tx.status === MessageStatus.New) { const provider = providerMap.get(tx.toChainId); - const interval = setInterval(async () => { - const txInterval = transactionToIntervalMap.get(tx.ethersTx.hash); + const txInterval = transactionToIntervalMap.get(tx.hash); if (txInterval !== interval) { clearInterval(txInterval); - transactionToIntervalMap.delete(tx.ethersTx.hash); + transactionToIntervalMap.delete(tx.hash); } - transactionToIntervalMap.set(tx.ethersTx.hash, interval); + transactionToIntervalMap.set(tx.hash, interval); if (!tx.msgHash) return; const contract = new ethers.Contract( @@ -206,9 +234,9 @@ if (messageStatus === MessageStatus.Done) { successToast("Bridge message processed successfully"); - const txOngoingInterval = transactionToIntervalMap.get(tx.ethersTx.hash); + const txOngoingInterval = transactionToIntervalMap.get(tx.hash); clearInterval(txOngoingInterval); - transactionToIntervalMap.delete(tx.ethersTx.hash); + transactionToIntervalMap.delete(tx.hash); } }, 20 * 1000); } diff --git a/packages/bridge-ui/src/components/TransactionDetail.svelte b/packages/bridge-ui/src/components/TransactionDetail.svelte index 06737673512..f16928e8bf5 100644 --- a/packages/bridge-ui/src/components/TransactionDetail.svelte +++ b/packages/bridge-ui/src/components/TransactionDetail.svelte @@ -12,8 +12,8 @@ Tx Hash - - {truncateString(transaction.ethersTx.hash)} + + {truncateString(transaction.hash)} diff --git a/packages/bridge-ui/src/components/form/BridgeForm.svelte b/packages/bridge-ui/src/components/form/BridgeForm.svelte index 6d2271aabed..a7daa4766a6 100644 --- a/packages/bridge-ui/src/components/form/BridgeForm.svelte +++ b/packages/bridge-ui/src/components/form/BridgeForm.svelte @@ -260,7 +260,8 @@ toChainId: $toChain.id, symbol: $token.symbol, amountInWei: amountInWei, - ethersTx: tx, + from: tx.from, + hash: tx.hash, status: MessageStatus.New, }; if (!transactions) { diff --git a/packages/bridge-ui/src/domain/chain.ts b/packages/bridge-ui/src/domain/chain.ts index 8ec816ddc30..8fa58a37983 100644 --- a/packages/bridge-ui/src/domain/chain.ts +++ b/packages/bridge-ui/src/domain/chain.ts @@ -17,15 +17,15 @@ const L1_RPC = import.meta?.env?.VITE_L1_RPC_URL ?? "https://l1rpc.internal.taik const L2_RPC = import.meta?.env?.VITE_L2_RPC_URL ?? "https://l2rpc.internal.taiko.xyz/"; -const L1_BRIDGE_ADDRESS = import.meta?.env?.VITE_MAINNET_BRIDGE_ADDRESS ?? "0x0237443359aB0b11EcDC41A7aF1C90226a88c70f"; +const L1_BRIDGE_ADDRESS = import.meta?.env?.VITE_MAINNET_BRIDGE_ADDRESS ?? "0xAE4C9bD0f7AE5398Df05043079596E2BF0079CE9"; const L2_BRIDGE_ADDRESS = import.meta?.env?.VITE_TAIKO_BRIDGE_ADDRESS ?? "0x0000777700000000000000000000000000000004"; -const L1_HEADER_SYNC_ADDRESS = import.meta?.env?.VITE_MAINNET_HEADER_SYNC_ADDRESS ?? "0xa6421A7f48498cee3aEb6428a8A2DD5fAA3AcE2f"; +const L1_HEADER_SYNC_ADDRESS = import.meta?.env?.VITE_MAINNET_HEADER_SYNC_ADDRESS ?? "0x9b557777Be33A8A2fE6aF93E017A0d139B439E5D"; const L2_HEADER_SYNC_ADDRESS = import.meta?.env?.VITE_TAIKO_HEADER_SYNC_ADDRESS ?? "0x0000777700000000000000000000000000000001"; -const L1_SIGNAL_SERVICE_ADDRESS = import.meta?.env?.VITE_MAINNET_SIGNAL_SERVICE_ADDRESS ?? "0x403cc7802725928652a3d116Bb1781005e2e76d3"; +const L1_SIGNAL_SERVICE_ADDRESS = import.meta?.env?.VITE_MAINNET_SIGNAL_SERVICE_ADDRESS ?? "0x162A36c9821eadeCFF9669A3940b7f72d055Cd1c"; const L2_SIGNAL_SERVICE_ADDRESS = import.meta?.env?.VITE_TAIKO_SIGNAL_SERVICE_ADDRESS ?? "0x0000777700000000000000000000000000000007"; diff --git a/packages/bridge-ui/src/domain/relayerApi.ts b/packages/bridge-ui/src/domain/relayerApi.ts new file mode 100644 index 00000000000..9ab84c65a2d --- /dev/null +++ b/packages/bridge-ui/src/domain/relayerApi.ts @@ -0,0 +1,16 @@ +import type { BridgeTransaction } from "./transactions"; + +export interface RelayerAPI { + GetAllByAddress( + address: string, + chainID?: number + ): Promise; + + GetBlockInfo(): Promise>; +} + +export type RelayerBlockInfo = { + chainId: number; + latestProcessedBlock: number; + latestBlock: number; +} \ No newline at end of file diff --git a/packages/bridge-ui/src/domain/transactions.ts b/packages/bridge-ui/src/domain/transactions.ts index b9610aef683..9a10613dd77 100644 --- a/packages/bridge-ui/src/domain/transactions.ts +++ b/packages/bridge-ui/src/domain/transactions.ts @@ -2,7 +2,8 @@ import type { BigNumber, ethers } from "ethers"; import type { Message, MessageStatus } from "./message"; export type BridgeTransaction = { - ethersTx: ethers.Transaction; + hash: string; + from: string; receipt?: ethers.providers.TransactionReceipt; status: MessageStatus; msgHash?: string; @@ -18,4 +19,9 @@ export interface Transactioner { address: string, chainID?: number ): Promise; + + UpdateStorageByAddress( + address: string, + txs: BridgeTransaction[] + ): void; } diff --git a/packages/bridge-ui/src/relayer-api/service.ts b/packages/bridge-ui/src/relayer-api/service.ts new file mode 100644 index 00000000000..1c90e79f947 --- /dev/null +++ b/packages/bridge-ui/src/relayer-api/service.ts @@ -0,0 +1,192 @@ +import axios from 'axios'; +import { BigNumber, Contract, ethers } from 'ethers'; +import Bridge from '../constants/abi/Bridge'; +import ERC20 from '../constants/abi/ERC20'; +import TokenVault from '../constants/abi/TokenVault'; +import { chains } from '../domain/chain'; +import { MessageStatus } from '../domain/message'; + +import type { BridgeTransaction } from '../domain/transactions'; +import { chainIdToTokenVaultAddress } from '../store/bridge'; +import { get } from 'svelte/store'; +import type { RelayerAPI, RelayerBlockInfo } from 'src/domain/relayerApi'; + +class RelayerAPIService implements RelayerAPI { + private readonly providerMap: Map; + private readonly baseUrl: string; + constructor(providerMap: Map, baseUrl: string) { + this.providerMap = providerMap; + this.baseUrl = baseUrl; + } + + async GetAllByAddress(address: string, chainID?: number): Promise { + if(!address) { + throw new Error("Address need to passed to fetch transactions"); + } + const params = { + address, + chainID, + } + + const requestURL = `${this.baseUrl}events`; + + const { data } = await axios.get(requestURL, { params }); + + if(data.length === 0) { + return []; + } + + const txs: BridgeTransaction[] = data.map((tx) => { + const depositValue = ethers.utils.parseUnits(tx.data.Message.DepositValue.toString(), 'wei'); + return { + status: tx.status, + message: { + id: tx.data.Message.Id, + to: tx.data.Message.To, + data: tx.data.Message.Data, + memo: tx.data.Message.Memo, + owner: tx.data.Message.Owner, + sender: tx.data.Message.Sender, + gasLimit: BigNumber.from(tx.data.Message.GasLimit), + callValue: tx.data.Message.CallValue, + srcChainId: BigNumber.from(tx.data.Message.SrcChainId), + destChainId: BigNumber.from(tx.data.Message.DestChainId), + depositValue: depositValue, + processingFee: BigNumber.from(tx.data.Message.ProcessingFee), + refundAddress: tx.data.Message.RefundAddress, + }, + amountInWei: tx.amount, + symbol: tx.canonicalTokenSymbol, + fromChainId: tx.data.Message.SrcChainId, + toChainId: tx.data.Message.DestChainId, + hash: tx.data.Raw.transactionHash, + from: tx.data.Message.Owner, + } + }) + + const bridgeTxs: BridgeTransaction[] = []; + + await Promise.all( + (txs || []).map(async (tx) => { + if (tx.message.owner.toLowerCase() !== address.toLowerCase()) return; + + const destChainId = tx.toChainId; + const destProvider = this.providerMap.get(destChainId); + + const srcProvider = this.providerMap.get(tx.fromChainId); + + const receipt = await srcProvider.getTransactionReceipt( + tx.hash + ); + + if (!receipt) { + bridgeTxs.push(tx); + return; + } + + tx.receipt = receipt; + + const destBridgeAddress = chains[destChainId].bridgeAddress; + + const srcBridgeAddress = chains[tx.fromChainId].bridgeAddress; + + const destContract: Contract = new Contract( + destBridgeAddress, + Bridge, + destProvider + ); + + const srcContract: Contract = new Contract( + srcBridgeAddress, + Bridge, + srcProvider + ); + + const events = await srcContract.queryFilter( + "MessageSent", + receipt.blockNumber, + receipt.blockNumber + ); + + const event = events.find( + (e) => e.args.message.owner.toLowerCase() === address.toLowerCase() + ); + + if (!event) { + bridgeTxs.push(tx); + return; + } + + const msgHash = event.args.msgHash; + + const messageStatus: number = await destContract.getMessageStatus( + msgHash + ); + + let amountInWei: BigNumber; + let symbol: string; + if (event.args.message.data !== "0x") { + const tokenVaultContract = new Contract( + get(chainIdToTokenVaultAddress).get(tx.fromChainId), + TokenVault, + srcProvider + ); + const filter = tokenVaultContract.filters.ERC20Sent(msgHash) + const erc20Events = await tokenVaultContract.queryFilter( + filter, + receipt.blockNumber, + receipt.blockNumber + ); + + const erc20Event = erc20Events.find( + (e) => e.args.msgHash.toLowerCase() === msgHash.toLowerCase() + ); + if (!erc20Event) return; + + const erc20Contract = new Contract( + erc20Event.args.token, + ERC20, + srcProvider + ); + symbol = await erc20Contract.symbol(); + amountInWei = BigNumber.from(erc20Event.args.amount); + } + + const bridgeTx: BridgeTransaction = { + message: event.args.message, + receipt: receipt, + msgHash: event.args.msgHash, + status: messageStatus, + amountInWei: amountInWei, + symbol: symbol, + fromChainId: tx.fromChainId, + toChainId: tx.toChainId, + hash: tx.hash, + from: tx.from, + }; + + bridgeTxs.push(bridgeTx); + }) + ); + + bridgeTxs.sort((tx) => (tx.status === MessageStatus.New ? -1 : 1)); + + return bridgeTxs; + } + + async GetBlockInfo(): Promise> { + const requestURL = `${this.baseUrl}blockInfo`; + const { data } = await axios.get(requestURL); + const blockInfoMap: Map = new Map(); + if(data?.data.length > 0) { + data.data.forEach((blockInfoByChain => { + blockInfoMap.set(blockInfoByChain.chainID, blockInfoByChain); + })); + } + + return blockInfoMap; + } + +} + +export default RelayerAPIService; \ No newline at end of file diff --git a/packages/bridge-ui/src/storage/service.spec.ts b/packages/bridge-ui/src/storage/service.spec.ts index 0ceeb8f47a7..bc9cb16ca89 100644 --- a/packages/bridge-ui/src/storage/service.spec.ts +++ b/packages/bridge-ui/src/storage/service.spec.ts @@ -49,15 +49,8 @@ providerMap.set( ); const mockTx: BridgeTransaction = { - ethersTx: { - chainId: CHAIN_ID_MAINNET, - hash: "0x123", - nonce: 0, - gasLimit: BigNumber.from(1), - data: "0x", - value: BigNumber.from(1), - from: "0x123", - }, + hash: "0x123", + from: "0x123", status: MessageStatus.New, fromChainId: CHAIN_ID_MAINNET, toChainId: CHAIN_ID_TAIKO, @@ -146,21 +139,8 @@ describe("storage tests", () => { expect(addresses).toEqual([ { - ethersTx: { - chainId: CHAIN_ID_MAINNET, - data: "0x", - from: "0x123", - gasLimit: { - hex: "0x01", - type: "BigNumber", - }, - hash: "0x123", - nonce: 0, - value: { - hex: "0x01", - type: "BigNumber", - }, - }, + from: "0x123", + hash: "0x123", fromChainId: CHAIN_ID_MAINNET, status: 0, toChainId: CHAIN_ID_TAIKO, @@ -195,21 +175,8 @@ describe("storage tests", () => { expect(addresses).toEqual([ { - ethersTx: { - chainId: CHAIN_ID_MAINNET, - data: "0x", - from: "0x123", - gasLimit: { - hex: "0x01", - type: "BigNumber", - }, - hash: "0x123", - nonce: 0, - value: { - hex: "0x01", - type: "BigNumber", - }, - }, + from: "0x123", + hash: "0x123", fromChainId: CHAIN_ID_MAINNET, receipt: { blockNumber: 1, @@ -254,21 +221,8 @@ describe("storage tests", () => { expect(addresses).toEqual([ { amountInWei: BigNumber.from(0x64), - ethersTx: { - chainId: CHAIN_ID_MAINNET, - data: "0x", - gasLimit: { - hex: "0x01", - type: "BigNumber", - }, - hash: "0x123", - nonce: 0, - value: { - hex: "0x01", - type: "BigNumber", - }, - from: "0x123", - }, + hash: "0x123", + from: "0x123", message: { owner: "0x123", }, @@ -285,7 +239,7 @@ describe("storage tests", () => { }); it("gets all transactions by address, CHAIN_TKO", async () => { - mockTx.ethersTx.chainId = CHAIN_ID_TAIKO; + mockTx.toChainId = CHAIN_ID_TAIKO; mockStorage.getItem.mockImplementation(() => { return JSON.stringify(mockTxs); }); @@ -319,21 +273,8 @@ describe("storage tests", () => { expect(addresses).toEqual([ { amountInWei: BigNumber.from(0x64), - ethersTx: { - chainId: CHAIN_ID_TAIKO, - from: "0x123", - data: "0x", - gasLimit: { - hex: "0x01", - type: "BigNumber", - }, - hash: "0x123", - nonce: 0, - value: { - hex: "0x01", - type: "BigNumber", - }, - }, + from: "0x123", + hash: "0x123", message: { owner: "0x123", }, diff --git a/packages/bridge-ui/src/storage/service.ts b/packages/bridge-ui/src/storage/service.ts index 423f960dfb0..4904d731ad6 100644 --- a/packages/bridge-ui/src/storage/service.ts +++ b/packages/bridge-ui/src/storage/service.ts @@ -10,6 +10,7 @@ import { MessageStatus } from "../domain/message"; interface storage { getItem(key: string): string; + setItem(key: string, value: unknown): void; } class StorageService implements Transactioner { @@ -36,7 +37,7 @@ class StorageService implements Transactioner { await Promise.all( (txs || []).map(async (tx) => { - if (tx.ethersTx.from.toLowerCase() !== address.toLowerCase()) return; + if (tx.from.toLowerCase() !== address.toLowerCase()) return; const destChainId = tx.toChainId; const destProvider = this.providerMap.get(destChainId); @@ -48,7 +49,7 @@ class StorageService implements Transactioner { } const receipt = await srcProvider.getTransactionReceipt( - tx.ethersTx.hash + tx.hash ); if (!receipt) { @@ -125,10 +126,11 @@ class StorageService implements Transactioner { } const bridgeTx: BridgeTransaction = { + hash: tx.hash, + from: tx.from, message: event.args.message, receipt: receipt, msgHash: event.args.msgHash, - ethersTx: tx.ethersTx, status: messageStatus, amountInWei: amountInWei, symbol: symbol, @@ -144,6 +146,10 @@ class StorageService implements Transactioner { return bridgeTxs; } + + UpdateStorageByAddress(address: string, txs: BridgeTransaction[]) { + this.storage.setItem(`transactions-${address.toLowerCase()}`, JSON.stringify(txs)); + } } export { StorageService }; diff --git a/packages/bridge-ui/src/store/relayerApi.ts b/packages/bridge-ui/src/store/relayerApi.ts new file mode 100644 index 00000000000..ce133cdd9b4 --- /dev/null +++ b/packages/bridge-ui/src/store/relayerApi.ts @@ -0,0 +1,7 @@ +import type { RelayerAPI, RelayerBlockInfo } from "../domain/relayerApi"; +import { writable } from "svelte/store"; + +const relayerApi = writable(); +const relayerBlockInfoMap = writable>(); + +export { relayerApi, relayerBlockInfoMap }; \ No newline at end of file