Skip to content

Commit

Permalink
Merge branch 'main' into testnet-token-buttons
Browse files Browse the repository at this point in the history
  • Loading branch information
dionysuzx committed Jul 21, 2023
2 parents 8f5df05 + 4f879cb commit 0caae2d
Show file tree
Hide file tree
Showing 65 changed files with 1,036 additions and 327 deletions.
5 changes: 5 additions & 0 deletions packages/bridge-ui-v2/src/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,8 @@ export const processingFeeComponent = {
closingDelayOptionClick: 300,
intervalComputeRecommendedFee: 20000,
};

export const bridge = {
noOwnerGasLimit: BigInt(140000),
noTokenDeployedGasLimit: BigInt(3000000),
};
180 changes: 180 additions & 0 deletions packages/bridge-ui-v2/src/components/Bridge/Amount/Amount.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
<script lang="ts">
import type { FetchBalanceResult } from '@wagmi/core';
import { t } from 'svelte-i18n';
import { formatUnits, parseUnits } from 'viem';
import Icon from '$components/Icon/Icon.svelte';
import { InputBox } from '$components/InputBox';
import { warningToast } from '$components/NotificationToast';
import { checkBalanceToBridge, getMaxAmountToBridge } from '$libs/bridge';
import { InsufficientAllowanceError, InsufficientBalanceError } from '$libs/error';
import { debounce } from '$libs/util/debounce';
import { uid } from '$libs/util/uid';
import { account } from '$stores/account';
import { network } from '$stores/network';
import { destNetwork, enteredAmount, processingFee, selectedToken } from '../state';
import Balance from './Balance.svelte';
let inputId = `input-${uid()}`;
let tokenBalance: FetchBalanceResult;
let inputBox: InputBox;
let computingMaxAmount = false;
// There are two possible errors that can happen when the user
// enters an amount:
// 1. Insufficient balance
// 2. Insufficient allowance
// The first one is an error and the user cannot proceed. The second one
// is a warning but the user must approve allowance before bridging.
let insufficientBalance = false;
let insufficientAllowance = false;
async function checkEnteredAmount() {
insufficientBalance = false;
insufficientAllowance = false;
if (
!$selectedToken ||
!$network ||
!$destNetwork ||
!$account?.address ||
$enteredAmount === BigInt(0) // no need to check if the amount is 0
)
return;
try {
await checkBalanceToBridge({
to: $account.address,
token: $selectedToken,
amount: $enteredAmount,
balance: tokenBalance.value,
srcChainId: $network.id,
destChainId: $destNetwork.id,
processingFee: $processingFee,
});
} catch (err) {
console.error(err);
switch (true) {
case err instanceof InsufficientBalanceError:
insufficientBalance = true;
break;
case err instanceof InsufficientAllowanceError:
insufficientAllowance = true;
break;
}
}
}
// We want to debounce this function for input events
const debouncedCheckEnteredAmount = debounce(checkEnteredAmount, 300);
// Will trigger on input events. We update the entered amount
// and check it's validity
function updateAmount(event: Event) {
insufficientBalance = false;
insufficientAllowance = false;
if (!$selectedToken) return;
const target = event.target as HTMLInputElement;
try {
$enteredAmount = parseUnits(target.value, $selectedToken.decimals);
debouncedCheckEnteredAmount();
} catch (err) {
$enteredAmount = BigInt(0);
}
}
// "MAX" button handler
async function useMaxAmount() {
insufficientBalance = false;
insufficientAllowance = false;
// We cannot calculate the max amount without these guys
if (!$selectedToken || !$network || !$destNetwork || !$account?.address) return;
computingMaxAmount = true;
try {
const maxAmount = await getMaxAmountToBridge({
to: $account.address,
token: $selectedToken,
balance: tokenBalance.value,
processingFee: $processingFee,
srcChainId: $network.id,
destChainId: $destNetwork.id,
amount: BigInt(1), // whatever amount to estimate the cost
});
// Update UI
inputBox.setValue(formatUnits(maxAmount, $selectedToken.decimals));
// Update state
$enteredAmount = maxAmount;
// Check validity
checkEnteredAmount();
} catch (err) {
console.error(err);
warningToast($t('amount_input.button.failed_max'));
} finally {
computingMaxAmount = false;
}
}
// Let's also trigger the check when either the processingFee or
// the selectedToken change and debounce it, just in case
// TODO: better way? maybe store.subscribe(), or different component
$: $processingFee && $selectedToken && debouncedCheckEnteredAmount();
</script>

