Skip to content

Commit

Permalink
WT-1957 Ignore casing on address check when finding balance in smart …
Browse files Browse the repository at this point in the history
…checkout (#1322)
  • Loading branch information
imx-mikhala authored Jan 16, 2024
1 parent 451cd9d commit 3cee9d1
Show file tree
Hide file tree
Showing 15 changed files with 96 additions and 34 deletions.
5 changes: 3 additions & 2 deletions packages/checkout/sdk/src/balances/balances.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
DEFAULT_TOKEN_DECIMALS, ERC20ABI, NATIVE,
} from '../env';
import { measureAsyncExecution } from '../logger/debugLogger';
import { isMatchingAddress } from '../utils/utils';

export const getBalance = async (
config: CheckoutConfiguration,
Expand Down Expand Up @@ -219,7 +220,7 @@ export const getBalances = async (
// For some reason isNativeToken always returns undefined.
// We have spent way too much time figuring out why this is happening.
// That we have given up -- keep it as it is for now.
if (!token.address || token.address.toLocaleLowerCase() === NATIVE) {
if (!token.address || isMatchingAddress(token.address, NATIVE)) {
allBalancePromises.push(
getBalance(config, web3Provider, walletAddress),
);
Expand All @@ -240,7 +241,7 @@ export const getBalances = async (
// For some reason isNativeToken always returns undefined.
// We have spent way too much time figuring out why this is happening.
// That we have given up -- keep it as it is for now.
if (!token.address || token.address.toLocaleLowerCase() === NATIVE) resp.value.token.address = NATIVE;
if (!token.address || isMatchingAddress(token.address, NATIVE)) resp.value.token.address = NATIVE;
return resp.value;
});

Expand Down
3 changes: 2 additions & 1 deletion packages/checkout/sdk/src/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import { AvailabilityService, availabilityService } from './availability';
import { loadUnresolved } from './widgets/load';
import { WidgetsInit } from './types/widgets';
import { HttpClient } from './api/http';
import { isMatchingAddress } from './utils/utils';

const SANDBOX_CONFIGURATION = {
baseConfig: {
Expand Down Expand Up @@ -490,7 +491,7 @@ export class Checkout {
}

const tokenList = await tokens.getTokenAllowList(this.config, { type: TokenFilterTypes.ONRAMP });
const token = tokenList.tokens?.find((t) => t.address?.toLowerCase() === params.tokenAddress?.toLowerCase());
const token = tokenList.tokens?.find((t) => isMatchingAddress(t.address, params.tokenAddress));
if (token) {
tokenAmount = params.tokenAmount;
tokenSymbol = token.symbol;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ import {
TokenFilterTypes,
} from '../../types';
import { TokenBalanceResult, TokenBalances } from '../routing/types';
import { isMatchingAddress } from '../../utils/utils';
import { RoutingTokensAllowList } from './types';

const filterTokens = (allowedTokens: TokenInfo[], balances: TokenBalanceResult | undefined) => {
if (balances && balances.success) {
return allowedTokens.filter((token) => {
if ('address' in token) {
return balances.balances.find((balance) => balance.token.address === token.address && balance.balance.gt(0));
return balances.balances.find(
(balance) => isMatchingAddress(balance.token.address, token.address)
&& balance.balance.gt(0),
);
}
return balances.balances.find((balance) => !('address' in balance.token) && balance.balance.gt(0));
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
getTokensFromRequirements,
} from './balanceRequirement';
import { ERC721ABI, NATIVE } from '../../env';
import { isMatchingAddress } from '../../utils/utils';

/**
* Gets the balances for all NATIVE and ERC20 balance requirements.
Expand Down Expand Up @@ -85,7 +86,7 @@ const getERC721Balances = async (
erc721Owners.forEach((erc721OwnerAddress, index) => {
const itemRequirement = erc721s.get(erc721OwnersPromiseIds[index]);
let itemCount = 0;
if (itemRequirement && ownerAddress === erc721OwnerAddress) {
if (itemRequirement && isMatchingAddress(ownerAddress, erc721OwnerAddress)) {
itemCount = 1;
}
erc721Balances.push({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
} from './types';
import { DEFAULT_TOKEN_DECIMALS, NATIVE, ZKEVM_NATIVE_TOKEN } from '../../env';
import { isNativeToken } from '../../tokens';
import { isMatchingAddress } from '../../utils/utils';

export const getTokensFromRequirements = (itemRequirements: ItemRequirement[]): TokenInfo[] => itemRequirements
.map((itemRequirement) => {
Expand Down Expand Up @@ -50,7 +51,7 @@ export const getERC721BalanceRequirement = (
// Find the requirements related balance
const itemBalanceResult = balances.find((balance) => {
const balanceERC721Result = balance as ERC721Balance;
return balanceERC721Result.contractAddress === itemRequirement.contractAddress
return isMatchingAddress(balanceERC721Result.contractAddress, itemRequirement.contractAddress)
&& balanceERC721Result.id === itemRequirement.id;
});

Expand Down Expand Up @@ -96,7 +97,7 @@ export const getTokenBalanceRequirement = (
// Get the requirements related balance
if (itemRequirement.type === ItemType.ERC20) {
itemBalanceResult = balances.find((balance) => {
return (balance as TokenBalance).token?.address === itemRequirement.tokenAddress;
return isMatchingAddress((balance as TokenBalance).token?.address, itemRequirement.tokenAddress);
});
} else if (itemRequirement.type === ItemType.NATIVE) {
itemBalanceResult = balances.find((balance) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
} from '../indexer/fetchL1Representation';
import { DEFAULT_TOKEN_DECIMALS } from '../../../env';
import { isNativeToken } from '../../../tokens';
import { isMatchingAddress } from '../../../utils/utils';

export const hasSufficientL1Eth = (
tokenBalanceResult: TokenBalanceResult,
Expand Down Expand Up @@ -147,7 +148,7 @@ export const bridgeRoute = async (
const { l1address } = l1RepresentationResult as L1ToL2TokenAddressMapping;
if (isNativeToken(l1address)) {
if (!allowedL1TokenList.find((token) => isNativeToken(token.address))) return undefined;
} else if (!allowedL1TokenList.find((token) => token.address === l1address)) {
} else if (!allowedL1TokenList.find((token) => isMatchingAddress(token.address, l1address))) {
return undefined;
}

Expand Down Expand Up @@ -191,7 +192,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);
const erc20balance = tokenBalanceResult.balances.find(
(balance) => isMatchingAddress(balance.token.address, l1address),
);

if (erc20balance && erc20balance.balance.gte(
bridgeRequirement.amount,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { constructBridgeRequirements } from './constructBridgeRequirements';
import { fetchL1ToL2Mappings } from './fetchL1ToL2Mappings';
import { INDEXER_ETH_ROOT_CONTRACT_ADDRESS, L1ToL2TokenAddressMapping } from '../indexer/fetchL1Representation';
import { getDexQuotes } from './getDexQuotes';
import { isMatchingAddress } from '../../../utils/utils';

export const abortBridgeAndSwap = (
bridgeableTokens: string[],
Expand Down Expand Up @@ -56,9 +57,11 @@ export const filterSwappableTokensByBridgeableAddresses = (
// TODO: Check for ETH (native) L1 in bridgeableTokens first
if (!bridgeableTokens.includes(addresses.l1address)) continue;
// Filter out the token that is required from the swappable tokens list
if (addresses.l2address === requiredTokenAddress) continue;
if (isMatchingAddress(addresses.l2address, requiredTokenAddress)) continue;

const tokenInfo = swappableTokens.find((token) => token.address === addresses.l2address);
const tokenInfo = swappableTokens.find(
(token) => isMatchingAddress(token.address, addresses.l2address),
);
if (!tokenInfo) continue;
filteredSwappableTokens.push(tokenInfo);
}
Expand Down Expand Up @@ -104,7 +107,7 @@ const modifyTokenBalancesWithBridgedAmount = (

const newBalance = l2balance.add(amount);

const tokenInfo = swappableTokens.find((token) => token.address === l2address) as TokenInfo;
const tokenInfo = swappableTokens.find((token) => isMatchingAddress(token.address, l2address)) as TokenInfo;

balanceMap.set(l2address, {
balance: newBalance,
Expand Down Expand Up @@ -143,7 +146,9 @@ export const reapplyOriginalSwapBalances = (

let originalBalance = BigNumber.from(0);
let originalFormattedBalance = '0';
const l2balance = tokenBalance.balances.find((balance) => balance.token.address === fundingItem.token.address);
const l2balance = tokenBalance.balances.find(
(balance) => isMatchingAddress(balance.token.address, fundingItem.token.address),
);
if (l2balance) {
originalBalance = l2balance.balance;
originalFormattedBalance = l2balance.formattedBalance;
Expand All @@ -170,15 +175,21 @@ export const constructBridgeAndSwapRoutes = (
const mapping = l1tol2Addresses.find(
(addresses) => {
if (bridgeFundingStep.fundingItem.token.address === undefined) {
return addresses.l1address === INDEXER_ETH_ROOT_CONTRACT_ADDRESS && addresses.l2address;
return isMatchingAddress(addresses.l1address, INDEXER_ETH_ROOT_CONTRACT_ADDRESS) && addresses.l2address;
}
return addresses.l1address === bridgeFundingStep.fundingItem.token.address && addresses.l2address;
return (
isMatchingAddress(
addresses.l1address,
bridgeFundingStep.fundingItem.token.address,
)
&& addresses.l2address
);
},
);
if (!mapping) continue;

const swapFundingStep = swapFundingSteps.find(
(step) => step.fundingItem.token.address === mapping.l2address,
(step) => isMatchingAddress(step.fundingItem.token.address, mapping.l2address),
);
if (!swapFundingStep) continue;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { BridgeRequirement } from '../bridge/bridgeRoute';
import { DexQuote, DexQuotes } from '../types';
import { INDEXER_ETH_ROOT_CONTRACT_ADDRESS, L1ToL2TokenAddressMapping } from '../indexer/fetchL1Representation';
import { BalanceCheckResult } from '../../balanceCheck/types';
import { isMatchingAddress } from '../../../utils/utils';

// The dex will return all the fees which is in a particular token (currently always IMX)
// If any of the fees are in the same token that is trying to be swapped (e.g. trying to swap IMX)
Expand All @@ -16,13 +17,13 @@ export const getFeesForTokenAddress = (
let fees = BigNumber.from(0);

dexQuote.quote.fees.forEach((fee) => {
if (fee.amount.token.address === tokenAddress) {
if (isMatchingAddress(fee.amount.token.address, tokenAddress)) {
fees = fees.add(fee.amount.value);
}
});

if (dexQuote.approval) {
if (dexQuote.approval.token.address === tokenAddress) {
if (isMatchingAddress(dexQuote.approval.token.address, tokenAddress)) {
fees = fees.add(dexQuote.approval.value);
}
}
Expand All @@ -40,7 +41,7 @@ export const getAmountFromBalanceRequirement = (
// Find if there is an existing balance requirement of the token attempting to be bridged->swapped
for (const requirement of balanceRequirements.balanceRequirements) {
if (requirement.type === ItemType.NATIVE || requirement.type === ItemType.ERC20) {
if (requirement.required.token.address === quotedTokenAddress) {
if (isMatchingAddress(requirement.required.token.address, quotedTokenAddress)) {
return requirement.required.balance;
}
}
Expand Down Expand Up @@ -97,9 +98,9 @@ export const constructBridgeRequirements = (

for (const [tokenAddress, quote] of dexQuotes) {
// Get the L2 balance for the token address
const l2balance = l2balances.find((balance) => balance.token.address === tokenAddress);
const l2balance = l2balances.find((balance) => isMatchingAddress(balance.token.address, tokenAddress));
const l1tol2TokenMapping = l1tol2addresses.find(
(token) => token.l2address === tokenAddress,
(token) => isMatchingAddress(token.l2address, tokenAddress),
);
if (!l1tol2TokenMapping) continue;

Expand All @@ -109,10 +110,10 @@ export const constructBridgeRequirements = (
// If the user does not have any L1 balance for this token then cannot bridge
const l1balance = l1balances.find((balance) => {
if (balance.token.address === undefined
&& l1address === INDEXER_ETH_ROOT_CONTRACT_ADDRESS) {
&& isMatchingAddress(l1address, INDEXER_ETH_ROOT_CONTRACT_ADDRESS)) {
return true;
}
return balance.token.address === l1address;
return isMatchingAddress(balance.token.address, l1address);
});
if (!l1balance) continue;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { createBlockchainDataInstance } from '../../../instance';
import { NATIVE } from '../../../env';
import { ChainId, ChainSlug, ImxAddressConfig } from '../../../types';
import { isNativeToken } from '../../../tokens';
import { isMatchingAddress } from '../../../utils/utils';

// If the root address evaluates to this then its ETH
export const INDEXER_ETH_ROOT_CONTRACT_ADDRESS = '0x0000000000000000000000000000000000000eee';
Expand Down Expand Up @@ -46,9 +47,8 @@ export const fetchL1Representation = async (
contractAddress: l2address,
});

// TODO: When bridge is ready we need to understand how L2 ETH will be mapped back to L1 ETH
const l1address = tokenData.result.root_contract_address;
if (l1address === INDEXER_ETH_ROOT_CONTRACT_ADDRESS) {
if (isMatchingAddress(l1address ?? '', INDEXER_ETH_ROOT_CONTRACT_ADDRESS)) {
return {
l1address: 'native',
l2address,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
import { BalanceERC20Requirement, BalanceNativeRequirement, BalanceRequirement } from '../../balanceCheck/types';
import { allowListCheckForOnRamp } from '../../allowList';
import { isNativeToken } from '../../../tokens';
import { isMatchingAddress } from '../../../utils/utils';

export const onRampRoute = async (
config: CheckoutConfiguration,
Expand All @@ -26,7 +27,7 @@ export const onRampRoute = async (
if (!required.token) return;
if (
isNativeToken(required.token.address)
|| token.address.toLowerCase() === required.token.address?.toLowerCase()
|| isMatchingAddress(token.address, required.token.address)
) {
hasAllowList = true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ describe('swapRoute', () => {
]);
});

xit('should recommend swap route for NATIVE', async () => {
it('should recommend swap route for NATIVE', async () => {
const balanceRequirement = {
type: ItemType.NATIVE,
sufficient: false,
Expand Down Expand Up @@ -328,6 +328,16 @@ describe('swapRoute', () => {
decimals: 18,
},
},
{
balance: BigNumber.from(10),
formattedBalance: '10',
token: {
name: 'ERC20',
symbol: 'ERC20',
decimals: 18,
address: '0xERC20_2',
},
},
],
}],
]);
Expand Down
18 changes: 12 additions & 6 deletions packages/checkout/sdk/src/smartCheckout/routing/swap/swapRoute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { BalanceCheckResult, BalanceRequirement } from '../../balanceCheck/types
import { TokenBalanceResult } from '../types';
import { quoteFetcher } from './quoteFetcher';
import { isNativeToken } from '../../../tokens';
import { isMatchingAddress } from '../../../utils/utils';

const constructFees = (
approvalGasFee: Amount | null | undefined,
Expand Down Expand Up @@ -175,7 +176,7 @@ export const checkUserCanCoverApprovalFees = (
const l2BalanceOfApprovalToken = l2Balances.find(
(balance) => (
isNativeToken(balance.token.address) && isNativeToken(approvalGasTokenAddress))
|| balance.token.address === approvalGasTokenAddress,
|| isMatchingAddress(balance.token.address, approvalGasTokenAddress),
);

if (!l2BalanceOfApprovalToken) return { sufficient: false, approvalGasFee, approvalGasTokenAddress };
Expand Down Expand Up @@ -244,7 +245,7 @@ export const checkUserCanCoverSwapFees = (
const l2BalanceOfFeeToken = l2Balances.find(
(balance) => (
isNativeToken(balance.token.address) && isNativeToken(tokenAddress))
|| balance.token.address === tokenAddress,
|| isMatchingAddress(balance.token.address, tokenAddress),
);
if (!l2BalanceOfFeeToken) {
return false;
Expand Down Expand Up @@ -276,7 +277,10 @@ export const checkIfUserCanCoverRequirement = (

balanceRequirements.balanceRequirements.forEach((requirement) => {
if (requirement.type === ItemType.NATIVE || requirement.type === ItemType.ERC20) {
if (requirement.required.token.address === quoteTokenAddress) {
if (
requirement.required.token.address
&& isMatchingAddress(requirement.required.token.address, quoteTokenAddress)
) {
balanceRequirementToken = requirement.required.token.address;
requirementExists = true;
// Get the balance that would remain if the requirement was removed from the users balance
Expand All @@ -289,13 +293,13 @@ export const checkIfUserCanCoverRequirement = (
if (!requirementExists) return true;

// Remove approval fees from the remainder if token matches as these need to be taken out to cover the swap
if (approvalFees.approvalGasTokenAddress === balanceRequirementToken) {
if (isMatchingAddress(approvalFees.approvalGasTokenAddress, balanceRequirementToken)) {
remainingBalance = remainingBalance.sub(approvalFees.approvalGasFee);
}

// Remove swap fees from the remainder if token matches as these need to be taken out to cover the swap
for (const swapFee of swapFees) {
if (swapFee.amount.token.address === balanceRequirementToken) {
if (isMatchingAddress(swapFee.amount.token.address, balanceRequirementToken)) {
remainingBalance = remainingBalance.sub(swapFee.amount.value);
}
}
Expand Down Expand Up @@ -340,7 +344,9 @@ export const swapRoute = async (
const quote = quotes.get(quoteTokenAddress);
if (!quote) continue;
// Find the balance the user has for this quoted token
const userBalanceOfQuotedToken = l2Balances.find((balance) => balance.token.address === quoteTokenAddress);
const userBalanceOfQuotedToken = l2Balances.find(
(balance) => isMatchingAddress(balance.token.address, quoteTokenAddress),
);
// If no balance found on L2 for this quoted token then continue
if (!userBalanceOfQuotedToken) continue;
// Check the amount of quoted token required against the user balance
Expand Down
Loading

0 comments on commit 3cee9d1

Please sign in to comment.