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

WT-1662 Bridge and swap route #907

Merged
merged 27 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
45edebc
BridgeAndSwap funding steps placeholder
dreamoftrees Sep 21, 2023
48e5635
WIP get bridge tokens and swap quotes
dreamoftrees Sep 22, 2023
7b91af1
push up current state of bridge -> swap
imx-mikhala Sep 22, 2023
cd98716
Remove unused type
imx-mikhala Sep 22, 2023
c0a7c45
bridge and swap funding steps
imx-mikhala Sep 27, 2023
efd5f9e
fix various bugs
imx-mikhala Sep 27, 2023
ccbc1f4
remove comment
imx-mikhala Sep 27, 2023
05c785c
rename
imx-mikhala Sep 27, 2023
4d01304
add test for getTokenBalances
imx-mikhala Sep 27, 2023
055a2f5
rename
imx-mikhala Sep 27, 2023
b0baee5
Fix routingCalculator, add tests
imx-mikhala Sep 27, 2023
de6bde2
Merge branch 'main' into WT-1662
imx-mikhala Sep 27, 2023
c07d99f
remove comments
imx-mikhala Sep 27, 2023
04116eb
remove comments
imx-mikhala Sep 27, 2023
30891b1
Merge remote-tracking branch 'origin/WT-1662' into WT-1662
imx-mikhala Sep 27, 2023
88911e3
remove unused type
imx-mikhala Sep 27, 2023
c9ff2e2
lint
imx-mikhala Sep 27, 2023
237f5c8
Merge remote-tracking branch 'refs/remotes/origin/main'
imx-mikhala Sep 28, 2023
00d8569
resolve conflicts from moving fetchl1representation
imx-mikhala Sep 28, 2023
ccca250
Merge branch 'main' into WT-1662
imx-mikhala Sep 28, 2023
e1b15cc
remove unused
imx-mikhala Sep 28, 2023
0e4d77a
Merge remote-tracking branch 'origin/WT-1662' into WT-1662
imx-mikhala Sep 28, 2023
dfb3995
Ensure ETH could be swapped if swappable, do not call bridge if amoun…
imx-mikhala Sep 28, 2023
0b9f352
remove console
imx-mikhala Sep 28, 2023
a9a58e9
remove log, address comment
imx-mikhala Sep 28, 2023
bf224de
Add test
imx-mikhala Sep 28, 2023
ff5aef1
Merge branch 'main' into WT-1662
imx-mikhala Sep 28, 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

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { BigNumber, Contract, ethers } from 'ethers';
import { Web3Provider } from '@ethersproject/providers';
import { BigNumber, ethers } from 'ethers';
import {
ChainId,
FundingRouteType,
Expand All @@ -9,22 +7,21 @@ import {
ItemType,
RoutingOptionsAvailable,
} from '../../../types';
import { CheckoutConfiguration, getL1ChainId, getL2ChainId } from '../../../config';
import { CheckoutConfiguration, getL1ChainId } from '../../../config';
import { FundingRouteStep, TokenBalanceResult } from '../types';
import { BalanceRequirement } from '../../balanceCheck/types';
import { createBlockchainDataInstance } from '../../../instance';
import { getEthBalance } from './getEthBalance';
import { bridgeGasEstimate } from './bridgeGasEstimate';
import { INDEXER_ETH_ROOT_CONTRACT_ADDRESS, getImxL1Representation, getIndexerChainName } from './constants';
import { estimateGasForBridgeApproval } from './estimateApprovalGas';
import { CheckoutError, CheckoutErrorType } from '../../../errors';
import { allowListCheckForBridge } from '../../allowList/allowListCheck';
import { INDEXER_ETH_ROOT_CONTRACT_ADDRESS, fetchL1Representation } from '../indexer/fetchL1Representation';

export const hasSufficientL1Eth = (
balances: TokenBalanceResult,
tokenBalanceResult: TokenBalanceResult,
totalFees: BigNumber,
): boolean => {
const balance = getEthBalance(balances);
const balance = getEthBalance(tokenBalanceResult);
return balance.gte(totalFees);
};

Expand All @@ -42,32 +39,6 @@ export const getTokenAddressFromRequirement = (
return '';
};

export const fetchL1Representation = async (
config: CheckoutConfiguration,
balanceRequirement: BalanceRequirement,
): Promise<string> => {
const l2address = getTokenAddressFromRequirement(balanceRequirement);
if (l2address === '') return '';

if (l2address === IMX_ADDRESS_ZKEVM) {
return await getImxL1Representation(getL1ChainId(config), config);
}

const chainName = getIndexerChainName(getL2ChainId(config));
if (chainName === '') return ''; // Chain name not a valid indexer chain name

const blockchainData = createBlockchainDataInstance(config);
const tokenData = await blockchainData.getToken({
chainName,
contractAddress: l2address,
});

const l1address = tokenData.result.root_contract_address;
if (l1address === null) return ''; // No L1 representation of this token

return l1address;
};

export const getBridgeGasEstimate = async (
config: CheckoutConfiguration,
readOnlyProviders: Map<ChainId, ethers.providers.JsonRpcProvider>,
Expand Down Expand Up @@ -105,18 +76,24 @@ export const isNativeEth = (address: string | undefined): boolean => {
return false;
};

export type BridgeRequirement = {
amount: BigNumber;
formattedAmount: string;
l2address: string;
};
export const bridgeRoute = async (
config: CheckoutConfiguration,
readOnlyProviders: Map<ChainId, ethers.providers.JsonRpcProvider>,
depositorAddress: string,
availableRoutingOptions: RoutingOptionsAvailable,
balanceRequirement: BalanceRequirement,
balances: Map<ChainId, TokenBalanceResult>,
bridgeRequirement: BridgeRequirement,
tokenBalanceResults: Map<ChainId, TokenBalanceResult>,
feeEstimates: Map<FundingRouteType, BigNumber>,
): Promise<FundingRouteStep | undefined> => {
if (!availableRoutingOptions.bridge) return undefined;
if (bridgeRequirement.l2address === undefined || bridgeRequirement.l2address === '') return undefined;
const chainId = getL1ChainId(config);
const tokenBalanceResult = balances.get(chainId);
const tokenBalanceResult = tokenBalanceResults.get(chainId);
const l1provider = readOnlyProviders.get(chainId);
if (!l1provider) {
throw new CheckoutError(
Expand All @@ -129,15 +106,17 @@ export const bridgeRoute = async (
// If no balances on layer 1 then Bridge cannot be an option
if (tokenBalanceResult === undefined || tokenBalanceResult.success === false) return undefined;

const allowedTokenList = await allowListCheckForBridge(config, balances, availableRoutingOptions);
const allowedTokenList = await allowListCheckForBridge(config, tokenBalanceResults, availableRoutingOptions);
if (allowedTokenList.length === 0) return undefined;

const bridgeFeeEstimate = await getBridgeGasEstimate(config, readOnlyProviders, feeEstimates);

// If the user has no ETH to cover the bridge fees or approval fees then bridge cannot be an option
if (!hasSufficientL1Eth(tokenBalanceResult, bridgeFeeEstimate)) return undefined;

const l1address = await fetchL1Representation(config, balanceRequirement);
const l1RepresentationResult = await fetchL1Representation(config, bridgeRequirement.l2address);
// No mapping on L1 for this token
const { l1address } = l1RepresentationResult;
if (l1address === '') return undefined;

// Ensure l1address is in the allowed token list
Expand All @@ -153,7 +132,7 @@ export const bridgeRoute = async (
l1provider,
depositorAddress,
l1address,
balanceRequirement.delta.balance,
bridgeRequirement.amount,
);

if (!hasSufficientL1Eth(
Expand All @@ -166,7 +145,9 @@ export const bridgeRoute = async (
const nativeETHBalance = tokenBalanceResult.balances
.find((balance) => isNativeEth(balance.token.address));

if (nativeETHBalance && nativeETHBalance.balance.gte(balanceRequirement.delta.balance.add(bridgeFeeEstimate))) {
if (nativeETHBalance && nativeETHBalance.balance.gte(
bridgeRequirement.amount.add(bridgeFeeEstimate),
)) {
return constructBridgeFundingRoute(chainId, nativeETHBalance);
}

Expand All @@ -175,7 +156,9 @@ export const bridgeRoute = async (

// Find the balance of the L1 representation of the token and check if the balance covers the delta
const erc20balance = tokenBalanceResult.balances.find((balance) => balance.token.address === l1address);
if (erc20balance && erc20balance.balance.gte(balanceRequirement.delta.balance)) {
if (erc20balance && erc20balance.balance.gte(
bridgeRequirement.amount,
)) {
return constructBridgeFundingRoute(chainId, erc20balance);
}

Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { CheckoutConfiguration } from '../../../config';
import { ChainId } from '../../../types';
import { estimateApprovalGas, estimateGasForBridgeApproval } from './estimateApprovalGas';
import { CheckoutErrorType } from '../../../errors';
import { INDEXER_ETH_ROOT_CONTRACT_ADDRESS } from './constants';
import { INDEXER_ETH_ROOT_CONTRACT_ADDRESS } from '../indexer/fetchL1Representation';

jest.mock('../../../instance');
jest.mock('../../../config');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { CheckoutConfiguration, getL1ChainId, getL2ChainId } from '../../../conf
import { ChainId } from '../../../types';
import * as instance from '../../../instance';
import { CheckoutError, CheckoutErrorType } from '../../../errors';
import { INDEXER_ETH_ROOT_CONTRACT_ADDRESS } from './constants';
import { INDEXER_ETH_ROOT_CONTRACT_ADDRESS } from '../indexer/fetchL1Representation';

export const estimateApprovalGas = async (
config: CheckoutConfiguration,
Expand Down
Loading