Skip to content

Commit

Permalink
Upgrade accounting page for moloch v3 (#149) (#150)
Browse files Browse the repository at this point in the history
* Add v2 and v3 panels for each tab (#131)



* PR for #132 (#138)

* organise memos in one hook

* split useAccounting into useAccountingV2 & useAccountingV3

* Remove useAccounting hook

---------



* needs clean up

* cleaned up useAccountingV3

* [WIP] Accounting Fixes + Transaction Data  (#141)

* fix issue

* fetch tx data

* proposal data

* wip

* wip2

* fetch all transactions

* use bigint for calculating sum of in/out flows

* transactions

* cache results

* display member & proposal & proposal link

* fix proposals

* fix type

* cleanup

* set balancesWithPricesV3 data for csv export

fix

* add elapsed days and ragequit shares

* add in/out/balance USD

* rename

* fix types

* token symbol value

---------



* render v3 spoils in spoils table  (#148)

* remove V2 data from accounting and fixed type and export errors

* fetch smartInvoices and add query to fecth raids

* create formatSpoils function and raids query mvp

* unsuccessful attempt to fetch raids in useAccountingV3

* fetch invoice and raids and map them together using formatSpoils

* render formatted spoils in spoilsTable

* clean up useAccountingV3 & useFormattedDataV3

* cleanup accounting.tsx

* change file names for useFormattedData to useFormattedAccountingV3

* Minor tweaks

---------

Co-authored-by: growindiedev <[email protected]>
Co-authored-by: Abhishek Ranjan <[email protected]>
Co-authored-by: Sero <[email protected]>
Co-authored-by: vidvidvid <[email protected]>
  • Loading branch information
5 people authored Jul 29, 2024
1 parent f8f6aac commit 6e2b23d
Show file tree
Hide file tree
Showing 14 changed files with 958 additions and 141 deletions.
3 changes: 3 additions & 0 deletions apps/frontend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ NEXT_PUBLIC_WC_PROJECT_ID=
# https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens
GITHUB_API_TOKEN=
GITHUB_API_URL='https://api.github.com/graphql'

# create one for prod here: https://thegraph.com/studio/
NEXT_PUBLIC_THE_GRAPH_API_KEY=
11 changes: 8 additions & 3 deletions apps/frontend/components/BalancesTable.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { Link, Tooltip } from '@raidguild/design-system';
import { ITokenBalanceLineItem } from '@raidguild/dm-types';
import {
ITokenBalanceLineItem,
ITokenBalanceLineItemV3,
} from '@raidguild/dm-types';
import { minMaxNumberFilter, sortNumeric } from '@raidguild/dm-utils';
// @ts-expect-error - no types from RT
import { createColumnHelper } from '@tanstack/react-table';
Expand All @@ -8,10 +11,12 @@ import DataTable from './DataTable';
import TokenWithUsdValue from './TokenWithUsdValue';

interface BalancesTableProps {
data: ITokenBalanceLineItem[];
data: ITokenBalanceLineItem[] | ITokenBalanceLineItemV3[];
}

const columnHelper = createColumnHelper<ITokenBalanceLineItem>();
const columnHelper = createColumnHelper<
ITokenBalanceLineItem | ITokenBalanceLineItemV3
>();

const columns = [
columnHelper.accessor('tokenExplorerLink', {
Expand Down
7 changes: 5 additions & 2 deletions apps/frontend/components/SiteLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ interface SiteLayoutProps {
data?: any;
subheader?: ReactNode;
emptyDataPhrase?: string;
error?: Error;
error?: Error | boolean;
isLoading?: boolean;
minHeight?: string;
}
Expand Down Expand Up @@ -157,7 +157,10 @@ const SiteLayout = ({
minHeight={minHeight}
>
<Flex w='100%' justify='center' pt={40}>
<Heading size='md'>Error loading data: {error.message}</Heading>
<Heading size='md'>
Error loading data
{typeof error === 'object' && `: ${error.message}`}
</Heading>
</Flex>
</GeneralLayout>
);
Expand Down
6 changes: 3 additions & 3 deletions apps/frontend/components/TransactionsTable.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Link, Tooltip } from '@raidguild/design-system';
import { IVaultTransaction } from '@raidguild/dm-types';
import { IVaultTransaction, IVaultTransactionV2 } from '@raidguild/dm-types';
import {
formatNumber,
minMaxDateFilter,
Expand All @@ -14,14 +14,14 @@ import DataTable from './DataTable';
import TokenWithUsdValue from './TokenWithUsdValue';

interface TransactionsTableProps {
data: IVaultTransaction[];
data: IVaultTransaction[] | IVaultTransactionV2[];
}

const columnHelper = createColumnHelper<any>();

const columns = [
columnHelper.accessor('date', {
cell: (info) => info.getValue().toLocaleString(),
cell: (info) => info.getValue()?.toLocaleString(),
header: 'Date',
meta: {
dataType: 'datetime',
Expand Down
184 changes: 63 additions & 121 deletions apps/frontend/pages/accounting.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* eslint-disable no-useless-computed-key */
/* eslint-disable simple-import-sort/imports */
import {
Button,
Flex,
Expand All @@ -9,156 +9,97 @@ import {
TabPanels,
Tabs,
} from '@raidguild/design-system';
import { useAccounting, useMemberList } from '@raidguild/dm-hooks';
import {
IMember,
ITokenBalanceLineItem,
IVaultTransaction,
} from '@raidguild/dm-types';
import {
exportToCsv,
formatDate,
REGEX_ETH_ADDRESS,
} from '@raidguild/dm-utils';
import _ from 'lodash';
useMemberList,
useFormattedAccountingV3,
useAccountingV3,
} from '@raidguild/dm-hooks';
import { exportToCsv } from '@raidguild/dm-utils';
import { useSession } from 'next-auth/react';
import { NextSeo } from 'next-seo';
import Papa from 'papaparse';
import { useCallback, useMemo } from 'react';
import { useCallback } from 'react';

import _ from 'lodash';
import BalancesTable from '../components/BalancesTable';
import SiteLayout from '../components/SiteLayout';
import SpoilsTable from '../components/SpoilsTable';
import TransactionsTable from '../components/TransactionsTable';

export const Accounting = () => {
const { data: session } = useSession();

const token = _.get(session, 'token');
const { data, loading, error } = useAccounting({
token,
});

const { data: memberData } = useMemberList({
token,
limit: 1000,
});

const { balances, spoils, transactions, tokenPrices } = data;

const members = useMemo(() => {
const memberArray = _.flatten(
_.get(memberData, 'pages')
) as unknown as IMember[];
return _.keyBy(memberArray, (m: IMember) => m.ethAddress?.toLowerCase());
}, [memberData]);

const withPrices = useCallback(
<T extends ITokenBalanceLineItem | IVaultTransaction>(items: T[]) =>
items.map((t) => {
const formattedDate = formatDate(t.date);
const tokenSymbol = t.tokenSymbol?.toLowerCase();
if (
tokenPrices[tokenSymbol] &&
tokenPrices[tokenSymbol][formattedDate]
) {
return {
...t,
priceConversion: tokenPrices[tokenSymbol][formattedDate],
};
}
if (tokenSymbol.includes('xdai')) {
return {
...t,
priceConversion: 1,
};
}
return t;
}),
[tokenPrices]
);

const balancesWithPrices = useMemo(
() => withPrices(balances),
[balances, withPrices]
);
const { loading, isError, error } = useAccountingV3();

const transactionsWithPrices = useMemo(
() => withPrices(transactions),
[transactions, withPrices]
);

const transactionsWithPricesAndMembers = useMemo(
() =>
transactionsWithPrices.map((t) => {
const ethAddress = t.proposalApplicant.toLowerCase();
const m = members[ethAddress];
const memberLink = m?.ethAddress.match(REGEX_ETH_ADDRESS)
? `/members/${ethAddress}`
: undefined;

return {
...t,
memberLink,
memberName: m?.name,
memberEnsName: m?.ensName,
};
}),
[transactionsWithPrices, members]
);
const {
formattedSpoils: formattedSpoilsV3,
members,
balancesWithPrices: balancesWithPricesV3,
transactionsWithPrices: transactionsWithPricesV3,
transactionsWithPricesAndMembers: transactionsWithPricesAndMembersV3,
} = useFormattedAccountingV3(memberData);

const onExportCsv = useCallback(
(type: 'transactions' | 'balances' | 'spoils') => {
let csvString = '';
if (type === 'transactions') {
const formattedTransactions = transactionsWithPrices.map((t) => ({
['Date']: t.date,
['Tx Explorer Link']: t.txExplorerLink,
['Elapsed Days']: t.elapsedDays,
['Type']: t.type,
['Applicant']: t.proposalApplicant,
['Applicant Member']:
const formattedTransactions = transactionsWithPricesV3.map((t) => ({
Date: t.date,
'Tx Explorer Link': t.txExplorerLink,
'Elapsed Days': t.elapsedDays,
Type: t.type,
Applicant: t.proposalApplicant,
'Applicant Member':
members[t.proposalApplicant.toLowerCase()]?.name || '-',
['Shares']: t.proposalShares,
['Loot']: t.proposalLoot,
['Title']: t.proposalTitle,
['Counterparty']: t.counterparty,
['Counterparty Member']:
Shares: t.proposalShares,
Loot: t.proposalLoot,
Title: t.proposalTitle,
Counterparty: t.counterparty,
'Counterparty Member':
members[t.counterparty.toLowerCase()]?.name || '-',
['Token Symbol']: t.tokenSymbol,
['Token Decimals']: t.tokenDecimals,
['Token Address']: t.tokenAddress,
['Inflow']: t.in,
['Inflow USD']: t.priceConversion
'Token Symbol': t.tokenSymbol,
'Token Decimals': t.tokenDecimals,
'Token Address': t.tokenAddress,
Inflow: t.in,
'Inflow USD': t.priceConversion
? `$${(t.in * t.priceConversion).toLocaleString()}`
: '$-',
['Outflow']: t.out,
['Outflow USD']: t.priceConversion
Outflow: t.out,
'Outflow USD': t.priceConversion
? `$${(t.out * t.priceConversion).toLocaleString()}`
: '$-',
['Balance']: t.balance,
['Balance USD']: t.priceConversion
Balance: t.balance,
'Balance USD': t.priceConversion
? `$${(t.balance * t.priceConversion).toLocaleString()}`
: '$-',
}));
csvString = Papa.unparse(formattedTransactions);
} else if (type === 'balances') {
if (type === 'balances') {
const formattedBalances = balancesWithPrices.map((b) => ({
['Token']: b.tokenSymbol,
['Tx Explorer Link']: b.tokenExplorerLink,
['Inflow']: b.inflow.tokenValue,
['Inflow USD']: b.priceConversion
const formattedBalances = balancesWithPricesV3.map((b) => ({
Token: b.tokenSymbol,
'Tx Explorer Link': b.tokenExplorerLink,
Inflow: b.inflow.tokenValue,
'Inflow USD': b.priceConversion
? `$${(
Number(b.inflow.tokenValue) * b.priceConversion
).toLocaleString()}`
: '$-',
['Outflow']: b.outflow.tokenValue,
['Outflow USD']: b.priceConversion
Outflow: b.outflow.tokenValue,
'Outflow USD': b.priceConversion
? `$${(
Number(b.outflow.tokenValue) * b.priceConversion
).toLocaleString()}`
: '$-',
['Balance']: b.closing.tokenValue,
['Balance USD']: b.priceConversion
Balance: b.closing.tokenValue,
'Balance USD': b.priceConversion
? `$${(
Number(b.closing.tokenValue) * b.priceConversion
).toLocaleString()}`
Expand All @@ -167,19 +108,19 @@ export const Accounting = () => {
csvString = Papa.unparse(formattedBalances);
}
} else if (type === 'spoils') {
const formattedSpoils = spoils.map((s) => ({
['Date']: s.date,
['Raid']: s.raidName,
const formattedSpoils = formattedSpoilsV3.map((s) => ({
Date: s.date,
Raid: s.raidName,
// TODO: Get this dynamically from the subgraph
['Token Symbol']: 'wxDAI',
['To DAO Treasury']: `$${s.parentShare.toLocaleString()}`,
['To Raid Party']: `$${s.childShare.toLocaleString()}`,
'Token Symbol': 'wxDAI',
'To DAO Treasury': `$${s.parentShare.toLocaleString()}`,
'To Raid Party': `$${s.childShare.toLocaleString()}`,
}));
csvString = Papa.unparse(formattedSpoils);
}
exportToCsv(csvString, `raidguild-treasury-${type}`);
},
[balancesWithPrices, members, spoils, transactionsWithPrices]
[balancesWithPricesV3, members, formattedSpoilsV3, transactionsWithPricesV3]
);

return (
Expand All @@ -189,13 +130,14 @@ export const Accounting = () => {
<SiteLayout
isLoading={loading}
data={[
...transactionsWithPricesAndMembers,
...balances,
...Object.values(tokenPrices),
...transactionsWithPricesV3,
...transactionsWithPricesAndMembersV3,
...balancesWithPricesV3,
// ...Object.values(tokenPrices),
]}
subheader={<Heading>Accounting</Heading>}
emptyDataPhrase='No transactions'
error={error}
error={error && isError}
>
<Tabs align='center' colorScheme='whiteAlpha' variant='soft-rounded'>
<TabList>
Expand Down Expand Up @@ -225,7 +167,7 @@ export const Accounting = () => {
Export Balances
</Button>
</Flex>
<BalancesTable data={balancesWithPrices} />
<BalancesTable data={balancesWithPricesV3} />
</TabPanel>
<TabPanel>
<Flex
Expand All @@ -241,7 +183,7 @@ export const Accounting = () => {
Export Transactions
</Button>
</Flex>
<TransactionsTable data={transactionsWithPricesAndMembers} />
<TransactionsTable data={transactionsWithPricesV3} />
</TabPanel>
<TabPanel>
<Flex
Expand All @@ -257,7 +199,7 @@ export const Accounting = () => {
Export Spoils
</Button>
</Flex>
<SpoilsTable data={spoils} />
<SpoilsTable data={formattedSpoilsV3} />
</TabPanel>
</TabPanels>
</Tabs>
Expand Down
14 changes: 14 additions & 0 deletions libs/dm-graphql/src/queries/accounting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,17 @@ export const TRANSACTIONS_QUERY = gql`
}
}
`;

export const TRANSACTIONS_QUERY_V3 = gql`
query AccountingQuery {
raids(
where: { invoice_address: { _is_null: false } }
order_by: { created_at: desc }
) {
id
invoice_address
name
created_at
}
}
`;
5 changes: 4 additions & 1 deletion libs/dm-hooks/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { default as useAccounting } from './useAccounting';
export { default as useAccountingV2 } from './useAccountingV2';
export { default as useAccountingV3 } from './useAccountingV3';
export { default as useApplicationDetail } from './useApplicationDetail';
export {
default as useApplicationList,
Expand All @@ -15,6 +16,8 @@ export { useContacts } from './useContacts';
export { default as useContactUpdate } from './useContactUpdate';
export { default as useDashboardList } from './useDashboardList';
export { default as useDefaultTitle } from './useDefaultTitle';
export { default as useFormattedAccountingV2 } from './useFormattedAccountingV2';
export { default as useFormattedAccountingV3 } from './useFormattedAccountingV3';
export * from './useLinks';
export { default as useLinksUpdate } from './useLinksUpdate';
export { default as useMemberCreate } from './useMemberCreate';
Expand Down
Loading

0 comments on commit 6e2b23d

Please sign in to comment.