diff --git a/v2/features/stats/JarStats.tsx b/v2/features/stats/JarStats.tsx index e7809508..42a31110 100644 --- a/v2/features/stats/JarStats.tsx +++ b/v2/features/stats/JarStats.tsx @@ -3,13 +3,18 @@ import { useSelector } from "react-redux"; import { PickleModelJson } from "picklefinance-core"; import { AssetWithData, CoreSelectors } from "v2/store/core"; -import type { JarChartData, SetFunction } from "v2/types"; +import type { JarChartData, SetFunction, UserTx } from "v2/types"; import ChartContainer from "v2/features/stats/jar/ChartContainer"; -import DocContainer from "v2/features/stats/jar/DocContainer"; +import DocContainer, { RelatedTokens } from "v2/features/stats/jar/DocContainer"; import RevTableContainer from "v2/features/stats/jar/RevTableContainer"; import FarmsTable from "v2/features/farms/FarmsTable"; import { JarSelectData } from "./JarSelect"; import { readyState } from "pages/stats"; +import { useAccount } from "v2/hooks"; +import TxHistoryTable from "./jar/userHistory/TxHistoryContainer"; +import { JarDefinition } from "picklefinance-core/lib/model/PickleModelJson"; +import { useTranslation } from "next-i18next"; +import TxHistoryContainer from "./jar/userHistory/TxHistoryContainer"; const JarStats: FC<{ core: PickleModelJson.PickleModelJson | undefined; @@ -18,14 +23,28 @@ const JarStats: FC<{ setReady: SetFunction; page: "platform" | "chain" | "jar" | undefined; }> = ({ core, jar, ready, setReady, page }) => { + const { t } = useTranslation("common"); + const account = useAccount(); let assets = useSelector(CoreSelectors.makeAssetsSelector({ filtered: false, paginated: false })); const [jarData, setJarData] = useState({} as JarChartData); + const [userJarHistory, setUserJarHistory] = useState([]); let asset: AssetWithData | undefined = {} as AssetWithData; + let assetJar: JarDefinition | undefined = {} as JarDefinition; if (jar && jar.value) asset = assets.find((a) => a.details.apiKey.toLowerCase() === jar.value.toLowerCase()); + if (asset) assetJar = core?.assets.jars.find((j) => j.contract == asset?.contract); + const addrs = Object.fromEntries( + Object.entries({ + User: "0xfeedc450742ac0d9bb38341d9939449e3270f76f", //account, + Jar: assetJar ? assetJar.contract.toLowerCase() : "jar not found", + Farm: assetJar && assetJar.farm ? assetJar.farm.farmAddress.toLowerCase() : "farm not found", + Null: "0x0000000000000000000000000000000000000000", + }).map(([key, value]) => [value, key]), + ); + // console.log(addrs); useEffect(() => { const getData = async (): Promise => { if (Object.keys(jar).length > 0) @@ -36,6 +55,19 @@ const JarStats: FC<{ getData(); }, [jar]); + useEffect(() => { + const getUserJarHistory = async (account: string | null | undefined): Promise => { + account = "0xfeedc450742ac0d9bb38341d9939449e3270f76f"; + account && + (await fetch(`https://api.pickle.finance/prod/protocol/userhistory/${account}`) + .then((resp) => resp.json()) + .then((jsonResp) => { + setUserJarHistory(jsonResp && jsonResp[jar.value] ? jsonResp[jar.value].reverse() : []); + })); + }; + getUserJarHistory(account); + }, [account]); + if (asset && page === "jar" && ready[page]) return ( <> @@ -46,14 +78,24 @@ const JarStats: FC<{ {jarData && jarData.documentation && } - {jarData && - jarData.revenueExpenses && - jarData.revenueExpenses.recentHarvests.length > 0 && ( - +
+ {userJarHistory && userJarHistory.length > 0 && ( + )} +
+ {jarData && jarData.documentation && ( + + )} + {jarData && + jarData.revenueExpenses && + jarData.revenueExpenses.recentHarvests.length > 0 && ( + + )} +
+
); return null; diff --git a/v2/features/stats/jar/DocContainer.tsx b/v2/features/stats/jar/DocContainer.tsx index fa3099eb..4cb22e36 100644 --- a/v2/features/stats/jar/DocContainer.tsx +++ b/v2/features/stats/jar/DocContainer.tsx @@ -16,7 +16,7 @@ const DocContainer: FC<{ docs: AssetDocumentationResult }> = ({ docs }) => { - + {/* */} ); }; @@ -98,7 +98,7 @@ const Risks: FC<{ risks: string[]; t: TFunction }> = ({ risks, t }) => ( ); -const RelatedTokens: FC<{ componentTokens: { [key: string]: string }; t: TFunction }> = ({ +export const RelatedTokens: FC<{ componentTokens: { [key: string]: string }; t: TFunction }> = ({ componentTokens, t, }) => { diff --git a/v2/features/stats/jar/userHistory/TxHistoryContainer.tsx b/v2/features/stats/jar/userHistory/TxHistoryContainer.tsx new file mode 100644 index 00000000..c8baf5c5 --- /dev/null +++ b/v2/features/stats/jar/userHistory/TxHistoryContainer.tsx @@ -0,0 +1,21 @@ +import { TFunction } from "next-i18next"; +import { FC } from "react"; +import { UserTx } from "v2/types"; +import { classNames } from "v2/utils"; +import TxHistoryTable from "./TxHistoryTable"; + +const TxHistoryContainer: FC<{ + txHistory: UserTx[]; + addrs: { [key: string]: string }; + t: TFunction; + className?: string; +}> = ({ txHistory, addrs, t, className }) => ( +
+

+ {"User History"} +

+ +
+); + +export default TxHistoryContainer; diff --git a/v2/features/stats/jar/userHistory/TxHistoryTable.tsx b/v2/features/stats/jar/userHistory/TxHistoryTable.tsx new file mode 100644 index 00000000..bf877e66 --- /dev/null +++ b/v2/features/stats/jar/userHistory/TxHistoryTable.tsx @@ -0,0 +1,47 @@ +import { FC } from "react"; +import { UserTx } from "v2/types"; +import { classNames } from "v2/utils"; +import TxTableBody from "./TxTableBody"; + +const TxHistoryTable: FC<{ + txHistory: UserTx[]; + addrs: { [key: string]: string }; + className?: string; +}> = ({ txHistory, addrs, className }) => { + return ( +
+
+
+ + + + + + + + {/* Chevron down/up column */} + + + + + +
+ {/*
+ +
*/} +
+
+
+ ); +}; + +const TxTableHeaderCell: FC<{ label: string }> = ({ label }) => ( + + {label} + +); + +export default TxHistoryTable; diff --git a/v2/features/stats/jar/userHistory/TxTableBody.tsx b/v2/features/stats/jar/userHistory/TxTableBody.tsx new file mode 100644 index 00000000..ae72ce28 --- /dev/null +++ b/v2/features/stats/jar/userHistory/TxTableBody.tsx @@ -0,0 +1,46 @@ +import { Disclosure, Transition } from "@headlessui/react"; +import { FC, Fragment } from "react"; +import { UserTx } from "v2/types"; +import { classNames } from "v2/utils"; +import TxTableRowBody from "./TxTableRowBody"; +import TxTableRowHeader from "./TxTableRowHeader"; +import TxTableSpacerRow from "./TxTableSpacerRow"; + +const TxTableBody: FC<{ txs: UserTx[]; addrs: { [key: string]: string } }> = ({ txs, addrs }) => ( + <>{txs && txs.map((tx) => )} +); + +const TxTableRow: FC<{ tx: UserTx; addrs: { [key: string]: string } }> = ({ tx, addrs }) => ( + <> + + {({ open }) => ( + <> + + + + + + + + + + + )} + + + +); + +export default TxTableBody; diff --git a/v2/features/stats/jar/userHistory/TxTableRowBody.tsx b/v2/features/stats/jar/userHistory/TxTableRowBody.tsx new file mode 100644 index 00000000..006aee8e --- /dev/null +++ b/v2/features/stats/jar/userHistory/TxTableRowBody.tsx @@ -0,0 +1,47 @@ +import { FC } from "react"; +import { UserTransfer } from "v2/types"; +import { formatNumber } from "v2/utils"; + +const TxTableRowBody: FC<{ transfers: UserTransfer[]; addrs: { [key: string]: string } }> = ({ + transfers, + addrs, +}) => ( + +
+
+ {transfers.map((t) => ( +

+ {transferToString(t, addrs)} +

+ ))} +
+
+ +); + +const transferToString = (transfer: UserTransfer, addrs: { [key: string]: string }) => { + const fromAddr = + addrs[transfer.fromAddress] || + transfer.fromAddress.slice(0, 5) + "..." + transfer.fromAddress.slice(-3); + const toAddr = + addrs[transfer.toAddress] || + transfer.toAddress.slice(0, 5) + "..." + transfer.toAddress.slice(-3); + + const burned = toAddr === "Null"; + const minted = fromAddr === "Null"; + const nTokens = + transfer.price && transfer.value + ? formatNumber(+transfer.price / transfer.value, 8) + : "an unknown number of"; + const value = transfer.value ? "(" + formatNumber(transfer.value, 2) + " USD)" : ""; + return burned + ? `${fromAddr} burned ${nTokens} tokens ${value}` + : minted + ? `${nTokens} ${value} were minted and sent to ${toAddr}` + : `${fromAddr} sent ${nTokens} tokens ${value} to ${toAddr}`; +}; + +export default TxTableRowBody; diff --git a/v2/features/stats/jar/userHistory/TxTableRowHeader.tsx b/v2/features/stats/jar/userHistory/TxTableRowHeader.tsx new file mode 100644 index 00000000..7dd8949f --- /dev/null +++ b/v2/features/stats/jar/userHistory/TxTableRowHeader.tsx @@ -0,0 +1,63 @@ +import { ChevronDownIcon } from "@heroicons/react/solid"; +import { FC, HTMLAttributes } from "react"; +import { UserTx } from "v2/types"; +import { classNames, formatDate } from "v2/utils"; + +const TxTableRowHeader: FC<{ tx: UserTx; open: boolean }> = ({ tx, open }) => { + return ( + <> + +

+ {formatDate(new Date(tx.timestamp * 1000))} +

+
+ +

+ {tx.blocknumber} +

+
+ +
+
+

+ {tx.transaction_type} +

+
+
+
+ +

+ {tx.hash.slice(0, 5) + "..." + tx.hash.slice(-3)} +

+
+ +
+
+
+ + ); +}; + +const RowCell: FC> = ({ children, className }) => ( + + {children} + +); + +export default TxTableRowHeader; diff --git a/v2/features/stats/jar/userHistory/TxTableSpacerRow.tsx b/v2/features/stats/jar/userHistory/TxTableSpacerRow.tsx new file mode 100644 index 00000000..af94b067 --- /dev/null +++ b/v2/features/stats/jar/userHistory/TxTableSpacerRow.tsx @@ -0,0 +1,11 @@ +import { FC } from "react"; + +const TxTableSpacerRow: FC = () => { + return ( + + + + ); +}; + +export default TxTableSpacerRow; diff --git a/v2/types/index.ts b/v2/types/index.ts index a1fd0ac9..e612417e 100644 --- a/v2/types/index.ts +++ b/v2/types/index.ts @@ -142,6 +142,27 @@ export interface Transfer { weiSent: string; } +export interface UserTx { + hash: string; + transaction_type: string; + chain_id: number; + timestamp: number; + blocknumber: number; + indexInBlock: number; + transfers: UserTransfer[]; +} + +export interface UserTransfer { + amount: string; + transfer_type: string; + log_index: number; + fromAddress: string; + toAddress: string; + tokenAddress: string; + price: string; + value: number; +} + export interface ApyChartData { timestamp: number; jarApr: number;