Skip to content

Commit

Permalink
Update rewards UI to latest changes
Browse files Browse the repository at this point in the history
  • Loading branch information
DannyDelott committed Jan 31, 2025
1 parent 2543e15 commit c129c97
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 69 deletions.
31 changes: 23 additions & 8 deletions apps/hyperdrive-trading/src/rewards/generated/RewardsClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ export interface RewardsResponse {
* @example "0x1234567890abcdef1234567890abcdef12345678"
*/
userAddress: `0x${string}`;
rewards: Rewards;
rewards: Reward[];
}

export type Rewards = {
export interface Reward {
/** @example 1 */
chainId: number;
/**
Expand All @@ -30,7 +30,7 @@ export type Rewards = {
* Amount of tokens claimable.
* @example "1000000000000000000"
*/
claimable: string;
claimableAmount: string;
/**
* Token address of the reward.
* @example "0xBAa5CC21fd487B8Fcc2F632f3F4E8D37262a0842"
Expand All @@ -43,7 +43,7 @@ export type Rewards = {
* @example 123892327
*/
merkleProofLastUpdated: number;
}[];
}

export type QueryParamsType = Record<string | number, any>;
export type ResponseFormat = keyof Omit<Body, "body" | "bodyUsed">;
Expand Down Expand Up @@ -303,13 +303,28 @@ export class RewardsApi<
/**
* @description Returns the rewards associated with a specific address.
*
* @name RewardsDetail
* @name RewardsUserDetail
* @summary Get rewards for an address.
* @request GET:/get/rewards/{address}
* @request GET:/get/rewards/user/{address}
*/
rewardsUserDetail: (address: string, params: RequestParams = {}) =>
this.request<RewardsResponse, void>({
path: `/get/rewards/user/${address}`,
method: "GET",
format: "json",
...params,
}),