<div class="AmountInput f-col space-y-2">
<div class="f-between-center text-secondary-content">
<label class="body-regular" for={inputId}>{$t('amount_input.label')}</label>
<Balance bind:value={tokenBalance} />
</div>

<div class="relative f-items-center">
<InputBox
id={inputId}
type="number"
placeholder="0.01"
min="0"
loading={computingMaxAmount}
error={insufficientBalance}
on:input={updateAmount}
bind:this={inputBox}
class="w-full input-box outline-none py-6 pr-16 px-[26px] title-subsection-bold placeholder:text-tertiary-content" />
<!-- TODO: talk to Jane about the MAX button and its styling -->
<button
class="absolute right-6 uppercase hover:font-bold"
disabled={!$selectedToken || !$network || computingMaxAmount}
on:click={useMaxAmount}>
{$t('amount_input.button.max')}
</button>
</div>

{#if insufficientBalance}
<!-- TODO: should we make another component for flat error messages? -->
<div class="f-items-center space-x-1 mt-3">
<Icon type="exclamation-circle" fillClass="fill-negative-sentiment" />
<div class="body-small-regular text-negative-sentiment">
{$t('amount_input.error.insufficient_balance')}
</div>
</div>
{/if}

{#if insufficientAllowance}
<div class="f-items-center space-x-1 mt-3">
<Icon type="exclamation-circle" fillClass="fill-warning-sentiment" />
<div class="body-small-regular text-warning-sentiment">
{$t('amount_input.error.insufficient_allowance')}
</div>
</div>
{/if}
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,26 @@
import { destNetwork, selectedToken } from '../state';
let tokenBalance: Maybe<FetchBalanceResult>;
export let value: Maybe<FetchBalanceResult>;
let computingTokenBalance = false;
let errorComputingTokenBalance = false;
async function updateTokenBalance(token: Maybe<Token>, account?: Account, srcChainId?: number, destChainId?: number) {
if (!token || !account || !account.address) return;
if (!token || !srcChainId || !account?.address) return;
computingTokenBalance = true;
errorComputingTokenBalance = false;
try {
tokenBalance = await getTokenBalance({
value = await getTokenBalance({
token,
srcChainId,
destChainId,
userAddress: account.address,
chainId: srcChainId,
});
} catch (error) {
console.error(error);
} catch (err) {
console.error(err);
errorComputingTokenBalance = true;
} finally {
computingTokenBalance = false;
Expand All @@ -50,7 +51,7 @@
<LoadingText mask="0.0000" />
<LoadingText mask="XXX" />
{:else}
{renderTokenBalance(tokenBalance)}
{renderTokenBalance(value)}
{/if}
</span>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as Amount } from './Amount.svelte';

This file was deleted.

This file was deleted.

10 changes: 5 additions & 5 deletions packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
import type { Account } from '$stores/account';
import { type Network, network } from '$stores/network';
import { AmountInput } from './AmountInput';
import { Amount } from './Amount';
import { ProcessingFee } from './ProcessingFee';
import { RecipientInput } from './RecipientInput';
import { Recipient } from './Recipient';
import { destNetwork, selectedToken } from './state';
import SwitchChainsButton from './SwitchChainsButton.svelte';
Expand Down Expand Up @@ -45,23 +45,23 @@
<TokenDropdown {tokens} bind:value={$selectedToken} />
</div>

<AmountInput />
<Amount />

<div class="f-justify-center">
<SwitchChainsButton />
</div>

<div class="space-y-2">
<ChainSelector label={$t('chain.to')} value={$destNetwork} readOnly />
<RecipientInput />
<!-- <RecipientInput /> -->
</div>
</div>

<ProcessingFee />

<div class="h-sep" />

<Button type="primary" class="px-[28px] py-[14px]">
<Button type="primary" class="px-[28px] py-[14px] rounded-full w-full">
<span class="body-bold">{$t('bridge.button.bridge')}</span>
</Button>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts">
import type { Address } from 'abitype';
import type { Address } from 'viem';
import { recommendProcessingFee } from '$libs/fee';
import { getBalance, type Token } from '$libs/token';
Expand All @@ -23,9 +23,8 @@
try {
// Get the balance of the user on the destination chain
const destBalance = await getBalance({
token,
userAddress,
chainId: destChainId,
srcChainId: destChainId,
});
// Calculate the recommended amount of ETH needed for processMessage call
Expand Down
Loading

0 comments on commit 0caae2d

Please sign in to comment.