Skip to content

Commit

Permalink
ENG-5293 feat(launchpad,1ui,graphql): bring protocol points query int…
Browse files Browse the repository at this point in the history
…o graphql and basic scaffolding of points route (#1013)

## Affected Packages

Apps

- [ ] data populator
- [ ] portal
- [ ] template
- [x] launchpad

Packages

- [x] 1ui
- [ ] api
- [x] graphql
- [ ] protocol
- [ ] sdk

Tools

- [ ] tools

## Overview

Brings the protocol points query into the graphql package. Basic
scaffolding of the points route with updated aggregated metrics
component. Query for relic points on points route.

Note: Tried setting up api package in launchpad so that we could query
for the other points, but realized that we'd have to set up privy for
auth. Will pick this back up after talking with the rest of the team.

## Screen Captures

If applicable, add screenshots or screen captures of your changes.

## Declaration

- [x] I hereby declare that I have abided by the rules and regulations
as outlined in the
[CONTRIBUTING.md](https://github.com/0xIntuition/intuition-ts/blob/main/CONTRIBUTING.md)

---------

Signed-off-by: Vital <[email protected]>
  • Loading branch information
Vitalsine85 authored Jan 3, 2025
1 parent ff9a1bb commit d91c44c
Show file tree
Hide file tree
Showing 15 changed files with 759 additions and 71 deletions.
23 changes: 23 additions & 0 deletions apps/launchpad/app/.server/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ApiError } from '@0xintuition/api'

import logger from '@lib/utils/logger'

import { setupAPI } from './auth'

export const fetchWrapper = async <T, A>({
method,
args,
}: {
method: (arg: A) => Promise<T>
args: A
}): Promise<T> => {
try {
await setupAPI()
return await method(args)
} catch (error: unknown) {
if (error instanceof ApiError) {
logger(`${error.name} - ${error.status}: ${error.message} ${error.url}`)
}
throw error
}
}
16 changes: 16 additions & 0 deletions apps/launchpad/app/.server/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { OpenAPI } from '@0xintuition/api'

import logger from '@lib/utils/logger'
import { getAuthHeaders } from '@lib/utils/misc'

export async function setupAPI() {
const apiUrl = process.env.API_URL

const apiKey = process.env.API_KEY

OpenAPI.BASE = apiUrl ?? ''

const headers = getAuthHeaders(apiKey !== null ? apiKey : '')
logger('headers', headers)
OpenAPI.HEADERS = headers as Record<string, string>
}
115 changes: 115 additions & 0 deletions apps/launchpad/app/lib/services/relics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
interface GraphQLResponse<T> {
data?: T
errors?: Array<{ message: string }>
}

const GetMintCountDocument = {
query: `
query GetMintCountUntilDate($address: String!, $cutoff_timestamp: Int!) {
voucherRedeemedEvents(
where: { redeemer: $address, timestamp_lte: $cutoff_timestamp }
) {
totalCount
}
}
`,
} as const

interface GetMintCountQuery {
voucherRedeemedEvents: {
totalCount: number
}
}

interface GetMintCountQueryVariables {
address: string
cutoff_timestamp: number
}

const GetRelicHoldingsDocument = {
query: `
query GetRelicHoldings($address: String!) {
account(address: $address) {
tokens {
totalCount
}
voucherRedeemedEvents {
totalCount
}
}
}
`,
} as const

interface GetRelicHoldingsQuery {
account: {
tokens: {
totalCount: number
}
voucherRedeemedEvents: {
totalCount: number
}
}
}

interface GetRelicHoldingsQueryVariables {
address: string
}

async function fetchGraphQL<T, V>(
document: { query: string },
variables: V,
): Promise<GraphQLResponse<T>> {
const endpoint = process.env.RELIC_GRAPHQL_ENDPOINT
if (!endpoint) {
throw new Error('RELIC_GRAPHQL_ENDPOINT not configured')
}

const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: document.query,
variables,
}),
})

