Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(bridge-ui-v2): account balance #14159

Merged
merged 39 commits into from
Jul 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
25b2902
wip
jscriptcoder Jul 10, 2023
d55c7ba
wip: faucet
jscriptcoder Jul 10, 2023
b64b0a2
wip: Modal
jscriptcoder Jul 11, 2023
2d1f1d2
wip
jscriptcoder Jul 11, 2023
bd2e4a7
wip
jscriptcoder Jul 11, 2023
2fd98ee
wip
jscriptcoder Jul 11, 2023
1ed70be
wip
jscriptcoder Jul 11, 2023
397a40b
Faucet
jscriptcoder Jul 11, 2023
2c00949
Merge branch 'main' into mint_test_tokens
jscriptcoder Jul 11, 2023
93166b7
minor change
jscriptcoder Jul 12, 2023
5b9cdb9
Merge branch 'mint_test_tokens' of https://github.com/taikoxyz/taiko-…
jscriptcoder Jul 12, 2023
8275a4b
Merge branch 'main' into mint_test_tokens
jscriptcoder Jul 12, 2023
1773ec5
wip: mobile
jscriptcoder Jul 12, 2023
8b4a0d0
Merge branch 'mint_test_tokens' of https://github.com/taikoxyz/taiko-…
jscriptcoder Jul 12, 2023
b374fd9
DesltopOrLarger helper component
jscriptcoder Jul 12, 2023
c95e2b7
minor change
jscriptcoder Jul 12, 2023
f9ad657
minor change
jscriptcoder Jul 12, 2023
2606310
Update packages/bridge-ui-v2/src/i18n/en.json
jscriptcoder Jul 12, 2023
c2e7283
fix eslint
jscriptcoder Jul 12, 2023
7834cce
Merge branch 'mint_test_tokens' of https://github.com/taikoxyz/taiko-…
jscriptcoder Jul 12, 2023
1a5c23f
minor change
jscriptcoder Jul 12, 2023
faa1986
rename free to fee
jscriptcoder Jul 12, 2023
888cf3f
typo
jscriptcoder Jul 12, 2023
30570c2
better use of conditions
jscriptcoder Jul 12, 2023
e972b2d
add minor comment
jscriptcoder Jul 12, 2023
c7d11d3
wip
jscriptcoder Jul 12, 2023
61cd279
add checkMintable test
jscriptcoder Jul 12, 2023
0029e85
minor change
jscriptcoder Jul 12, 2023
ae9999a
token minting coverage
jscriptcoder Jul 12, 2023
cb284a8
excluding coverage
jscriptcoder Jul 12, 2023
b18931a
merge main
jscriptcoder Jul 12, 2023
842372e
wip
jscriptcoder Jul 13, 2023
16caf7d
wip
jscriptcoder Jul 13, 2023
5276925
render user balabnce
jscriptcoder Jul 13, 2023
430b0de
error handling
jscriptcoder Jul 13, 2023
1033b55
Merge branch 'main' into account_balance
jscriptcoder Jul 13, 2023
231f686
remove unnecessary comment
jscriptcoder Jul 13, 2023
a6b825a
small comment
jscriptcoder Jul 13, 2023
1f1d2f3
small comment
jscriptcoder Jul 13, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/bridge-ui-v2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"private": true,
"scripts": {
"dev": "vite dev",
"dev:a3": "vite dev --mode a3",
"build": "vite build",
"preview": "vite preview",
"test:pw": "playwright test",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,69 @@
<script lang="ts">
import type { FetchBalanceResult, GetAccountResult, PublicClient } from '@wagmi/core';
import { t } from 'svelte-i18n';

import { InputBox } from '$components/InputBox';
import LoadingText from '$components/LoadingText/LoadingText.svelte';
import { getBalance as getTokenBalance, type Token } from '$libs/token';
import { getLogger } from '$libs/util/logger';
import { truncateString } from '$libs/util/truncateString';
import { uid } from '$libs/util/uid';
import { account } from '$stores/account';
import { destChain, srcChain } from '$stores/network';

const log = getLogger('AmountInput');

export let token: Token;

let inputId = `input-${uid()}`;

let tokenBalance: Maybe<FetchBalanceResult>;
let computingTokenBalance = false;

async function updateTokenBalance(
token?: Token,
account?: GetAccountResult<PublicClient>,
srcChainId?: number,
destChainId?: number,
) {
if (!token || !account || !account.address) return;

computingTokenBalance = true;

try {
tokenBalance = await getTokenBalance(token, account.address, srcChainId, destChainId);

log('Token balance', tokenBalance);
} catch (error) {
console.error(error);

throw Error(`failed to get balance for ${token.symbol}`, { cause: error });
} finally {
computingTokenBalance = false;
}
}

export function renderTokenBalance(balance: Maybe<FetchBalanceResult>) {
if (!balance) return '0.00';
return `${truncateString(balance.formatted, 6)} ${balance.symbol}`;
}

$: updateTokenBalance(token, $account, $srcChain?.id, $destChain?.id);
</script>

<div class="f-col space-y-2">
<div class="f-between-center text-secondary-content">
<label class="body-regular" for={inputId}>{$t('amount_input.label')}</label>
<div class="body-small-regular">
<span>{$t('amount_input.balance')}:</span>
<span>399.92 ETH</span>
<span>
{#if computingTokenBalance}
<LoadingText mask="0.0000" class="text-white" />
<LoadingText mask="XXX" class="text-white" />
{:else}
{renderTokenBalance(tokenBalance)}
{/if}
</span>
</div>
</div>
<div class="relative f-items-center">
Expand Down
8 changes: 5 additions & 3 deletions packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,21 @@
import { ProcessingFee } from '$components/ProcessingFee';
import { RecipientInput } from '$components/RecipientInput';
import { TokenDropdown } from '$components/TokenDropdown';
import { tokens } from '$libs/token';
import { type Token, tokens } from '$libs/token';
import { destChain, srcChain } from '$stores/network';

let selectedToken: Token;
</script>

<Card class="md:w-[524px]" title={$t('bridge.title')} text={$t('bridge.subtitle')}>
<div class="space-y-[35px]">
<div class="space-y-4">
<div class="space-y-2">
<ChainSelector label={$t('chain.from')} value={$srcChain} />
<TokenDropdown {tokens} />
<TokenDropdown {tokens} bind:value={selectedToken} />
</div>

<AmountInput />
<AmountInput token={selectedToken} />

<div class="f-justify-center">
<button class="f-center rounded-full bg-secondary-icon w-[30px] h-[30px]">
Expand Down
11 changes: 5 additions & 6 deletions packages/bridge-ui-v2/src/components/Faucet/Faucet.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
let checkingMintable = false;
let switchingNetwork = false;

let selectedToken: Maybe<Token>;
let selectedToken: Token;
let mintButtonEnabled = false;
let reasonNotMintable = '';

Expand Down Expand Up @@ -90,15 +90,16 @@
// This function will check whether or not the button to mint should be
// enabled. If it shouldn't it'll also set the reason why so we can inform
// the user why they can't mint
async function shouldEnableMintButton(token: Maybe<Token>, network: Maybe<Chain>) {
async function updateMintButtonState(token?: Token, network?: Chain) {
if (!token || !network) return false;

checkingMintable = true;
mintButtonEnabled = false;
reasonNotMintable = '';

try {
await checkMintable(token, network);
return true;
mintButtonEnabled = true;
} catch (error) {
console.error(error);

Expand All @@ -121,8 +122,6 @@
} finally {
checkingMintable = false;
}

return false;
}

function getAlertMessage(connected: boolean, wrongChain: boolean, reasonNotMintable: string) {
Expand All @@ -135,7 +134,7 @@
$: wrongChain = isWrongChain($srcChain);
$: alertMessage = getAlertMessage(connected, wrongChain, reasonNotMintable);

$: shouldEnableMintButton(selectedToken, $srcChain).then((enable) => (mintButtonEnabled = enable));
$: updateMintButtonState(selectedToken, $srcChain);
</script>

<Card class="md:w-[524px]" title={$t('faucet.title')} text={$t('faucet.subtitle')}>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script lang="ts">
import { classNames } from '$libs/util/classNames';

export let mask = 'Loading';

let classes = classNames('animate-pulse blur-sm font-bold', $$props.class);
</script>

<span class={classes}>{mask}</span>
Empty file.
27 changes: 20 additions & 7 deletions packages/bridge-ui-v2/src/libs/chain/chains.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Chain } from '@wagmi/core';
import type { Address } from 'abitype';

import {
PUBLIC_L1_BRIDGE_ADDRESS,
Expand All @@ -8,13 +9,15 @@ import {
PUBLIC_L1_EXPLORER_URL,
PUBLIC_L1_RPC_URL,
PUBLIC_L1_SIGNAL_SERVICE_ADDRESS,
PUBLIC_L1_TOKEN_VAULT_ADDRESS,
PUBLIC_L2_BRIDGE_ADDRESS,
PUBLIC_L2_CHAIN_ID,
PUBLIC_L2_CHAIN_NAME,
PUBLIC_L2_CROSS_CHAIN_SYNC_ADDRESS,
PUBLIC_L2_EXPLORER_URL,
PUBLIC_L2_RPC_URL,
PUBLIC_L2_SIGNAL_SERVICE_ADDRESS,
PUBLIC_L2_TOKEN_VAULT_ADDRESS,
} from '$env/static/public';

export const mainnetChain: Chain = {
Expand Down Expand Up @@ -61,15 +64,25 @@ export const taikoChain: Chain = {

export const chains = [mainnetChain, taikoChain];

export const chainContractsMap = {
export const chainContractsMap: Record<
string,
{
bridgeAddress: Address;
tokenVaultAddress: Address;
crossChainSyncAddress: Address;
signalServiceAddress: Address;
}
> = {
[PUBLIC_L1_CHAIN_ID]: {
bridgeAddress: PUBLIC_L1_BRIDGE_ADDRESS,
crossChainSyncAddress: PUBLIC_L1_CROSS_CHAIN_SYNC_ADDRESS,
signalServiceAddress: PUBLIC_L1_SIGNAL_SERVICE_ADDRESS,
bridgeAddress: PUBLIC_L1_BRIDGE_ADDRESS as Address,
tokenVaultAddress: PUBLIC_L1_TOKEN_VAULT_ADDRESS as Address,
crossChainSyncAddress: PUBLIC_L1_CROSS_CHAIN_SYNC_ADDRESS as Address,
signalServiceAddress: PUBLIC_L1_SIGNAL_SERVICE_ADDRESS as Address,
},
[PUBLIC_L2_CHAIN_ID]: {
bridgeAddress: PUBLIC_L2_BRIDGE_ADDRESS,
crossChainSyncAddress: PUBLIC_L2_CROSS_CHAIN_SYNC_ADDRESS,
signalServiceAddress: PUBLIC_L2_SIGNAL_SERVICE_ADDRESS,
bridgeAddress: PUBLIC_L2_BRIDGE_ADDRESS as Address,
tokenVaultAddress: PUBLIC_L2_TOKEN_VAULT_ADDRESS as Address,
crossChainSyncAddress: PUBLIC_L2_CROSS_CHAIN_SYNC_ADDRESS as Address,
signalServiceAddress: PUBLIC_L2_SIGNAL_SERVICE_ADDRESS as Address,
},
};
49 changes: 49 additions & 0 deletions packages/bridge-ui-v2/src/libs/token/getAddress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { getContract } from '@wagmi/core';
import { zeroAddress } from 'viem';

import { tokenVaultABI } from '$abi';
import { chainContractsMap } from '$libs/chain';
import { getLogger } from '$libs/util/logger';

import { isETH } from './tokens';
import type { Token } from './types';

const log = getLogger('token:getAddress');

export async function getAddress(token: Token, srcChainId?: number, destChainId?: number) {
if (!srcChainId) return;

// Get the address for the token on the source chain
let address = token.addresses[srcChainId];

// If the token isn't ETH or has no address...
if (!isETH(token) && (!address || address === zeroAddress)) {
if (!destChainId) return;

// Find the address on the destination chain instead. We are
// most likely on Taiko chain and the token hasn't yet been
// deployed on it.
const destChainTokenAddress = token.addresses[destChainId];

// Get the TokenVault contract on the source chain. The idea is to find
// the bridged address for the token on the destination chain if it's been
// deployed there. This is registered in the TokenVault contract,
// cacnonicalToBridged mapping.
const srcTokenVaultContract = getContract({
abi: tokenVaultABI,
address: chainContractsMap[srcChainId].tokenVaultAddress,
});

try {
address = await srcTokenVaultContract.read.canonicalToBridged([BigInt(destChainId), destChainTokenAddress]);

log(`Bridged address for ${token.symbol} is "${address}"`);
} catch (error) {
console.error(error);

throw Error(`Failed to get address for ${token.symbol} on chain ${srcChainId}`, { cause: error });
}
}

return address;
}
26 changes: 26 additions & 0 deletions packages/bridge-ui-v2/src/libs/token/getBalance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { fetchBalance } from '@wagmi/core';
import type { Address } from 'abitype';
import { zeroAddress } from 'viem';

import { getAddress } from './getAddress';
import { isETH } from './tokens';
import type { Token } from './types';

export async function getBalance(token: Token, userAddress: Address, srcChainId?: number, destChainId?: number) {
if (isETH(token)) {
return fetchBalance({ address: userAddress });
}

// We are dealing with an ERC20 token. We need to first find out its address
// on the current chain in order to fetch the balance.
const tokenAddress = await getAddress(token, srcChainId, destChainId);

if (!tokenAddress || tokenAddress === zeroAddress) return null;

// Wagmi is an excellent library 😊
return fetchBalance({
address: userAddress,
chainId: srcChainId,
token: tokenAddress,
});
}
2 changes: 2 additions & 0 deletions packages/bridge-ui-v2/src/libs/token/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export { checkMintable } from './checkMintable';
export { getAddress } from './getAddress';
export { getBalance } from './getBalance';
export { mint } from './mint';
export * from './tokens';
export * from './types';
9 changes: 9 additions & 0 deletions packages/bridge-ui-v2/src/libs/token/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,12 @@ export const testERC20Tokens: Token[] = jsonParseWithDefault<TokenEnv[]>(PUBLIC_
);

export const tokens = [ETHToken, ...testERC20Tokens];

export function isETH(token: Token) {
// Should be fine just by checking the symbol
return token.symbol.toLocaleLowerCase() === ETHToken.symbol.toLocaleLowerCase();
}

export function isERC20(token: Token): boolean {
return !isETH(token);
}
3 changes: 3 additions & 0 deletions packages/bridge-ui-v2/src/libs/util/truncateString.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function truncateString(str: string, maxlength: number, strBoundary = '…') {
return str.length > maxlength ? `${str.substring(0, maxlength)}${strBoundary}` : str;
}