diff --git a/packages/bridge-ui/index.html b/packages/bridge-ui/index.html index 772cde44be..d52ecefa97 100644 --- a/packages/bridge-ui/index.html +++ b/packages/bridge-ui/index.html @@ -2,12 +2,14 @@ - - - - - + + + + Bridge diff --git a/packages/bridge-ui/package.json b/packages/bridge-ui/package.json index a76b1abf72..d084f58c3f 100644 --- a/packages/bridge-ui/package.json +++ b/packages/bridge-ui/package.json @@ -42,6 +42,7 @@ "rollup-plugin-polyfill-node": "^0.10.2", "svelte": "^3.53.1", "svelte-check": "^2.8.0", + "svelte-heros-v2": "^0.3.10", "svelte-jester": "^2.1.5", "svelte-loader": "^3.1.2", "svelte-preprocess": "^4.10.7", diff --git a/packages/bridge-ui/public/taiko-favicon.png b/packages/bridge-ui/public/taiko-favicon.png new file mode 100644 index 0000000000..28da6ff134 Binary files /dev/null and b/packages/bridge-ui/public/taiko-favicon.png differ diff --git a/packages/bridge-ui/src/App.svelte b/packages/bridge-ui/src/App.svelte index 654d6d0a88..b8e9a6d8c6 100644 --- a/packages/bridge-ui/src/App.svelte +++ b/packages/bridge-ui/src/App.svelte @@ -166,6 +166,8 @@ const interval = setInterval(async () => { tx.interval = interval; + if (!tx.signal) return; + const contract = new ethers.Contract( chains[tx.toChainId].bridgeAddress, BridgeABI, @@ -201,16 +203,13 @@ -
-
- - - -
- - - -
+
+ + + +
+ +
diff --git a/packages/bridge-ui/src/components/AddressDropdown.svelte b/packages/bridge-ui/src/components/AddressDropdown.svelte index b977e24274..3308f227e8 100644 --- a/packages/bridge-ui/src/components/AddressDropdown.svelte +++ b/packages/bridge-ui/src/components/AddressDropdown.svelte @@ -1,20 +1,18 @@ diff --git a/packages/bridge-ui/src/components/ChainDropdown.svelte b/packages/bridge-ui/src/components/ChainDropdown.svelte index 3388ef2128..93ddf19bb9 100644 --- a/packages/bridge-ui/src/components/ChainDropdown.svelte +++ b/packages/bridge-ui/src/components/ChainDropdown.svelte @@ -1,14 +1,12 @@ - diff --git a/packages/bridge-ui/src/components/TaikoBanner.svelte b/packages/bridge-ui/src/components/TaikoBanner.svelte index 83824f5c1c..2ea89ce246 100644 --- a/packages/bridge-ui/src/components/TaikoBanner.svelte +++ b/packages/bridge-ui/src/components/TaikoBanner.svelte @@ -4,7 +4,7 @@
diff --git a/packages/bridge-ui/src/components/Tooltip.svelte b/packages/bridge-ui/src/components/Tooltip.svelte new file mode 100644 index 0000000000..1ccae3a015 --- /dev/null +++ b/packages/bridge-ui/src/components/Tooltip.svelte @@ -0,0 +1,11 @@ + + + (isOpen = true)} + size="18" + variation="outline" +/> diff --git a/packages/bridge-ui/src/components/Transaction.svelte b/packages/bridge-ui/src/components/Transaction.svelte index fbe65fd94c..52c494c71c 100644 --- a/packages/bridge-ui/src/components/Transaction.svelte +++ b/packages/bridge-ui/src/components/Transaction.svelte @@ -2,12 +2,12 @@ import type { BridgeTransaction } from "../domain/transactions"; import { chains, CHAIN_MAINNET, CHAIN_TKO } from "../domain/chain"; import type { Chain } from "../domain/chain"; - import TransactionsIcon from "./icons/Transactions.svelte"; + import { ArrowTopRightOnSquare } from "svelte-heros-v2"; import { MessageStatus } from "../domain/message"; import { Contract, ethers } from "ethers"; import { bridges } from "../store/bridge"; import { signer } from "../store/signer"; - import { pendingTransactions } from "../store/transactions"; + import { pendingTransactions, showTransactionDetails } from "../store/transactions"; import { errorToast, successToast } from "../utils/toast"; import { _ } from "svelte-i18n"; import { @@ -21,12 +21,15 @@ import HeaderSync from "../constants/abi/HeaderSync"; import { providers } from "../store/providers"; import { fetchSigner, switchNetwork } from "@wagmi/core"; + import Tooltip from "./Tooltip.svelte"; + import TooltipModal from "./modals/TooltipModal.svelte"; export let transaction: BridgeTransaction; export let fromChain: Chain; export let toChain: Chain; + let tooltipOpen: boolean = false; let processable: boolean = false; onMount(async () => { @@ -91,7 +94,7 @@ const srcBlock = await $providers .get(chains[transaction.message.srcChainId.toNumber()].id) .getBlock(latestSyncedHeader); - return transaction.receipt.blockNumber >= srcBlock.number; + return transaction.receipt.blockNumber <= srcBlock.number; } @@ -110,20 +113,7 @@ : ethers.utils.formatUnits(transaction.amountInWei)} {transaction.message?.data !== "0x" ? transaction.symbol : "ETH"} - - - - window.open( - `${fromChain.explorerUrl}/tx/${transaction.ethersTx.hash}`, - "_blank" - )} - > - - - - + {#if !processable} Pending... @@ -161,9 +151,52 @@ {:else if transaction.status === MessageStatus.Done} Claimed {/if} + (tooltipOpen = true)}> + + + + + + $showTransactionDetails = transaction}> + + + + +
+ A bridge message will pass through various states: +

+
    +
  • + Pending: 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. +
  • +
  • + Claimable: Your asset is ready to be claimed on the + destination chain, and requires a transaction. +
  • +
  • + Claimed: Your asset has finished bridging, and is + available to you on the destination chain. +
  • +
  • + Retry: The relayer has failed to process this + message, and you must retry the processing yourself. +
  • +
  • + Failed: Your bridged asset is unable to be processed, + and is available to you on the source chain. +
  • +
+
+
+
+ diff --git a/packages/bridge-ui/src/components/form/SelectChain.svelte b/packages/bridge-ui/src/components/form/SelectChain.svelte index b4242b5797..6a4fc919b8 100644 --- a/packages/bridge-ui/src/components/form/SelectChain.svelte +++ b/packages/bridge-ui/src/components/form/SelectChain.svelte @@ -1,5 +1,5 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/packages/bridge-ui/src/components/icons/Logo.svelte b/packages/bridge-ui/src/components/icons/Logo.svelte deleted file mode 100644 index d94830b972..0000000000 --- a/packages/bridge-ui/src/components/icons/Logo.svelte +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - diff --git a/packages/bridge-ui/src/components/icons/MetaMask.svelte b/packages/bridge-ui/src/components/icons/MetaMask.svelte index ba7c238d95..c4b2517cab 100644 --- a/packages/bridge-ui/src/components/icons/MetaMask.svelte +++ b/packages/bridge-ui/src/components/icons/MetaMask.svelte @@ -1,133 +1,179 @@ - - - - - - - - - - - - - - - - - - - - - - - - + .st10 { + fill: #e2761b; + stroke: #e2761b; + stroke-linecap: round; + stroke-linejoin: round; + } + .st11 { + fill: #e4761b; + stroke: #e4761b; + stroke-linecap: round; + stroke-linejoin: round; + } + .st2 { + fill: #d7c1b3; + stroke: #d7c1b3; + stroke-linecap: round; + stroke-linejoin: round; + } + .st3 { + fill: #233447; + stroke: #233447; + stroke-linecap: round; + stroke-linejoin: round; + } + .st4 { + fill: #cd6116; + stroke: #cd6116; + stroke-linecap: round; + stroke-linejoin: round; + } + .st5 { + fill: #e4751f; + stroke: #e4751f; + stroke-linecap: round; + stroke-linejoin: round; + } + .st6 { + fill: #f6851b; + stroke: #f6851b; + stroke-linecap: round; + stroke-linejoin: round; + } + .st7 { + fill: #c0ad9e; + stroke: #c0ad9e; + stroke-linecap: round; + stroke-linejoin: round; + } + .st8 { + fill: #161616; + stroke: #161616; + stroke-linecap: round; + stroke-linejoin: round; + } + .st9 { + fill: #763d16; + stroke: #763d16; + stroke-linecap: round; + stroke-linejoin: round; + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/bridge-ui/src/components/icons/TaikoLogo.svelte b/packages/bridge-ui/src/components/icons/TaikoLogo.svelte new file mode 100644 index 0000000000..26595968c9 --- /dev/null +++ b/packages/bridge-ui/src/components/icons/TaikoLogo.svelte @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + diff --git a/packages/bridge-ui/src/components/icons/Transactions.svelte b/packages/bridge-ui/src/components/icons/Transactions.svelte deleted file mode 100644 index 35a8f9eebf..0000000000 --- a/packages/bridge-ui/src/components/icons/Transactions.svelte +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/packages/bridge-ui/src/components/modals/Modal.svelte b/packages/bridge-ui/src/components/modals/Modal.svelte index 9892318aef..093e428bbb 100644 --- a/packages/bridge-ui/src/components/modals/Modal.svelte +++ b/packages/bridge-ui/src/components/modals/Modal.svelte @@ -12,7 +12,15 @@ }; - + + diff --git a/packages/bridge-ui/src/components/modals/TooltipModal.svelte b/packages/bridge-ui/src/components/modals/TooltipModal.svelte new file mode 100644 index 0000000000..a7ea557e7f --- /dev/null +++ b/packages/bridge-ui/src/components/modals/TooltipModal.svelte @@ -0,0 +1,17 @@ + + + +
+
+ +
+ +
+
diff --git a/packages/bridge-ui/src/constants/abi/MintableERC20.ts b/packages/bridge-ui/src/constants/abi/MintableERC20.ts new file mode 100644 index 0000000000..1547279fae --- /dev/null +++ b/packages/bridge-ui/src/constants/abi/MintableERC20.ts @@ -0,0 +1,255 @@ +export default [ + { + inputs: [], + stateMutability: "nonpayable", + type: "constructor", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "owner", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "spender", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "value", + type: "uint256", + }, + ], + name: "Approval", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "from", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "to", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "value", + type: "uint256", + }, + ], + name: "Transfer", + type: "event", + }, + { + inputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + { + internalType: "address", + name: "", + type: "address", + }, + ], + name: "allowance", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "spender", + type: "address", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "approve", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + name: "balanceOf", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "burn", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "decimals", + outputs: [ + { + internalType: "uint8", + name: "", + type: "uint8", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "mint", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "name", + outputs: [ + { + internalType: "string", + name: "", + type: "string", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "symbol", + outputs: [ + { + internalType: "string", + name: "", + type: "string", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "totalSupply", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "recipient", + type: "address", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "transfer", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "sender", + type: "address", + }, + { + internalType: "address", + name: "recipient", + type: "address", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "transferFrom", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, +]; diff --git a/packages/bridge-ui/src/domain/token.ts b/packages/bridge-ui/src/domain/token.ts index 2b3f1f40f6..db6b374394 100644 --- a/packages/bridge-ui/src/domain/token.ts +++ b/packages/bridge-ui/src/domain/token.ts @@ -2,6 +2,7 @@ import Eth from "../components/icons/ETH.svelte"; import type { ComponentType } from "svelte"; import Tko from "../components/icons/TKO.svelte"; import { CHAIN_MAINNET, CHAIN_TKO } from "./chain"; +import Horse from "../components/icons/Horse.svelte"; type Address = { chainId: number; @@ -66,7 +67,7 @@ export const HORSE: Token = { ], decimals: 18, symbol: "HORSE", - logoComponent: Tko, + logoComponent: Horse, }; export const tokens = [ETH, HORSE]; diff --git a/packages/bridge-ui/src/erc20/bridge.spec.ts b/packages/bridge-ui/src/erc20/bridge.spec.ts index 0dce4d84ed..a785151632 100644 --- a/packages/bridge-ui/src/erc20/bridge.spec.ts +++ b/packages/bridge-ui/src/erc20/bridge.spec.ts @@ -195,12 +195,12 @@ describe("bridge tests", () => { opts.tokenAddress, opts.amountInWei, BigNumber.from(100000), - 0, + opts.processingFeeInWei, "0xfake", - opts.memo - // { - // value: opts.processingFeeInWei, - // } + opts.memo, + { + value: opts.processingFeeInWei, + } ); }); @@ -231,9 +231,12 @@ describe("bridge tests", () => { opts.tokenAddress, opts.amountInWei, BigNumber.from(0), - 0, + BigNumber.from(0), "0xfake", - "" + "", + { + value: BigNumber.from(0), + } ); }); diff --git a/packages/bridge-ui/src/erc20/bridge.ts b/packages/bridge-ui/src/erc20/bridge.ts index e16aafeb2f..86aaada459 100644 --- a/packages/bridge-ui/src/erc20/bridge.ts +++ b/packages/bridge-ui/src/erc20/bridge.ts @@ -105,12 +105,12 @@ class ERC20Bridge implements Bridge { opts.tokenAddress, opts.amountInWei, message.gasLimit, - 0, + message.processingFee, message.refundAddress, - message.memo - // { - // value: message.processingFee.add(message.callValue), - // } + message.memo, + { + value: message.processingFee.add(message.callValue), + } ); return tx; diff --git a/packages/bridge-ui/src/i18n.js b/packages/bridge-ui/src/i18n.js index 7cf63c05c7..8f994bd81b 100644 --- a/packages/bridge-ui/src/i18n.js +++ b/packages/bridge-ui/src/i18n.js @@ -1,42 +1,41 @@ - import { _, dictionary, locale } from "svelte-i18n"; function setupI18n({ withLocale: _locale } = { withLocale: "en" }) { - dictionary.set({ - en: { - home: { - title: "Taiko Bridge", - selectToken: "Select Token", - to: "To", - bridge: "Bridge", - approve: "Approve" - }, - "bridgeForm": { - fieldLabel: "Bridge Token", - maxLabel: "Max:", - processingFeeLabel: "Processing Fee", - bridge: "Bridge", - approve: "Approve", - }, - nav: { - connect: "Connect Wallet" - }, - toast: { - transactionSent: "Transaction sent", - errorSendingTransaction: "Error sending transaction", - errorDisconneting: "Could not disconnect" - }, - switchChainModal: { - title: "Not on the right network", - subtitle: "Your current network is not supported. Please select one:" - }, - connectModal: { - title: "Connect Wallet" - } - } - }) + dictionary.set({ + en: { + home: { + title: "Taiko Bridge", + selectToken: "Select Token", + to: "To", + bridge: "Bridge", + approve: "Approve", + }, + bridgeForm: { + fieldLabel: "Amount", + maxLabel: "Max:", + processingFeeLabel: "Processing Fee", + bridge: "Bridge", + approve: "Approve", + }, + nav: { + connect: "Connect Wallet", + }, + toast: { + transactionSent: "Transaction sent", + errorSendingTransaction: "Error sending transaction", + errorDisconneting: "Could not disconnect", + }, + switchChainModal: { + title: "Not on the right network", + subtitle: "Your current network is not supported. Please select one:", + }, + connectModal: { + title: "Connect Wallet", + }, + }, + }); - locale.set(_locale); + locale.set(_locale); } export { _, setupI18n }; diff --git a/packages/bridge-ui/src/pages/home/Home.svelte b/packages/bridge-ui/src/pages/home/Home.svelte index d243adc0db..358f34620f 100644 --- a/packages/bridge-ui/src/pages/home/Home.svelte +++ b/packages/bridge-ui/src/pages/home/Home.svelte @@ -8,29 +8,27 @@ let activeTab: string = "bridge"; -
-
-
-
- (activeTab = "bridge")}>Bridge - (activeTab = "transactions")} - >Transactions ({$transactions.length}) - -
- {#if activeTab === "bridge"} - -
- -
- {:else} - - {/if} +
+
+
+ (activeTab = "bridge")}>Bridge + (activeTab = "transactions")} + >Transactions ({$transactions.length}) +
+ {#if activeTab === "bridge"} + +
+ +
+ {:else} + + {/if}
diff --git a/packages/bridge-ui/src/store/transactions.ts b/packages/bridge-ui/src/store/transactions.ts index f0061fad60..4f5ad98b25 100644 --- a/packages/bridge-ui/src/store/transactions.ts +++ b/packages/bridge-ui/src/store/transactions.ts @@ -6,4 +6,5 @@ import type { BridgeTransaction, Transactioner } from "../domain/transactions"; const pendingTransactions = writable([]); const transactions = writable([]); const transactioner = writable(); -export { pendingTransactions, transactions, transactioner }; +const showTransactionDetails = writable(); +export { pendingTransactions, transactions, transactioner, showTransactionDetails }; diff --git a/packages/bridge-ui/src/utils/recommendProcessingFee.ts b/packages/bridge-ui/src/utils/recommendProcessingFee.ts index 6f8012777d..2a42880159 100644 --- a/packages/bridge-ui/src/utils/recommendProcessingFee.ts +++ b/packages/bridge-ui/src/utils/recommendProcessingFee.ts @@ -27,10 +27,16 @@ export async function recommendProcessingFee( // to make it enticing, we say 900k. let gasLimit = ethGasLimit; if (token.symbol.toLowerCase() !== ETH.symbol.toLowerCase()) { - const srcChainAddr = token.addresses.find( + let srcChainAddr = token.addresses.find( (t) => t.chainId === fromChain.id ).address; + if (!srcChainAddr || srcChainAddr === "0x00") { + srcChainAddr = token.addresses.find( + (t) => t.chainId === toChain.id + ).address; + } + const chainIdsToTokenVault = get(chainIdToTokenVaultAddress); const tokenVault = new Contract( chainIdsToTokenVault.get(fromChain.id), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 92039fc1bc..e56ae85a5f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -46,6 +46,7 @@ importers: rollup-plugin-polyfill-node: ^0.10.2 svelte: ^3.53.1 svelte-check: ^2.8.0 + svelte-heros-v2: ^0.3.10 svelte-i18n: ^3.5.1 svelte-jester: ^2.1.5 svelte-loader: ^3.1.2 @@ -100,6 +101,7 @@ importers: rollup-plugin-polyfill-node: 0.10.2_rollup@2.79.1 svelte: 3.53.1 svelte-check: 2.9.2_abcb4yglsprjylcfcbep3tcqgq + svelte-heros-v2: 0.3.10 svelte-jester: 2.3.2_jest@27.5.1+svelte@3.53.1 svelte-loader: 3.1.4_svelte@3.53.1 svelte-preprocess: 4.10.7_axwq5llc4jwkf7awicvy3hu32q @@ -18728,6 +18730,10 @@ packages: resolution: {integrity: sha512-oU+Xv7Dl4kRU2kdFjsoPLfJfnt5hUhsFUZtuzI3Ku/f2iAFZqBoEuXOqK3N9ngD4dxQOmN4OKWPHVi3NeAeAfQ==} dev: true + /svelte-heros-v2/0.3.10: + resolution: {integrity: sha512-e5ZhYN8blZwfhb2k4KYSfHnbbddonDNsglqMpwXLLwPVPBiJXeGpcPy2zkffq6kvkLS2tYipYOaAPSXimgtuUg==} + dev: true + /svelte-hmr/0.14.12_svelte@3.53.1: resolution: {integrity: sha512-4QSW/VvXuqVcFZ+RhxiR8/newmwOCTlbYIezvkeN6302YFRE8cXy0naamHcjz8Y9Ce3ITTZtrHrIL0AGfyo61w==} engines: {node: ^12.20 || ^14.13.1 || >= 16}