if (!response.ok) {
throw new Error(`GraphQL request failed: ${response.statusText}`)
}

return response.json()
}

export async function fetchRelicCounts(address: string) {
const cutoffTimestamp = 1735516799

const [mintCountData, holdingsData] = await Promise.all([
fetchGraphQL<GetMintCountQuery, GetMintCountQueryVariables>(
GetMintCountDocument,
{
address,
cutoff_timestamp: cutoffTimestamp,
},
),
fetchGraphQL<GetRelicHoldingsQuery, GetRelicHoldingsQueryVariables>(
GetRelicHoldingsDocument,
{
address,
},
),
])

const mintCount = mintCountData?.data?.voucherRedeemedEvents?.totalCount ?? 0
const holdCount = holdingsData?.data?.account?.tokens?.totalCount ?? 0

return {
mintCount,
holdCount,
nftMintPoints: mintCount * 2000000,
nftHoldPoints: holdCount * 250000,
totalNftPoints: mintCount * 2000000 + holdCount * 250000,
}
}
11 changes: 11 additions & 0 deletions apps/launchpad/app/lib/utils/misc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export function getAuthHeaders(apiKey?: string) {
const headers: HeadersInit = {
'Content-Type': 'application/json',
}

if (apiKey) {
headers['x-api-key'] = apiKey
}

return headers
}
22 changes: 22 additions & 0 deletions apps/launchpad/app/lib/utils/points.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { GetFeeTransfersQuery } from '@0xintuition/graphql'

export const POINTS_CUTOFF_TIMESTAMP = 1733356800

