Skip to content

Commit

Permalink
display correct contract for dialog, update metadata fetching
Browse files Browse the repository at this point in the history
  • Loading branch information
KorbinianK committed Nov 22, 2023
1 parent a722387 commit e6b37de
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 127 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
export let nft: NFT;
export let srcChainId = $network?.id;
const dispatch = createEventDispatcher();
const selectNFT = () => {
Expand All @@ -31,7 +33,7 @@
modalOpen = false;
};
$: currentChain = $network?.id;
$: currentChain = Number(srcChainId) || $network?.id;
$: imgUrl = nft.metadata?.image || placeholderUrl;
</script>
Expand Down
126 changes: 64 additions & 62 deletions packages/bridge-ui-v2/src/components/Transactions/Transaction.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@
tokenId: Number(tokenIds[0]),
type: item.tokenType,
})) as NFT;
token = await fetchNFTImageUrl(token, Number(item.srcChainId), Number(item.destChainId));
loading = false;
}
Expand All @@ -109,9 +109,11 @@
$: imgUrl = token?.metadata?.image || placeholderUrl;
$: itemAmountDisplay = item.tokenType === TokenType.ERC721 ? '---' : item.amount;
$: isNFT = [TokenType.ERC1155, TokenType.ERC721].includes(item.tokenType);
</script>

