Feat/amb 2389/asset usd balances (#684)

* feat: show asset balances in USD (AMB-2389)

Add prices { usd } to the getTapSupportedAssets query via the Amboss
trade API. Wire the price data through the server types and resolver,
then display a USD equivalent (≈ $X.XX) per balance entry in AssetsList.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: format balance using precision and display USD value

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Bufo <32884105+bufo24@users.noreply.github.com>
Co-authored-by: Rajat Khanduri <38693805+wthrajat@users.noreply.github.com>
This commit is contained in:
Michael G 2026-04-02 11:33:41 -06:00 committed by GitHub
parent 211aa23bc0
commit e3f882b329
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 74 additions and 1 deletions

View file

@ -812,6 +812,11 @@ type TapAssetList {
assets: [TapAsset!]!
}
type TapAssetPrice {
id: String
usd: Float
}
enum TapAssetType {
COLLECTIBLE
NORMAL
@ -874,6 +879,7 @@ type TapSupportedAsset {
groupKey: String
id: String!
precision: Int!
prices: TapAssetPrice
symbol: String!
}

View file

@ -19,6 +19,7 @@ export type GetTapSupportedAssetsQuery = {
precision: number;
assetId?: string | null;
groupKey?: string | null;
prices?: { __typename?: 'TapAssetPrice'; usd?: number | null } | null;
}>;
};
};
@ -33,6 +34,9 @@ export const GetTapSupportedAssetsDocument = gql`
precision
assetId
groupKey
prices {
usd
}
}
}
}

View file

@ -10,6 +10,9 @@ export const GET_TAP_SUPPORTED_ASSETS = gql`
precision
assetId
groupKey
prices {
usd
}
}
}
}

View file

@ -5,6 +5,7 @@ import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { useGetTapAssetsQuery } from '../../graphql/queries/__generated__/getTapAssets.generated';
import { useGetTapBalancesQuery } from '../../graphql/queries/__generated__/getTapBalances.generated';
import { useGetTapSupportedAssetsQuery } from '../../graphql/queries/__generated__/getTapSupportedAssets.generated';
import { TapBalanceGroupBy } from '../../graphql/types';
import { getErrorContent } from '../../utils/error';
import { cn } from '../../lib/utils';
@ -55,6 +56,17 @@ export const AssetsList: FC = () => {
onError: error => toast.error(getErrorContent(error)),
});
const { data: supportedData } = useGetTapSupportedAssetsQuery();
const priceMap = new Map<string, { usd: number; precision: number }>();
for (const asset of supportedData?.getTapSupportedAssets?.list || []) {
const price = asset.prices?.usd;
if (price == null) continue;
const entry = { usd: price, precision: asset.precision };
if (asset.assetId) priceMap.set(asset.assetId, entry);
if (asset.groupKey) priceMap.set(asset.groupKey, entry);
}
if (assetsLoading || balancesLoading) {
return (
<div className="flex justify-center items-center py-12">
@ -112,6 +124,15 @@ export const AssetsList: FC = () => {
const keyLabel =
groupBy === TapBalanceGroupBy.GroupKey ? 'Group key' : 'Asset ID';
const priceEntry =
priceMap.get(entry.groupKey || '') ||
priceMap.get(entry.assetId || '');
const usdValue =
priceEntry && entry.balance
? (Number(entry.balance) / 10 ** priceEntry.precision) *
priceEntry.usd
: null;
return (
<Card key={`${keyValue}-${i}`}>
<CardContent className="p-4">
@ -132,8 +153,25 @@ export const AssetsList: FC = () => {
</div>
<div className="flex flex-col items-end gap-1">
<span className="text-lg font-semibold">
{entry.balance || '0'}
{priceEntry
? (
Number(entry.balance) /
10 ** priceEntry.precision
).toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})
: entry.balance || '0'}
</span>
{usdValue != null && (
<span className="text-xs text-muted-foreground">
$
{usdValue.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</span>
)}
</div>
</div>
</CardContent>

View file

@ -47,6 +47,10 @@ export const getSupportedAssetsQuery = gql`
description
precision
type
prices {
id
usd
}
taproot_asset_details {
asset_id
group_key

View file

@ -882,6 +882,7 @@ describe('TapdResolver trading queries', () => {
precision: 2,
assetId: undefined,
groupKey: 'tapGroupKey1',
prices: null,
});
});
@ -922,6 +923,7 @@ describe('TapdResolver trading queries', () => {
precision: 0,
assetId: 'tapAssetId2',
groupKey: undefined,
prices: null,
});
});
@ -982,6 +984,7 @@ describe('TapdResolver trading queries', () => {
precision: 0,
assetId: undefined,
groupKey: undefined,
prices: null,
});
});
});

View file

@ -77,6 +77,7 @@ interface TradeApiSupportedAsset {
description?: string;
precision?: number;
taproot_asset_details?: { asset_id?: string; group_key?: string };
prices?: { id?: string; usd?: number };
}
const ASSET_TYPE_MAP: Record<string, TapAssetType> = {
@ -767,6 +768,7 @@ export class TapdResolver {
precision: a.precision ?? 0,
assetId: a.taproot_asset_details?.asset_id,
groupKey: a.taproot_asset_details?.group_key,
prices: a.prices ?? null,
})),
totalCount: assets.total_count,
};

View file

@ -1,5 +1,6 @@
import {
Field,
Float,
InputType,
Int,
ObjectType,
@ -416,6 +417,15 @@ export class TapTradeOfferList {
totalCount: number;
}
@ObjectType()
export class TapAssetPrice {
@Field({ nullable: true })
id?: string;
@Field(() => Float, { nullable: true })
usd?: number;
}
@ObjectType()
export class TapSupportedAsset {
@Field()
@ -435,6 +445,9 @@ export class TapSupportedAsset {
@Field({ nullable: true })
groupKey?: string;
@Field(() => TapAssetPrice, { nullable: true })
prices?: TapAssetPrice;
}
@ObjectType()