export function calculateProtocolPoints(data: GetFeeTransfersQuery) {
const beforeCutoffAmount = data.before_cutoff?.aggregate?.sum?.amount ?? '0'
const afterCutoffAmount = data.after_cutoff?.aggregate?.sum?.amount ?? '0'

const beforeCutoffPoints =
(BigInt(beforeCutoffAmount) * BigInt(10000000)) / BigInt(1e18)
const afterCutoffPoints =
(BigInt(afterCutoffAmount) * BigInt(1000000)) / BigInt(1e18)
const totalPoints = beforeCutoffPoints + afterCutoffPoints

return {
beforeCutoffAmount,
afterCutoffAmount,
beforeCutoffPoints: beforeCutoffPoints.toString(),
afterCutoffPoints: afterCutoffPoints.toString(),
totalPoints: totalPoints.toString(),
}
}
20 changes: 15 additions & 5 deletions apps/launchpad/app/routes/_app+/network.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,21 @@ export default function Network() {
</div>
<div className="py-4 bg-gradient-to-b from-[#060504] to-[#101010]">
<AggregatedMetrics
tvl={+formatUnits(stats?.contract_balance ?? 0, 18)}
atomsCount={stats?.total_atoms ?? 0}
triplesCount={stats?.total_triples ?? 0}
signalsCount={stats?.total_signals ?? 0}
usersCount={stats?.total_accounts ?? 0}
metrics={[
{
label: 'TVL',
value: +formatUnits(stats?.contract_balance ?? 0, 18),
suffix: 'ETH',
},
{ label: 'Atoms', value: stats?.total_atoms ?? 0 },
{ label: 'Triples', value: stats?.total_triples ?? 0 },
{
label: 'Signals',
value: stats?.total_signals ?? 0,
hideOnMobile: true,
},
{ label: 'Users', value: stats?.total_accounts ?? 0 },
]}
className="[&>div]:after:hidden sm:[&>div]:after:block"
/>
</div>
Expand Down
121 changes: 121 additions & 0 deletions apps/launchpad/app/routes/_app+/points.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import {
AggregatedMetrics,
PageHeader,
Text,
TextVariant,
TextWeight,
} from '@0xintuition/1ui'
import { UsersService } from '@0xintuition/api'
import {
fetcher,
GetFeeTransfersDocument,
GetFeeTransfersQuery,
GetFeeTransfersQueryVariables,
useGetFeeTransfersQuery,
} from '@0xintuition/graphql'

import logger from '@lib/utils/logger'
import {
calculateProtocolPoints,
POINTS_CUTOFF_TIMESTAMP,
} from '@lib/utils/points'
import { LoaderFunctionArgs } from '@remix-run/node'
import { useLoaderData } from '@remix-run/react'
import { dehydrate, QueryClient } from '@tanstack/react-query'
import { fetchRelicCounts } from 'app/lib/services/relics'

import { fetchWrapper } from '../../.server/api'

export async function loader({ request }: LoaderFunctionArgs) {
logger('request', request)

const queryClient = new QueryClient()

await queryClient.prefetchQuery({
queryKey: ['get-fee-transfers-user'],
queryFn: () =>
fetcher<GetFeeTransfersQuery, GetFeeTransfersQueryVariables>(
GetFeeTransfersDocument,
{
address: '0xB95ca3D3144e9d1DAFF0EE3d35a4488A4A5C9Fc5'.toLowerCase(),
cutoff_timestamp: POINTS_CUTOFF_TIMESTAMP,
},
),
})

const [relicCounts] = await Promise.all([
fetchRelicCounts(
'0xB95ca3D3144e9d1DAFF0EE3d35a4488A4A5C9Fc5'.toLowerCase(),
),
])

// This is using legacy API. TODO: Update to use GraphQL when these are available.
const userTotals = await fetchWrapper({
method: UsersService.getUserTotals,
args: {
id: '0xB95ca3D3144e9d1DAFF0EE3d35a4488A4A5C9Fc5'.toLowerCase(),
},
})

return {
dehydratedState: dehydrate(queryClient),
relicHoldCount: relicCounts.holdCount,
mintCount: relicCounts.mintCount,
userTotals,
}
}

export default function Points() {
const { relicHoldCount, mintCount, userTotals } =
useLoaderData<typeof loader>()
const { data: feeData } = useGetFeeTransfersQuery(
{
address: '0xB95ca3D3144e9d1DAFF0EE3d35a4488A4A5C9Fc5'.toLowerCase(),
cutoff_timestamp: POINTS_CUTOFF_TIMESTAMP,
},
{
queryKey: ['get-fee-transfers-user'],
},
)

const protocolPoints = feeData ? calculateProtocolPoints(feeData) : null
const nftMintPoints = mintCount * 2000000
const nftHoldPoints = relicHoldCount * 250000
const totalNftPoints = nftMintPoints + nftHoldPoints

return (
<div className="flex-1 p-10 max-lg:p-6">
<div className="mx-auto max-w-[1280px] flex flex-col gap-8">
<PageHeader title="Points" lastUpdated={'3s'} />

<div className="flex flex-col rounded-xl overflow-hidden theme-border">
<div className="py-4 bg-gradient-to-b from-[#060504] to-[#101010]">
<AggregatedMetrics
metrics={[
{ label: 'Portal', value: userTotals?.quest_points ?? '0' },
{
label: 'Protocol',
value: protocolPoints?.totalPoints ?? '0',
},
{ label: 'NFT', value: totalNftPoints },
{
label: 'Referrals',
value: userTotals?.referral_points ?? '0',
hideOnMobile: true,
},
{ label: 'Community', value: '0' },
]}
/>
</div>
</div>

<div className="flex flex-col gap-4">
<Text variant={TextVariant.headline} weight={TextWeight.medium}>
Points Distribution
</Text>
{/* Additional points-specific components will go here */}
</div>
</div>
</div>
)
}
1 change: 1 addition & 0 deletions apps/launchpad/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"dependencies": {
"@0xintuition/1ui": "workspace:^",
"@0xintuition/graphql": "workspace:^",
"@0xintuition/api": "workspace:^",
"@popperjs/core": "^2.11.8",
"@privy-io/react-auth": "^1.97.0",
"@privy-io/wagmi": "^0.2.13",
Expand Down
Loading

0 comments on commit d91c44c

Please sign in to comment.