Skip to content

Commit

Permalink
feat(bridge): bridge design (#369)
Browse files Browse the repository at this point in the history
* fix(bridge): wagmi init

* feat(bridge): implement design

* feat(bridge): add processing fee mock

* fix(bridge): review fixes

* feat(bridge): add default case and test

* fix(bridge): update test

* feat(bridge): single field bridge form

* fix(bridge): processing fee

* feat(bridge-ui): Transactions (#372)

* feat(bridge-ui): ERC20 Bridge + approval (#353)

* erc20 bridge + approve + requires allowance

* bridge form checks allowance, disables button

* if allowance is required, show approve button instead

* set allowance required to false after successful approval

* requiresallowance should take approveopts, not bridgeopts

* show Approve text appropriatley

* Update packages/bridge-ui/src/components/form/BridgeForm.svelte

Co-authored-by: David <[email protected]>

* Update packages/bridge-ui/src/eth/bridge.ts

Co-authored-by: David <[email protected]>

* Update packages/bridge-ui/src/components/form/BridgeForm.svelte

Co-authored-by: David <[email protected]>

* Update packages/bridge-ui/src/components/form/BridgeForm.svelte

Co-authored-by: David <[email protected]>

* inverse approve logic

Co-authored-by: David <[email protected]>

* .

* lottie player

* update lockfile

Co-authored-by: David <[email protected]>

* feat: add chain selector

* make ETH/TKO use ts

* ignore components folder

* inline block avatar

* switch ethereum chain, chain navbar selection, default store values, label changing, modal

* test coverage for switchEthereumChain

* list

* readapt bridge form to work

* text

* feat(bridge): switch chain modal styles

* fix(bridge): chain dropdown styles

* tests

* rpc urls + reactive balanes

* import type

* cli test

Co-authored-by: jeff <[email protected]>
Co-authored-by: David <[email protected]>
Co-authored-by: Jeffery Walsh <[email protected]>
  • Loading branch information
4 people authored Dec 6, 2022
1 parent a546ee9 commit 04702db
Show file tree
Hide file tree
Showing 48 changed files with 1,234 additions and 2,192 deletions.
5 changes: 5 additions & 0 deletions packages/bridge-ui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap" rel="stylesheet">

<title>Bridge</title>
</head>
<body>
Expand Down
1 change: 1 addition & 0 deletions packages/bridge-ui/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export default {
preset: "ts-jest",
testEnvironment: "jsdom",
testPathIgnorePatterns: ["<rootDir>/node_modules/"],
coveragePathIgnorePatterns: ["<rootDir>/src/components/"],
testTimeout: 40 * 1000,
watchPathIgnorePatterns: ["node_modules"],
};
11 changes: 8 additions & 3 deletions packages/bridge-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,19 @@
"ts-loader": "^9.2.6",
"tslib": "^2.4.0",
"typescript": "^4.6.4",
"vite": "^3.0.0"
"vite": "^3.0.0",
"vite-plugin-static-copy": "^0.12.0"
},
"dependencies": {
"@ethersproject/experimental": "^5.7.0",
"@lottiefiles/svelte-lottie-player": "^0.2.0",
"@sveltestack/svelte-query": "^1.6.0",
"@wagmi/core": "^0.7.5",
"axios": "^1.2.0",
"ethers": "^5.7.1",
"extend-expect": "link:@testing-library/jest-dom/extend-expect",
"identicon.js": "^2.3.3",
"svelte-i18n": "^3.5.1",
"svelte-spa-router": "^3.2.0",
"wagmi": "^0.8.6"
"svelte-spa-router": "^3.2.0"
}
}
30 changes: 26 additions & 4 deletions packages/bridge-ui/src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,23 @@
import { wrap } from "svelte-spa-router/wrap";
import QueryProvider from "./components/providers/QueryProvider.svelte";
import Router from "svelte-spa-router";
import Navbar from "./components/Navbar.svelte";
import { SvelteToast } from "@zerodevx/svelte-toast";
import { SvelteToast, toast } 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 { CHAIN_MAINNET, CHAIN_TKO } from "./domain/chain";
import ERC20Bridge from "./erc20/bridge";
import { pendingTransactions } 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 SwitchEthereumChainModal from "./components/modals/SwitchEthereumChainModal.svelte";
const ethBridge = new ETHBridge();
const erc20Bridge = new ERC20Bridge();
Expand All @@ -30,6 +35,20 @@
return store;
});
const relayerURL = import.meta.env.VITE_RELAYER_URL;
const transactioner: Transactioner = new RelayerService(relayerURL);
pendingTransactions.subscribe((store) => {
store.forEach(async (tx) => {
await $signer.provider.waitForTransaction(tx.hash, 3);
toast.push("Transaction completed!");
const s = store;
s.pop();
pendingTransactions.set(s);
});
});
const routes = {
"/": wrap({
component: Home,
Expand All @@ -41,10 +60,12 @@

<QueryProvider>
<main>
<Navbar />
<Navbar {transactioner} />
<Router {routes} />
</main>
<SvelteToast />

<SwitchEthereumChainModal />
</QueryProvider>

<style global lang="postcss">
Expand All @@ -54,5 +75,6 @@
main {
margin: 0;
font-family: "Inter", sans-serif;
}
</style>
46 changes: 46 additions & 0 deletions packages/bridge-ui/src/app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
.btn.btn-wide {
width: 194px;
height: 56px;
}

.btn.btn-token-select {
width: 140px;
height: 60px;
}

.btn.btn-square {
border-radius: 4px;
}

/* Invert accent button colors */
.btn.btn-accent {
background-color: hsla(var(--af) / var(--tw-bg-opacity, 1));
border-color: hsla(var(--af) / var(--tw-bg-opacity, 1));
height: 60px;
}

.btn.btn-accent:hover {
background-color: hsla(var(--a) / var(--tw-bg-opacity, 1));
border-color: hsla(var(--a) / var(--tw-bg-opacity, 1));
}

.dropdown .dropdown-content {
border-radius: 0 0 var(--rounded-box) var(--rounded-box);
}

.input-group .input.input-primary {
border-radius: 0.5rem;
}

.form-control .input-group :first-child {
border-radius: 0.5rem;
}

.form-control .input-group :last-child {
border-radius: 0.5rem;
}

.taiko-banner {
background-image: url('assets/taiko-banner.svg');
background-repeat: no-repeat;
}
1 change: 1 addition & 0 deletions packages/bridge-ui/src/assets/lottie/loader.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"nm":"Comp 1","mn":"","layers":[{"ty":4,"nm":"Shape Layer 2","mn":"","sr":1,"st":0,"op":300,"ip":0,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"tt":0,"hasMask":false,"td":0,"ao":0,"ks":{"a":{"a":0,"k":[-22.637,19.301,0],"ix":1},"s":{"a":0,"k":[33.33333333333334,33.33333333333334,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[100.02066666666668,100.00000000000003,0],"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":80,"ix":11},"r":{"a":1,"k":[{"o":{"x":0.472,"y":0.326},"i":{"x":0.526,"y":0.673},"s":[0],"t":0},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[183],"t":60}],"ix":10}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[368.602,368.602],"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":49,"ix":5},"d":[],"c":{"a":0,"k":[0.9882,0.0588,0.7529],"ix":3}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-22.699,19.301],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"tm","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Filter - Trim","nm":"Trim Paths 1","ix":2,"e":{"a":1,"k":[{"o":{"x":0.559,"y":0},"i":{"x":0.504,"y":1},"s":[1],"t":0},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[50],"t":30}],"ix":2},"o":{"a":0,"k":0,"ix":3},"s":{"a":1,"k":[{"o":{"x":0.579,"y":0},"i":{"x":0.438,"y":1},"s":[0],"t":30},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[49],"t":60}],"ix":1},"m":1}],"ind":0},{"ty":4,"nm":"Shape Layer 1","mn":"","sr":1,"st":0,"op":300,"ip":0,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"tt":0,"hasMask":false,"td":0,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[33.33333333333334,33.33333333333334,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[107.56633333333339,93.56633333333338,0],"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":10,"ix":11},"r":{"a":0,"k":0,"ix":10}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[368.602,368.602],"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":2,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":49,"ix":5},"d":[],"c":{"a":0,"k":[0.698,0.0588,0.5373],"ix":3}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-22.699,19.301],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":1}],"ddd":0,"h":200,"w":200,"meta":{"a":"","k":"","d":"","g":"@lottiefiles/toolkit-js 0.17.3","tc":"#ffffff"},"v":"5.5.0","fr":30,"op":60,"ip":0,"assets":[]}
15 changes: 15 additions & 0 deletions packages/bridge-ui/src/assets/taiko-banner.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
64 changes: 56 additions & 8 deletions packages/bridge-ui/src/components/AddressDropdown.svelte
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
<script lang="ts">
import { onMount } from "svelte";
import { _ } from "svelte-i18n";
import { toast } from "@zerodevx/svelte-toast";
import { addressSubsection } from "../utils/addressSubsection";
import { onMount } from "svelte";
import { signer } from "../store/signer";
import { pendingTransactions } from "../store/transactions";
import ChevDown from "./icons/ChevDown.svelte";
import { toast } from "@zerodevx/svelte-toast";
import { getAddressAvatarFromIdenticon } from "../utils/addressAvatar";
import type { BridgeTransaction } from "src/domain/transactions";
import { LottiePlayer } from "@lottiefiles/svelte-lottie-player";
import type { Signer } from "ethers";
export let transactions: BridgeTransaction[] = [];
let address: string;
let addressAvatarImgData: string;
onMount(async () => {
address = await $signer.getAddress();
setAddress($signer);
});
$: setAddress($signer).catch((e) => console.error(e));
async function setAddress(signer: Signer) {
address = await signer.getAddress();
addressAvatarImgData = getAddressAvatarFromIdenticon(address);
}
async function copyToClipboard(clip: string) {
await navigator.clipboard.writeText(clip);
}
Expand All @@ -25,15 +41,42 @@
}
</script>

