Skip to content

Commit

Permalink
feat(bridge-ui): add custom ERC20 tokens support (#13170)
Browse files Browse the repository at this point in the history
Co-authored-by: jeff <[email protected]>
Co-authored-by: Daniel Wang <[email protected]>
Co-authored-by: David <[email protected]>
  • Loading branch information
4 people authored Feb 23, 2023
1 parent 66316e9 commit 227d8de
Show file tree
Hide file tree
Showing 32 changed files with 1,691 additions and 926 deletions.
26 changes: 15 additions & 11 deletions packages/bridge-ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,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.a1.taiko.xyz/"
VITE_L2_RPC_URL="https://l2rpc.a1.taiko.xyz/"
VITE_RELAYER_URL="http://localhost:4102/"
VITE_TEST_ERC20_ADDRESS_MAINNET="0x3E3a3416DbCc5cb4448B6b171fF15f9Da35Ab72d"
VITE_MAINNET_CHAIN_ID=31338
VITE_TAIKO_CHAIN_ID=167003
VITE_MAINNET_CHAIN_NAME="Ethereum A1"
VITE_TAIKO_CHAIN_NAME="Taiko A1"
VITE_L1_RPC_URL="https://l1rpc.internal.taiko.xyz/"
VITE_L2_RPC_URL="https://l2rpc.internal.taiko.xyz/"
VITE_RELAYER_URL="https://relayer.internal.taiko.xyz/"
VITE_TEST_ERC20_ADDRESS_MAINNET="0x1B5Ccd66cc2408A0084047720167F6234Dc5498A"
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_TAIKO_TOKEN_VAULT_ADDRESS="0x0000777700000000000000000000000000000002"
VITE_MAINNET_TOKEN_VAULT_ADDRESS="0xD0dfd5baCf160B97C8eE3ecb463F18c08673160c"
VITE_MAINNET_HEADER_SYNC_ADDRESS="0xa6421A7f48498cee3aEb6428a8A2DD5fAA3AcE2f"
VITE_TAIKO_HEADER_SYNC_ADDRESS="0x0000777700000000000000000000000000000001"
VITE_MAINNET_HEADER_SYNC_ADDRESS="0x7B3AF414448ba906f02a1CA307C56c4ADFF27ce7"
VITE_MAINNET_BRIDGE_ADDRESS="0x3612E284D763f42f5E4CB72B1602b23DAEC3cA60"
VITE_MAINNET_BRIDGE_ADDRESS="0x0237443359aB0b11EcDC41A7aF1C90226a88c70f"
VITE_TAIKO_BRIDGE_ADDRESS="0x0000777700000000000000000000000000000004"
VITE_MAINNET_SIGNAL_SERVICE_ADDRESS="0x403cc7802725928652a3d116Bb1781005e2e76d3"
VITE_TAIKO_SIGNAL_SERVICE_ADDRESS="0x0000777700000000000000000000000000000007"
```
8 changes: 4 additions & 4 deletions packages/bridge-ui/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ export default {
],
coverageThreshold: {
global: {
statements: 96,
branches: 79,
functions: 91,
lines: 97,
statements: 95,
branches: 72,
functions: 89,
lines: 96,
},
},
modulePathIgnorePatterns: ["<rootDir>/public/build/"],
Expand Down
19 changes: 16 additions & 3 deletions packages/bridge-ui/src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@
import BridgeABI from "./constants/abi/Bridge";
import { providers } from "./store/providers";
import HeaderAnnouncement from "./components/HeaderAnnouncement.svelte";
import type { TokenService } from "./domain/token";
import { CustomTokenService } from "./storage/customTokenService";
import { userTokens, tokenService } from "./store/userToken";
const providerMap: Map<number, ethers.providers.JsonRpcProvider> = new Map<
number,
Expand Down Expand Up @@ -130,15 +133,25 @@
providerMap
);
const tokenStore: TokenService = new CustomTokenService(
window.localStorage,
);
tokenService.set(tokenStore);
transactioner.set(storageTransactioner);
signer.subscribe(async (store) => {
if (store) {
const userAddress = await store.getAddress();
const txs = await $transactioner.GetAllByAddress(
await store.getAddress()
userAddress
);
transactions.set(txs);
const tokens = await $tokenService.GetTokens(userAddress)
userTokens.set(tokens);
}
return store;
});
Expand Down Expand Up @@ -180,7 +193,7 @@
}
transactionToIntervalMap.set(tx.ethersTx.hash, interval);
if (!tx.signal) return;
if (!tx.msgHash) return;
const contract = new ethers.Contract(
chains[tx.toChainId].bridgeAddress,
Expand All @@ -189,7 +202,7 @@
);
const messageStatus: MessageStatus =
await contract.getMessageStatus(tx.signal);
await contract.getMessageStatus(tx.msgHash);
if (messageStatus === MessageStatus.Done) {
successToast("Bridge message processed successfully");
Expand Down
4 changes: 4 additions & 0 deletions packages/bridge-ui/src/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@
border-radius: 0.5rem;
}

ul.token-dropdown li.cursor-pointer:last-child, ul.token-dropdown li.cursor-pointer:first-child {
border-radius: 0;
}

.taiko-banner {
background-image: url('assets/taiko-banner.svg');
background-repeat: no-repeat;
Expand Down
Binary file added packages/bridge-ui/src/assets/erc20.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 6 additions & 2 deletions packages/bridge-ui/src/components/MessageStatusTooltip.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@
<ul class="list-disc ml-4">
<li class="mb-2">
<strong>Pending</strong>: Your asset is not ready to be bridged. Taiko
A1 => Ethereum A1 bridging can take several hours before being ready.
Ethereum A1 => Taiko A1 should be available to claim within minutes.
A1 => {import.meta.env
? import.meta.env.VITE_MAINNET_CHAIN_NAME
: "Ethereum A2"} bridging can take several hours before being ready.
{import.meta.env
? import.meta.env.VITE_MAINNET_CHAIN_NAME
: "Ethereum A2"} => {import.meta.env ? import.meta.env.VITE_TAIKO_CHAIN_NAME : "Taiko A2"} should be available to claim within minutes.
</li>
<li class="mb-2">
<strong>Claimable</strong>: Your asset is ready to be claimed on the
Expand Down
5 changes: 2 additions & 3 deletions packages/bridge-ui/src/components/Transaction.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import HeaderSync from "../constants/abi/HeaderSync";
import { providers } from "../store/providers";
import { fetchSigner, switchNetwork } from "@wagmi/core";
import Tooltip from "./Tooltip.svelte";
import Bridge from "../constants/abi/Bridge";
import ButtonWithTooltip from "./ButtonWithTooltip.svelte";
Expand Down Expand Up @@ -64,7 +63,7 @@
.Claim({
signer: $signer,
message: bridgeTx.message,
signal: bridgeTx.signal,
msgHash: bridgeTx.msgHash,
destBridgeAddress:
chains[bridgeTx.message.destChainId.toNumber()].bridgeAddress,
srcBridgeAddress:
Expand Down Expand Up @@ -109,7 +108,7 @@
$providers.get(chains[transaction.message.destChainId.toNumber()].id)
);
transaction.status = await contract.getMessageStatus(transaction.signal);
transaction.status = await contract.getMessageStatus(transaction.msgHash);
transaction = transaction;
if (transaction.status === MessageStatus.Done) clearInterval(interval);
}, 20 * 1000);
Expand Down
102 changes: 97 additions & 5 deletions packages/bridge-ui/src/components/buttons/SelectToken.svelte
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
<script lang="ts">
import { getProvider } from '@wagmi/core'
import { token } from "../../store/token";
import { bridgeType } from "../../store/bridge";
import { ETH, tokens } from "../../domain/token";
import type { Token } from "../../domain/token";
import { BridgeType } from "../../domain/bridge";
import { ChevronDown } from "svelte-heros-v2";
import { successToast } from "../../utils/toast";
import { ChevronDown, PlusCircle } from "svelte-heros-v2";
import { errorToast, successToast } from "../../utils/toast";
import { ethers } from "ethers";
import ERC20 from "../../constants/abi/ERC20";
import { signer } from '../../store/signer';
import { userTokens, tokenService } from '../../store/userToken';
import { fromChain, toChain } from '../../store/chain';
import Erc20 from '../icons/ERC20.svelte';
import AddCustomErc20 from '../form/AddCustomERC20.svelte';
let dropdownElement: HTMLDivElement;
Expand All @@ -25,23 +33,85 @@
document.activeElement.blur();
}
}
let showAddressField = false;
let loading = false;
async function addERC20(event: SubmitEvent) {
try {
loading = true;
const eventTarget = event.target as HTMLFormElement;
const { customTokenAddress } = eventTarget;
const tokenAddress = customTokenAddress.value;
if(!ethers.utils.isAddress(tokenAddress)) {
throw new Error();
}
const provider = getProvider();
const contract = new ethers.Contract(tokenAddress, ERC20, provider);
const userAddress = await $signer.getAddress();
const erc20Check = await contract.balanceOf(userAddress);
const [tokenName, decimals, symbol] = await Promise.all([
contract.name(),
contract.decimals(),
contract.symbol(),
])
const token = {
name: tokenName,
addresses: [
{
chainId: $fromChain.id,
address: tokenAddress,
},
{
chainId: $toChain.id,
address: "0x00",
}
],
decimals: decimals,
symbol: symbol,
logoComponent: null,
}
const updateTokensList = await $tokenService.StoreToken(token, userAddress);
select(token);
userTokens.set(updateTokensList);
eventTarget.reset();
showAddressField = false;
} catch(error) {
errorToast("Not a valid ERC20 address");
console.error(error);
} finally {
loading = false;
}
}
let customTokens = [];
userTokens.subscribe(value => {
if(value)
customTokens = value;
})
</script>

<div class="dropdown dropdown-bottom" bind:this={dropdownElement}>
<label
tabindex="0"
class="flex items-center justify-center hover:cursor-pointer"
>
<svelte:component this={$token.logoComponent} />
{#if $token.logoComponent}
<svelte:component this={$token.logoComponent} />
{:else}
<Erc20 />
{/if}
<p class="px-2 text-sm">{$token.symbol.toUpperCase()}</p>
<ChevronDown size='20' />
</label>
<ul
tabindex="0"
class="dropdown-content menu my-2 shadow-xl bg-dark-2 rounded-box p-2"
class="token-dropdown dropdown-content menu my-2 shadow-xl bg-dark-2 rounded-box p-2"
>
{#each tokens as t}
<li class="cursor-pointer w-full hover:bg-dark-5 px-4 py-4">
<li class="cursor-pointer w-full hover:bg-dark-5 px-4 py-4 rounded-none">
<button on:click={async () => await select(t)} class="flex hover:bg-dark-5">
<svelte:component this={t.logoComponent} height={22} width={22} />
<span class="text-sm font-medium bg-transparent px-2"
Expand All @@ -50,5 +120,27 @@
</button>
</li>
{/each}
{#each customTokens as t}
<li class="cursor-pointer w-full hover:bg-dark-5 px-4 py-4">
<button on:click={async () => await select(t)} class="flex hover:bg-dark-5">
<Erc20 height={22} width={22} />
<span class="text-sm font-medium bg-transparent px-2"
>{t.symbol.toUpperCase()}</span
>
</button>
</li>
{/each}
<li class="cursor-pointer hover:bg-dark-5 px-4 py-4">
<button on:click={() => showAddressField = true} class="flex hover:bg-dark-5 justify-between items-center">
<PlusCircle size='25' />
<span class="text-sm font-medium bg-transparent flex-1 w-[100px] px-0 pl-2"
>Add Custom</span
>
</button>
</li>
</ul>

{#if showAddressField}
<AddCustomErc20 bind:showAddressField addERC20={addERC20} bind:loading />
{/if}
</div>
Loading

0 comments on commit 227d8de

Please sign in to comment.