{#if [TokenType.ETH, TokenType.ERC20].includes(item.tokenType)}
{#if isNFT}
<!-- We disable these warnings as we dynamically add the role -->
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
Expand All @@ -120,48 +122,50 @@
tabindex="0"
on:click={handleClick}
on:keydown={handlePress}
class="flex text-primary-content md:h-[80px] h-[45px] w-full">
class="flex text-primary-content items-center md:h-[80px] h-[45px] w-full relative">
{#if isDesktopOrLarger}
<div class="w-1/5 py-2 flex flex-row">
<button class="w-2/6 py-2 flex flex-row space-x-[8px]" on:click={() => (nftInfoOpen = true)}>
{#if loading}
<div class="rounded-[10px] w-[50px] h-[50px] bg-neutral flex items-center justify-center">
<Spinner />
</div>
<div class="f-col text-left space-y-1">
<LoadingText mask="&nbsp;" class="min-w-[50px] max-w-[50px] h-4" />
<LoadingText mask="&nbsp;" class="min-w-[90px] max-w-[90px] h-4" />
<LoadingText mask="&nbsp;" class="min-w-[20px] max-w-[20px] h-4" />
</div>
{:else}
<img alt="nft" src={imgUrl} class="rounded-[10px] min-w-[50px] max-w-[50px] bg-neutral self-center" />
<div class="f-col text-left">
<div class="text-sm">{token?.name ? truncateString(token?.name, 15) : ''}</div>
<div class="text-sm text-secondary-content">
{token?.metadata?.name ? truncateString(token?.metadata?.name, 15) : ''}
</div>
<div class="text-sm text-secondary-content">{token?.tokenId}</div>
</div>
{/if}
</button>

<div class="w-1/6 py-2 flex flex-row">
<ChainSymbolName chainId={item.srcChainId} />
</div>
<div class="w-1/5 py-2 flex flex-row">
<div class="w-1/6 py-2 flex flex-row">
<ChainSymbolName chainId={item.destChainId} />
</div>
<div class="w-1/5 py-2 flex flex-col justify-center">
{#if item.tokenType === TokenType.ERC20}
{formatUnits(item.amount ? item.amount : BigInt(0), item.decimals)}
{:else if item.tokenType === TokenType.ETH}
{formatEther(item.amount ? item.amount : BigInt(0))}
{/if}
{item.symbol}
<div class="w-1/6 py-2 flex flex-col justify-center">
{itemAmountDisplay}
</div>
{:else}
<div class="flex text-primary-content h-[80px] w-full">
<div class="flex-col">
<div class="flex">
<ChainSymbolName chainId={item.srcChainId} />
<i role="img" aria-label="arrow to" class="mx-auto px-2">
<Icon type="arrow-right" />
</i>
<ChainSymbolName chainId={item.destChainId} />
</div>
<div class="py-2 flex flex-col justify-center">
{formatEther(item.amount ? item.amount : BigInt(0))}
{item.symbol}
</div>
</div>
</div>
TODO: mobile view
{/if}

<div class="sm:w-1/4 md:w-1/5 py-2 flex flex-col justify-center">
<div class="sm:w-1/4 md:w-1/6 py-2 flex flex-col justify-center">
<Status
on:click={isDesktopOrLarger ? undefined : openDetails}
bridgeTx={item}
on:insufficientFunds={handleInsufficientFunds} />
<!-- <div class="btn btn-primary" on:click={isDesktopOrLarger ? undefined : openDetails}></div> -->
</div>
<div class="hidden md:flex w-1/5 py-2 flex flex-col justify-center">
<div class="hidden md:flex w-1/6 py-2 flex flex-col justify-center">
<a
class="flex justify-start py-3 link"
href={`${chainConfig[Number(item.srcChainId)].urls.explorer}/tx/${item.hash}`}
Expand All @@ -171,7 +175,7 @@
</a>
</div>
</div>
{:else if [TokenType.ERC1155, TokenType.ERC721].includes(item.tokenType)}
{:else if !isNFT}
<!-- We disable these warnings as we dynamically add the role -->
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
Expand All @@ -180,50 +184,48 @@
tabindex="0"
on:click={handleClick}
on:keydown={handlePress}
class="flex text-primary-content items-center md:h-[80px] h-[45px] w-full relative">
class="flex text-primary-content md:h-[80px] h-[45px] w-full">
{#if isDesktopOrLarger}
<button class="w-2/6 py-2 flex flex-row space-x-[8px]" on:click={() => (nftInfoOpen = true)}>
{#if loading}
<div class="rounded-[10px] w-[50px] h-[50px] bg-neutral flex items-center justify-center">
<Spinner />
</div>
<div class="f-col text-left space-y-1">
<LoadingText mask="&nbsp;" class="min-w-[50px] max-w-[50px] h-4" />
<LoadingText mask="&nbsp;" class="min-w-[90px] max-w-[90px] h-4" />
<LoadingText mask="&nbsp;" class="min-w-[20px] max-w-[20px] h-4" />
</div>
{:else}
<img alt="nft" src={imgUrl} class="rounded-[10px] min-w-[50px] max-w-[50px] bg-neutral self-center" />
<div class="f-col text-left">
<div class="text-sm">{token?.name ? truncateString(token?.name, 15) : ''}</div>
<div class="text-sm text-secondary-content">
{token?.metadata?.name ? truncateString(token?.metadata?.name, 15) : ''}
</div>
<div class="text-sm text-secondary-content">{token?.tokenId}</div>
</div>
{/if}
</button>

<div class="w-1/6 py-2 flex flex-row">
<div class="w-1/5 py-2 flex flex-row">
<ChainSymbolName chainId={item.srcChainId} />
</div>
<div class="w-1/6 py-2 flex flex-row">
<div class="w-1/5 py-2 flex flex-row">
<ChainSymbolName chainId={item.destChainId} />
</div>
<div class="w-1/6 py-2 flex flex-col justify-center">
{itemAmountDisplay}
<div class="w-1/5 py-2 flex flex-col justify-center">
{#if item.tokenType === TokenType.ERC20}
{formatUnits(item.amount ? item.amount : BigInt(0), item.decimals)}
{:else if item.tokenType === TokenType.ETH}
{formatEther(item.amount ? item.amount : BigInt(0))}
{/if}
{item.symbol}
</div>
{:else}
TODO: mobile view
<div class="flex text-primary-content h-[80px] w-full">
<div class="flex-col">
<div class="flex">
<ChainSymbolName chainId={item.srcChainId} />
<i role="img" aria-label="arrow to" class="mx-auto px-2">
<Icon type="arrow-right" />
</i>
<ChainSymbolName chainId={item.destChainId} />
</div>
<div class="py-2 flex flex-col justify-center">
{formatEther(item.amount ? item.amount : BigInt(0))}
{item.symbol}
</div>
</div>
</div>
{/if}
<div class="sm:w-1/4 md:w-1/6 py-2 flex flex-col justify-center">

<div class="sm:w-1/4 md:w-1/5 py-2 flex flex-col justify-center">
<Status
on:click={isDesktopOrLarger ? undefined : openDetails}
bridgeTx={item}
on:insufficientFunds={handleInsufficientFunds} />
<!-- <div class="btn btn-primary" on:click={isDesktopOrLarger ? undefined : openDetails}></div> -->
</div>
<div class="hidden md:flex w-1/6 py-2 flex flex-col justify-center">
<div class="hidden md:flex w-1/5 py-2 flex flex-col justify-center">
<a
class="flex justify-start py-3 link"
href={`${chainConfig[Number(item.srcChainId)].urls.explorer}/tx/${item.hash}`}
Expand All @@ -240,7 +242,7 @@
<MobileDetailsDialog {closeDetails} {detailsOpen} selectedItem={item} on:insufficientFunds={handleInsufficientFunds} />

{#if token}
<NftInfoDialog bind:modalOpen={nftInfoOpen} nft={token} viewOnly />
<NftInfoDialog bind:modalOpen={nftInfoOpen} nft={token} srcChainId={Number(item.srcChainId)} viewOnly />
{/if}

<InsufficientFunds bind:modalOpen={insufficientModal} />
34 changes: 14 additions & 20 deletions packages/bridge-ui-v2/src/libs/token/fetchNFTImageUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { safeParseUrl } from '$libs/util/safeParseUrl';

import { getCrossChainAddress } from './getCrossChainAddress';
import { getTokenWithInfoFromAddress } from './getTokenWithInfoFromAddress';
import { type NFT, type NFTMetadata, TokenType } from './types';
import { type NFT, TokenType } from './types';

const log = getLogger('libs:token:fetchNFTImageUrl');

Expand Down Expand Up @@ -57,42 +57,35 @@ const testImageLoad = (url: string): Promise<boolean> => {

export const fetchNFTImageUrl = async (token: NFT, srcChainId: number, destChainId: number): Promise<NFT> => {
try {
let metadata = token.metadata;
let tokenWithMetadata: NFT | null = token;

if (!metadata) {
const crossChainData = await crossChainFetchNFTMetadata(token, srcChainId, destChainId);
if (!crossChainData) throw new Error('No cross chain data found');
metadata = crossChainData;
if (!tokenWithMetadata.metadata) {
tokenWithMetadata = await crossChainFetchNFTMetadata(token, srcChainId, destChainId);
if (!tokenWithMetadata) throw new Error('No cross chain data found');
}

if (!metadata?.image) throw new Error('No image found');
if (!tokenWithMetadata.metadata?.image) throw new Error('No image found');

const url = safeParseUrl(metadata.image);
if (!url) throw new Error(`Invalid image URL: ${metadata.image}`);
const url = safeParseUrl(tokenWithMetadata.metadata.image);
if (!url) throw new Error(`Invalid image URL: ${tokenWithMetadata.metadata.image}`);

const imageUrl = await fetchImageUrl(url, token.tokenId);
token.metadata = {
...metadata,
tokenWithMetadata.metadata = {
...tokenWithMetadata.metadata,
image: imageUrl,
};

return token;
return tokenWithMetadata;
} catch (error) {
log(`Error fetching image for ${token.name} id: ${token.tokenId}`, error);
return token;
}
};

const crossChainFetchNFTMetadata = async (
token: NFT,
srcChainId: number,
destChainId: number,
): Promise<NFTMetadata | null> => {
let metadata = null;
const crossChainFetchNFTMetadata = async (token: NFT, srcChainId: number, destChainId: number): Promise<NFT | null> => {
let canonicalAddress = null;
try {
metadata = await fetchNFTMetadata(token);
return metadata;
return await fetchNFTMetadata(token);
} catch (error) {
log(`Error fetching metadata for ${token.name} id: ${token.tokenId}`, error);

Expand Down Expand Up @@ -128,6 +121,7 @@ const crossChainFetchNFTMetadata = async (
tokenId: token.tokenId,
type: token.type,
})) as NFT;
cToken.addresses = { ...token.addresses, [destChainId]: canonicalAddress };

return await fetchNFTMetadata(cToken);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,6 @@ describe('getCrossChainAddress', () => {
// Then
expect(result).toEqual(preconfiguredAddress);
expect(mockTokenVaultContract.read.bridgedToCanonical).toHaveBeenCalledWith([mockToken.addresses[srcChainId]]);
expect(mockTokenVaultContract.read.canonicalToBridged).toHaveBeenCalledWith([
BigInt(destChainId),
preconfiguredAddress,
]);
});

it('should return the bridged address if stored or configured', async () => {
Expand Down
12 changes: 9 additions & 3 deletions packages/bridge-ui-v2/src/libs/token/getCrossChainAddress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export async function getCrossChainAddress({

// first we need to figure out the canonical address of the token
const canonicalTokenInfo = (await srcTokenVaultContract.read.bridgedToCanonical([srcChainTokenAddress])) as Address;
const canonicalTokenAddress = canonicalTokenInfo[1]; // this will break if the contracts ever change the order of the return values
const canonicalTokenAddress = canonicalTokenInfo[1] as Address; // this will break if the contracts ever change the order of the return values

// if the canonical address is 0x0, then the token is canonical
if (canonicalTokenAddress === zeroAddress) {
Expand All @@ -102,13 +102,19 @@ export async function getCrossChainAddress({
srcChainTokenAddress,
])) as Address;
} else {
return canonicalTokenAddress as Address;
// return canonicalTokenAddress as Address;
// if we have found a canonical, we can check for the bridged address on the source token vault
// e.g. bridging L2 -> L1 with native L1 token
return (await srcTokenVaultContract.read.canonicalToBridged([
const bridgedCanonical = (await srcTokenVaultContract.read.canonicalToBridged([
BigInt(destChainId),
canonicalTokenAddress,
])) as Address;

if (bridgedCanonical === srcChainTokenAddress) {
return canonicalTokenAddress;
} else {
return bridgedCanonical;
}
}
}
}
Expand Down
Loading

0 comments on commit e6b37de

Please sign in to comment.