/**
* @description Returns mocked rewards from mainnet_test.json for a specific address.
*
* @name RewardsStubDetail
* @summary Get stubbed rewards for an address.
* @request GET:/get/rewards/stub/{address}
*/
rewardsDetail: (address: string, params: RequestParams = {}) =>
rewardsStubDetail: (address: string, params: RequestParams = {}) =>
this.request<RewardsResponse, void>({
path: `/get/rewards/${address}`,
path: `/get/rewards/stub/${address}`,
method: "GET",
format: "json",
...params,
Expand Down
113 changes: 70 additions & 43 deletions apps/hyperdrive-trading/src/rewards/generated/rewards-swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,57 +16,57 @@
"description": "The user's blockchain address.",
"example": "0x1234567890abcdef1234567890abcdef12345678"
},
"rewards": { "$ref": "#/components/schemas/Rewards" }
"rewards": {
"type": "array",
"items": { "$ref": "#/components/schemas/Reward" }
}
},
"required": ["userAddress", "rewards"]
},
"Rewards": {
"type": "array",
"items": {
"type": "object",
"properties": {
"chainId": { "type": "integer", "example": 1 },
"claimContractAddress": {
"type": "string",
"description": "Address of the claim contract.",
"example": "0x0000000000000000000000000000000000000000"
},
"claimable": {
"type": "string",
"description": "Amount of tokens claimable.",
"example": "1000000000000000000"
},
"rewardTokenAddress": {
"type": "string",
"description": "Token address of the reward.",
"example": "0xBAa5CC21fd487B8Fcc2F632f3F4E8D37262a0842"
},
"merkleProof": {
"type": "array",
"items": { "type": "string" },
"nullable": true,
"example": ["0xProof1", "0xProof2", "0xProof3"]
},
"merkleProofLastUpdated": {
"type": "integer",
"description": "Timestamp of the last merkle proof update.",
"example": 123892327
}
"Reward": {
"type": "object",
"properties": {
"chainId": { "type": "integer", "example": 1 },
"claimContractAddress": {
"type": "string",
"description": "Address of the claim contract.",
"example": "0x0000000000000000000000000000000000000000"
},
"required": [
"chainId",
"claimContractAddress",
"claimable",
"rewardTokenAddress",
"merkleProof",
"merkleProofLastUpdated"
]
}
"claimableAmount": {
"type": "string",
"description": "Amount of tokens claimable.",
"example": "1000000000000000000"
},
"rewardTokenAddress": {
"type": "string",
"description": "Token address of the reward.",
"example": "0xBAa5CC21fd487B8Fcc2F632f3F4E8D37262a0842"
},
"merkleProof": {
"type": "array",
"items": { "type": "string" },
"nullable": true,
"example": ["0xProof1", "0xProof2", "0xProof3"]
},
"merkleProofLastUpdated": {
"type": "integer",
"description": "Timestamp of the last merkle proof update.",
"example": 123892327
}
},
"required": [
"chainId",
"claimContractAddress",
"claimableAmount",
"rewardTokenAddress",
"merkleProof",
"merkleProofLastUpdated"
]
}
}
},
"paths": {
"/get/rewards/{address}": {
"/get/rewards/user/{address}": {
"get": {
"summary": "Get rewards for an address.",
"description": "Returns the rewards associated with a specific address.",
Expand All @@ -91,6 +91,33 @@
"400": { "description": "Bad request" }
}
}
},
"/get/rewards/stub/{address}": {
"get": {
"summary": "Get stubbed rewards for an address.",
"description": "Returns mocked rewards from mainnet_test.json for a specific address.",
"parameters": [
{
"in": "path",
"name": "address",
"required": true,
"schema": { "type": "string" },
"description": "The address to retrieve rewards for."
}
],
"responses": {
"200": {
"description": "Successful response",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/RewardsResponse" }
}
}
},
"404": { "description": "No rewards found for the address." },
"500": { "description": "Internal Server Error." }
}
}
}
},
"tags": []
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {
AppConfig,
// eslint-disable-next-line no-restricted-imports
appConfig as appConfigFromImport,
isMainnetChain,
mainnetAppConfig,
testnetAppConfig,
Expand All @@ -9,12 +11,24 @@ import { useFeatureFlag } from "src/ui/base/featureFlags/featureFlags";
import { useTokenList } from "src/ui/tokenlist/useTokenList";
import { useChainId } from "wagmi";

export function useAppConfigForConnectedChain(): AppConfig {
interface UseAppConfigForConnectedChainOptions {
/**
* Only include configurations for the connected chain. If false, this will
* include both testnet/forks and mainnet chains. Defaults to true.
*/
strict?: boolean;
}
export function useAppConfigForConnectedChain(
options: UseAppConfigForConnectedChainOptions = { strict: true },
): AppConfig {
const connectedChainId = useChainId();

const appConfig = isMainnetChain(connectedChainId)
? mainnetAppConfig
: testnetAppConfig;
let appConfig = appConfigFromImport;
if (options.strict) {
appConfig = isMainnetChain(connectedChainId)
? mainnetAppConfig
: testnetAppConfig;
}

// Add any zap tokens to the appConfig using uniswap's tokenlist
const { isFlagEnabled } = useFeatureFlag("zaps");
Expand All @@ -32,5 +46,5 @@ export function useAppConfigForConnectedChain(): AppConfig {
};
}

return appConfig;
return appConfigFromImport;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function RewardsContainer({
account: Address | undefined;
}): ReactElement {
const { rewards, rewardsStatus } = usePortfolioRewardsData({ account });
const appConfig = useAppConfigForConnectedChain();
const appConfig = useAppConfigForConnectedChain({ strict: false });
if (!account) {
return <NoWalletConnected />;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from "@tanstack/react-table";
import classNames from "classnames";
import { ReactElement } from "react";
import { Rewards } from "src/rewards/generated/RewardsClient";
import { Reward } from "src/rewards/generated/RewardsClient";
import { useAppConfigForConnectedChain } from "src/ui/appconfig/useAppConfigForConnectedChain";
import { Pagination } from "src/ui/base/components/Pagination";
import { formatBalance } from "src/ui/base/formatting/formatBalance";
Expand All @@ -21,9 +21,9 @@ export function RewardsTableDesktop({
rewards,
}: {
account: Address;
rewards: Rewards;
rewards: Reward[];
}): ReactElement {
const appConfig = useAppConfigForConnectedChain();
const appConfig = useAppConfigForConnectedChain({ strict: false });
const tableInstance = useReactTable({
columns: getColumns(appConfig),
data: rewards || [],
Expand Down Expand Up @@ -132,8 +132,6 @@ export function RewardsTableDesktop({
);
}

// TODO: Remove this type once the swagger is defined properly
type Reward = NonNullable<Rewards[number]>;
const columnHelper = createColumnHelper<Reward>();

function getColumns(appConfig: AppConfig) {
Expand All @@ -142,6 +140,8 @@ function getColumns(appConfig: AppConfig) {
id: "asset",
header: "Asset",
cell: ({ row }) => {
console.log("row", row, appConfig);

const token = getToken({
appConfig,
chainId: row.original.chainId,
Expand All @@ -168,7 +168,7 @@ function getColumns(appConfig: AppConfig) {
<div className="flex flex-col">
<span className="flex font-dmMono text-neutral-content">
{formatBalance({
balance: BigInt(row.original.claimable) || 0n,
balance: BigInt(row.original.claimableAmount) || 0n,
decimals: token.decimals,
places: token.places,
})}{" "}
Expand Down
12 changes: 12 additions & 0 deletions apps/hyperdrive-trading/src/ui/portfolio/rewards/queryKeys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import "src/base/makeQueryKey";
import { Address } from "viem";
interface RewardQueryKeys {
userRewards: {
account: Address | undefined;
};
}
declare module "src/base/makeQueryKey" {
interface QueryKeys {
rewards: RewardQueryKeys;
}
}
26 changes: 20 additions & 6 deletions apps/hyperdrive-trading/src/ui/portfolio/rewards/useRewardsData.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,40 @@
import { useQuery } from "@tanstack/react-query";
import { makeQueryKey } from "src/base/makeQueryKey";
import { Rewards, RewardsApi } from "src/rewards/generated/RewardsClient";
import { makeQueryKey2 } from "src/base/makeQueryKey";
import { Reward, RewardsApi } from "src/rewards/generated/RewardsClient";
import { Address } from "viem";

export function usePortfolioRewardsData({
account,
}: {
account: Address | undefined;
}): {
rewards: Rewards | undefined;
rewards: Reward[] | undefined;
rewardsStatus: "error" | "success" | "loading";
} {
const queryEnabled = !!account;
const { data: rewards, status: rewardsStatus } = useQuery({
queryKey: makeQueryKey("rewards", { account }),
queryKey: makeQueryKey2({
namespace: "rewards",
queryId: "userRewards",
params: { account },
}),
queryFn: queryEnabled
? async () => {
const rewardsApi = new RewardsApi({
baseUrl: import.meta.env.VITE_REWARDS_BASE_URL,
});
const response = await rewardsApi.get.rewardsDetail(account);
return response.rewards;
try {
const response = await rewardsApi.get.rewardsStubDetail(account);
return response.rewards;
} catch (error: any) {
// This throws a 404 if the account does not have any rewards, which
// is fine, just return an empty array and display no rewards
if (error.error.error === "No rewards found for this address") {
return [];
}
// There are no other well-known errors we can catch, so re-throw
throw error;
}
}
: undefined,
enabled: queryEnabled,
Expand Down

0 comments on commit c129c97

Please sign in to comment.