From de6c327adc324303f19b736af51ba0436df1cf58 Mon Sep 17 00:00:00 2001 From: Picodes <41673773+Picodes@users.noreply.github.com> Date: Thu, 19 Dec 2024 11:01:47 +0100 Subject: [PATCH] feat: position route (#47) * feat: position route * fix: issue * chore: position page * lint --- src/api/services/liquidity.service.ts | 20 +++++++++ .../opportunity/opportunity.service.ts | 2 +- .../element/position/PositionLibrary.tsx | 32 ++++++++++++++ .../element/position/PositionTable.tsx | 23 ++++++++++ .../element/position/PositionTableRow.tsx | 43 +++++++++++++++++++ ...rtunities.$chain.$type.$id.leaderboard.tsx | 1 + ...erkl.users.$address.$chainId.liquidity.tsx | 26 +++++++++++ .../_merkl.users.$address.liquidity.tsx | 5 --- src/routes/_merkl.users.$address.tsx | 2 +- 9 files changed, 147 insertions(+), 7 deletions(-) create mode 100644 src/api/services/liquidity.service.ts create mode 100644 src/components/element/position/PositionLibrary.tsx create mode 100644 src/components/element/position/PositionTable.tsx create mode 100644 src/components/element/position/PositionTableRow.tsx create mode 100644 src/routes/_merkl.users.$address.$chainId.liquidity.tsx delete mode 100644 src/routes/_merkl.users.$address.liquidity.tsx diff --git a/src/api/services/liquidity.service.ts b/src/api/services/liquidity.service.ts new file mode 100644 index 00000000..4eabd27f --- /dev/null +++ b/src/api/services/liquidity.service.ts @@ -0,0 +1,20 @@ +import { api } from "../index.server"; +import { fetchWithLogs } from "../utils"; + +export abstract class LiquidityService { + static async #fetch( + call: () => Promise, + resource = "Positions", + ): Promise> { + const { data, status } = await fetchWithLogs(call); + + if (status === 404) throw new Response(`${resource} not found`, { status }); + if (status === 500) throw new Response(`${resource} unavailable`, { status }); + if (data == null) throw new Response(`${resource} unavailable`, { status }); + return data; + } + + static async getForUser(query: Parameters["0"]["query"]) { + return await LiquidityService.#fetch(async () => api.v4.liquidity.index.get({ query })); + } +} diff --git a/src/api/services/opportunity/opportunity.service.ts b/src/api/services/opportunity/opportunity.service.ts index 23083241..1ca78e0e 100644 --- a/src/api/services/opportunity/opportunity.service.ts +++ b/src/api/services/opportunity/opportunity.service.ts @@ -40,7 +40,7 @@ export abstract class OpportunityService { //TODO: updates tags to take an array if (config.tags && !opportunityWithCampaigns.tags.includes(config.tags?.[0])) - throw new Response("Opportunity inacessible", { status: 403 }); + throw new Response("Opportunity inaccessible", { status: 403 }); return opportunityWithCampaigns; } diff --git a/src/components/element/position/PositionLibrary.tsx b/src/components/element/position/PositionLibrary.tsx new file mode 100644 index 00000000..819f683c --- /dev/null +++ b/src/components/element/position/PositionLibrary.tsx @@ -0,0 +1,32 @@ +import type { PositionT } from "@merkl/api/dist/src/modules/v4/liquidity"; +import { Text, Title } from "dappkit"; +import { useMemo } from "react"; +import OpportunityPagination from "../opportunity/OpportunityPagination"; +import { PositionTable } from "./PositionTable"; +import PositionTableRow from "./PositionTableRow"; + +export type IProps = { + positions: PositionT[]; + count?: number; +}; + +export default function PositionLibrary(props: IProps) { + const { positions, count } = props; + + const rows = useMemo(() => { + return positions?.map(row => ); + }, [positions]); + + return ( + (index < 2 ? "bg-accent-8" : "bg-main-8")} + header={ + + Your Liquidity + + } + footer={count !== undefined && }> + {!!rows.length ? rows : No positions detected} + + ); +} diff --git a/src/components/element/position/PositionTable.tsx b/src/components/element/position/PositionTable.tsx new file mode 100644 index 00000000..8e28a067 --- /dev/null +++ b/src/components/element/position/PositionTable.tsx @@ -0,0 +1,23 @@ +import { createTable } from "dappkit"; + +export const [PositionTable, PositionRow, PositionColumns] = createTable({ + source: { + name: "Source", + size: "minmax(120px,150px)", + compact: "1fr", + className: "justify-start", + main: true, + }, + flags: { + name: "Flags", + size: "minmax(170px,1fr)", + compactSize: "1fr", + className: "justify-start", + }, + tokens: { + name: "Tokens", + size: "minmax(30px,1fr)", + compactSize: "minmax(20px,1fr)", + className: "justify-start", + }, +}); diff --git a/src/components/element/position/PositionTableRow.tsx b/src/components/element/position/PositionTableRow.tsx new file mode 100644 index 00000000..79d78d67 --- /dev/null +++ b/src/components/element/position/PositionTableRow.tsx @@ -0,0 +1,43 @@ +import type { PositionT } from "@merkl/api/dist/src/modules/v4/liquidity"; +import { type Component, Group, PrimitiveTag, Text, mergeClass } from "dappkit"; +import React from "react"; +import Token from "../token/Token"; +import { PositionRow } from "./PositionTable"; + +export type PositionRowProps = Component<{ + row: PositionT; +}>; + +export default function PositionTableRow({ row, className, ...props }: PositionRowProps) { + return ( + {row.opportunity.name}} + flagsColumn={ + + + {row.flags?.id} + + + {row.flags?.range} + + + } + tokensColumn={ + + {row.tokens.map((token, index) => ( + <> + + {token.breakdown.map((breakdown, index) => ( + + {breakdown.type} {breakdown.value} + + ))} + + ))} + + } + /> + ); +} diff --git a/src/routes/_merkl.opportunities.$chain.$type.$id.leaderboard.tsx b/src/routes/_merkl.opportunities.$chain.$type.$id.leaderboard.tsx index 5639cdcf..23a49326 100644 --- a/src/routes/_merkl.opportunities.$chain.$type.$id.leaderboard.tsx +++ b/src/routes/_merkl.opportunities.$chain.$type.$id.leaderboard.tsx @@ -34,6 +34,7 @@ export async function loader({ params: { id, type, chain: chainId }, request }: const { rewards, count, total } = await RewardService.getManyFromRequest(request, { chainId: chain.id, + campaignId: campaigns?.[0]?.campaignId, }); return json({ diff --git a/src/routes/_merkl.users.$address.$chainId.liquidity.tsx b/src/routes/_merkl.users.$address.$chainId.liquidity.tsx new file mode 100644 index 00000000..c2d73ce5 --- /dev/null +++ b/src/routes/_merkl.users.$address.$chainId.liquidity.tsx @@ -0,0 +1,26 @@ +import type { LoaderFunctionArgs } from "@remix-run/node"; +import { json, useLoaderData } from "@remix-run/react"; +import { Container } from "dappkit"; +import { LiquidityService } from "src/api/services/liquidity.service"; +import PositionLibrary from "src/components/element/position/PositionLibrary"; +import { isAddress } from "viem"; + +export async function loader({ params: { address, chainId } }: LoaderFunctionArgs) { + if (!address || !isAddress(address)) throw ""; + if (!chainId && Number(chainId).toString() === chainId) throw ""; + + const positions = await LiquidityService.getForUser({ + address, + chainId: Number(chainId), + }); + return json({ positions }); +} + +export default function Index() { + const { positions } = useLoaderData(); + return ( + + + + ); +} diff --git a/src/routes/_merkl.users.$address.liquidity.tsx b/src/routes/_merkl.users.$address.liquidity.tsx deleted file mode 100644 index 05018678..00000000 --- a/src/routes/_merkl.users.$address.liquidity.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { Container } from "dappkit"; - -export default function Index() { - return liquidity; -} diff --git a/src/routes/_merkl.users.$address.tsx b/src/routes/_merkl.users.$address.tsx index b4897418..7af1e749 100644 --- a/src/routes/_merkl.users.$address.tsx +++ b/src/routes/_merkl.users.$address.tsx @@ -111,7 +111,7 @@ export default function Index() { Liquidity ), - link: `/users/${address}/liquidity`, + link: `/users/${address}/${chainId}/liquidity`, key: crypto.randomUUID(), }, {