From 819e30b03a80b0ec299ae5306ee78536c7efeb49 Mon Sep 17 00:00:00 2001 From: Tom Robiquet Date: Wed, 12 Jul 2023 16:50:50 +0200 Subject: [PATCH 1/3] fix identity display --- components/portfolio/PortfolioIdentity.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/portfolio/PortfolioIdentity.tsx b/components/portfolio/PortfolioIdentity.tsx index e8fbd9402..bf41a16e0 100644 --- a/components/portfolio/PortfolioIdentity.tsx +++ b/components/portfolio/PortfolioIdentity.tsx @@ -19,7 +19,7 @@ const PortfolioIdentity = ({ address }: { address: string }) => { return (
{address && } - {identity?.twitter && ( + {identity?.displayName && (
{identity.displayName}
From 075ed1643254ecfcb448ec9a82d9d7e5e6e5c4e4 Mon Sep 17 00:00:00 2001 From: Tom Robiquet Date: Wed, 12 Jul 2023 17:19:15 +0200 Subject: [PATCH 2/3] add latest trades --- components/front-page/LatestTrades.tsx | 74 +++++++++ components/front-page/PopularCategories.tsx | 1 - lib/hooks/queries/useLatestTrades.tsx | 158 ++++++++++++++++++++ pages/index.tsx | 2 + 4 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 components/front-page/LatestTrades.tsx create mode 100644 lib/hooks/queries/useLatestTrades.tsx diff --git a/components/front-page/LatestTrades.tsx b/components/front-page/LatestTrades.tsx new file mode 100644 index 000000000..3b123af86 --- /dev/null +++ b/components/front-page/LatestTrades.tsx @@ -0,0 +1,74 @@ +import Table, { TableColumn, TableData } from "components/ui/Table"; +import Link from "next/link"; +import { formatNumberLocalized } from "lib/util"; +import { useLatestTrades } from "lib/hooks/queries/useLatestTrades"; +import { ZTG } from "@zeitgeistpm/sdk-next"; + +const columns: TableColumn[] = [ + { + header: "Market", + accessor: "question", + type: "component", + }, + { + header: "Outcome", + accessor: "outcome", + type: "text", + }, + { + header: "Trade", + accessor: "trade", + type: "text", + }, + { + header: "Cost", + accessor: "cost", + type: "text", + }, + { + header: "Price", + accessor: "price", + type: "text", + }, + { + header: "Time", + accessor: "time", + type: "text", + }, +]; + +const TradeHistoryTable = () => { + const { data: trades } = useLatestTrades(); + + const tableData: TableData[] | undefined = trades?.map((trade) => { + return { + question: ( + + {trade?.question} + + ), + outcome: trade.outcomeName, + trade: trade.type === "buy" ? "Buy" : "Sell", + cost: formatNumberLocalized(trade.cost.div(ZTG).toNumber()), + price: formatNumberLocalized(trade.outcomePrice.toNumber()), + time: new Intl.DateTimeFormat("default", { + dateStyle: "medium", + timeStyle: "medium", + }).format(trade.time), + }; + }); + + return ( +
+

+ Latest Trades +

+ + + ); +}; + +export default TradeHistoryTable; diff --git a/components/front-page/PopularCategories.tsx b/components/front-page/PopularCategories.tsx index 76cb4099b..0c6c2df4b 100644 --- a/components/front-page/PopularCategories.tsx +++ b/components/front-page/PopularCategories.tsx @@ -1,7 +1,6 @@ import { FC } from "react"; import Image from "next/image"; import Link from "next/link"; -import { motion } from "framer-motion"; export const CATEGORIES = [ { name: "Sports", imagePath: "/category/sports.png" }, diff --git a/lib/hooks/queries/useLatestTrades.tsx b/lib/hooks/queries/useLatestTrades.tsx new file mode 100644 index 000000000..939fe5104 --- /dev/null +++ b/lib/hooks/queries/useLatestTrades.tsx @@ -0,0 +1,158 @@ +import { useQuery } from "@tanstack/react-query"; +import { + getIndexOf, + getMarketIdOf, + IOBaseAssetId, + IOMarketOutcomeAssetId, + isIndexedSdk, + isRpcSdk, + parseAssetId, +} from "@zeitgeistpm/sdk-next"; +import Decimal from "decimal.js"; +import { gql } from "graphql-request"; +import { useSdkv2 } from "../useSdkv2"; +import { HistoricalSwapOrderByInput } from "@zeitgeistpm/indexer"; +import { marketMetaFilter } from "lib/gql/constants"; +import { BLOCK_TIME_SECONDS } from "lib/constants"; + +export const transactionHistoryKey = "latest-trades"; + +const marketHeaderQuery = gql` + query MarketTransactionHeader($marketIds: [Int!]) { + markets( + where: { + marketId_in: $marketIds + ${marketMetaFilter} + } + orderBy: marketId_ASC + ) { + marketId + question + categories { + name + } + } + } +`; + +const lookupOutcomeAsset = (asset: string, markets: MarketHeader[]) => { + const assetId = parseAssetId(asset).unwrap(); + const market = lookupMarket(asset, markets); + + if (IOMarketOutcomeAssetId.is(assetId)) { + const index = getIndexOf(assetId); + return market && market.categories[index].name; + } +}; + +const lookupMarket = (asset: string, markets: MarketHeader[]) => { + const assetId = parseAssetId(asset).unwrap(); + + if (IOMarketOutcomeAssetId.is(assetId)) { + const marketId = getMarketIdOf(assetId); + const market = markets.find((market) => market.marketId === marketId); + return market; + } +}; + +type MarketHeader = { + marketId: number; + question: string; + categories: { name: string }[]; +}; + +export type TradeItem = { + marketId: number; + question: string; + type: "buy" | "sell"; + outcomeName: string; + outcomePrice: Decimal; + time: Date; + cost: Decimal; +}; + +export const useLatestTrades = () => { + const [sdk, id] = useSdkv2(); + + const query = useQuery( + [id, transactionHistoryKey], + async () => { + if (isIndexedSdk(sdk) && isRpcSdk(sdk)) { + const { historicalSwaps } = await sdk.indexer.historicalSwaps({ + limit: 3, + order: HistoricalSwapOrderByInput.BlockNumberDesc, + }); + + let marketIds = new Set(); + + historicalSwaps.forEach((swap) => { + const assetInId = parseAssetId(swap.assetIn).unwrap(); + const assetOutId = parseAssetId(swap.assetOut).unwrap(); + + if (IOMarketOutcomeAssetId.is(assetInId)) { + marketIds.add(getMarketIdOf(assetInId)); + } else if (IOMarketOutcomeAssetId.is(assetOutId)) { + marketIds.add(getMarketIdOf(assetOutId)); + } + }); + + const marketIdsArray = Array.from(marketIds).sort((a, b) => a - b); + + const { markets } = await sdk.indexer.client.request<{ + markets: MarketHeader[]; + }>(marketHeaderQuery, { + marketIds: marketIdsArray, + }); + + const trades: TradeItem[] = historicalSwaps + .map((swap) => { + const market = + lookupMarket(swap.assetIn, markets) ?? + lookupMarket(swap.assetOut, markets); + const outcome = + lookupOutcomeAsset(swap.assetIn, markets) ?? + lookupOutcomeAsset(swap.assetOut, markets); + + if (!market || !outcome) { + return; + } + + const assetInId = parseAssetId(swap.assetIn).unwrap(); + + const assetInIsBaseAsset = IOBaseAssetId.is(assetInId); + + const item: TradeItem = { + marketId: market.marketId, + question: market.question, + outcomeName: outcome, + type: assetInIsBaseAsset === true ? "buy" : "sell", + time: new Date(swap.timestamp), + cost: + assetInIsBaseAsset === true + ? new Decimal(swap.assetAmountIn) + : new Decimal(swap.assetAmountOut), + outcomePrice: + assetInIsBaseAsset === true + ? new Decimal(swap.assetAmountIn).div(swap.assetAmountOut) + : new Decimal(swap.assetAmountOut).div(swap.assetAmountIn), + }; + + return item; + }) + .filter((trade): trade is TradeItem => trade != null); + + return trades; + } + + return []; + }, + { + keepPreviousData: true, + initialData: [], + enabled: Boolean(sdk && isIndexedSdk(sdk) && isRpcSdk(sdk)), + refetchInterval: BLOCK_TIME_SECONDS * 1000, + }, + ); + + return query; +}; diff --git a/pages/index.tsx b/pages/index.tsx index 3fe831a58..87450967b 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -19,6 +19,7 @@ import { IGetPlaiceholderOptions, IGetPlaiceholderReturn, } from "plaiceholder"; +import LatestTrades from "components/front-page/LatestTrades"; const getPlaiceholders = ( paths: string[], @@ -116,6 +117,7 @@ const IndexPage: NextPage<{ /> )} + ); From f9803f766223a9b0643c7c826c6c993cceab9799 Mon Sep 17 00:00:00 2001 From: Tom Robiquet Date: Thu, 13 Jul 2023 12:06:45 +0200 Subject: [PATCH 3/3] refactor --- components/front-page/LatestTrades.tsx | 2 +- lib/gql/market-header.ts | 40 ++++++++++++ lib/hooks/queries/useLatestTrades.tsx | 86 ++++++++------------------ lib/hooks/queries/useTradeHistory.ts | 35 +---------- 4 files changed, 71 insertions(+), 92 deletions(-) create mode 100644 lib/gql/market-header.ts diff --git a/components/front-page/LatestTrades.tsx b/components/front-page/LatestTrades.tsx index 3b123af86..551ec8fa3 100644 --- a/components/front-page/LatestTrades.tsx +++ b/components/front-page/LatestTrades.tsx @@ -44,7 +44,7 @@ const TradeHistoryTable = () => { return { question: ( {trade?.question} diff --git a/lib/gql/market-header.ts b/lib/gql/market-header.ts new file mode 100644 index 000000000..4dfd260a0 --- /dev/null +++ b/lib/gql/market-header.ts @@ -0,0 +1,40 @@ +import { gql } from "graphql-request"; +import { marketMetaFilter } from "./constants"; +import { IndexerContext, Sdk } from "@zeitgeistpm/sdk-next"; + +const marketHeaderQuery = gql` + query MarketTransactionHeader($marketIds: [Int!]) { + markets( + where: { + marketId_in: $marketIds + ${marketMetaFilter} + } + orderBy: marketId_ASC + ) { + marketId + question + categories { + name + } + } + } +`; + +export type MarketHeader = { + marketId: number; + question: string; + categories: { name: string }[]; +}; + +export const getMarketHeaders = async ( + sdk: Sdk, + marketIds: number[], +) => { + const { markets } = await sdk.indexer.client.request<{ + markets: MarketHeader[]; + }>(marketHeaderQuery, { + marketIds: marketIds, + }); + + return markets; +}; diff --git a/lib/hooks/queries/useLatestTrades.tsx b/lib/hooks/queries/useLatestTrades.tsx index 939fe5104..4fb0cfd18 100644 --- a/lib/hooks/queries/useLatestTrades.tsx +++ b/lib/hooks/queries/useLatestTrades.tsx @@ -1,66 +1,20 @@ import { useQuery } from "@tanstack/react-query"; +import { HistoricalSwapOrderByInput } from "@zeitgeistpm/indexer"; import { getIndexOf, getMarketIdOf, IOBaseAssetId, IOMarketOutcomeAssetId, isIndexedSdk, - isRpcSdk, parseAssetId, } from "@zeitgeistpm/sdk-next"; import Decimal from "decimal.js"; -import { gql } from "graphql-request"; -import { useSdkv2 } from "../useSdkv2"; -import { HistoricalSwapOrderByInput } from "@zeitgeistpm/indexer"; -import { marketMetaFilter } from "lib/gql/constants"; import { BLOCK_TIME_SECONDS } from "lib/constants"; +import { getMarketHeaders, MarketHeader } from "lib/gql/market-header"; +import { useSdkv2 } from "../useSdkv2"; export const transactionHistoryKey = "latest-trades"; -const marketHeaderQuery = gql` - query MarketTransactionHeader($marketIds: [Int!]) { - markets( - where: { - marketId_in: $marketIds - ${marketMetaFilter} - } - orderBy: marketId_ASC - ) { - marketId - question - categories { - name - } - } - } -`; - -const lookupOutcomeAsset = (asset: string, markets: MarketHeader[]) => { - const assetId = parseAssetId(asset).unwrap(); - const market = lookupMarket(asset, markets); - - if (IOMarketOutcomeAssetId.is(assetId)) { - const index = getIndexOf(assetId); - return market && market.categories[index].name; - } -}; - -const lookupMarket = (asset: string, markets: MarketHeader[]) => { - const assetId = parseAssetId(asset).unwrap(); - - if (IOMarketOutcomeAssetId.is(assetId)) { - const marketId = getMarketIdOf(assetId); - const market = markets.find((market) => market.marketId === marketId); - return market; - } -}; - -type MarketHeader = { - marketId: number; - question: string; - categories: { name: string }[]; -}; - export type TradeItem = { marketId: number; question: string; @@ -77,13 +31,13 @@ export const useLatestTrades = () => { const query = useQuery( [id, transactionHistoryKey], async () => { - if (isIndexedSdk(sdk) && isRpcSdk(sdk)) { + if (isIndexedSdk(sdk)) { const { historicalSwaps } = await sdk.indexer.historicalSwaps({ limit: 3, order: HistoricalSwapOrderByInput.BlockNumberDesc, }); - let marketIds = new Set(); + const marketIds = new Set(); historicalSwaps.forEach((swap) => { const assetInId = parseAssetId(swap.assetIn).unwrap(); @@ -98,11 +52,7 @@ export const useLatestTrades = () => { const marketIdsArray = Array.from(marketIds).sort((a, b) => a - b); - const { markets } = await sdk.indexer.client.request<{ - markets: MarketHeader[]; - }>(marketHeaderQuery, { - marketIds: marketIdsArray, - }); + const markets = await getMarketHeaders(sdk, marketIdsArray); const trades: TradeItem[] = historicalSwaps .map((swap) => { @@ -118,7 +68,6 @@ export const useLatestTrades = () => { } const assetInId = parseAssetId(swap.assetIn).unwrap(); - const assetInIsBaseAsset = IOBaseAssetId.is(assetInId); const item: TradeItem = { @@ -148,11 +97,30 @@ export const useLatestTrades = () => { }, { keepPreviousData: true, - initialData: [], - enabled: Boolean(sdk && isIndexedSdk(sdk) && isRpcSdk(sdk)), + enabled: Boolean(sdk && isIndexedSdk(sdk)), refetchInterval: BLOCK_TIME_SECONDS * 1000, }, ); return query; }; + +const lookupOutcomeAsset = (asset: string, markets: MarketHeader[]) => { + const assetId = parseAssetId(asset).unwrap(); + const market = lookupMarket(asset, markets); + + if (IOMarketOutcomeAssetId.is(assetId)) { + const index = getIndexOf(assetId); + return market && market.categories[index].name; + } +}; + +const lookupMarket = (asset: string, markets: MarketHeader[]) => { + const assetId = parseAssetId(asset).unwrap(); + + if (IOMarketOutcomeAssetId.is(assetId)) { + const marketId = getMarketIdOf(assetId); + const market = markets.find((market) => market.marketId === marketId); + return market; + } +}; diff --git a/lib/hooks/queries/useTradeHistory.ts b/lib/hooks/queries/useTradeHistory.ts index 8f7ff6569..c4dee5bb6 100644 --- a/lib/hooks/queries/useTradeHistory.ts +++ b/lib/hooks/queries/useTradeHistory.ts @@ -1,4 +1,5 @@ import { useQuery } from "@tanstack/react-query"; +import { HistoricalSwapOrderByInput } from "@zeitgeistpm/indexer"; import { getIndexOf, getMarketIdOf, @@ -11,31 +12,11 @@ import { parseAssetId, } from "@zeitgeistpm/sdk-next"; import Decimal from "decimal.js"; -import { gql } from "graphql-request"; +import { getMarketHeaders, MarketHeader } from "lib/gql/market-header"; import { useSdkv2 } from "../useSdkv2"; -import { HistoricalSwapOrderByInput } from "@zeitgeistpm/indexer"; -import { marketMetaFilter } from "lib/gql/constants"; export const transactionHistoryKey = "trade-history"; -const marketHeaderQuery = gql` - query MarketTransactionHeader($marketIds: [Int!]) { - markets( - where: { - marketId_in: $marketIds - ${marketMetaFilter} - } - orderBy: marketId_ASC - ) { - marketId - question - categories { - name - } - } - } -`; - const lookupAssetName = ( asset: string, marketsMap: Map, @@ -90,12 +71,6 @@ const calculatePrice = ( } }; -type MarketHeader = { - marketId: number; - question: string; - categories: { name: string }[]; -}; - export type TradeHistoryItem = { marketId: MarketId; question: string; @@ -144,11 +119,7 @@ export const useTradeHistory = (address?: string) => { const marketIdsArray = Array.from(marketIds).sort((a, b) => a - b); - const { markets } = await sdk.indexer.client.request<{ - markets: MarketHeader[]; - }>(marketHeaderQuery, { - marketIds: marketIdsArray, - }); + const markets = await getMarketHeaders(sdk, marketIdsArray); const marketsMap: Map = new Map(); marketIdsArray.forEach((marketId) => {