diff --git a/src/components/UserInfoTable/AccountsTable.jsx b/src/components/UserInfoTable/AccountsTable.jsx new file mode 100644 index 0000000..905e3d6 --- /dev/null +++ b/src/components/UserInfoTable/AccountsTable.jsx @@ -0,0 +1,73 @@ +import { Button } from 'antd'; +import React from 'react'; +import { + useSelectedOpenOrdersAccount, + useMarket, + useSelectedBaseCurrencyAccount, + useSelectedQuoteCurrencyAccount, +} from '../../utils/markets'; +import DataTable from '../layout/DataTable'; +import { useConnection } from '../../utils/connection'; +import { useWallet } from '../../utils/wallet'; +import { settleFunds } from '../../utils/send'; + +export default function AccountsTable({ accountBalances }) { + const baseCurrencyAccount = useSelectedBaseCurrencyAccount(); + const quoteCurrencyAccount = useSelectedQuoteCurrencyAccount(); + const connection = useConnection(); + const [, wallet] = useWallet(); + const openOrdersAccount = useSelectedOpenOrdersAccount(true); + const { market } = useMarket(); + + async function onSettleFunds() { + return await settleFunds({ + market, + openOrders: openOrdersAccount, + connection, + wallet, + baseCurrencyAccount, + quoteCurrencyAccount, + }); + } + + const columns = [ + { + title: 'Key', + dataIndex: 'key', + key: 'key', + }, + { + title: 'Coin', + dataIndex: 'coin', + key: 'coin', + }, + { + title: 'Orders', + dataIndex: 'orders', + key: 'orders', + }, + { + title: 'Unsettled', + dataIndex: 'unsettled', + key: 'unsettled', + }, + { + key: 'action', + render: () => ( +
+ +
+ ), + }, + ]; + return ( + + ); +} diff --git a/src/components/UserInfoTable/WalletBalancesTable.jsx b/src/components/UserInfoTable/WalletBalancesTable.jsx new file mode 100644 index 0000000..87a028d --- /dev/null +++ b/src/components/UserInfoTable/WalletBalancesTable.jsx @@ -0,0 +1,34 @@ +import React from 'react'; +import DataTable from '../layout/DataTable'; + +export default function WalletBalancesTable({ + loaded = false, + walletBalances, +}) { + const columns = [ + { + title: 'Key', + dataIndex: 'key', + key: 'key', + }, + { + title: 'Coin', + dataIndex: 'coin', + key: 'coin', + }, + { + title: 'Wallet Balance', + dataIndex: 'wallet', + key: 'wallet', + }, + ]; + return ( + + ); +} diff --git a/src/components/layout/DataTable.jsx b/src/components/layout/DataTable.jsx index 2c6af8c..5532a94 100644 --- a/src/components/layout/DataTable.jsx +++ b/src/components/layout/DataTable.jsx @@ -6,6 +6,7 @@ export default function DataTable({ columns, emptyLabel = 'No data', pagination = false, + loading = false, pageSize = 10, }) { const customizeRenderEmpty = () => ( @@ -27,6 +28,7 @@ export default function DataTable({ dataSource={dataSource} columns={columns} pagination={pagination ? { pagination: true, pageSize } : false} + loading={loading} /> ); diff --git a/src/pages/BalancesPage.jsx b/src/pages/BalancesPage.jsx index 0c8327c..c186cf5 100644 --- a/src/pages/BalancesPage.jsx +++ b/src/pages/BalancesPage.jsx @@ -1,14 +1,26 @@ import React from 'react'; -import { useBalancesForAllMarkets } from '../utils/markets'; +import { Tabs } from 'antd'; +import { useWalletBalancesForAllMarkets, useOpenOrderAccountBalancesForAllMarkets } from '../utils/markets'; import FloatingElement from '../components/layout/FloatingElement'; -import BalancesTable from '../components/UserInfoTable/BalancesTable'; +import WalletBalancesTable from '../components/UserInfoTable/WalletBalancesTable'; +import AccountsTable from '../components/UserInfoTable/AccountsTable'; + +const { TabPane } = Tabs; export default function BalancesPage() { - const [balances] = useBalancesForAllMarkets(); - console.log(JSON.stringify(balances)); + const [walletBalances, loadedWalletBalances] = useWalletBalancesForAllMarkets(); + const [accountBalances, accountBalancesLoaded] = useOpenOrderAccountBalancesForAllMarkets(); + return ( - + + + + + + + + ); } diff --git a/src/utils/markets.js b/src/utils/markets.js index 4dcbb29..00b8d9f 100644 --- a/src/utils/markets.js +++ b/src/utils/markets.js @@ -592,13 +592,13 @@ export function useBalances() { ]; } -export function useBalancesForAllMarkets() { +export function useWalletBalancesForAllMarkets() { const [connected, wallet] = useWallet(); const connection = useConnection(); const allMarkets = useAllMarkets(); - async function getBalancesForAllMarkets() { + async function getWalletBalancesForAllMarkets() { let balances = []; if (!connected) { return balances; @@ -612,26 +612,32 @@ export function useBalancesForAllMarkets() { } const baseCurrency = marketName.includes('/') && marketName.split('/')[0]; if (!balances.find((balance) => balance.coin === baseCurrency)) { - const baseBalance = await getBalanceForMarket( + const baseBalance = await getCurrencyBalance( market, connection, wallet, - baseCurrency, true, ); - balances.push(baseBalance); + balances.push({ + key: baseCurrency, + coin: baseCurrency, + wallet: baseBalance, + }); } const quoteCurrency = marketName.includes('/') && marketName.split('/')[1]; if (!balances.find((balance) => balance.coin === quoteCurrency)) { - const quoteBalance = await getBalanceForMarket( + const quoteBalance = await getCurrencyBalance( market, connection, wallet, - quoteCurrency, false, ); - balances.push(quoteBalance); + balances.push({ + key: quoteCurrency, + coin: quoteCurrency, + wallet: quoteBalance, + }); } } @@ -639,9 +645,9 @@ export function useBalancesForAllMarkets() { } return useAsyncData( - getBalancesForAllMarkets, + getWalletBalancesForAllMarkets, tuple( - 'getBalancesForAllMarkets', + 'getWalletBalancesForAllMarkets', connected, connection, wallet, @@ -651,16 +657,7 @@ export function useBalancesForAllMarkets() { ); } -async function getBalanceForMarket( - market, - connection, - wallet, - currency, - base = true, -) { - if (!market) { - return; - } +async function getCurrencyBalance(market, connection, wallet, base = true) { const currencyAccounts = base ? await market.findBaseTokenAccountsForOwner(connection, wallet.publicKey) : await market.findQuoteTokenAccountsForOwner(connection, wallet.publicKey); @@ -668,33 +665,90 @@ async function getBalanceForMarket( const tokenAccountBalances = await connection.getTokenAccountBalance( currencyAccount.pubkey, ); - const currencyBalances = tokenAccountBalances?.value?.uiAmount; - const openOrdersAccounts = await market.findOpenOrdersAccountsForOwner( - connection, - wallet.publicKey, - ); - const openOrdersAccount = openOrdersAccounts && openOrdersAccounts[0]; - const inOrders = base - ? openOrdersAccount?.baseTokenTotal && - openOrdersAccount?.baseTokenFree && - market.baseSplSizeToNumber( - openOrdersAccount.baseTokenTotal.sub(openOrdersAccount.baseTokenFree), - ) - : openOrdersAccount?.quoteTokenTotal && - openOrdersAccount?.quoteTokenFree && - market.quoteSplSizeToNumber( - openOrdersAccount.quoteTokenTotal.sub(openOrdersAccount.quoteTokenFree), - ); - const unsettled = base - ? openOrdersAccount?.baseTokenFree && - market.baseSplSizeToNumber(openOrdersAccount.baseTokenFree) - : openOrdersAccount?.quoteTokenFree && - market.baseSplSizeToNumber(openOrdersAccount.baseTokenFree); - return { - key: currency, - coin: currency, - wallet: currencyBalances, - orders: inOrders, - unsettled: unsettled, - }; + return tokenAccountBalances?.value?.uiAmount; +} + +export function useOpenOrderAccountBalancesForAllMarkets() { + const [connected, wallet] = useWallet(); + + const connection = useConnection(); + const allMarkets = useAllMarkets(); + + async function getOpenOrderAccountsForAllMarkets() { + let accounts = []; + if (!connected) { + return accounts; + } + + let marketData; + for (marketData of allMarkets) { + const { market, marketName } = marketData; + if (!market) { + return accounts; + } + const openOrderAccounts = await market.findOpenOrdersAccountsForOwner( + connection, + wallet.publicKey, + ); + if (!openOrderAccounts) { + continue; + } + const baseCurrency = marketName.includes('/') && marketName.split('/')[0]; + const quoteCurrency = + marketName.includes('/') && marketName.split('/')[1]; + + const openOrderAccountBalances = []; + openOrderAccounts.forEach((openOrdersAccount) => { + const inOrdersBase = + openOrdersAccount?.baseTokenTotal && + openOrdersAccount?.baseTokenFree && + market.baseSplSizeToNumber( + openOrdersAccount.baseTokenTotal.sub( + openOrdersAccount.baseTokenFree, + ), + ); + const inOrdersQuote = + openOrdersAccount?.quoteTokenTotal && + openOrdersAccount?.quoteTokenFree && + market.baseSplSizeToNumber( + openOrdersAccount.quoteTokenTotal.sub( + openOrdersAccount.quoteTokenFree, + ), + ); + const unsettledBase = + openOrdersAccount?.baseTokenFree && + market.baseSplSizeToNumber(openOrdersAccount.baseTokenFree); + const unsettledQuote = + openOrdersAccount?.quoteTokenFree && + market.baseSplSizeToNumber(openOrdersAccount.quoteTokenFree); + openOrderAccountBalances.push({ + key: baseCurrency, + coin: baseCurrency, + orders: inOrdersBase, + unsettled: unsettledBase, + }); + openOrderAccountBalances.push({ + key: quoteCurrency, + coin: quoteCurrency, + orders: inOrdersQuote, + unsettled: unsettledQuote, + }); + }); + accounts = accounts.concat(openOrderAccountBalances); + } + + return accounts; + } + + return useAsyncData( + getOpenOrderAccountsForAllMarkets, + tuple( + 'getOpenOrderAccountsForAllMarkets', + connected, + connection, + wallet, + allMarkets, + ), + { refreshInterval: _SLOW_REFRESH_INTERVAL }, + ); }