Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
"@cosmjs/tendermint-rpc": "^0.32.1",
"@datadog/browser-logs": "^5.23.3",
"@dydxprotocol/v4-client-js": "3.4.0",
"@dydxprotocol/v4-localization": "1.1.371",
"@dydxprotocol/v4-localization": "1.1.373",
"@dydxprotocol/v4-proto": "^7.0.0-dev.0",
"@emotion/is-prop-valid": "^1.3.0",
"@hugocxl/react-to-image": "^0.0.9",
Expand Down
8 changes: 4 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions src/components/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,6 @@ import {
WithdrawIcon,
XCircleIcon,
} from '@/icons';
import { ChaosLabsIcon } from '@/icons/chaos-labs';
import { LogoShortIcon } from '@/icons/logo-short';
import SignalIcon from '@/icons/signal.svg';
import UsdcIcon from '@/icons/usdc.svg';
Expand All @@ -177,7 +176,6 @@ export enum IconName {
Caret = 'Caret',
CautionCircle = 'CautionCircle',
CautionCircleStroked = 'CautionCircleStroked',
ChaosLabs = 'ChaosLabs',
Chat = 'Chat',
ChefHat = 'ChefHat',
Check = 'Check',
Expand Down Expand Up @@ -336,7 +334,6 @@ const icons = {
[IconName.Caret]: CaretIcon,
[IconName.CautionCircle]: CautionCircleIcon,
[IconName.CautionCircleStroked]: CautionCircleStrokeIcon,
[IconName.ChaosLabs]: ChaosLabsIcon,
[IconName.Chat]: ChatIcon,
[IconName.ChefHat]: ChefHatIcon,
[IconName.Check]: CheckIcon,
Expand Down
7 changes: 5 additions & 2 deletions src/components/Link.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { forwardRef } from 'react';
import { forwardRef, HTMLAttributes } from 'react';

import styled, { css } from 'styled-components';

Expand All @@ -25,7 +25,10 @@ type StyleProps = {
className?: string;
};

export const Link = forwardRef<HTMLAnchorElement, ElementProps & StyleProps>(
export const Link = forwardRef<
HTMLAnchorElement,
ElementProps & StyleProps & HTMLAttributes<HTMLAnchorElement>
>(
(
{
analyticsConfig,
Expand Down
2 changes: 2 additions & 0 deletions src/constants/statsig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export enum StatsigFlags {
ffTurnkeyWeb = 'ff_turnkey_web',
ffSwapEnabled = 'ff_swap_ui_web',
ffSpot = 'ff_spot',
ffHideMarketsFilter = 'ff_hide_markets_filter',
ffOpenInterestFilter = 'ff_open_interest_filter',
abPopupDeposit = 'ab_popup_deposit',
ffSpotBonk = 'ff_spot_bonk',
ffBonkPnlLeaderboard = 'ff_bonk_pnl_leaderboard',
Expand Down
170 changes: 20 additions & 150 deletions src/hooks/rewards/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,133 +1,13 @@
import { BonsaiCore } from '@/bonsai/ontology';
import { useQuery } from '@tanstack/react-query';

import { DydxAddress } from '@/constants/wallets';

import { useAppSelector } from '@/state/appTypes';

import { wrapAndLogError } from '@/lib/asyncUtils';
import { mapIfPresent } from '@/lib/do';

import { useQueryChaosLabsIncentives } from '../useQueryChaosLabsIncentives';
import {
CURRENT_SURGE_REWARDS_DETAILS,
feesToEstimatedDollarRewards,
pointsToEstimatedDollarRewards,
pointsToEstimatedDydxRewards,
} from './util';

export type ChaosLabsPointsItem = {
rank: number;
account: DydxAddress;
accountLabel: string;
incentivePoints: number;
updatedAt: number;
estimatedDydxRewards: number | string;
};

const volumeQuery = `query VolumeLeaderboard($query: LeaderboardQuery!) {\n incentivesLeaderboard(query: $query) {\n rank\n account\n accountLabel\n markets\n incentivePoints\n updatedAt\n roi\n pnl\n __typename\n }\n}`;

async function getChaosLabsPointsDistribution() {
const res = await fetch(`https://cloud.chaoslabs.co/query/ccar-perpetuals`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
protocol: 'dydx-v4',
'apollographql-client-name': 'dydx-v4',
},
body: JSON.stringify({
operationName: 'VolumeLeaderboard',
query: volumeQuery,
variables: {
query: {
type: 'Volume',
skip: 0,
sort: 'takerFeesRank',
order: 'Ascending',
search: null,
},
},
}),
});
const parsedRes = (await res.json()) as {
data: { incentivesLeaderboard: ChaosLabsPointsItem[] };
};

return parsedRes.data.incentivesLeaderboard;
}

export function useChaosLabsPointsDistribution() {
const { data: pointsInfo, isLoading: rewardsInfoLoading } = useTotalRewardsPoints();
const dydxPrice = useAppSelector(BonsaiCore.rewardParams.data).tokenPrice;

const { data: leaderboardItems, isLoading: leaderboardItemsLoading } = useQuery({
queryKey: ['chaoslabs/points'],
queryFn: wrapAndLogError(
() => getChaosLabsPointsDistribution(),
'LaunchIncentives/fetchDistribution',
true
),
});

return {
isLoading: rewardsInfoLoading || leaderboardItemsLoading || !dydxPrice,
data: leaderboardItems?.map((item) => ({
...item,
estimatedDydxRewards: pointsToEstimatedDydxRewards(
item.incentivePoints,
pointsInfo?.totalPoints,
dydxPrice,
CURRENT_SURGE_REWARDS_DETAILS.rewardAmountUsd
),
})),
};
}

async function getTotalRewardsPoints() {
const res = await fetch('https://cloud.chaoslabs.co/query/api/dydx/total-points');
const data = (await res.json()) as { totalPoints: number; seasonNumber: number };
return data;
}
import { feesToEstimatedDollarRewards } from './util';

export function useTotalRewardsPoints() {
return useQuery({
queryKey: ['total-rewards-points'],
queryFn: () => getTotalRewardsPoints(),
});
}

export const useChaosLabsUsdRewards = ({
dydxAddress,
season,
totalUsdRewards,
}: {
dydxAddress?: DydxAddress;
season?: number;
totalUsdRewards?: number;
}) => {
const { data: totalPoints, isLoading: totalPointsLoading } = useTotalRewardsPoints();
const { data: points, isLoading: pointsLoading } = useQueryChaosLabsIncentives({
dydxAddress,
season,
});

return {
data: mapIfPresent(
pointsToEstimatedDollarRewards(
points?.incentivePoints,
totalPoints?.totalPoints,
totalUsdRewards
),
points?.totalFees,
(pointRewards, feesPaid) => {
return pointRewards + feesPaid;
}
),
isLoading: totalPointsLoading || pointsLoading,
};
};

export type ChaosLabsPnlItem = {
export type ClcPnlItem = {
address: string;
pnl: number;
startOfThisWeekPnlSnapshot: {
Expand All @@ -139,30 +19,26 @@ export type ChaosLabsPnlItem = {
dollarReward: number;
};

async function getChaosLabsPnlDistribution() {
async function getClcPnlDistribution() {
const res = await fetch(
`https://pp-external-api-ffb2ad95ef03.herokuapp.com/api/dydx-weekly-clc?perPage=1000`,
{
method: 'GET',
}
);
const parsedRes = (await res.json()) as {
data: ChaosLabsPnlItem[];
data: ClcPnlItem[];
};

return parsedRes.data;
}

export function useChaosLabsPnlDistribution() {
export function useClcPnlDistribution() {
const dydxPrice = useAppSelector(BonsaiCore.rewardParams.data).tokenPrice;

const { data: pnlItems, isLoading: pnlItemsLoading } = useQuery({
queryKey: ['chaoslabs/pnls'],
queryFn: wrapAndLogError(
() => getChaosLabsPnlDistribution(),
'LaunchIncentives/fetchPnls',
true
),
queryKey: ['clc-pnls'],
queryFn: wrapAndLogError(() => getClcPnlDistribution(), 'LaunchIncentives/fetchPnls', true),
});

return {
Expand All @@ -171,13 +47,7 @@ export function useChaosLabsPnlDistribution() {
};
}

export type ChaosLabsLeaderboardItem = {
rank: number;
account: string;
estimatedDydxRewards: string | number;
};

export type ChaosLabsCompetitionItem = {
export type IncentiveCompetitionItem = {
rank: number;
account: string;
dollarReward: number;
Expand All @@ -191,35 +61,35 @@ export type BonkPnlItem = {
position: number;
};

export function useChaosLabsFeeLeaderboard({ address }: { address?: string }) {
export function useFeeLeaderboard({ address }: { address?: string }) {
return useQuery({
queryKey: ['chaoslabs/fee-leaderboard', address],
queryKey: ['dydx-fee-leaderboard', address],
queryFn: wrapAndLogError(
() => getChaosLabsFeeLeaderboard({ address }),
() => getDydxFeeLeaderboard({ address }),
'LaunchIncentives/fetchFeeLeaderboard',
true
),
});
}

export type ChaosLabsFeeLeaderboardItemWithRewards = {
export type DydxFeeLeaderboardItemWithRewards = {
address: string;
total_fees: number;
rank: number;
estimatedDollarRewards: number;
estimatedDydxRewards: number;
};

export type ChaosLabsFeeLeaderboardItem = {
export type DydxFeeLeaderboardItem = {
address: string;
total_fees: number;
rank: number;
};

type ChaosLabsFeeLeaderboardResponse = {
type DydxFeeLeaderboardResponse = {
success: boolean;
addressEntry?: ChaosLabsFeeLeaderboardItem;
data: ChaosLabsFeeLeaderboardItem[];
addressEntry?: DydxFeeLeaderboardItem;
data: DydxFeeLeaderboardItem[];
pagination?: {
total: number;
totalPages: number;
Expand All @@ -229,9 +99,9 @@ type ChaosLabsFeeLeaderboardResponse = {
};

export const addRewardsToLeaderboardEntry = (
entry: ChaosLabsFeeLeaderboardItem,
entry: DydxFeeLeaderboardItem,
dydxPrice: number | undefined
): ChaosLabsFeeLeaderboardItemWithRewards => {
): DydxFeeLeaderboardItemWithRewards => {
const dollarRewards = feesToEstimatedDollarRewards(entry.total_fees);
const dydxRewards = dydxPrice ? dollarRewards / dydxPrice : 0;
return {
Expand All @@ -241,12 +111,12 @@ export const addRewardsToLeaderboardEntry = (
};
};

async function getChaosLabsFeeLeaderboard({ address }: { address?: string }) {
async function getDydxFeeLeaderboard({ address }: { address?: string }) {
const res = await fetch(
`https://pp-external-api-ffb2ad95ef03.herokuapp.com/api/dydx-fee-leaderboard?perPage=1000${address ? `&address=${address}` : ''}`
);

const data = (await res.json()) as ChaosLabsFeeLeaderboardResponse;
const data = (await res.json()) as DydxFeeLeaderboardResponse;
return {
leaderboard: data.data,
addressEntry: data.addressEntry,
Expand Down
7 changes: 3 additions & 4 deletions src/hooks/rewards/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,17 @@ export function pointsToEstimatedDollarRewards(

export function feesToEstimatedDollarRewards(totalFees?: number): number {
if (!totalFees) return 0;
return totalFees * 0.5;
return totalFees * CURRENT_SURGE_REWARDS_DETAILS.rebateFraction;
}

// Move to Chaos Labs query once its available
export const CURRENT_SURGE_REWARDS_DETAILS = {
season: 9,
season: 10,
rewardAmount: '',
rewardAmountUsd: 0,
rebatePercent: '50%',
rebatePercentNumeric: '50',
rebateFraction: 0.5,
endTime: '2025-12-31T23:59:59.000Z', // end of month
endTime: '2026-01-31T23:59:59.000Z', // end of jan 2026
};

export const DEC_2025_COMPETITION_DETAILS = {
Expand Down
3 changes: 0 additions & 3 deletions src/hooks/surgeRewards.ts

This file was deleted.

Loading
Loading