Skip to content
5 changes: 4 additions & 1 deletion src/components/transactions/Bridge/BridgeActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,10 @@ export const BridgeActions = React.memo(
signatureAmount: amountToBridge,
onApprovalTxConfirmed: fetchApprovedAmount,
chainId: sourceChainId,
amountToApprove: parseUnits(amountToBridge || '0', 18).toString(),
amountToApprove: parseUnits(
amountToBridge && Number(amountToBridge) ? amountToBridge : '0',
18
).toString(),
});

// Update gas estimation
Expand Down
36 changes: 31 additions & 5 deletions src/components/transactions/Bridge/BridgeConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,15 @@ import {
AaveV3BaseSepolia,
AaveV3Ethereum,
AaveV3Gnosis,
AaveV3InkWhitelabel,
AaveV3Sepolia,
GhoArbitrum,
GhoAvalanche,
GhoBase,
GhoEthereum,
GhoGnosis,
GhoInk,
GhoMantle,
} from '@bgd-labs/aave-address-book';
import { constants } from 'ethers';
import { TokenInfoWithBalance } from 'src/hooks/generic/useTokensBalance';
Expand All @@ -24,7 +29,7 @@ type Config = {
sourceChainId: ChainId | number;
router: string;
chainSelector: string;
subgraphKey: SubgraphKey;
subgraphKey?: SubgraphKey;
tokenOracle: string; // Used to get the GHO price
wrappedNativeOracle: string; // Used to get the fee price in USD
lockReleaseTokenPool?: string; // Only exists on Ethereum
Expand Down Expand Up @@ -149,7 +154,7 @@ const prodConfig: Config[] = [
{
sourceChainId: ChainId.avalanche,
chainSelector: '6433500567565415381',
burnMintTokenPool: '0xDe6539018B095353A40753Dc54C91C68c9487D4E',
burnMintTokenPool: GhoAvalanche.GHO_CCIP_TOKEN_POOL,
router: '0xF4c7E640EdA248ef95972845a62bdC74237805dB',
tokenOracle: '0x360d8aa8F6b09B7BC57aF34db2Eb84dD87bf4d12',
wrappedNativeOracle: AaveV3Avalanche.ASSETS.WAVAX.ORACLE,
Expand Down Expand Up @@ -183,7 +188,7 @@ const prodConfig: Config[] = [
{
sourceChainId: ChainId.xdai,
chainSelector: '465200170687744372',
burnMintTokenPool: '0xDe6539018B095353A40753Dc54C91C68c9487D4E',
burnMintTokenPool: GhoGnosis.GHO_CCIP_TOKEN_POOL,
router: '0x4aAD6071085df840abD9Baf1697d5D5992bDadce',
tokenOracle: '0x360d8aa8F6b09B7BC57aF34db2Eb84dD87bf4d12',
wrappedNativeOracle: AaveV3Gnosis.ASSETS.WXDAI.ORACLE,
Expand All @@ -205,10 +210,10 @@ const prodConfig: Config[] = [
{
sourceChainId: ChainId.ink,
chainSelector: '3461204551265785888',
burnMintTokenPool: '0xDe6539018B095353A40753Dc54C91C68c9487D4E',
burnMintTokenPool: GhoInk.GHO_CCIP_TOKEN_POOL,
router: '0xca7c90A52B44E301AC01Cb5EB99b2fD99339433A',
tokenOracle: '0x20fd5f3FCac8883a3A0A2bBcD658A2d2c6EFa6B6',
wrappedNativeOracle: '0xA17887fd35B14A4c6e6ec87458591941934d444c',
wrappedNativeOracle: AaveV3InkWhitelabel.ASSETS.WETH.ORACLE,
subgraphKey: 'ccip-ink',
feeTokens: [
{
Expand All @@ -224,6 +229,27 @@ const prodConfig: Config[] = [
},
],
},
{
sourceChainId: ChainId.mantle,
chainSelector: '1556008542357238666',
burnMintTokenPool: GhoMantle.GHO_CCIP_TOKEN_POOL,
router: '0x670052635a9850bb45882Cb2eCcF66bCff0F41B7',
tokenOracle: '0x360d8aa8F6b09B7BC57aF34db2Eb84dD87bf4d12',
wrappedNativeOracle: '0xD97F20bEbeD74e8144134C4b148fE93417dd0F96',
feeTokens: [
{
name: 'Mantle',
symbol: 'MNT',
decimals: 18,
address: constants.AddressZero, // Use zero address for network token ccip
chainId: ChainId.mantle,
extensions: {
isNative: true,
},
balance: '0',
},
],
},
];

const testnetConfig: Config[] = [
Expand Down
97 changes: 55 additions & 42 deletions src/components/transactions/Bridge/BridgeModalContent.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { ChainId } from '@aave/contract-helpers';
import { AaveV3InkWhitelabel } from '@bgd-labs/aave-address-book';
import { SwitchVerticalIcon } from '@heroicons/react/outline';
import { ExternalLinkIcon, SwitchVerticalIcon } from '@heroicons/react/outline';
import { Trans } from '@lingui/macro';
import {
Box,
Expand All @@ -16,7 +15,7 @@ import { BigNumber } from 'bignumber.js';
import { constants } from 'ethers';
import { formatUnits } from 'ethers/lib/utils';
import React, { useEffect, useState } from 'react';
import { Link, ROUTES } from 'src/components/primitives/Link';
import { Link } from 'src/components/primitives/Link';
import { Row } from 'src/components/primitives/Row';
import { Warning } from 'src/components/primitives/Warning';
import { TextWithTooltip } from 'src/components/TextWithTooltip';
Expand All @@ -36,9 +35,9 @@ import { GHO_SYMBOL } from 'src/utils/ghoUtilities';
import { getNetworkConfig, marketsData } from 'src/utils/marketsAndNetworksConfig';

import { AssetInput } from '../AssetInput';
import { BaseSuccessView } from '../FlowCommons/BaseSuccess';
import { TxErrorView } from '../FlowCommons/Error';
import { GasEstimationError } from '../FlowCommons/GasEstimationError';
import { TxSuccessView } from '../FlowCommons/Success';
import { ChangeNetworkWarning } from '../Warnings/ChangeNetworkWarning';
import { BridgeActionProps, BridgeActions } from './BridgeActions';
import { BridgeAmount } from './BridgeAmount';
Expand All @@ -57,18 +56,6 @@ import { useTimeToDestination } from './useGetFinalityTime';
const defaultNetwork = supportedNetworksWithBridge[0];

function getUseBridgeTokensParams(chainId: number): UseBridgeTokensParams {
const tokenOracle = getConfigFor(chainId).tokenOracle;

if (chainId === ChainId.ink) {
// no market config available yet for ink, so values are set here
return {
chainId,
ghoTokenAddress: AaveV3InkWhitelabel.ASSETS.GHO.UNDERLYING,
tokenOracle,
walletBalanceProviderAddress: AaveV3InkWhitelabel.WALLET_BALANCE_PROVIDER,
};
}

const market = Object.values(marketsData).filter(
(md) => md.chainId === chainId && md.v3 === true && md.addresses.GHO_TOKEN_ADDRESS
)[0];
Expand All @@ -85,7 +72,7 @@ function getUseBridgeTokensParams(chainId: number): UseBridgeTokensParams {
}

export const BridgeModalContent = () => {
const { mainTxState: bridgeTxState, txError, close, gasLimit } = useModalContext();
const { mainTxState: bridgeTxState, txError, gasLimit } = useModalContext();
const user = useRootStore((state) => state.account);
const [destinationAccount, setDestinationAccount] = useState(user);
const [amount, setAmount] = useState('');
Expand All @@ -106,13 +93,29 @@ export const BridgeModalContent = () => {
const { data: estimatedTimeToDestination, isFetching: loadingEstimatedTime } =
useTimeToDestination(sourceNetworkObj.chainId);

const getFilteredFeeTokens = (chainId: number) => {
return laneConfig
.filter((token) => token.sourceChainId === chainId)
const getFilteredFeeTokens = (sourceChainId: number, destinationChainId: number) => {
const sourceFeeTokens = laneConfig
.filter((config) => config.sourceChainId === sourceChainId)
.flatMap((config) => config.feeTokens);

const destinationFeeTokenSymbols = new Set(
laneConfig
.filter((config) => config.sourceChainId === destinationChainId)
.flatMap((config) => config.feeTokens)
.map((token) => token.symbol)
);

// Only include non-native fee tokens (e.g. GHO) if the destination chain also supports them.
// This prevents using a fee token on a lane where the onRamp doesn't support it.
return sourceFeeTokens.filter(
(token) => token.extensions?.isNative || destinationFeeTokenSymbols.has(token.symbol)
);
};

const filteredFeeTokensByChainId = getFilteredFeeTokens(sourceNetworkObj.chainId);
const filteredFeeTokensByChainId = getFilteredFeeTokens(
sourceNetworkObj.chainId,
destinationNetworkObj.chainId
);

const { data: feeTokenListWithBalance, isFetching: loadingTokenBalances } = useTokensBalance(
filteredFeeTokensByChainId,
Expand Down Expand Up @@ -144,6 +147,17 @@ export const BridgeModalContent = () => {
}
}, [feeTokenListWithBalance, sourceNetworkObj]);

useEffect(() => {
// Reset selected fee token when destination changes if current selection is no longer valid
const validSymbols = filteredFeeTokensByChainId.map((t) => t.symbol);
if (selectedFeeToken && !validSymbols.includes(selectedFeeToken.symbol)) {
setSelectedFeeToken(
feeTokenListWithBalance?.find((t) => validSymbols.includes(t.symbol)) ||
filteredFeeTokensByChainId[0]
);
}
}, [destinationNetworkObj]);

useEffect(() => {
// reset when source network changes
setAmount('');
Expand Down Expand Up @@ -228,7 +242,10 @@ export const BridgeModalContent = () => {
setSourceNetworkObj(destinationNetworkObj);
setDestinationNetworkObj(currentSourceNetworkObj);

const newFilteredFeeTokens = getFilteredFeeTokens(destinationNetworkObj.chainId);
const newFilteredFeeTokens = getFilteredFeeTokens(
destinationNetworkObj.chainId,
currentSourceNetworkObj.chainId
);
setSelectedFeeToken(newFilteredFeeTokens[0]);
};

Expand Down Expand Up @@ -273,28 +290,18 @@ export const BridgeModalContent = () => {

if (bridgeTxState.success) {
return (
<TxSuccessView
customAction={
<Box mt={5}>
<Button
component={Link}
href={ROUTES.bridge}
variant="outlined"
size="small"
onClick={close}
>
<Trans>View Bridge Transactions</Trans>
</Button>
</Box>
}
customText={
<BaseSuccessView
txHash={bridgeTxState.txHash}
customExplorerLink={`https://ccip.chain.link/tx/${bridgeTxState.txHash}`}
customExplorerLinkText={<Trans>View on CCIP Explorer</Trans>}
>
<Typography sx={{ mt: 2, textAlign: 'center' }}>
<Trans>
Asset has been successfully sent to CCIP contract. You can check the status of the
transactions below
</Trans>
}
action={<Trans>Bridged Via CCIP</Trans>}
/>
</Typography>
</BaseSuccessView>
);
}

Expand Down Expand Up @@ -341,11 +348,17 @@ export const BridgeModalContent = () => {
>
<Button
component={Link}
href={ROUTES.bridge}
href={`https://ccip.chain.link/address/${user}`}
target="_blank"
rel="noopener"
sx={{ mr: 8 }}
variant="surface"
size="small"
onClick={close}
endIcon={
<SvgIcon sx={{ width: 14, height: 14 }}>
<ExternalLinkIcon />
</SvgIcon>
}
>
<Trans>Transactions</Trans>
</Button>
Expand Down
6 changes: 3 additions & 3 deletions src/components/transactions/Bridge/useGetBridgeMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const useGetBridgeMessage = ({
const tokenAmounts: TokenAmount[] = [
{
token: sourceTokenAddress,
amount: parseUnits(amount, 18).toString() || '0',
amount: parseUnits(amount && Number(amount) ? amount : '0', 18).toString(),
},
];

Expand All @@ -66,7 +66,7 @@ export const useGetBridgeMessage = ({
const destinationChainSelector = getChainSelectorFor(destinationChainId);
const fees: BigNumber = await sourceRouter.getFee(destinationChainSelector, message);

const amountBN = utils.parseUnits(amount, 18);
const amountBN = utils.parseUnits(amount && Number(amount) ? amount : '0', 18);
const updatedAmount = amountBN.sub(fees);

// If the fee token is not the native token, we need to update the tokenAmounts to subtract fees
Expand All @@ -90,7 +90,6 @@ export const useGetBridgeMessage = ({
let transactionCostUsd;

if (feeToken === constants.AddressZero) {
console.log('fee token ETH');
// Handling for Ether (native token)
const sourceLaneConfig = laneConfig.find(
(config) => config.sourceChainId === sourceChainId
Expand Down Expand Up @@ -140,6 +139,7 @@ export const useGetBridgeMessage = ({
setBridgeFee(fees.toString());
} catch (e) {
setError(e.message);
console.log('---------');
console.error(e);
} finally {
setLoading(false);
Expand Down
7 changes: 6 additions & 1 deletion src/hooks/useBridgeTransactionHistory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,12 @@ export const useBridgeTransactionHistory = (sender: string) => {
return useQuery({
queryFn: async () => {
const txs = await Promise.all(
laneConfig.map((config) => getSendRequests(config.subgraphKey, sender))
laneConfig.map((config) => {
if (config.subgraphKey) {
return getSendRequests(config.subgraphKey, sender);
}
return Promise.resolve([]);
})
);
return mergeAndSortTransactions(txs);
},
Expand Down
2 changes: 1 addition & 1 deletion src/locales/el/messages.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/locales/en/messages.js

Large diffs are not rendered by default.

12 changes: 4 additions & 8 deletions src/locales/en/messages.po
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,10 @@ msgstr "Add to wallet"
msgid "Invalid amount to mint"
msgstr "Invalid amount to mint"

#: src/components/transactions/Bridge/BridgeModalContent.tsx
msgid "View on CCIP Explorer"
msgstr "View on CCIP Explorer"

#: src/components/AddressBlockedModal.tsx
msgid "Disconnect Wallet"
msgstr "Disconnect Wallet"
Expand Down Expand Up @@ -746,10 +750,6 @@ msgstr "MAI has been paused due to a community decision. Supply, borrows and rep
msgid "Underlying token"
msgstr "Underlying token"

#: src/components/transactions/Bridge/BridgeModalContent.tsx
msgid "Bridged Via CCIP"
msgstr "Bridged Via CCIP"

#: src/modules/dashboard/lists/ListBottomText.tsx
msgid "Since this is a test network, you can get any of the assets if you have ETH on your wallet"
msgstr "Since this is a test network, you can get any of the assets if you have ETH on your wallet"
Expand Down Expand Up @@ -1545,10 +1545,6 @@ msgstr "You cancelled the transaction."
msgid "Borrow info"
msgstr "Borrow info"

#: src/components/transactions/Bridge/BridgeModalContent.tsx
msgid "View Bridge Transactions"
msgstr "View Bridge Transactions"

#: src/ui-config/errorMapping.tsx
msgid "Invalid return value of the flashloan executor function"
msgstr "Invalid return value of the flashloan executor function"
Expand Down
2 changes: 1 addition & 1 deletion src/locales/es/messages.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/locales/fr/messages.js

Large diffs are not rendered by default.

9 changes: 7 additions & 2 deletions src/ui-config/marketsConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ import {
AaveV3Soneium,
AaveV3Sonic,
AaveV3ZkSync,
GhoAvalanche,
GhoGnosis,
GhoMantle,
} from '@bgd-labs/aave-address-book';
import { ReactNode } from 'react';

Expand Down Expand Up @@ -354,7 +357,7 @@ export const marketsData: {
COLLECTOR: AaveV3Avalanche.COLLECTOR,
DEBT_SWITCH_ADAPTER: AaveV3Avalanche.DEBT_SWAP_ADAPTER,
WITHDRAW_SWITCH_ADAPTER: AaveV3Avalanche.WITHDRAW_SWAP_ADAPTER,
GHO_TOKEN_ADDRESS: '0xfc421ad3c883bf9e7c4f42de845c4e4405799e73',
GHO_TOKEN_ADDRESS: GhoAvalanche.GHO_TOKEN,
},
},
[CustomMarket.proto_linea_v3]: {
Expand Down Expand Up @@ -518,6 +521,7 @@ export const marketsData: {
WALLET_BALANCE_PROVIDER: AaveV3InkWhitelabel.WALLET_BALANCE_PROVIDER,
UI_POOL_DATA_PROVIDER: AaveV3InkWhitelabel.UI_POOL_DATA_PROVIDER,
UI_INCENTIVE_DATA_PROVIDER: AaveV3InkWhitelabel.UI_INCENTIVE_DATA_PROVIDER,
GHO_TOKEN_ADDRESS: AaveV3InkWhitelabel.ASSETS.GHO.UNDERLYING,
// COLLECTOR: AaveV3InkWhitelabel.COLLECTOR,
},
},
Expand Down Expand Up @@ -554,6 +558,7 @@ export const marketsData: {
UI_INCENTIVE_DATA_PROVIDER: AaveV3Mantle.UI_INCENTIVE_DATA_PROVIDER,
COLLECTOR: AaveV3Mantle.COLLECTOR,
L2_ENCODER: AaveV3Mantle.L2_ENCODER,
GHO_TOKEN_ADDRESS: GhoMantle.GHO_TOKEN,
},
},
[CustomMarket.proto_fuji]: {
Expand Down Expand Up @@ -599,7 +604,7 @@ export const marketsData: {
COLLECTOR: AaveV3Gnosis.COLLECTOR,
DEBT_SWITCH_ADAPTER: AaveV3Gnosis.DEBT_SWAP_ADAPTER,
WITHDRAW_SWITCH_ADAPTER: AaveV3Gnosis.WITHDRAW_SWAP_ADAPTER,
GHO_TOKEN_ADDRESS: '0xfc421ad3c883bf9e7c4f42de845c4e4405799e73',
GHO_TOKEN_ADDRESS: GhoGnosis.GHO_TOKEN,
},
},
[CustomMarket.proto_bnb_v3]: {
Expand Down