diff --git a/docs/docs/03-sdk/03-class-reference.md b/docs/docs/03-sdk/03-class-reference.md index edba76a..b166712 100644 --- a/docs/docs/03-sdk/03-class-reference.md +++ b/docs/docs/03-sdk/03-class-reference.md @@ -58,6 +58,12 @@ sprinter.getAvailableChains().then(chains => { Fetches the user's balances for specified tokens across multiple blockchains. If no tokens are specified, it fetches balances for all available tokens. +:::note + +Method will always return native tokens under `ETH` key + +::: + #### Parameters - `account`: Targeted account address. diff --git a/packages/sdk/src/api.ts b/packages/sdk/src/api.ts index 7d62a08..9f78493 100644 --- a/packages/sdk/src/api.ts +++ b/packages/sdk/src/api.ts @@ -7,6 +7,7 @@ import type { FetchOptions, FungibleToken, FungibleTokenBalance, + NativeTokenBalance, Solution, SolutionOptions, SolutionResponse, @@ -88,6 +89,21 @@ export async function getUserFungibleTokens( return response.data; } +export async function getUserNativeTokens( + address: Address, + { baseUrl, signal }: FetchOptions = {}, +): Promise { + const url = new URL( + `/accounts/${address}/assets/native`, + baseUrl || BASE_URL, + ); + const response = await fetch(url, { signal }).then( + (response) => response.json() as unknown as { data: NativeTokenBalance[] }, + ); + + return response.data; +} + export async function getSolution( { account, diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index 215090f..34fe716 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -4,6 +4,7 @@ import { getContractSolution, getSupportedChains, getUserFungibleTokens, + getUserNativeTokens, setBaseUrl, BASE_URL, getContractCallSolution, @@ -14,9 +15,9 @@ import type { ContractSolutionOptions, FetchOptions, FungibleToken, - FungibleTokenBalance, SolutionOptions, SolutionResponse, + TokenBalance, TokenSymbol, } from "./types"; @@ -56,18 +57,23 @@ class Sprinter { tokens?: FungibleToken[], options: FetchOptions = {}, ): Promise<{ - [sybol: TokenSymbol]: { balances: FungibleTokenBalance[]; total: string }; + [sybol: TokenSymbol]: { balances: TokenBalance[]; total: string }; }> { const tokenList = tokens || (await this.getAvailableTokens(options)); - const balances = await Promise.all( - tokenList.map((token) => - getUserFungibleTokens(account, token.symbol).then((balances) => ({ - symbol: token.symbol, - balances, - })), + const [balances, nativeTokens] = await Promise.all([ + Promise.all( + tokenList.map((token) => + getUserFungibleTokens(account, token.symbol, options).then( + (balances) => ({ + symbol: token.symbol, + balances, + }), + ), + ), ), - ); + getUserNativeTokens(account, options), + ]); return balances.reduce( (previousValue, { symbol, balances }) => { @@ -79,9 +85,16 @@ class Sprinter { }; return previousValue; }, - {} as { + { + ["ETH"]: { + total: nativeTokens + .reduce((prev, cur) => prev + BigInt(cur.balance), 0n) + .toString(), + balances: nativeTokens, + }, + } as { [symbol: TokenSymbol]: { - balances: FungibleTokenBalance[]; + balances: TokenBalance[]; total: string; }; }, diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index 8431f4a..4246351 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -22,12 +22,16 @@ export interface Chain { rpcurls: string[]; } -export interface FungibleTokenBalance { +export interface TokenBalance { balance: string /* big number as string*/; chainId: ChainID; tokenDecimals: number; } +export type FungibleTokenBalance = TokenBalance; + +export type NativeTokenBalance = TokenBalance; + export interface SolutionOptions { account: Address; destinationChain: ChainID; diff --git a/web/src/lib/components/Portfolio.svelte b/web/src/lib/components/Portfolio.svelte index 5dc1e76..4979dc8 100644 --- a/web/src/lib/components/Portfolio.svelte +++ b/web/src/lib/components/Portfolio.svelte @@ -13,11 +13,22 @@ const balances = $sprinter.getUserBalances($selectedAccount as Address); const chains = $sprinter.getAvailableChains(); - $: total = balances.then((b) => - Object.values(b).reduce( - (p, c) => (p += Number(fromWei(c.total, c.balances[0].tokenDecimals))), - 0 - ) + $: totalTokens = balances.then((b) => + Object.keys(b) + .filter((sybols) => !['WETH', 'ETH'].includes(sybols)) + .reduce((p, cKey) => { + const token = b[cKey]; + return (p += Number(fromWei(token.total, token.balances[0].tokenDecimals))); + }, 0) + ); + + $: totalNative = balances.then((b) => + Object.keys(b) + .filter((sybols) => ['WETH', 'ETH'].includes(sybols)) + .reduce((p, cKey) => { + const token = b[cKey]; + return (p += Number(fromWei(token.total, token.balances[0].tokenDecimals))); + }, 0) ); async function handleListClick(index: number) { @@ -35,6 +46,22 @@ }; modalStore.trigger(modal); } + + const ethLogo = 'https://scan.buildwithsygma.com/assets/icons/evm.svg'; + async function handleNativeClick() { + const networks = await chains; + const selectedBalances = (await balances)['ETH']; + + const modal: ModalSettings = { + type: 'component', + component: { ref: TokenModal }, + title: 'Ethereum', + buttonTextCancel: 'close', + value: { networks, balances: selectedBalances.balances }, + meta: { icon: ethLogo, sybol: 'ETH', decimals: 18 } + }; + modalStore.trigger(modal); + }
@@ -42,8 +69,8 @@
Balance
-
- {#await total} +
+ {#await totalTokens}
{:then result} ${result.toFixed(2)} @@ -51,6 +78,15 @@ - {JSON.stringify(error)} {/await}
+
+ {#await totalNative} +
+ {:then result} + {result.toFixed(2)} ETH + {:catch error} + - {JSON.stringify(error)} + {/await} +
{/each} {:then [tokens, balances, chains]} + handleNativeClick()} + > + +
+ {`ETH-LOGO`} +
+
+
Ethereum
+
+ + + {fromWei(balances['ETH'].total, 18)} ETH + + + + + {#each tokens as token, i} [ + ...tokens, + { + addresses: [], + decimals: 18, + logoURI: 'https://scan.buildwithsygma.com/assets/icons/evm.svg', + name: 'Ethereum', + symbol: 'ETH' + } + ]); + const allBalances = $sprinter.getUserBalances(); const chains = $sprinter.getAvailableChains(); const drawerStore = getDrawerStore();