<div class="dropdown dropdown-end">
<label tabindex="0" class="btn m-1">
<span class="pr-2">{addressSubsection(address)}</span>
<div class="dropdown dropdown-bottom">
<button tabindex="0" class="btn btn-wide justify-around">
<span class="font-normal flex-1 text-left">
{#if $pendingTransactions && $pendingTransactions.length}
{$pendingTransactions.length} Pending
<div class="inline-block">
<LottiePlayer
src="/lottie/loader.json"
autoplay={true}
loop={true}
controls={false}
renderer="svg"
background="transparent"
height={26}
width={26}
controlsLayout={[]}
/>
</div>
{:else}
<img
width="26"
height="26"
src="data:image/png;base64,{addressAvatarImgData}"
class="rounded-full mr-2 inline-block"
alt="avatar"
/>

{addressSubsection(address)}
{/if}
</span>

<ChevDown />
</label>
</button>
<ul
tabindex="0"
class="dropdown-content menu p-2 shadow bg-base-100 rounded-box w-52"
class="dropdown-content menu p-2 shadow bg-dark-3 rounded-box w-[194px]"
>
<li>
<span
Expand All @@ -46,5 +89,10 @@
>Disconnect</span
>
</li>
{#if transactions && transactions.length}
<li>
<span class="cursor-pointer"> {transactions.length} Transactions</span>
</li>
{/if}
</ul>
</div>
70 changes: 70 additions & 0 deletions packages/bridge-ui/src/components/ChainDropdown.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<script lang="ts">
import { _ } from "svelte-i18n";
import ChevDown from "./icons/ChevDown.svelte";
import { fromChain } from "../store/chain";
import MetaMask from "./icons/MetaMask.svelte";
import { switchEthereumChain } from "../utils/switchEthereumChain";
import { ethereum } from "../store/ethereum";
import { CHAIN_MAINNET, CHAIN_TKO } from "../domain/chain";
import type { Chain } from "../domain/chain";
import { ethers } from "ethers";
import { signer } from "../store/signer";
const changeChain = async (chain: Chain) => {
await switchEthereumChain($ethereum, chain);
const provider = new ethers.providers.Web3Provider(window.ethereum);
await provider.send("eth_requestAccounts", []);
signer.set(provider.getSigner());
};
</script>

<div class="dropdown dropdown-bottom mr-4">
<button tabindex="0" class="btn btn-wide justify-around">
<span class="font-normal flex-1 text-left">
{#if $fromChain}
<svelte:component this={$fromChain.icon} />
<span class="ml-2">{$fromChain.name}</span>
{:else}
<span class="ml-2">Invalid Chain</span>
{/if}
</span>

<ChevDown />
</button>
<ul
tabindex="0"
class="dropdown-content flex menu p-2 shadow bg-dark-3 rounded-box w-[194px]"
>
<li>
<button
class="flex items-center px-2 py-4 hover:bg-dark-5 rounded-xl justify-around"
on:click={async () => {
await changeChain(CHAIN_MAINNET);
}}
>
<svelte:component this={CHAIN_MAINNET.icon} height={24} />
<span class="pl-1.5 text-left flex-1">{CHAIN_MAINNET.name}</span>
<MetaMask />
</button>
</li>
<li>
<button
class="flex items-center px-2 py-4 hover:bg-dark-5 rounded-xl justify-around"
on:click={async () => {
await changeChain(CHAIN_TKO);
}}
>
<svelte:component this={CHAIN_TKO.icon} height={24} />
<span class="pl-1.5 text-left flex-1">{CHAIN_TKO.name}</span>
<MetaMask />
</button>
</li>
</ul>
</div>

<style>
.menu li > span {
padding-left: 0px;
}
</style>
34 changes: 28 additions & 6 deletions packages/bridge-ui/src/components/Navbar.svelte
Original file line number Diff line number Diff line change
@@ -1,19 +1,41 @@
<script>
<script lang="ts">
import Connect from "./buttons/Connect.svelte";
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
);
}
</script>

<div class="navbar bg-base-100">
<div class="flex-1">
<nav class="navbar h-[125px] py-8 px-32">
<div class="navbar-end justify-start">
<Logo />
</div>
<div class="flex-none">
<div class="navbar-end">
{#if $signer}
<AddressDropdown />
<ChainDropdown />
<AddressDropdown {transactions} />
{:else}
<Connect />
{/if}
</div>
</div>
</nav>
11 changes: 11 additions & 0 deletions packages/bridge-ui/src/components/TaikoBanner.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script>
import { _ } from "svelte-i18n";
import SelectChain from "./form/SelectChain.svelte";
</script>

<div class="taiko-banner bg-cover bg-center bg-no-repeat w-full h-36 rounded-lg py-4 flex flex-col items-center justify-center">
<h1 class="text-2xl font-bold">
{$_("home.title")}
</h1>
<SelectChain />
</div>
Loading

0 comments on commit 04702db

Please sign in to comment.