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

refactor: bye bye @avalabs/bridge-sdk, hello @avalabs/bridge-unified #57

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"@avalabs/avalanche-module": "0.11.2",
"@avalabs/avalanchejs": "4.1.0-alpha.7",
"@avalabs/bitcoin-module": "0.11.2",
"@avalabs/bridge-unified": "0.0.0-feat-ictt-configs-20241009072139",
"@avalabs/bridge-unified": "0.0.0-CP-8544-BTC-bridge-2-20241024221835",
"@avalabs/core-bridge-sdk": "3.1.0-alpha.10",
"@avalabs/core-chains-sdk": "3.1.0-alpha.10",
"@avalabs/core-coingecko-sdk": "3.1.0-alpha.10",
Expand Down
6 changes: 4 additions & 2 deletions src/components/common/TokenEllipsis.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { PropsWithChildren } from 'react';
import { Tooltip } from '@avalabs/core-k2-components';
import { SxProps, Tooltip } from '@avalabs/core-k2-components';
import { truncateAddress } from '@avalabs/core-utils-sdk';

interface TokenEllipsisProps {
maxLength: number;
text: string;
className?: string;
sx?: SxProps;
}

function isTruncated(maxLength, text) {
Expand All @@ -16,6 +17,7 @@ export function TokenEllipsis({
maxLength,
text,
className,
sx,
}: PropsWithChildren<TokenEllipsisProps>) {
const name =
text.length <= maxLength ? text : truncateAddress(text, maxLength / 2);
Expand All @@ -26,7 +28,7 @@ export function TokenEllipsis({
title={text}
disableHoverListener={!isTruncated(maxLength, text)}
disableFocusListener={!isTruncated(maxLength, text)}
sx={{ cursor: 'pointer' }}
sx={sx ?? { cursor: 'pointer' }}
>
<>{name}</>
</Tooltip>
Expand Down
25 changes: 8 additions & 17 deletions src/components/common/TokenSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
} from 'react';
import { useSettingsContext } from '@src/contexts/SettingsProvider';
import { ContainedDropdown } from '@src/components/common/ContainedDropdown';
import { AssetBalance } from '@src/pages/Bridge/models';
import EthLogo from '@src/images/tokens/eth.png';
import {
hasUnconfirmedBTCBalance,
Expand Down Expand Up @@ -45,7 +44,6 @@ const InputContainer = styled(Card)`
align-items: center;
padding: 8px 16px;
background: ${({ theme }) => theme.palette.grey[850]};
cursor: pointer;
display: flex;
`;

Expand All @@ -71,7 +69,7 @@ const StyledDropdownMenuItem = styled(DropdownItem)`

interface TokenSelectProps {
selectedToken?: TokenWithBalance | null;
onTokenChange(token: TokenWithBalance | AssetBalance): void;
onTokenChange(token: TokenWithBalance): void;
maxAmount?: bigint;
inputAmount?: bigint;
onInputAmountChange?(data: { amount: string; bigint: bigint }): void;
Expand All @@ -83,7 +81,6 @@ interface TokenSelectProps {
label?: string;
selectorLabel?: string;
tokensList?: TokenWithBalance[];
bridgeTokensList?: AssetBalance[];
isValueLoading?: boolean;
hideErrorMessage?: boolean;
skipHandleMaxAmount?: boolean;
Expand All @@ -107,7 +104,6 @@ export function TokenSelect({
isValueLoading,
hideErrorMessage,
skipHandleMaxAmount,
bridgeTokensList,
setIsOpen,
containerRef,
withMaxButton = true,
Expand Down Expand Up @@ -140,13 +136,10 @@ export function TokenSelect({
},
[onInputAmountChange, maxAmountString]
);
const hideTokenDropdown =
(bridgeTokensList && bridgeTokensList.length < 2) ||
(tokensList && tokensList.length < 2);
const hideTokenDropdown = tokensList && tokensList.length < 2;

const displayTokenList = useDisplaytokenlist({
tokensList,
bridgeTokensList,
searchQuery,
});

Expand Down Expand Up @@ -187,9 +180,8 @@ export function TokenSelect({

useEffect(() => {
// when only one token is present, auto select it
const tokens = bridgeTokensList ?? tokensList;
const hasOnlyOneToken = tokens?.length === 1;
const theOnlyToken = hasOnlyOneToken ? tokens[0] : undefined;
const hasOnlyOneToken = tokensList?.length === 1;
const theOnlyToken = hasOnlyOneToken ? tokensList[0] : undefined;
const isOnlyTokenNotSelected =
theOnlyToken && theOnlyToken?.symbol !== selectedToken?.symbol;

Expand All @@ -198,17 +190,16 @@ export function TokenSelect({
return;
}
// when selected token is not supported, clear it
const supportedSymbols =
tokens?.flatMap((tok) => [tok.symbol, tok.symbolOnNetwork]) ?? [];
const supportedSymbols = tokensList?.flatMap((tok) => tok.symbol) ?? [];

if (
selectedToken &&
tokens?.[0] &&
tokensList?.[0] &&
!supportedSymbols.includes(selectedToken.symbol)
) {
onTokenChange(tokens[0]);
onTokenChange(tokensList[0]);
}
}, [bridgeTokensList, tokensList, onTokenChange, selectedToken]);
}, [tokensList, onTokenChange, selectedToken]);

const rowRenderer = useCallback(
({ key, index, style }) => {
Expand Down
10 changes: 7 additions & 3 deletions src/components/common/TokenSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ export function TokenSelector({
const { t } = useTranslation();
return (
<Stack
onClick={onClick}
onClick={hideCaretIcon ? undefined : onClick}
sx={{
alignItems: 'center',
width: '100%',
flexDirection: 'row',
cursor: 'pointer',
cursor: hideCaretIcon ? 'default' : 'pointer',
pr: 2,
}}
>
Expand All @@ -51,7 +51,11 @@ export function TokenSelector({
<>
{token.icon}
<Typography variant="body2" sx={{ mx: 1 }} fontWeight={600}>
<TokenEllipsis maxLength={7} text={token.name || ''} />
<TokenEllipsis
maxLength={7}
text={token.name || ''}
sx={{ cursor: hideCaretIcon ? 'default' : 'pointer' }}
/>
</Typography>
{!hideCaretIcon ? (
isOpen ? (
Expand Down
48 changes: 33 additions & 15 deletions src/contexts/UnifiedBridgeProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {

Check failure on line 1 in src/contexts/UnifiedBridgeProvider.tsx

View workflow job for this annotation

GitHub Actions / Lint and build

Argument of type 'BridgeType[]' is not assignable to parameter of type '(EvmBridgeInitializer | AvaToBtcBridgeInitializer | BtcToAvaBridgeInitializer)[]'.

Check failure on line 1 in src/contexts/UnifiedBridgeProvider.tsx

View workflow job for this annotation

GitHub Actions / Lint and build

Argument of type '{ asset: BridgeAsset; fromAddress: `0x${string}`; amount: bigint; sourceChain: Chain; targetChain: Chain; }' is not assignable to parameter of type 'GasEstimationParams'.

Check failure on line 1 in src/contexts/UnifiedBridgeProvider.tsx

View workflow job for this annotation

GitHub Actions / Lint and build

Argument of type '{ asset: BridgeAsset; fromAddress: `0x${string}`; amount: bigint; sourceChain: Chain; targetChain: Chain; onStepChange: (step: BridgeStepDetails) => void; sign: ({ from, to, data }: { ...; }) => Promise<...>; }' is not assignable to parameter of type 'TransferParams'.
createContext,
useCallback,
useState,
Expand Down Expand Up @@ -73,9 +73,13 @@
getErrorMessage(errorCode: UnifiedBridgeErrorCode): string;
transferableAssets: BridgeAsset[];
state: UnifiedBridgeState;
assets: ChainAssetMap;
availableChainIds: string[];
isReady: boolean;
}

const DEFAULT_STATE = {
assets: {},
state: UNIFIED_BRIDGE_DEFAULT_STATE,
estimateTransferGas() {
throw new Error('Bridge not ready');
Expand All @@ -99,6 +103,8 @@
throw new Error('Bridge not ready');
},
transferableAssets: [],
availableChainIds: [],
isReady: false,
};

const UnifiedBridgeContext = createContext<UnifiedBridgeContext>(DEFAULT_STATE);
Expand Down Expand Up @@ -129,19 +135,26 @@
const [state, setState] = useState<UnifiedBridgeState>(
UNIFIED_BRIDGE_DEFAULT_STATE
);
const [isReady, setIsReady] = useState(false);
const { featureFlags } = useFeatureFlagContext();
const isCCTPDisabled = !featureFlags[FeatureGates.UNIFIED_BRIDGE_CCTP];
const disabledBridgeTypes = useMemo(
() =>
isCCTPDisabled
? [
BridgeType.CCTP,
BridgeType.ICTT_ERC20_ERC20,
BridgeType.AVALANCHE_EVM,
]
: [BridgeType.AVALANCHE_EVM, BridgeType.ICTT_ERC20_ERC20],
[isCCTPDisabled]
);
const isICTTDisabled = false; // TODO: feature flag it
const isABDisabled = false; // TODO: feature flag it
const disabledBridgeTypes = useMemo(() => {
const disabledBridges: BridgeType[] = [];

if (isCCTPDisabled) {
disabledBridges.push(BridgeType.CCTP);
}
if (isICTTDisabled) {
disabledBridges.push(BridgeType.ICTT_ERC20_ERC20);
}
if (isABDisabled) {
disabledBridges.push(BridgeType.AVALANCHE_EVM);
}

return disabledBridges;
}, [isCCTPDisabled, isABDisabled, isICTTDisabled]);

const environment = useMemo(() => {
if (typeof activeNetwork?.isTestnet !== 'boolean') {
Expand Down Expand Up @@ -202,21 +215,24 @@
}

setAssets(chainAssetsMap);
setIsReady(true);
});

return () => {
isMounted = false;
};
}, [core]);

const availableChainIds = useMemo(() => Object.keys(assets ?? {}), [assets]);

const buildChain = useCallback(
(chainId: string): Chain => {
const network = getNetwork(chainId);

assert(network, CommonError.UnknownNetwork);

return {
chainId,
chainId: network.caipId,
chainName: network.chainName,
rpcUrl: network.rpcUrl,
networkToken: {
Expand All @@ -236,9 +252,6 @@
return [];
}

// UnifiedBridge SDK returns the chain IDs in CAIP2 format.
// This is good, but we need to translate it to numeric chain ids
// until we make the switch in extension:
return assets[activeNetwork.caipId] ?? [];
}, [activeNetwork, assets]);

Expand Down Expand Up @@ -359,6 +372,8 @@
const identifier =
asset.type === TokenType.NATIVE ? asset.symbol : asset.address;

assert(identifier, UnifiedBridgeError.InvalidFee);

return feeMap[identifier.toLowerCase()] ?? 0n;
},
[activeNetwork, core, buildChain, getAsset]
Expand Down Expand Up @@ -531,8 +546,11 @@
return (
<UnifiedBridgeContext.Provider
value={{
assets,
availableChainIds,
estimateTransferGas,
getErrorMessage,
isReady,
state,
analyzeTx,
getAssetIdentifierOnTargetChain,
Expand Down
75 changes: 18 additions & 57 deletions src/hooks/useDisplayTokenList.ts
Original file line number Diff line number Diff line change
@@ -1,81 +1,42 @@
import { AssetBalance } from '@src/pages/Bridge/models';
import { useMemo } from 'react';
import { formatTokenAmount } from '@avalabs/core-bridge-sdk';
import Big from 'big.js';
import { partition } from 'lodash';
import { isUnifiedBridgeAsset } from '@src/pages/Bridge/utils/isUnifiedBridgeAsset';
import { normalizeBalance } from '@src/utils/normalizeBalance';
import { TokenWithBalance } from '@avalabs/vm-module-types';
import { isNFT } from '@src/background/services/balances/nft/utils/isNFT';

function formatBalance(balance: Big | undefined) {
return balance ? formatTokenAmount(balance, 6) : '-';
}

export interface DisplayToken {
name: string;
symbol: string;
displayValue: string;
token: TokenWithBalance | AssetBalance;
token: TokenWithBalance;
decimals: number;
}

export const useDisplaytokenlist = ({
tokensList,
bridgeTokensList,
searchQuery,
}: {
tokensList?: TokenWithBalance[];
bridgeTokensList?: AssetBalance[];
searchQuery: string;
}) => {
const displayTokenList: DisplayToken[] = useMemo(() => {
const initialList = [
...(tokensList
? tokensList
.filter((token) =>
searchQuery.length
? token.name
.toLowerCase()
.includes(searchQuery.toLowerCase()) ||
token.symbol.toLowerCase().includes(searchQuery.toLowerCase())
: true
)
.map((token): DisplayToken => {
return {
name: token.name,
symbol: token.symbol,
displayValue: token.balanceDisplayValue ?? '',
token,
decimals: isNFT(token) ? 0 : token.decimals,
};
})
: []),
...(bridgeTokensList
? bridgeTokensList
.filter((token) =>
searchQuery.length
? token.symbol
.toLowerCase()
.includes(searchQuery.toLowerCase()) ||
token.symbolOnNetwork
?.toLowerCase()
.includes(searchQuery.toLocaleLowerCase())
: true
)
.map((token): DisplayToken => {
return {
name: token.symbolOnNetwork || token.symbol,
symbol: token.asset.symbol,
displayValue: formatBalance(token.balance),
token,
decimals: isUnifiedBridgeAsset(token.asset)
? token.asset.decimals
: token.asset.denomination,
};
})
: []),
];
const initialList = (tokensList ?? [])
.filter((token) =>
searchQuery.length
? token.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
token.symbol.toLowerCase().includes(searchQuery.toLowerCase())
: true
)
.map((token): DisplayToken => {
return {
name: token.name,
symbol: token.symbol,
displayValue: token.balanceDisplayValue ?? '',
token,
decimals: isNFT(token) ? 0 : token.decimals,
};
});

const [tokensWithBalance, tokensWithoutBalance]: DisplayToken[][] =
partition(initialList, (token) => {
Expand Down Expand Up @@ -107,6 +68,6 @@ export const useDisplaytokenlist = ({
return tokenOne.name.localeCompare(tokenTwo.name);
}),
];
}, [tokensList, bridgeTokensList, searchQuery]);
}, [tokensList, searchQuery]);
return displayTokenList;
};
Loading
Loading