diff --git a/packages/app-extension/src/components/Unlocked/TokenBalances/index.tsx b/packages/app-extension/src/components/Unlocked/TokenBalances/index.tsx
index 672f8a3..c16f4dd 100644
--- a/packages/app-extension/src/components/Unlocked/TokenBalances/index.tsx
+++ b/packages/app-extension/src/components/Unlocked/TokenBalances/index.tsx
@@ -58,8 +58,9 @@ export function TokenBalances() {
const swapEnabled = blockchain === Blockchain.SOLANA && !isDevnet;
// Use SOLANA or X1 as providerId based on the connection URL
+ // Note: Backpack GraphQL API accepts "SOLANA" without network suffix
const providerId = isSolanaNetwork
- ? "SOLANA-mainnet"
+ ? "SOLANA"
: (blockchain.toUpperCase() as ProviderId);
return (
diff --git a/packages/app-extension/src/components/Unlocked/index.tsx b/packages/app-extension/src/components/Unlocked/index.tsx
index 9f70abf..c55d206 100644
--- a/packages/app-extension/src/components/Unlocked/index.tsx
+++ b/packages/app-extension/src/components/Unlocked/index.tsx
@@ -1,4 +1,4 @@
-import { lazy, Suspense } from "react";
+import { lazy, Suspense, useMemo } from "react";
import { ApolloProvider } from "@apollo/client";
import { BACKPACK_CONFIG_VERSION, createApolloClient } from "@coral-xyz/common";
import { Loading } from "@coral-xyz/react-common";
@@ -35,10 +35,17 @@ function Bootstrap() {
function WithApollo({ children }: { children: any }) {
const headers = useApolloClientHeaders();
- const apolloClient = createApolloClient(
- "backpack-extension",
- BACKPACK_CONFIG_VERSION,
- headers
+
+ // Memoize Apollo Client to prevent recreation on every render
+ // Only recreate when headers actually change
+ const apolloClient = useMemo(
+ () => createApolloClient(
+ "backpack-extension",
+ BACKPACK_CONFIG_VERSION,
+ headers
+ ),
+ [headers]
);
+
return {children};
}
diff --git a/packages/common/src/apollo/index.ts b/packages/common/src/apollo/index.ts
index 76c2f45..9a86c49 100644
--- a/packages/common/src/apollo/index.ts
+++ b/packages/common/src/apollo/index.ts
@@ -14,7 +14,7 @@ import {
import { RetryLink } from "@apollo/client/link/retry";
import { LocalStorageWrapper, persistCacheSync } from "apollo3-cache-persist";
-import { BACKEND_API_URL, X1_JSON_SERVER_URL } from "../constants";
+import { BACKEND_API_URL, X1_JSON_SERVER_URL, BACKPACK_GRAPHQL_API_URL } from "../constants";
const cache = new InMemoryCache({
addTypename: true,
@@ -276,7 +276,7 @@ export function createApolloClient(
headers?: Record
): ApolloClient {
const httpLink = createHttpLink({
- uri: `${BACKEND_API_URL}/`,
+ uri: BACKPACK_GRAPHQL_API_URL, // Use official Backpack GraphQL API
headers,
});
@@ -285,7 +285,7 @@ export function createApolloClient(
console.log("🚀 GraphQL Query:", operation.operationName);
console.log("📋 Query:", operation.query.loc?.source.body);
console.log("🔧 Variables:", JSON.stringify(operation.variables, null, 2));
- console.log("🌐 URL:", BACKEND_API_URL);
+ console.log("🌐 GraphQL URL:", BACKPACK_GRAPHQL_API_URL);
return forward(operation).map((response) => {
console.log("✅ Response for", operation.operationName, ":", response);
return response;
diff --git a/packages/common/src/constants.ts b/packages/common/src/constants.ts
index 34f5573..1d2efb3 100644
--- a/packages/common/src/constants.ts
+++ b/packages/common/src/constants.ts
@@ -441,6 +441,8 @@ export const BACKEND_API_URL_PROD = "http://162.250.126.66:4000";
export const BACKEND_API_URL_DEV = "http://localhost:4000";
export const BACKEND_API_URL = BACKEND_API_URL_PROD; // Default for build - use production
export const X1_JSON_SERVER_URL = "http://162.250.126.66:4000";
+// Official Backpack GraphQL API endpoint for Solana token data
+export const BACKPACK_GRAPHQL_API_URL = "https://backpack-api.xnfts.dev/v2/graphql";
export const MESSAGING_COMMUNICATION_PUSH = "MESSAGING_COMMUNICATION_PUSH";
export const MESSAGING_COMMUNICATION_FETCH = "MESSAGINyarG_COMMUNICATION_FETCH";
export const MESSAGING_COMMUNICATION_FETCH_RESPONSE =
diff --git a/packages/data-components/src/apollo/gql.ts b/packages/data-components/src/apollo/gql.ts
new file mode 100644
index 0000000..7d7613c
--- /dev/null
+++ b/packages/data-components/src/apollo/gql.ts
@@ -0,0 +1,20 @@
+import { DocumentNode } from "@apollo/client";
+import { TypedDocumentNode as DocumentTypeDecoration } from "@graphql-typed-document-node/core";
+
+/**
+ * The gql function is used to parse GraphQL queries into a document that can be used by Apollo Client.
+ */
+export type TypedDocumentString =
+ string & {
+ __apiType?: DocumentTypeDecoration;
+ };
+
+export function gql(source: TemplateStringsArray): DocumentNode;
+export function gql(source: string): DocumentNode;
+export function gql(source: string | TemplateStringsArray): DocumentNode {
+ const doc = Array.isArray(source) ? source[0] : source;
+
+ // Use require to avoid bundling issues
+ const { parse } = require("graphql");
+ return parse(doc);
+}
diff --git a/packages/data-components/src/apollo/graphql.ts b/packages/data-components/src/apollo/graphql.ts
index ca36d5e..253d7c2 100644
--- a/packages/data-components/src/apollo/graphql.ts
+++ b/packages/data-components/src/apollo/graphql.ts
@@ -1,9 +1,94 @@
-// Stub GraphQL types - GraphQL removed
-export type ProviderId = "SOLANA" | "ETHEREUM" | "X1";
-export type Transaction = any;
-export type TokenBalance = any;
-export type GetTokenBalancesQuery = any;
+// GraphQL Types for Backpack API
+export type ProviderId =
+ // Backpack GraphQL API (https://backpack-api.xnfts.dev/v2/graphql) accepts:
+ | "SOLANA" // ✅ For all Solana networks (mainnet/devnet/testnet)
+ | "ETHEREUM" // ✅ For all Ethereum networks
+ // X1 REST API (http://162.250.126.66:4000) accepts:
+ | "X1" // ✅ X1 blockchain (legacy format)
+ | "X1-mainnet" // ✅ X1 mainnet (with network suffix)
+ | "X1-testnet" // ✅ X1 testnet (with network suffix)
+ // TypeScript compatibility (NOT accepted by Backpack GraphQL API):
+ | "SOLANA-mainnet" // ⚠️ Type-only, use "SOLANA" for actual API calls
+ | "SOLANA-devnet" // ⚠️ Type-only, use "SOLANA" for actual API calls
+ | "SOLANA-testnet" // ⚠️ Type-only, use "SOLANA" for actual API calls
+ | "ETHEREUM-mainnet" // ⚠️ Type-only, use "ETHEREUM" for actual API calls
+ | "ETHEREUM-goerli" // ⚠️ Type-only, use "ETHEREUM" for actual API calls
+ | "ETHEREUM-sepolia";// ⚠️ Type-only, use "ETHEREUM" for actual API calls
+
+export type GetTokenBalancesQueryVariables = {
+ address: string;
+ providerId: ProviderId;
+};
+
+export type TokenListEntry = {
+ __typename?: "TokenListEntry";
+ id: string;
+ address: string;
+ decimals: number;
+ logo?: string | null;
+ name: string;
+ symbol: string;
+};
+
+export type MarketData = {
+ __typename?: "MarketData";
+ id: string;
+ percentChange?: number | null;
+ price?: number | null;
+ value?: number | null;
+ valueChange?: number | null;
+};
+
+export type TokenBalance = {
+ __typename?: "TokenBalance";
+ id: string;
+ address: string;
+ amount: string;
+ decimals: number;
+ displayAmount: string;
+ marketData?: MarketData | null;
+ token: string;
+ tokenListEntry?: TokenListEntry | null;
+};
+
+export type TokenBalanceEdge = {
+ __typename?: "TokenBalanceEdge";
+ node: TokenBalance;
+};
+
+export type TokenBalanceConnection = {
+ __typename?: "TokenBalanceConnection";
+ edges: TokenBalanceEdge[];
+};
+
+export type BalanceAggregate = {
+ __typename?: "BalanceAggregate";
+ id: string;
+ percentChange?: number | null;
+ value: number;
+ valueChange?: number | null;
+};
+
+export type Balances = {
+ __typename?: "Balances";
+ id: string;
+ aggregate?: BalanceAggregate | null;
+ tokens?: TokenBalanceConnection | null;
+};
+
+export type Wallet = {
+ __typename?: "Wallet";
+ id: string;
+ balances?: Balances | null;
+};
+
+export type GetTokenBalancesQuery = {
+ __typename?: "Query";
+ wallet?: Wallet | null;
+};
+
+// Additional query types for other components
export type GetTransactionsQuery = any;
-export type GetNftSpotlightAggregateQuery = any;
export type GetTokensForWalletDetailsQuery = any;
+export type GetNftSpotlightAggregateQuery = any;
export type GetCollectiblesQuery = any;
diff --git a/packages/data-components/src/apollo/index.ts b/packages/data-components/src/apollo/index.ts
index 04dc0f7..d80e8b6 100644
--- a/packages/data-components/src/apollo/index.ts
+++ b/packages/data-components/src/apollo/index.ts
@@ -1,3 +1,2 @@
-// Stub apollo client - GraphQL removed
-export const gql = (_query: any) => null;
-export const useApolloClient = () => ({ query: () => Promise.resolve({ data: null }) });
+export { gql } from "./gql";
+export * from "./graphql";
diff --git a/packages/data-components/src/components/Balances/index.tsx b/packages/data-components/src/components/Balances/index.tsx
index 8d5e6b0..56b6a50 100644
--- a/packages/data-components/src/components/Balances/index.tsx
+++ b/packages/data-components/src/components/Balances/index.tsx
@@ -22,7 +22,9 @@ import {
} from "./BalanceSummary";
import { BalancesTable } from "./BalancesTable";
import type { ResponseBalanceSummary, ResponseTokenBalance } from "./utils";
+import { gql } from "../../apollo";
import type { ProviderId } from "../../apollo/graphql";
+import { usePolledSuspenseQuery } from "../../hooks";
import type { DataComponentScreenProps } from "../common";
export {
@@ -33,7 +35,52 @@ export {
export { BalancesTable } from "./BalancesTable";
export type { ResponseBalanceSummary, ResponseTokenBalance };
-const DEFAULT_POLLING_INTERVAL_SECONDS = 1;
+const DEFAULT_POLLING_INTERVAL_SECONDS = 60;
+
+// GraphQL query for Solana token balances from Backpack API
+export const GET_TOKEN_BALANCES_QUERY = gql(`
+ query GetTokenBalances($address: String!, $providerId: ProviderID!) {
+ wallet(address: $address, providerId: $providerId) {
+ id
+ balances {
+ id
+ aggregate {
+ id
+ percentChange
+ value
+ valueChange
+ }
+ tokens {
+ edges {
+ node {
+ id
+ address
+ amount
+ decimals
+ displayAmount
+ marketData {
+ id
+ percentChange
+ price
+ value
+ valueChange
+ }
+ token
+ tokenListEntry {
+ id
+ address
+ decimals
+ logo
+ name
+ symbol
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+`);
export type TokenBalancesProps = DataComponentScreenProps & {
address: string;
@@ -86,201 +133,212 @@ function _TokenBalances({
hiddenTokenAddresses(providerId.toLowerCase() as Blockchain)
);
- // Get connection URL to detect which network we're on
- // Always use Blockchain.X1 because the network toggle changes X1's RPC URL,
- // not a separate Solana blockchain config
- const connectionUrl = useBlockchainConnectionUrl(Blockchain.X1);
+ // Determine blockchain from providerId
+ const blockchain = providerId.includes("SOLANA")
+ ? Blockchain.SOLANA
+ : providerId.includes("ETHEREUM")
+ ? Blockchain.ETHEREUM
+ : Blockchain.X1;
+
+ // Get connection URL for the correct blockchain
+ const connectionUrl = useBlockchainConnectionUrl(blockchain);
const apiUrl = useRecoilValue(backendApiUrl);
- const [rawBalances, setRawBalances] = useState([]);
+ // Detect if this is a Solana network or X1 network
+ const isSolanaNetwork = blockchain === Blockchain.SOLANA;
+ const isX1Network = blockchain === Blockchain.X1;
+
+ // Determine the correct providerId based on blockchain
+ let finalProviderId = providerId;
+
+ // For Solana: Use simple "SOLANA" - Backpack GraphQL API doesn't support network suffixes
+ if (blockchain === Blockchain.SOLANA) {
+ finalProviderId = "SOLANA" as ProviderId;
+ }
+ // For X1: Detect network from connection URL (X1 REST API supports network suffixes)
+ else if (connectionUrl && connectionUrl.includes("x1.xyz")) {
+ if (connectionUrl.includes("testnet")) {
+ finalProviderId = "X1-testnet" as ProviderId;
+ } else if (connectionUrl.includes("mainnet")) {
+ finalProviderId = "X1-mainnet" as ProviderId;
+ }
+ }
+
+ // For Solana networks: Use GraphQL
+ // For X1 networks: Use REST API (x1-json-server)
+ return isSolanaNetwork ? (
+
+ ) : (
+
+ );
+}
+// Solana token balances using GraphQL (official Backpack API)
+function SolanaTokenBalances({
+ address,
+ fetchPolicy,
+ onItemClick,
+ pollingIntervalSeconds,
+ providerId,
+ summaryStyle,
+ tableFooterComponent,
+ widgets,
+ hidden,
+}: Omit & {
+ hidden: string[] | null;
+}) {
+ const { data, error } = usePolledSuspenseQuery<
+ import("../../apollo/graphql").GetTokenBalancesQuery,
+ import("../../apollo/graphql").GetTokenBalancesQueryVariables,
+ any
+ >(
+ pollingIntervalSeconds ?? DEFAULT_POLLING_INTERVAL_SECONDS,
+ GET_TOKEN_BALANCES_QUERY,
+ {
+ fetchPolicy,
+ errorPolicy: "all",
+ variables: {
+ address,
+ providerId,
+ },
+ }
+ );
+
+ // Log GraphQL errors for debugging
useEffect(() => {
- const fetchBalances = async () => {
- try {
- console.log(
- "🔵 [TokenBalances] ========== FETCH BALANCES START =========="
- );
- console.log("🔵 [TokenBalances] Input providerId:", providerId);
- console.log("🔵 [TokenBalances] ConnectionURL:", connectionUrl);
-
- // Determine the correct providerId based on the connection URL
- // Since we treat Solana networks as RPC alternatives for X1 wallets,
- // we need to detect the network from the URL, not the blockchain type
- let finalProviderId = providerId;
-
- if (connectionUrl) {
- console.log("🔵 [TokenBalances] Checking connection URL...");
- console.log(
- "🔵 [TokenBalances] URL includes 'solana.com'?",
- connectionUrl.includes("solana.com")
- );
- console.log(
- "🔵 [TokenBalances] URL includes 'solana-mainnet.quiknode.pro'?",
- connectionUrl.includes("solana-mainnet.quiknode.pro")
- );
- console.log(
- "🔵 [TokenBalances] URL includes 'solana'?",
- connectionUrl.includes("solana")
- );
- console.log(
- "🔵 [TokenBalances] URL includes 'x1.xyz'?",
- connectionUrl.includes("x1.xyz")
- );
-
- // Check for Solana networks first (including QuickNode)
- if (connectionUrl.includes("solana")) {
- console.log("🟢 [TokenBalances] Detected SOLANA network!");
- // Check mainnet first (must come before testnet check)
- if (
- connectionUrl.includes("mainnet") ||
- connectionUrl.includes("solana-mainnet")
- ) {
- finalProviderId = "SOLANA-mainnet" as ProviderId;
- console.log("🟢 [TokenBalances] Set to SOLANA-mainnet");
- } else if (connectionUrl.includes("devnet")) {
- finalProviderId = "SOLANA-devnet" as ProviderId;
- console.log("🟢 [TokenBalances] Set to SOLANA-devnet");
- } else if (connectionUrl.includes("testnet")) {
- finalProviderId = "SOLANA-testnet" as ProviderId;
- console.log("🟢 [TokenBalances] Set to SOLANA-testnet");
- }
- }
- // Check for X1 networks
- else if (connectionUrl.includes("x1.xyz")) {
- console.log("🟡 [TokenBalances] Detected X1 network!");
- if (connectionUrl.includes("testnet")) {
- finalProviderId = "X1-testnet" as ProviderId;
- console.log("🟡 [TokenBalances] Set to X1-testnet");
- } else if (connectionUrl.includes("mainnet")) {
- finalProviderId = "X1-mainnet" as ProviderId;
- console.log("🟡 [TokenBalances] Set to X1-mainnet");
- }
- }
+ if (error) {
+ console.error("❌ [SolanaTokenBalances] GraphQL Error:", error);
+ }
+ }, [error]);
+
+ const { balances, omissions } = useMemo<{
+ balances: ResponseTokenBalance[];
+ omissions: { value: number; valueChange: number };
+ }>(() => {
+ let balances =
+ data?.wallet?.balances?.tokens?.edges.map((e) => e.node) ?? [];
+
+ const omissions = { value: 0, valueChange: 0 };
+ if (hidden && hidden.length > 0) {
+ balances = balances.filter((b) => {
+ if (hidden.includes(b.token)) {
+ omissions.value += b.marketData?.value ?? 0;
+ omissions.valueChange += b.marketData?.valueChange ?? 0;
+ return false;
}
+ return true;
+ });
+ }
- console.log("🔵 [TokenBalances] Final providerId:", finalProviderId);
+ return { balances, omissions };
+ }, [data, hidden]);
- const url = `${apiUrl}/wallet/${address}?providerId=${finalProviderId}`;
- console.log("🌐 [TokenBalances] Fetching from:", url);
+ const aggregate: ResponseBalanceSummary = useMemo(() => {
+ const baseAggregate = data?.wallet?.balances?.aggregate;
+ const aggregate: ResponseBalanceSummary = {
+ id: baseAggregate?.id ?? "",
+ percentChange: baseAggregate?.percentChange ?? 0,
+ value: (baseAggregate?.value ?? 0) - omissions.value,
+ valueChange: (baseAggregate?.valueChange ?? 0) - omissions.valueChange,
+ };
+ return aggregate;
+ }, [data, omissions]);
+ return (
+
+
+ {widgets}
+
+
+ );
+}
+
+// X1 token balances using REST API (x1-json-server)
+function X1TokenBalances({
+ address,
+ pollingIntervalSeconds,
+ onItemClick,
+ providerId,
+ summaryStyle,
+ tableFooterComponent,
+ widgets,
+ hidden,
+ connectionUrl,
+ apiUrl,
+}: Omit & {
+ hidden: string[] | null;
+ connectionUrl: string | null;
+ apiUrl: string;
+}) {
+ const [rawBalances, setRawBalances] = useState([]);
+
+ useEffect(() => {
+ const fetchBalances = async () => {
+ try {
+ const url = `${apiUrl}/wallet/${address}?providerId=${providerId}`;
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
- console.log("✅ [TokenBalances] JSON Response:", data);
-
- // Detect if we're on a Solana network by checking the connection URL
- const isSolanaNetwork = connectionUrl?.includes("solana") || false;
- console.log("🔵 [TokenBalances] isSolanaNetwork:", isSolanaNetwork);
const nativeMintAddress = "11111111111111111111111111111111";
// Transform JSON server response to token format
- const transformedTokens = data.tokens.map(
- (token: any, index: number) => {
- console.log(`🔵 [TokenBalances] Processing token ${index}:`, token);
-
- // Check if this is the native token (SOL/XNT)
- const isNativeToken = token.mint === nativeMintAddress;
- console.log(
- `🔵 [TokenBalances] Token ${index} - isNativeToken:`,
- isNativeToken
- );
- console.log(
- `🔵 [TokenBalances] Token ${index} - mint:`,
- token.mint
- );
- console.log(
- `🔵 [TokenBalances] Token ${index} - expected mint:`,
- nativeMintAddress
- );
-
- // Determine display values based on network
- let displaySymbol = token.symbol;
- let displayName = token.name;
- let displayLogo = token.logo;
-
- console.log(
- `🔵 [TokenBalances] Token ${index} - Original symbol:`,
- displaySymbol
- );
- console.log(
- `🔵 [TokenBalances] Token ${index} - Original name:`,
- displayName
- );
- console.log(
- `🔵 [TokenBalances] Token ${index} - Original logo:`,
- displayLogo
- );
-
- if (isNativeToken) {
- console.log(
- `🔵 [TokenBalances] Token ${index} - Is native token, checking network...`
- );
- if (isSolanaNetwork) {
- console.log(
- `🟢 [TokenBalances] Token ${index} - SOLANA network detected, setting SOL values`
- );
- // On Solana networks, show SOL
- displaySymbol = "SOL";
- displayName = "Solana Native Token";
- displayLogo = "solana.png";
- } else {
- console.log(
- `🟡 [TokenBalances] Token ${index} - X1 network detected, setting XNT values`
- );
- // On X1 networks, show XNT
- displaySymbol = "XNT";
- displayName = token.name;
- displayLogo = "x1.png";
- }
- }
-
- console.log(
- `🔵 [TokenBalances] Token ${index} - Final symbol:`,
- displaySymbol
- );
- console.log(
- `🔵 [TokenBalances] Token ${index} - Final name:`,
- displayName
- );
- console.log(
- `🔵 [TokenBalances] Token ${index} - Final logo:`,
- displayLogo
- );
-
- return {
- id: token.mint,
- address: token.mint,
- amount: Math.floor(
- token.balance * Math.pow(10, token.decimals)
- ).toString(),
- decimals: token.decimals,
- displayAmount: token.balance.toString(),
- token: token.mint,
- tokenListEntry: {
- id: displaySymbol.toLowerCase(),
- address: token.mint,
- decimals: token.decimals,
- logo: displayLogo,
- name: displayName,
- symbol: displaySymbol,
- },
- marketData: {
- id: `${displaySymbol.toLowerCase()}-market`,
- price: token.price,
- value: token.valueUSD,
- percentChange: 0,
- valueChange: 0,
- },
- };
- }
- );
+ const transformedTokens = data.tokens.map((token: any) => ({
+ id: token.mint,
+ address: token.mint,
+ amount: Math.floor(
+ token.balance * Math.pow(10, token.decimals)
+ ).toString(),
+ decimals: token.decimals,
+ displayAmount: token.balance.toString(),
+ token: token.mint,
+ tokenListEntry: {
+ id: token.symbol.toLowerCase(),
+ address: token.mint,
+ decimals: token.decimals,
+ logo: token.logo,
+ name: token.name,
+ symbol: token.symbol,
+ },
+ marketData: {
+ id: `${token.symbol.toLowerCase()}-market`,
+ price: token.price,
+ value: token.valueUSD,
+ percentChange: 0,
+ valueChange: 0,
+ },
+ }));
setRawBalances(transformedTokens);
} catch (error) {
- console.error("❌ [TokenBalances] Fetch error:", error);
+ console.error("❌ [X1TokenBalances] Fetch error:", error);
setRawBalances([]);
}
};
@@ -295,14 +353,8 @@ function _TokenBalances({
return () => clearInterval(interval);
}
return undefined;
- }, [address, providerId, pollingIntervalSeconds, connectionUrl, apiUrl]);
-
- /**
- * Memoized value of the individual wallet token balances that
- * returned from the REST API. Also calculates the
- * monetary value and value change to be omitted from the total balance
- * aggregation based on the user's hidden token settings.
- */
+ }, [address, providerId, pollingIntervalSeconds, apiUrl]);
+
const { balances, omissions } = useMemo<{
balances: ResponseTokenBalance[];
omissions: { value: number; valueChange: number };
@@ -310,37 +362,31 @@ function _TokenBalances({
let balances = rawBalances;
// Override native token price to $1.00 for X1 blockchain (XNT)
- // For Solana, use the market price from the server
- const isX1Network = connectionUrl?.includes("x1.xyz") || false;
- const nativeMintAddress = "11111111111111111111111111111111"; // Native token address for SVM chains
-
- if (isX1Network) {
- balances = balances.map((balance) => {
- // Check if this is the native XNT token
- if (
- balance.token === nativeMintAddress &&
- balance.tokenListEntry?.symbol === "XNT"
- ) {
- const amount = parseFloat(balance.displayAmount || "0");
- const fixedPrice = 1.0;
- const fixedValue = amount * fixedPrice;
-
- return {
- ...balance,
- marketData: balance.marketData
- ? {
- ...balance.marketData,
- price: fixedPrice,
- value: fixedValue,
- percentChange: 0, // No change for fixed price
- valueChange: 0,
- }
- : null,
- };
- }
- return balance;
- });
- }
+ const nativeMintAddress = "11111111111111111111111111111111";
+ balances = balances.map((balance) => {
+ if (
+ balance.token === nativeMintAddress &&
+ balance.tokenListEntry?.symbol === "XNT"
+ ) {
+ const amount = parseFloat(balance.displayAmount || "0");
+ const fixedPrice = 1.0;
+ const fixedValue = amount * fixedPrice;
+
+ return {
+ ...balance,
+ marketData: balance.marketData
+ ? {
+ ...balance.marketData,
+ price: fixedPrice,
+ value: fixedValue,
+ percentChange: 0,
+ valueChange: 0,
+ }
+ : null,
+ };
+ }
+ return balance;
+ });
const omissions = { value: 0, valueChange: 0 };
if (hidden && hidden.length > 0) {
@@ -355,12 +401,8 @@ function _TokenBalances({
}
return { balances, omissions };
- }, [rawBalances, hidden, providerId, connectionUrl]);
+ }, [rawBalances, hidden]);
- /**
- * Memoized value of the inner balance summary aggregate
- * calculated from the token balances.
- */
const aggregate: ResponseBalanceSummary = useMemo(() => {
const totalValue = balances.reduce(
(sum, b) => sum + (b.marketData?.value ?? 0),
@@ -371,12 +413,13 @@ function _TokenBalances({
0
);
- return {
+ const aggregate: ResponseBalanceSummary = {
id: "",
percentChange: totalValue > 0 ? (totalValueChange / totalValue) * 100 : 0,
value: totalValue,
valueChange: totalValueChange,
};
+ return aggregate;
}, [balances]);
return (
diff --git a/packages/data-components/src/components/Balances/utils.ts b/packages/data-components/src/components/Balances/utils.ts
index ba26c78..80386fa 100644
--- a/packages/data-components/src/components/Balances/utils.ts
+++ b/packages/data-components/src/components/Balances/utils.ts
@@ -1,8 +1,11 @@
import type { GetTokenBalancesQuery } from "../../apollo/graphql";
-export type ResponseBalanceSummary = NonNullable<
- NonNullable["balances"]
->["aggregate"];
+export type ResponseBalanceSummary = {
+ id: string;
+ percentChange: number;
+ value: number;
+ valueChange: number;
+};
export type ResponseTokenBalance = NonNullable<
NonNullable<