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
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;
}
3 changes: 3 additions & 0 deletions apps/hyperdrive-trading/src/ui/portfolio/queryKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ interface PortfolioQueryKeys {
| (OpenLongPositionReceived & { hyperdrive: HyperdriveConfig })[]
| undefined;
};
unclaimedRewards: {
account: Address | undefined;
};
openShortPositions: {
account: Address | undefined;
hyperdrives: HyperdriveConfig[];
Expand Down
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 Down Expand Up @@ -168,7 +166,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
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: "portfolio",
queryId: "unclaimedRewards",
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
Loading
Loading