Skip to content

Commit

Permalink
add quota check for bridging
Browse files Browse the repository at this point in the history
  • Loading branch information
KorbinianK committed Jun 3, 2024
1 parent 65f71d1 commit b145771
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
let stepDescription: string;
let hasEnoughEth: boolean = false;
let exceedsQuota: boolean = false;
let bridgingStatus: BridgingStatus;
let needsManualReviewConfirmation: boolean;
Expand Down Expand Up @@ -50,7 +51,7 @@
<div class="space-y-[30px] mt-[30px]">
{#if activeStep === BridgeSteps.IMPORT}
<!-- IMPORT STEP -->
<ImportStep bind:hasEnoughEth />
<ImportStep bind:hasEnoughEth bind:exceedsQuota />
{:else if activeStep === BridgeSteps.REVIEW}
<!-- REVIEW STEP -->
<ReviewStep
Expand All @@ -68,6 +69,7 @@
<!-- NAVIGATION -->
<StepNavigation
bind:activeStep
bind:exceedsQuota
{bridgingStatus}
bind:needsManualReviewConfirmation
bind:needsManualRecipientConfirmation />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
let validInput = false;
export let hasEnoughEth: boolean = false;
export let exceedsQuota: boolean = false;
const reset = () => {
$recipientAddress = null;
Expand All @@ -25,4 +26,4 @@

<ChainSelector type={ChainSelectorType.COMBINED} />

<TokenInput bind:validInput bind:hasEnoughEth />
<TokenInput bind:validInput bind:hasEnoughEth bind:exceedsQuota />
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script lang="ts">
import { onMount } from 'svelte';
import { t } from 'svelte-i18n';
import { type Address,zeroAddress } from 'viem';
import { formatUnits, parseUnits } from 'viem/utils';
import { FlatAlert } from '$components/Alert';
Expand All @@ -22,10 +23,12 @@
import { InputBox } from '$components/InputBox';
import { LoadingText } from '$components/LoadingText';
import OnAccount from '$components/OnAccount/OnAccount.svelte';
import { OnNetwork } from '$components/OnNetwork';
import { TokenDropdown } from '$components/TokenDropdown';
import { getMaxAmountToBridge } from '$libs/bridge';
import { fetchBalance, tokens } from '$libs/token';
import { ContractType, getMaxAmountToBridge } from '$libs/bridge';
import { exceedsQuota as checkQuota } from '$libs/bridge/checkBridgeQuota';
import { getContractAddressByType } from '$libs/bridge/getContractAddressByType';
import { ETHToken, fetchBalance, tokens } from '$libs/token';
import { getTokenAddresses } from '$libs/token/getTokenAddresses';
import { isToken } from '$libs/token/isToken';
import { refreshUserBalance, renderBalance } from '$libs/util/balance';
import { debounce } from '$libs/util/debounce';
Expand All @@ -35,11 +38,13 @@
import { account } from '$stores/account';
import { ethBalance } from '$stores/balance';
import { connectedSourceChain } from '$stores/network';
import type { TokenInfo } from '$stores/tokenInfo';
const log = getLogger('TokenInput');
export let validInput = false;
export let hasEnoughEth: boolean = false;
export let exceedsQuota: boolean = false;
let inputId = `input-${uid()}`;
let inputBox: InputBox;
Expand Down Expand Up @@ -134,6 +139,54 @@
$computingBalance = false;
};
const checkIfAmountExceedsQuota = async () => {
log('checking if amount exceeds quota');
if (!$selectedToken || !$connectedSourceChain || !$destNetwork) return false;
let tokenAddress: Address = zeroAddress;
if ($selectedToken === ETHToken) {
// ETH does not have a token address
} else {
// fetch the correct token address for the destination chain
const tokenInfo = await getTokenAddresses({
token: $selectedToken,
srcChainId: $connectedSourceChain.id,
destChainId: $destNetwork.id,
});
if (!tokenInfo) return false;
log('tokenInfo', tokenInfo);
// get address that matches destination chain
const getAddressForChain = (tokenInfo: TokenInfo, chainId: number): Address | null => {
if (tokenInfo.canonical?.chainId === chainId) {
return tokenInfo.canonical.address;
}
if (tokenInfo.bridged?.chainId === chainId) {
return tokenInfo.bridged.address;
}
return null;
};
const destChainAddress = getAddressForChain(tokenInfo, $destNetwork.id);
log('destChainAddress', destChainAddress);
if (!destChainAddress) return false;
tokenAddress = destChainAddress;
}
const quotaManagerAddress = getContractAddressByType({
srcChainId: Number($destNetwork.id),
destChainId: Number($connectedSourceChain.id),
contractType: ContractType.QUOTAMANAGER,
});
log('quotaManagerAddress', quotaManagerAddress);
exceeds = await checkQuota({
tokenAddress,
amount: $enteredAmount,
quotaManagerAddress,
chainId: $destNetwork.id,
});
log('exceedsQuota', exceeds);
};
let previousSelectedToken = $selectedToken;
$: if ($selectedToken !== previousSelectedToken) {
Expand All @@ -143,7 +196,18 @@
$: disabled = !$account || !$account.isConnected;
$: validAmount = $enteredAmount > BigInt(0);
$: validAmount = $enteredAmount > BigInt(0) && !exceeds;
$: exceeds = false;
$: if (exceeds) {
exceedsQuota = true;
} else {
exceedsQuota = false;
}
$: if ($enteredAmount > 0n) {
checkIfAmountExceedsQuota();
}
$: skipValidate =
!$connectedSourceChain ||
Expand All @@ -156,7 +220,7 @@
let invalidInput: boolean;
$: {
if ($enteredAmount !== 0n) {
invalidInput = $errorComputingBalance || $insufficientBalance || $insufficientAllowance;
invalidInput = $errorComputingBalance || $insufficientBalance || $insufficientAllowance || exceeds;
} else {
invalidInput = false;
}
Expand Down Expand Up @@ -238,12 +302,14 @@
</div>

<div class="flex mt-[8px] min-h-[24px]">
{#if displayFeeMsg}
{#if displayFeeMsg && !exceedsQuota}
<div class="f-row items-center gap-1">
<Icon type="info-circle" size={15} fillClass="fill-tertiary-content" /><span
class="text-sm text-tertiary-content"
>{$t('recipient.label')} <ProcessingFee textOnly class="text-tertiary-content" bind:hasEnoughEth /></span>
</div>
{:else if exceedsQuota}
<FlatAlert type="error" message={$t('bridge.errors.amount_exceeds_quota')} class="relative" />
{:else if showInsufficientBalanceAlert}
<FlatAlert type="error" message={$t('bridge.errors.insufficient_balance.title')} class="relative" />
{:else if showInvalidTokenAlert}
Expand All @@ -254,7 +320,6 @@
</div>
</div>

<OnNetwork change={reset} />
<OnAccount change={reset} />

<style>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
export let needsManualRecipientConfirmation: boolean;
export let bridgingStatus: BridgingStatus;
export let exceedsQuota: boolean;
let nextStepButtonText: string;
let manuallyConfirmedReviewStep = false;
let manuallyConfirmedRecipientStep = false;
Expand Down Expand Up @@ -77,7 +79,7 @@
<div class="h-sep mt-0" />
<ActionButton
priority="primary"
disabled={!$importDone || disabled}
disabled={!$importDone || disabled || exceedsQuota}
loading={validatingImport}
on:click={() => handleNextStep()}>
<span class="body-bold">{nextStepButtonText}</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import { Tooltip } from '$components/Tooltip';
import { claimConfig } from '$config';
import { type BridgeTransaction } from '$libs/bridge';
import { checkBridgeQuota } from '$libs/bridge/checkBridgeQuota';
import { checkEnoughBridgeQuotaForClaim } from '$libs/bridge/checkBridgeQuota';
import { getChainName, isL2Chain } from '$libs/chain';
import { config } from '$libs/wagmi';
import { account } from '$stores/account';
Expand Down Expand Up @@ -51,7 +51,7 @@
const results = await Promise.allSettled([
checkEnoughBalance($account.address, Number(tx.destChainId)),
checkBridgeQuota({
checkEnoughBridgeQuotaForClaim({
transaction: tx,
amount: tx.amount,
}),
Expand Down
1 change: 1 addition & 0 deletions packages/bridge-ui/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
}
},
"errors": {
"amount_exceeds_quota": "This amount exceeds the quota. Please lower the amount.",
"approve_error": {
"message": "We encountered an issue while trying to approve this token.",
"title": "Approval failed"
Expand Down
66 changes: 58 additions & 8 deletions packages/bridge-ui/src/libs/bridge/checkBridgeQuota.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,52 @@ import { getContractAddressByType } from './getContractAddressByType';

const log = getLogger('bridge:checkBridgeQuota');

export const checkBridgeQuota = async ({
/**
* Checks if the specified amount exceeds the quota for a given token address and chain ID.
* If the token address is not provided, the zero address (ETH) is used.
*
* @param {Object} params - The parameters for checking the quota.
* @param {Address} params.tokenAddress - The token address (optional).
* @param {bigint} params.amount - The amount to check.
* @param {Address} params.quotaManagerAddress - The quota manager address.
* @param {number} params.chainId - The chain ID of the quota manager.
* @returns {Promise<boolean>} - A promise that resolves to `true` if the amount exceeds the quota, `false` otherwise.
*/
export const exceedsQuota = async ({
tokenAddress,
amount,
quotaManagerAddress,
chainId,
}: {
tokenAddress?: Address;
amount: bigint;
quotaManagerAddress: Address;
chainId: number;
}) => {
try {
const address = tokenAddress || zeroAddress; // if tokenAddress is not provided, use zero address (=ETH)

const quota = await getQuotaForAddress(quotaManagerAddress, address, chainId);
log('Quota:', quota, 'Amount:', amount, 'Has enough quota:', amount <= quota);
if (amount > quota) {
log('Not enough quota', quota, amount);
return true;
}
return false;
} catch (e) {
log('Error getting quota manager address', e);
return false;
}
};

/**
* Checks if there is enough bridge quota for a claim transaction.
* @param {Object} options - The options for checking bridge quota.
* @param {BridgeTransaction} options.transaction - The bridge transaction.
* @param {bigint} options.amount - The amount of tokens to be claimed.
* @returns {Promise<boolean>} - A promise that resolves to `true` if there is enough quota, or `false` otherwise.
*/
export const checkEnoughBridgeQuotaForClaim = async ({
transaction,
amount,
}: {
Expand Down Expand Up @@ -46,13 +91,7 @@ export const checkBridgeQuota = async ({
contractType: ContractType.QUOTAMANAGER,
});

const quota = await readContract(config, {
address: quotaManagerAddress,
abi: quotaManagerAbi,
chainId: Number(transaction.destChainId),
functionName: 'availableQuota',
args: [tokenAddress, 0n],
});
const quota = await getQuotaForAddress(quotaManagerAddress, tokenAddress, Number(transaction.destChainId));

if (amount > quota) {
log('Not enough quota', quota, amount);
Expand All @@ -66,3 +105,14 @@ export const checkBridgeQuota = async ({
return true;
}
};

const getQuotaForAddress = async (quotaManagerAddress: Address, address: Address, chainId: number) => {
const quota = await readContract(config, {
address: quotaManagerAddress,
abi: quotaManagerAbi,
chainId: chainId,
functionName: 'availableQuota',
args: [address, 0n],
});
return quota;
};

0 comments on commit b145771

Please sign in to comment.