From ec74391d8dd446b9778affb6113dce6b46321287 Mon Sep 17 00:00:00 2001 From: keyvan Date: Mon, 9 Sep 2024 19:48:40 -0700 Subject: [PATCH 01/18] feat(hermes): add publisher caps endpoint to js client --- apps/hermes/client/js/package.json | 2 +- apps/hermes/client/js/src/HermesClient.ts | 25 +++++++++++++- apps/hermes/client/js/src/zodSchemas.ts | 41 +++++++++++++++++++++-- 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/apps/hermes/client/js/package.json b/apps/hermes/client/js/package.json index dc609edfec..c5ac8aaa47 100644 --- a/apps/hermes/client/js/package.json +++ b/apps/hermes/client/js/package.json @@ -1,6 +1,6 @@ { "name": "@pythnetwork/hermes-client", - "version": "1.0.4", + "version": "1.1.0", "description": "Pyth Hermes Client", "author": { "name": "Pyth Data Association" diff --git a/apps/hermes/client/js/src/HermesClient.ts b/apps/hermes/client/js/src/HermesClient.ts index f4a12eaed6..537d3aa92f 100644 --- a/apps/hermes/client/js/src/HermesClient.ts +++ b/apps/hermes/client/js/src/HermesClient.ts @@ -5,11 +5,12 @@ import { camelToSnakeCaseObject } from "./utils"; // Accessing schema objects export type AssetType = z.infer; -export type BinaryPriceUpdate = z.infer; +export type BinaryPriceUpdate = z.infer; export type EncodingType = z.infer; export type PriceFeedMetadata = z.infer; export type PriceIdInput = z.infer; export type PriceUpdate = z.infer; +export type PublisherCaps = z.infer; const DEFAULT_TIMEOUT: DurationInMs = 5000; const DEFAULT_HTTP_RETRIES = 3; @@ -120,6 +121,28 @@ export class HermesClient { ); } + /** + * Fetch the latest publisher stake caps. + * This endpoint can be customized by specifying the encoding type and whether the results should also return the parsed publisher caps. + * This will throw an error if there is a network problem or the price service returns a non-ok response. + * + * @param options Optional parameters: + * - encoding: Encoding type. If specified, return the price update in the encoding specified by the encoding parameter. Default is hex. + * - parsed: Boolean to specify if the parsed price update should be included in the response. Default is false. + * + * @returns PublisherCaps object containing the latest publisher stake caps. + */ + async getLatestPublisherCaps(options?: { + encoding?: EncodingType; + parsed?: boolean; + }): Promise { + const url = new URL("v2/updates/publisher_stake_caps/latest", this.baseURL); + if (options) { + this.appendUrlSearchParams(url, options); + } + return await this.httpRequest(url.toString(), schemas.PublisherCaps); + } + /** * Fetch the latest price updates for a set of price feed IDs. * This endpoint can be customized by specifying the encoding type and whether the results should also return the parsed price update using the options object. diff --git a/apps/hermes/client/js/src/zodSchemas.ts b/apps/hermes/client/js/src/zodSchemas.ts index 5745eabdce..ce00bcbefe 100644 --- a/apps/hermes/client/js/src/zodSchemas.ts +++ b/apps/hermes/client/js/src/zodSchemas.ts @@ -9,7 +9,7 @@ const PriceFeedMetadata = z .passthrough(); const PriceIdInput = z.string(); const EncodingType = z.enum(["hex", "base64"]); -const BinaryPriceUpdate = z +const BinaryUpdate = z .object({ data: z.array(z.string()), encoding: EncodingType }) .passthrough(); const RpcPrice = z @@ -38,10 +38,24 @@ const ParsedPriceUpdate = z .passthrough(); const PriceUpdate = z .object({ - binary: BinaryPriceUpdate, + binary: BinaryUpdate, parsed: z.array(ParsedPriceUpdate).nullish(), }) .passthrough(); +const ParsedPublisherCap = z.object({ + publisher: z.string(), + cap: z.number().int(), +}); + +const PublisherCaps = z.object({ + binary: BinaryUpdate, + parsed: z + .object({ + publisher_stake_caps: z.array(ParsedPublisherCap), + }) + .array() + .nullish(), +}); export const schemas = { AssetType, @@ -50,11 +64,12 @@ export const schemas = { PriceFeedMetadata, PriceIdInput, EncodingType, - BinaryPriceUpdate, + BinaryUpdate, RpcPrice, RpcPriceFeedMetadataV2, ParsedPriceUpdate, PriceUpdate, + PublisherCaps, }; const endpoints = makeApi([ @@ -196,6 +211,26 @@ Given a collection of price feed ids, retrieve the latest Pyth price for each pr }, ], }, + { + method: "get", + path: "/v2/updates/publisher_stake_caps/latest", + alias: "latest_publisher_stake_caps", + description: `Get the most recent publisher stake caps update data.`, + requestFormat: "json", + parameters: [ + { + name: "encoding", + type: "Query", + schema: z.enum(["hex", "base64"]).optional(), + }, + { + name: "parsed", + type: "Query", + schema: z.boolean().optional(), + }, + ], + response: z.array(PublisherCaps), + }, ]); export const api = new Zodios(endpoints); From be70475a4e1e5da6e82cb0fe1bf18b1837d86a6c Mon Sep 17 00:00:00 2001 From: keyvan Date: Mon, 9 Sep 2024 19:52:10 -0700 Subject: [PATCH 02/18] feat(staking): fetch pool capacity from hermes --- apps/staking/package.json | 7 +- apps/staking/src/api.ts | 13 ++- apps/staking/src/hooks/use-dashboard-data.ts | 4 +- apps/staking/src/hooks/use-stake-account.tsx | 8 +- pnpm-lock.yaml | 113 +++++++------------ 5 files changed, 68 insertions(+), 77 deletions(-) diff --git a/apps/staking/package.json b/apps/staking/package.json index aaceb7fe64..564ff9d999 100644 --- a/apps/staking/package.json +++ b/apps/staking/package.json @@ -26,20 +26,21 @@ "@bonfida/spl-name-service": "^3.0.0", "@heroicons/react": "^2.1.4", "@next/third-parties": "^14.2.5", + "@pythnetwork/hermes-client": "workspace:*", "@pythnetwork/staking-sdk": "workspace:*", "@solana/wallet-adapter-base": "^0.9.20", - "@solana/wallet-adapter-react": "^0.15.28", "@solana/wallet-adapter-react-ui": "^0.9.27", + "@solana/wallet-adapter-react": "^0.15.28", "@solana/wallet-adapter-wallets": "0.19.10", "@solana/web3.js": "^1.95.2", "clsx": "^2.1.1", "dnum": "^2.13.1", "next": "^14.2.5", "pino": "^9.3.2", - "react": "^18.3.1", - "react-aria": "^3.34.3", "react-aria-components": "^1.3.3", + "react-aria": "^3.34.3", "react-dom": "^18.3.1", + "react": "^18.3.1", "recharts": "^2.12.7", "swr": "^2.2.5" }, diff --git a/apps/staking/src/api.ts b/apps/staking/src/api.ts index 05c3819426..88dd3f0313 100644 --- a/apps/staking/src/api.ts +++ b/apps/staking/src/api.ts @@ -1,6 +1,7 @@ // TODO remove these disables when moving off the mock APIs /* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/require-await */ +import type { HermesClient } from "@pythnetwork/hermes-client"; import { getAmountByTargetAndState, getCurrentEpoch, @@ -144,6 +145,7 @@ export const getStakeAccounts = async ( export const loadData = async ( client: PythStakingClient, + hermesClient: HermesClient, stakeAccount: StakeAccountPositions, ): Promise => { const [ @@ -179,6 +181,15 @@ export const loadData = async ( epoch: currentEpoch, }); + const publisherCaps = await hermesClient.getLatestPublisherCaps({ + parsed: true, + }); + + const getPublisherCap = (publisher: PublicKey) => + BigInt(publisherCaps.parsed?.[0]?.publisher_stake_caps.find( + ({ publisher: p }) => p === publisher.toBase58(), + )?.cap ?? 0); + return { lastSlash: undefined, // TODO availableRewards: 0n, // TODO @@ -198,7 +209,7 @@ export const loadData = async ( isSelf: false, // TODO name: undefined, // TODO numFeeds: 0, // TODO - poolCapacity: 100n, // TODO + poolCapacity: getPublisherCap(publisher), poolUtilization: 0n, // TODO publicKey: publisher, qualityRanking: 0, // TODO diff --git a/apps/staking/src/hooks/use-dashboard-data.ts b/apps/staking/src/hooks/use-dashboard-data.ts index 1c507094cc..ccde6aa0a6 100644 --- a/apps/staking/src/hooks/use-dashboard-data.ts +++ b/apps/staking/src/hooks/use-dashboard-data.ts @@ -12,11 +12,11 @@ const REFRESH_INTERVAL = 1 * ONE_MINUTE_IN_MS; export const getCacheKey = (stakeAccount: PublicKey) => stakeAccount.toBase58(); export const useDashboardData = () => { - const { client, account } = useSelectedStakeAccount(); + const { client, hermesClient, account } = useSelectedStakeAccount(); const { data, isLoading, mutate, ...rest } = useSWR( getCacheKey(account.address), - () => loadData(client, account), + () => loadData(client, hermesClient, account), { refreshInterval: REFRESH_INTERVAL, }, diff --git a/apps/staking/src/hooks/use-stake-account.tsx b/apps/staking/src/hooks/use-stake-account.tsx index 3efd0d44d9..1c110cc3a8 100644 --- a/apps/staking/src/hooks/use-stake-account.tsx +++ b/apps/staking/src/hooks/use-stake-account.tsx @@ -1,5 +1,6 @@ "use client"; +import { HermesClient } from "@pythnetwork/hermes-client"; import type { StakeAccountPositions } from "@pythnetwork/staking-sdk"; import { PythStakingClient } from "@pythnetwork/staking-sdk"; import { useConnection, useWallet } from "@solana/wallet-adapter-react"; @@ -38,12 +39,14 @@ const State = { Loaded: ( client: PythStakingClient, + hermesClient: HermesClient, account: StakeAccountPositions, allAccounts: [StakeAccountPositions, ...StakeAccountPositions[]], selectAccount: (account: StakeAccountPositions) => void, ) => ({ type: StateType.Loaded as const, client, + hermesClient, account, allAccounts, selectAccount, @@ -83,7 +86,7 @@ const useStakeAccountState = () => { (account: StakeAccountPositions) => { setState((cur) => cur.type === StateType.Loaded - ? State.Loaded(cur.client, account, cur.allAccounts, setAccount) + ? State.Loaded(cur.client, cur.hermesClient, account, cur.allAccounts, setAccount) : cur, ); }, @@ -109,6 +112,8 @@ const useStakeAccountState = () => { signTransaction: wallet.signTransaction, }, }); + // TODO: use env var to support mainnet + const hermesClient = new HermesClient("https://hermes-beta.pyth.network"); getStakeAccounts(client) .then((accounts) => { const [firstAccount, ...otherAccounts] = accounts; @@ -116,6 +121,7 @@ const useStakeAccountState = () => { setState( State.Loaded( client, + hermesClient, firstAccount, [firstAccount, ...otherAccounts], setAccount, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6355639e7a..ae38b9afb2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -68,7 +68,7 @@ importers: version: 2.1.1 connectkit: specifier: ^1.8.2 - version: 1.8.2(m5fu6jwi7nvuqo5lp7m3jyfehy) + version: 1.8.2(@babel/core@7.24.7)(@tanstack/react-query@5.45.1(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1)(viem@2.15.1(bufferutil@4.0.8)(typescript@5.5.2)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.10.4(@react-native-async-storage/async-storage@1.23.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10)))(@tanstack/query-core@5.45.0)(@tanstack/react-query@5.45.1(react@18.3.1))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@9.0.21)(react-dom@18.3.1(react@18.3.1))(react-i18next@13.5.0(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.2)(utf-8-validate@5.0.10)(viem@2.15.1(bufferutil@4.0.8)(typescript@5.5.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)) cryptocurrency-icons: specifier: ^0.18.1 version: 0.18.1 @@ -111,7 +111,7 @@ importers: version: 4.9.1 '@cprussin/eslint-config': specifier: ^3.0.0 - version: 3.0.0(@typescript-eslint/eslint-plugin@7.13.1(eslint@9.5.0)(typescript@5.5.2))(jest@29.7.0(@types/node@20.14.7)(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2)))(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2))(typescript@5.5.2) + version: 3.0.0(@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2))(eslint@9.5.0)(typescript@5.5.2))(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2))(jest@29.7.0(@types/node@20.14.7)(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2)))(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2))(typescript@5.5.2) '@cprussin/jest-config': specifier: ^1.4.1 version: 1.4.1(@babel/core@7.24.7)(@jest/globals@29.7.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(@types/jest@29.5.12)(@types/node@20.14.7)(babel-jest@29.7.0(@babel/core@7.24.7))(bufferutil@4.0.8)(eslint@9.5.0)(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2))(utf-8-validate@5.0.10) @@ -333,6 +333,9 @@ importers: '@next/third-parties': specifier: ^14.2.5 version: 14.2.6(next@14.2.6(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + '@pythnetwork/hermes-client': + specifier: workspace:* + version: link:../hermes/client/js '@pythnetwork/staking-sdk': specifier: workspace:* version: link:../../governance/pyth_staking_sdk @@ -387,7 +390,7 @@ importers: version: 4.9.1 '@cprussin/eslint-config': specifier: ^3.0.0 - version: 3.0.0(@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4))(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(jest@29.7.0(@types/node@22.2.0)(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4)))(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4))(typescript@5.5.4) + version: 3.0.0(@typescript-eslint/eslint-plugin@7.13.1(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4))(jest@29.7.0(@types/node@22.2.0)(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4)))(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4))(typescript@5.5.4) '@cprussin/jest-config': specifier: ^1.4.1 version: 1.4.1(@babel/core@7.24.7)(@jest/globals@29.7.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(@types/jest@29.5.12)(@types/node@22.2.0)(babel-jest@29.7.0(@babel/core@7.24.7))(bufferutil@4.0.8)(eslint@9.9.0(jiti@1.21.0))(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4))(utf-8-validate@5.0.10) @@ -1444,7 +1447,7 @@ importers: version: 0.9.24(bufferutil@4.0.7)(encoding@0.1.13)(google-protobuf@3.21.4)(utf-8-validate@6.0.3) '@matterlabs/hardhat-zksync': specifier: ^1.1.0 - version: 1.1.0(w3kf4mhrlhkqd2mcprd2ix74zu) + version: 1.1.0(@matterlabs/hardhat-zksync-deploy@0.6.6(encoding@0.1.13)(ethers@5.7.2(bufferutil@4.0.7)(utf-8-validate@6.0.3))(hardhat@2.22.8(bufferutil@4.0.7)(ts-node@10.9.2(@types/node@22.5.1)(typescript@4.9.5))(typescript@4.9.5)(utf-8-validate@6.0.3))(zksync-web3@0.13.4(ethers@5.7.2(bufferutil@4.0.7)(utf-8-validate@6.0.3))))(@matterlabs/hardhat-zksync-ethers@1.1.0(bufferutil@4.0.7)(encoding@0.1.13)(ethers@5.7.2(bufferutil@4.0.7)(utf-8-validate@6.0.3))(ts-node@10.9.2(@types/node@22.5.1)(typescript@4.9.5))(typescript@4.9.5)(utf-8-validate@6.0.3)(zksync-ethers@6.11.2(ethers@5.7.2(bufferutil@4.0.7)(utf-8-validate@6.0.3))))(@matterlabs/hardhat-zksync-node@1.1.1(encoding@0.1.13)(hardhat@2.22.8(bufferutil@4.0.7)(ts-node@10.9.2(@types/node@22.5.1)(typescript@4.9.5))(typescript@4.9.5)(utf-8-validate@6.0.3)))(@matterlabs/hardhat-zksync-solc@0.3.17(encoding@0.1.13)(hardhat@2.22.8(bufferutil@4.0.7)(ts-node@10.9.2(@types/node@22.5.1)(typescript@4.9.5))(typescript@4.9.5)(utf-8-validate@6.0.3)))(@matterlabs/hardhat-zksync-upgradable@1.5.2(bufferutil@4.0.7)(encoding@0.1.13)(ts-node@10.9.2(@types/node@22.5.1)(typescript@4.9.5))(typescript@4.9.5)(utf-8-validate@6.0.3))(@matterlabs/hardhat-zksync-verify@1.6.0(@nomicfoundation/hardhat-verify@2.0.9(hardhat@2.22.8(bufferutil@4.0.7)(ts-node@10.9.2(@types/node@22.5.1)(typescript@4.9.5))(typescript@4.9.5)(utf-8-validate@6.0.3)))(encoding@0.1.13)(hardhat@2.22.8(bufferutil@4.0.7)(ts-node@10.9.2(@types/node@22.5.1)(typescript@4.9.5))(typescript@4.9.5)(utf-8-validate@6.0.3)))(bufferutil@4.0.7)(ts-node@10.9.2(@types/node@22.5.1)(typescript@4.9.5))(typescript@4.9.5)(utf-8-validate@6.0.3) '@matterlabs/hardhat-zksync-deploy': specifier: ^0.6.6 version: 0.6.6(encoding@0.1.13)(ethers@5.7.2(bufferutil@4.0.7)(utf-8-validate@6.0.3))(hardhat@2.22.8(bufferutil@4.0.7)(ts-node@10.9.2(@types/node@22.5.1)(typescript@4.9.5))(typescript@4.9.5)(utf-8-validate@6.0.3))(zksync-web3@0.13.4(ethers@5.7.2(bufferutil@4.0.7)(utf-8-validate@6.0.3))) @@ -24321,7 +24324,7 @@ snapshots: transitivePeerDependencies: - debug - '@cprussin/eslint-config@3.0.0(@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4))(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(jest@29.7.0(@types/node@22.2.0)(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4)))(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4))(typescript@5.5.4)': + '@cprussin/eslint-config@3.0.0(@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2))(eslint@9.5.0)(typescript@5.5.2))(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2))(jest@29.7.0(@types/node@20.14.7)(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2)))(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2))(typescript@5.5.2)': dependencies: '@babel/core': 7.24.7 '@babel/eslint-parser': 7.24.7(@babel/core@7.24.7)(eslint@9.5.0) @@ -24333,22 +24336,22 @@ snapshots: eslint: 9.5.0 eslint-config-prettier: 9.1.0(eslint@9.5.0) eslint-config-turbo: 1.13.4(eslint@9.5.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(eslint@9.5.0) - eslint-plugin-jest: 28.6.0(@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4))(eslint@9.5.0)(jest@29.7.0(@types/node@22.2.0)(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4)))(typescript@5.5.4) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2))(eslint@9.5.0) + eslint-plugin-jest: 28.6.0(@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2))(eslint@9.5.0)(typescript@5.5.2))(eslint@9.5.0)(jest@29.7.0(@types/node@20.14.7)(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2)))(typescript@5.5.2) eslint-plugin-jest-dom: 5.4.0(eslint@9.5.0) eslint-plugin-jsonc: 2.16.0(eslint@9.5.0) eslint-plugin-jsx-a11y: 6.8.0(eslint@9.5.0) eslint-plugin-n: 17.9.0(eslint@9.5.0) eslint-plugin-react: 7.34.2(eslint@9.5.0) eslint-plugin-react-hooks: 4.6.2(eslint@9.5.0) - eslint-plugin-storybook: 0.8.0(eslint@9.5.0)(typescript@5.5.4) - eslint-plugin-tailwindcss: 3.17.3(tailwindcss@3.4.4(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4))) - eslint-plugin-testing-library: 6.2.2(eslint@9.5.0)(typescript@5.5.4) + eslint-plugin-storybook: 0.8.0(eslint@9.5.0)(typescript@5.5.2) + eslint-plugin-tailwindcss: 3.17.3(tailwindcss@3.4.4(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2))) + eslint-plugin-testing-library: 6.2.2(eslint@9.5.0)(typescript@5.5.2) eslint-plugin-tsdoc: 0.3.0 eslint-plugin-unicorn: 53.0.0(eslint@9.5.0) globals: 15.6.0 - tailwindcss: 3.4.4(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4)) - typescript-eslint: 7.13.1(eslint@9.5.0)(typescript@5.5.4) + tailwindcss: 3.4.4(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2)) + typescript-eslint: 7.13.1(eslint@9.5.0)(typescript@5.5.2) transitivePeerDependencies: - '@testing-library/dom' - '@typescript-eslint/eslint-plugin' @@ -24399,7 +24402,7 @@ snapshots: - ts-node - typescript - '@cprussin/eslint-config@3.0.0(@typescript-eslint/eslint-plugin@7.13.1(eslint@9.5.0)(typescript@5.5.2))(jest@29.7.0(@types/node@20.14.7)(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2)))(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2))(typescript@5.5.2)': + '@cprussin/eslint-config@3.0.0(@typescript-eslint/eslint-plugin@7.13.1(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4))(jest@29.7.0(@types/node@22.2.0)(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4)))(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4))(typescript@5.5.4)': dependencies: '@babel/core': 7.24.7 '@babel/eslint-parser': 7.24.7(@babel/core@7.24.7)(eslint@9.5.0) @@ -24411,22 +24414,22 @@ snapshots: eslint: 9.5.0 eslint-config-prettier: 9.1.0(eslint@9.5.0) eslint-config-turbo: 1.13.4(eslint@9.5.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(eslint@9.5.0) - eslint-plugin-jest: 28.6.0(@typescript-eslint/eslint-plugin@7.13.1(eslint@9.5.0)(typescript@5.5.2))(eslint@9.5.0)(jest@29.7.0(@types/node@20.14.7)(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2)))(typescript@5.5.2) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2))(eslint@9.5.0) + eslint-plugin-jest: 28.6.0(@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@8.3.0(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4))(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4))(eslint@9.5.0)(jest@29.7.0(@types/node@22.2.0)(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4)))(typescript@5.5.4) eslint-plugin-jest-dom: 5.4.0(eslint@9.5.0) eslint-plugin-jsonc: 2.16.0(eslint@9.5.0) eslint-plugin-jsx-a11y: 6.8.0(eslint@9.5.0) eslint-plugin-n: 17.9.0(eslint@9.5.0) eslint-plugin-react: 7.34.2(eslint@9.5.0) eslint-plugin-react-hooks: 4.6.2(eslint@9.5.0) - eslint-plugin-storybook: 0.8.0(eslint@9.5.0)(typescript@5.5.2) - eslint-plugin-tailwindcss: 3.17.3(tailwindcss@3.4.4(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2))) - eslint-plugin-testing-library: 6.2.2(eslint@9.5.0)(typescript@5.5.2) + eslint-plugin-storybook: 0.8.0(eslint@9.5.0)(typescript@5.5.4) + eslint-plugin-tailwindcss: 3.17.3(tailwindcss@3.4.4(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4))) + eslint-plugin-testing-library: 6.2.2(eslint@9.5.0)(typescript@5.5.4) eslint-plugin-tsdoc: 0.3.0 eslint-plugin-unicorn: 53.0.0(eslint@9.5.0) globals: 15.6.0 - tailwindcss: 3.4.4(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2)) - typescript-eslint: 7.13.1(eslint@9.5.0)(typescript@5.5.2) + tailwindcss: 3.4.4(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4)) + typescript-eslint: 7.13.1(eslint@9.5.0)(typescript@5.5.4) transitivePeerDependencies: - '@testing-library/dom' - '@typescript-eslint/eslint-plugin' @@ -28087,8 +28090,8 @@ snapshots: - encoding - supports-color - '@matterlabs/hardhat-zksync@1.1.0(w3kf4mhrlhkqd2mcprd2ix74zu)': - dependencies: + ? '@matterlabs/hardhat-zksync@1.1.0(@matterlabs/hardhat-zksync-deploy@0.6.6(encoding@0.1.13)(ethers@5.7.2(bufferutil@4.0.7)(utf-8-validate@6.0.3))(hardhat@2.22.8(bufferutil@4.0.7)(ts-node@10.9.2(@types/node@22.5.1)(typescript@4.9.5))(typescript@4.9.5)(utf-8-validate@6.0.3))(zksync-web3@0.13.4(ethers@5.7.2(bufferutil@4.0.7)(utf-8-validate@6.0.3))))(@matterlabs/hardhat-zksync-ethers@1.1.0(bufferutil@4.0.7)(encoding@0.1.13)(ethers@5.7.2(bufferutil@4.0.7)(utf-8-validate@6.0.3))(ts-node@10.9.2(@types/node@22.5.1)(typescript@4.9.5))(typescript@4.9.5)(utf-8-validate@6.0.3)(zksync-ethers@6.11.2(ethers@5.7.2(bufferutil@4.0.7)(utf-8-validate@6.0.3))))(@matterlabs/hardhat-zksync-node@1.1.1(encoding@0.1.13)(hardhat@2.22.8(bufferutil@4.0.7)(ts-node@10.9.2(@types/node@22.5.1)(typescript@4.9.5))(typescript@4.9.5)(utf-8-validate@6.0.3)))(@matterlabs/hardhat-zksync-solc@0.3.17(encoding@0.1.13)(hardhat@2.22.8(bufferutil@4.0.7)(ts-node@10.9.2(@types/node@22.5.1)(typescript@4.9.5))(typescript@4.9.5)(utf-8-validate@6.0.3)))(@matterlabs/hardhat-zksync-upgradable@1.5.2(bufferutil@4.0.7)(encoding@0.1.13)(ts-node@10.9.2(@types/node@22.5.1)(typescript@4.9.5))(typescript@4.9.5)(utf-8-validate@6.0.3))(@matterlabs/hardhat-zksync-verify@1.6.0(@nomicfoundation/hardhat-verify@2.0.9(hardhat@2.22.8(bufferutil@4.0.7)(ts-node@10.9.2(@types/node@22.5.1)(typescript@4.9.5))(typescript@4.9.5)(utf-8-validate@6.0.3)))(encoding@0.1.13)(hardhat@2.22.8(bufferutil@4.0.7)(ts-node@10.9.2(@types/node@22.5.1)(typescript@4.9.5))(typescript@4.9.5)(utf-8-validate@6.0.3)))(bufferutil@4.0.7)(ts-node@10.9.2(@types/node@22.5.1)(typescript@4.9.5))(typescript@4.9.5)(utf-8-validate@6.0.3)' + : dependencies: '@matterlabs/hardhat-zksync-deploy': 0.6.6(encoding@0.1.13)(ethers@5.7.2(bufferutil@4.0.7)(utf-8-validate@6.0.3))(hardhat@2.22.8(bufferutil@4.0.7)(ts-node@10.9.2(@types/node@22.5.1)(typescript@4.9.5))(typescript@4.9.5)(utf-8-validate@6.0.3))(zksync-web3@0.13.4(ethers@5.7.2(bufferutil@4.0.7)(utf-8-validate@6.0.3))) '@matterlabs/hardhat-zksync-ethers': 1.1.0(bufferutil@4.0.7)(encoding@0.1.13)(ethers@5.7.2(bufferutil@4.0.7)(utf-8-validate@6.0.3))(ts-node@10.9.2(@types/node@22.5.1)(typescript@4.9.5))(typescript@4.9.5)(utf-8-validate@6.0.3)(zksync-ethers@6.11.2(ethers@5.7.2(bufferutil@4.0.7)(utf-8-validate@6.0.3))) '@matterlabs/hardhat-zksync-node': 1.1.1(encoding@0.1.13)(hardhat@2.22.8(bufferutil@4.0.7)(ts-node@10.9.2(@types/node@22.5.1)(typescript@4.9.5))(typescript@4.9.5)(utf-8-validate@6.0.3)) @@ -33967,7 +33970,7 @@ snapshots: '@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(eslint@9.5.0)(typescript@5.5.4)': dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 7.13.1(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4) + '@typescript-eslint/parser': 7.13.1(eslint@9.5.0)(typescript@5.5.4) '@typescript-eslint/scope-manager': 7.13.1 '@typescript-eslint/type-utils': 7.13.1(eslint@9.5.0)(typescript@5.5.4) '@typescript-eslint/utils': 7.13.1(eslint@9.5.0)(typescript@5.5.4) @@ -33982,25 +33985,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4)': - dependencies: - '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 7.13.1(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4) - '@typescript-eslint/scope-manager': 7.13.1 - '@typescript-eslint/type-utils': 7.13.1(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4) - '@typescript-eslint/utils': 7.13.1(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4) - '@typescript-eslint/visitor-keys': 7.13.1 - eslint: 9.9.0(jiti@1.21.0) - graphemer: 1.4.0 - ignore: 5.3.1 - natural-compare: 1.4.0 - ts-api-utils: 1.3.0(typescript@5.5.4) - optionalDependencies: - typescript: 5.5.4 - transitivePeerDependencies: - - supports-color - optional: true - '@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@8.3.0(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4))(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4)': dependencies: '@eslint-community/regexpp': 4.10.0 @@ -34149,14 +34133,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.13.1(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4)': + '@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4)': dependencies: '@typescript-eslint/scope-manager': 7.13.1 '@typescript-eslint/types': 7.13.1 '@typescript-eslint/typescript-estree': 7.13.1(typescript@5.5.4) '@typescript-eslint/visitor-keys': 7.13.1 debug: 4.3.5 - eslint: 9.9.0(jiti@1.21.0) + eslint: 9.5.0 optionalDependencies: typescript: 5.5.4 transitivePeerDependencies: @@ -35027,8 +35011,8 @@ snapshots: '@vue/shared@3.4.34': {} - '@wagmi/connectors@5.0.16(mehtb7r3xxh3anmscqllj3vxmi)': - dependencies: + ? '@wagmi/connectors@5.0.16(@react-native-async-storage/async-storage@1.23.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.3)(@wagmi/core@2.11.4(@tanstack/query-core@5.45.0)(@types/react@18.3.3)(bufferutil@4.0.8)(immer@9.0.21)(react@18.3.1)(typescript@5.5.2)(utf-8-validate@5.0.10)(viem@2.15.1(bufferutil@4.0.8)(typescript@5.5.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8))(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react-i18next@13.5.0(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.2)(utf-8-validate@5.0.10)(viem@2.15.1(bufferutil@4.0.8)(typescript@5.5.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)' + : dependencies: '@coinbase/wallet-sdk': 4.0.3 '@metamask/sdk': 0.26.0(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react-i18next@13.5.0(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(utf-8-validate@5.0.10) '@safe-global/safe-apps-provider': 0.18.1(bufferutil@4.0.8)(typescript@5.5.2)(utf-8-validate@5.0.10)(zod@3.23.8) @@ -37893,8 +37877,8 @@ snapshots: transitivePeerDependencies: - supports-color - connectkit@1.8.2(m5fu6jwi7nvuqo5lp7m3jyfehy): - dependencies: + ? connectkit@1.8.2(@babel/core@7.24.7)(@tanstack/react-query@5.45.1(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1)(viem@2.15.1(bufferutil@4.0.8)(typescript@5.5.2)(utf-8-validate@5.0.10)(zod@3.23.8))(wagmi@2.10.4(@react-native-async-storage/async-storage@1.23.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10)))(@tanstack/query-core@5.45.0)(@tanstack/react-query@5.45.1(react@18.3.1))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@9.0.21)(react-dom@18.3.1(react@18.3.1))(react-i18next@13.5.0(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.2)(utf-8-validate@5.0.10)(viem@2.15.1(bufferutil@4.0.8)(typescript@5.5.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)) + : dependencies: '@tanstack/react-query': 5.45.1(react@18.3.1) buffer: 6.0.3 detect-browser: 5.3.0 @@ -39508,11 +39492,11 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint@9.5.0): + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint@9.5.0): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 7.13.1(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4) + '@typescript-eslint/parser': 7.13.1(eslint@9.5.0)(typescript@5.5.2) eslint: 9.5.0 eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: @@ -39535,7 +39519,7 @@ snapshots: eslint: 9.5.0 eslint-compat-utils: 0.5.1(eslint@9.5.0) - eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(eslint@9.5.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2))(eslint@9.5.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -39545,7 +39529,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.5.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint@9.5.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint@9.5.0) hasown: 2.0.2 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -39556,7 +39540,7 @@ snapshots: semver: 6.3.1 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 7.13.1(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4) + '@typescript-eslint/parser': 7.13.1(eslint@9.5.0)(typescript@5.5.2) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -39622,13 +39606,13 @@ snapshots: eslint: 9.5.0 requireindex: 1.2.0 - eslint-plugin-jest@28.6.0(@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4))(eslint@9.5.0)(jest@29.7.0(@types/node@22.2.0)(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4)))(typescript@5.5.4): + eslint-plugin-jest@28.6.0(@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2))(eslint@9.5.0)(typescript@5.5.2))(eslint@9.5.0)(jest@29.7.0(@types/node@20.14.7)(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2)))(typescript@5.5.2): dependencies: - '@typescript-eslint/utils': 7.7.1(eslint@9.5.0)(typescript@5.5.4) + '@typescript-eslint/utils': 7.7.1(eslint@9.5.0)(typescript@5.5.2) eslint: 9.5.0 optionalDependencies: - '@typescript-eslint/eslint-plugin': 7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4) - jest: 29.7.0(@types/node@22.2.0)(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4)) + '@typescript-eslint/eslint-plugin': 7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2))(eslint@9.5.0)(typescript@5.5.2) + jest: 29.7.0(@types/node@20.14.7)(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2)) transitivePeerDependencies: - supports-color - typescript @@ -39644,17 +39628,6 @@ snapshots: - supports-color - typescript - eslint-plugin-jest@28.6.0(@typescript-eslint/eslint-plugin@7.13.1(eslint@9.5.0)(typescript@5.5.2))(eslint@9.5.0)(jest@29.7.0(@types/node@20.14.7)(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2)))(typescript@5.5.2): - dependencies: - '@typescript-eslint/utils': 7.7.1(eslint@9.5.0)(typescript@5.5.2) - eslint: 9.5.0 - optionalDependencies: - '@typescript-eslint/eslint-plugin': 7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2))(eslint@9.5.0)(typescript@5.5.2) - jest: 29.7.0(@types/node@20.14.7)(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2)) - transitivePeerDependencies: - - supports-color - - typescript - eslint-plugin-jsonc@2.16.0(eslint@9.5.0): dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@9.5.0) @@ -50887,7 +50860,7 @@ snapshots: typescript-eslint@7.13.1(eslint@9.5.0)(typescript@5.5.4): dependencies: '@typescript-eslint/eslint-plugin': 7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(eslint@9.5.0)(typescript@5.5.4) - '@typescript-eslint/parser': 7.13.1(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4) + '@typescript-eslint/parser': 7.13.1(eslint@9.5.0)(typescript@5.5.4) '@typescript-eslint/utils': 7.13.1(eslint@9.5.0)(typescript@5.5.4) eslint: 9.5.0 optionalDependencies: @@ -51434,7 +51407,7 @@ snapshots: wagmi@2.10.4(@react-native-async-storage/async-storage@1.23.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10)))(@tanstack/query-core@5.45.0)(@tanstack/react-query@5.45.1(react@18.3.1))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@9.0.21)(react-dom@18.3.1(react@18.3.1))(react-i18next@13.5.0(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.2)(utf-8-validate@5.0.10)(viem@2.15.1(bufferutil@4.0.8)(typescript@5.5.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8): dependencies: '@tanstack/react-query': 5.45.1(react@18.3.1) - '@wagmi/connectors': 5.0.16(mehtb7r3xxh3anmscqllj3vxmi) + '@wagmi/connectors': 5.0.16(@react-native-async-storage/async-storage@1.23.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.3)(@wagmi/core@2.11.4(@tanstack/query-core@5.45.0)(@types/react@18.3.3)(bufferutil@4.0.8)(immer@9.0.21)(react@18.3.1)(typescript@5.5.2)(utf-8-validate@5.0.10)(viem@2.15.1(bufferutil@4.0.8)(typescript@5.5.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8))(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react-i18next@13.5.0(i18next@22.5.1)(react-dom@18.3.1(react@18.3.1))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.2)(utf-8-validate@5.0.10)(viem@2.15.1(bufferutil@4.0.8)(typescript@5.5.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) '@wagmi/core': 2.11.4(@tanstack/query-core@5.45.0)(@types/react@18.3.3)(bufferutil@4.0.8)(immer@9.0.21)(react@18.3.1)(typescript@5.5.2)(utf-8-validate@5.0.10)(viem@2.15.1(bufferutil@4.0.8)(typescript@5.5.2)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) react: 18.3.1 use-sync-external-store: 1.2.0(react@18.3.1) From 3c138bcfffa4068e28cbf360e28007057d0c6323 Mon Sep 17 00:00:00 2001 From: keyvan Date: Mon, 9 Sep 2024 22:20:58 -0700 Subject: [PATCH 03/18] go --- apps/staking/src/api.ts | 82 ++++++++++++++----- .../src/app/api/publishers-ranking/route.tsx | 10 +++ apps/staking/src/hooks/use-stake-account.tsx | 8 +- governance/pyth_staking_sdk/src/index.ts | 1 + .../src/pyth-staking-client.ts | 24 +----- governance/pyth_staking_sdk/src/types.ts | 8 ++ .../pyth_staking_sdk/src/utils/clock.ts | 4 + governance/pyth_staking_sdk/src/utils/pool.ts | 28 +++++++ 8 files changed, 121 insertions(+), 44 deletions(-) create mode 100644 apps/staking/src/app/api/publishers-ranking/route.tsx create mode 100644 governance/pyth_staking_sdk/src/utils/pool.ts diff --git a/apps/staking/src/api.ts b/apps/staking/src/api.ts index 88dd3f0313..7ab5ae3362 100644 --- a/apps/staking/src/api.ts +++ b/apps/staking/src/api.ts @@ -3,6 +3,8 @@ import type { HermesClient } from "@pythnetwork/hermes-client"; import { + epochToDate, + extractPublisherData, getAmountByTargetAndState, getCurrentEpoch, PositionState, @@ -11,6 +13,13 @@ import { } from "@pythnetwork/staking-sdk"; import { PublicKey } from "@solana/web3.js"; +type PublisherRankings = { + publisher: string; + rank: number; + numSymbols: number; + timestamp: string; +}[]; + type Data = { total: bigint; availableRewards: bigint; @@ -150,18 +159,24 @@ export const loadData = async ( ): Promise => { const [ stakeAccountCustody, - publishers, + poolData, ownerAtaAccount, currentEpoch, unlockSchedule, ] = await Promise.all([ client.getStakeAccountCustody(stakeAccount.address), - client.getPublishers(), + client.getPoolDataAccount(), client.getOwnerPythAtaAccount(), getCurrentEpoch(client.connection), client.getUnlockSchedule(stakeAccount.address), ]); + const publishers = extractPublisherData(poolData); + + const publisherRankingsResponse = await fetch("/api/publishers-ranking"); + const publisherRankings = + (await publisherRankingsResponse.json()) as PublisherRankings; + const filterGovernancePositions = (positionState: PositionState) => getAmountByTargetAndState({ stakeAccountPositions: stakeAccount, @@ -186,9 +201,11 @@ export const loadData = async ( }); const getPublisherCap = (publisher: PublicKey) => - BigInt(publisherCaps.parsed?.[0]?.publisher_stake_caps.find( - ({ publisher: p }) => p === publisher.toBase58(), - )?.cap ?? 0); + BigInt( + publisherCaps.parsed?.[0]?.publisher_stake_caps.find( + ({ publisher: p }) => p === publisher.toBase58(), + )?.cap ?? 0, + ); return { lastSlash: undefined, // TODO @@ -204,23 +221,44 @@ export const loadData = async ( unlockSchedule, locked: unlockSchedule.reduce((sum, { amount }) => sum + amount, 0n), walletAmount: ownerAtaAccount.amount, - integrityStakingPublishers: publishers.map(({ pubkey: publisher }) => ({ - apyHistory: [], // TODO - isSelf: false, // TODO - name: undefined, // TODO - numFeeds: 0, // TODO - poolCapacity: getPublisherCap(publisher), - poolUtilization: 0n, // TODO - publicKey: publisher, - qualityRanking: 0, // TODO - selfStake: 0n, // TODO - positions: { - warmup: filterOISPositions(publisher, PositionState.LOCKING), - staked: filterOISPositions(publisher, PositionState.LOCKED), - cooldown: filterOISPositions(publisher, PositionState.PREUNLOCKING), - cooldown2: filterOISPositions(publisher, PositionState.UNLOCKED), - }, - })), + integrityStakingPublishers: publishers.map((publisherData) => { + const publisherRanking = publisherRankings.find( + (ranking) => ranking.publisher === publisherData.pubkey.toBase58(), + ); + return { + apyHistory: publisherData.apyHistory.map(({ epoch, apy }) => ({ + date: epochToDate(epoch + 1n), + apy: Number(apy), + })), + isSelf: + publisherData.stakeAccount?.equals(stakeAccount.address) ?? false, + name: undefined, // TODO + numFeeds: publisherRanking?.numSymbols ?? 0, + poolCapacity: getPublisherCap(publisherData.pubkey), + poolUtilization: publisherData.totalDelegation, + publicKey: publisherData.pubkey, + qualityRanking: publisherRanking?.rank ?? 0, + selfStake: publisherData.selfDelegation, + positions: { + warmup: filterOISPositions( + publisherData.pubkey, + PositionState.LOCKING, + ), + staked: filterOISPositions( + publisherData.pubkey, + PositionState.LOCKED, + ), + cooldown: filterOISPositions( + publisherData.pubkey, + PositionState.PREUNLOCKING, + ), + cooldown2: filterOISPositions( + publisherData.pubkey, + PositionState.UNLOCKED, + ), + }, + }; + }), }; }; diff --git a/apps/staking/src/app/api/publishers-ranking/route.tsx b/apps/staking/src/app/api/publishers-ranking/route.tsx new file mode 100644 index 0000000000..37bab519c1 --- /dev/null +++ b/apps/staking/src/app/api/publishers-ranking/route.tsx @@ -0,0 +1,10 @@ +import { NextResponse } from "next/server"; + +export async function GET() { + const publisherRankingsResponse = await fetch( + "https://www.pyth.network/api/publishers-ranking?cluster=pythnet", + ); + + const publisherRankings = (await publisherRankingsResponse.json()) as JSON; + return NextResponse.json(publisherRankings); +} diff --git a/apps/staking/src/hooks/use-stake-account.tsx b/apps/staking/src/hooks/use-stake-account.tsx index 1c110cc3a8..e119574a49 100644 --- a/apps/staking/src/hooks/use-stake-account.tsx +++ b/apps/staking/src/hooks/use-stake-account.tsx @@ -86,7 +86,13 @@ const useStakeAccountState = () => { (account: StakeAccountPositions) => { setState((cur) => cur.type === StateType.Loaded - ? State.Loaded(cur.client, cur.hermesClient, account, cur.allAccounts, setAccount) + ? State.Loaded( + cur.client, + cur.hermesClient, + account, + cur.allAccounts, + setAccount, + ) : cur, ); }, diff --git a/governance/pyth_staking_sdk/src/index.ts b/governance/pyth_staking_sdk/src/index.ts index 184a2233b8..ff078d6ca7 100644 --- a/governance/pyth_staking_sdk/src/index.ts +++ b/governance/pyth_staking_sdk/src/index.ts @@ -1,4 +1,5 @@ export * from "./pyth-staking-client"; export * from "./utils/clock"; export * from "./utils/position"; +export * from "./utils/pool"; export * from "./types"; diff --git a/governance/pyth_staking_sdk/src/pyth-staking-client.ts b/governance/pyth_staking_sdk/src/pyth-staking-client.ts index 59a6f80cd6..f7ee6d4690 100644 --- a/governance/pyth_staking_sdk/src/pyth-staking-client.ts +++ b/governance/pyth_staking_sdk/src/pyth-staking-client.ts @@ -21,6 +21,7 @@ import type { StakeAccountPositions, } from "./types"; import { convertBigIntToBN, convertBNToBigInt } from "./utils/bn"; +import { extractPublisherData } from "./utils/pool"; import { deserializeStakeAccountPositions } from "./utils/position"; import { sendTransaction } from "./utils/transaction"; import { getUnlockSchedule } from "./utils/vesting"; @@ -190,26 +191,6 @@ export class PythStakingClient { return convertBNToBigInt(poolDataAccountAnchor); } - public async getPublishers(): Promise< - { - pubkey: PublicKey; - stakeAccount: PublicKey | null; - }[] - > { - const poolData = await this.getPoolDataAccount(); - - return poolData.publishers - .map((publisher, index) => ({ - pubkey: publisher, - stakeAccount: - poolData.publisherStakeAccounts[index] === undefined || - poolData.publisherStakeAccounts[index].equals(PublicKey.default) - ? null - : poolData.publisherStakeAccounts[index], - })) - .filter(({ pubkey }) => !pubkey.equals(PublicKey.default)); - } - public async stakeToGovernance( stakeAccountPositions: PublicKey, amount: bigint, @@ -315,7 +296,8 @@ export class PythStakingClient { public async advanceDelegationRecord(stakeAccountPositions: PublicKey) { // TODO: optimize to only send transactions for publishers that have positive rewards - const publishers = await this.getPublishers(); + const poolData = await this.getPoolDataAccount(); + const publishers = extractPublisherData(poolData); // anchor does not calculate the correct pda for other programs // therefore we need to manually calculate the pdas diff --git a/governance/pyth_staking_sdk/src/types.ts b/governance/pyth_staking_sdk/src/types.ts index 10fbcd13d8..c57edd8292 100644 --- a/governance/pyth_staking_sdk/src/types.ts +++ b/governance/pyth_staking_sdk/src/types.ts @@ -54,6 +54,14 @@ export type StakeAccountPositions = { }; }; +export type PublisherData = { + pubkey: PublicKey; + stakeAccount: PublicKey | null; + totalDelegation: bigint; + selfDelegation: bigint; + apyHistory: { epoch: bigint; apy: bigint, selfApy: bigint }[]; +}[]; + export enum PositionState { UNLOCKED, LOCKING, diff --git a/governance/pyth_staking_sdk/src/utils/clock.ts b/governance/pyth_staking_sdk/src/utils/clock.ts index 0c9e41ef2c..2caf48506d 100644 --- a/governance/pyth_staking_sdk/src/utils/clock.ts +++ b/governance/pyth_staking_sdk/src/utils/clock.ts @@ -17,3 +17,7 @@ export const getCurrentEpoch: ( const timestamp = await getCurrentSolanaTimestamp(connection); return timestamp / EPOCH_DURATION; }; + +export const epochToDate = (epoch: bigint): Date => { + return new Date(Number(epoch * EPOCH_DURATION * 1000n)); +}; diff --git a/governance/pyth_staking_sdk/src/utils/pool.ts b/governance/pyth_staking_sdk/src/utils/pool.ts new file mode 100644 index 0000000000..3779067f73 --- /dev/null +++ b/governance/pyth_staking_sdk/src/utils/pool.ts @@ -0,0 +1,28 @@ +import { PublicKey } from "@solana/web3.js"; + +import type { PoolDataAccount, PublisherData } from "../types"; + + +export const extractPublisherData = (poolData: PoolDataAccount): PublisherData => { + return poolData.publishers + .filter((publisher) => !publisher.equals(PublicKey.default)) + .map((publisher, index) => ({ + pubkey: publisher, + stakeAccount: + poolData.publisherStakeAccounts[index] === undefined || + poolData.publisherStakeAccounts[index].equals(PublicKey.default) + ? null + : poolData.publisherStakeAccounts[index], + totalDelegation: + (poolData.delState[index]?.totalDelegation ?? 0n) + + (poolData.selfDelState[index]?.totalDelegation ?? 0n), + selfDelegation: poolData.selfDelState[index]?.totalDelegation ?? 0n, + apyHistory: poolData.events + .filter((event) => event.epoch > 0n) + .map((event) => ({ + epoch: event.epoch, + apy: event.y * (event.eventData[index]?.otherRewardRatio ?? 0n) / 1_000_000n, + selfApy: event.y * (event.eventData[index]?.selfRewardRatio ?? 0n) / 1_000_000n, + })).sort((a, b) => Number(a.epoch) - Number(b.epoch)), + })); +}; \ No newline at end of file From 6479b8836d7e837b5d7e9d1e18679fff88f7cffa Mon Sep 17 00:00:00 2001 From: keyvan Date: Mon, 9 Sep 2024 22:42:06 -0700 Subject: [PATCH 04/18] fix: last apy --- apps/staking/src/api.ts | 11 +++++++---- .../src/components/OracleIntegrityStaking/index.tsx | 7 ++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/staking/src/api.ts b/apps/staking/src/api.ts index 7ab5ae3362..53b3966719 100644 --- a/apps/staking/src/api.ts +++ b/apps/staking/src/api.ts @@ -56,6 +56,7 @@ type Data = { poolUtilization: bigint; numFeeds: number; qualityRanking: number; + lastApy: number; apyHistory: { date: Date; apy: number }[]; positions?: | { @@ -225,11 +226,13 @@ export const loadData = async ( const publisherRanking = publisherRankings.find( (ranking) => ranking.publisher === publisherData.pubkey.toBase58(), ); + const apyHistory = publisherData.apyHistory.map(({ epoch, apy }) => ({ + date: epochToDate(epoch + 1n), + apy: Number(apy), + })) return { - apyHistory: publisherData.apyHistory.map(({ epoch, apy }) => ({ - date: epochToDate(epoch + 1n), - apy: Number(apy), - })), + apyHistory, + lastApy: apyHistory.at(-1)?.apy ?? 0, isSelf: publisherData.stakeAccount?.equals(stakeAccount.address) ?? false, name: undefined, // TODO diff --git a/apps/staking/src/components/OracleIntegrityStaking/index.tsx b/apps/staking/src/components/OracleIntegrityStaking/index.tsx index 0ded2050bf..6f3f457680 100644 --- a/apps/staking/src/components/OracleIntegrityStaking/index.tsx +++ b/apps/staking/src/components/OracleIntegrityStaking/index.tsx @@ -416,6 +416,7 @@ type PublisherProps = { numFeeds: number; qualityRanking: number; apyHistory: { date: Date; apy: number }[]; + lastApy: number; positions?: | { warmup?: bigint | undefined; @@ -518,11 +519,7 @@ const Publisher = ({
- {calculateApy( - publisher.poolCapacity, - publisher.poolUtilization, - publisher.isSelf, - )} + {publisher.lastApy} %
From d023080b8e5d077ee3b7ee8109350c08c436c56e Mon Sep 17 00:00:00 2001 From: keyvan Date: Mon, 9 Sep 2024 22:47:41 -0700 Subject: [PATCH 05/18] foix: format --- governance/pyth_staking_sdk/src/types.ts | 2 +- governance/pyth_staking_sdk/src/utils/pool.ts | 20 ++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/governance/pyth_staking_sdk/src/types.ts b/governance/pyth_staking_sdk/src/types.ts index c57edd8292..f9e859dee8 100644 --- a/governance/pyth_staking_sdk/src/types.ts +++ b/governance/pyth_staking_sdk/src/types.ts @@ -59,7 +59,7 @@ export type PublisherData = { stakeAccount: PublicKey | null; totalDelegation: bigint; selfDelegation: bigint; - apyHistory: { epoch: bigint; apy: bigint, selfApy: bigint }[]; + apyHistory: { epoch: bigint; apy: bigint; selfApy: bigint }[]; }[]; export enum PositionState { diff --git a/governance/pyth_staking_sdk/src/utils/pool.ts b/governance/pyth_staking_sdk/src/utils/pool.ts index 3779067f73..9df6672e25 100644 --- a/governance/pyth_staking_sdk/src/utils/pool.ts +++ b/governance/pyth_staking_sdk/src/utils/pool.ts @@ -2,9 +2,10 @@ import { PublicKey } from "@solana/web3.js"; import type { PoolDataAccount, PublisherData } from "../types"; - -export const extractPublisherData = (poolData: PoolDataAccount): PublisherData => { - return poolData.publishers +export const extractPublisherData = ( + poolData: PoolDataAccount, +): PublisherData => { + return poolData.publishers .filter((publisher) => !publisher.equals(PublicKey.default)) .map((publisher, index) => ({ pubkey: publisher, @@ -21,8 +22,13 @@ export const extractPublisherData = (poolData: PoolDataAccount): PublisherData = .filter((event) => event.epoch > 0n) .map((event) => ({ epoch: event.epoch, - apy: event.y * (event.eventData[index]?.otherRewardRatio ?? 0n) / 1_000_000n, - selfApy: event.y * (event.eventData[index]?.selfRewardRatio ?? 0n) / 1_000_000n, - })).sort((a, b) => Number(a.epoch) - Number(b.epoch)), + apy: + (event.y * (event.eventData[index]?.otherRewardRatio ?? 0n)) / + 1_000_000n, + selfApy: + (event.y * (event.eventData[index]?.selfRewardRatio ?? 0n)) / + 1_000_000n, + })) + .sort((a, b) => Number(a.epoch) - Number(b.epoch)), })); -}; \ No newline at end of file +}; From 31d6da428588989a4f201d4ca33a7151474cc5fe Mon Sep 17 00:00:00 2001 From: keyvan Date: Mon, 9 Sep 2024 23:06:16 -0700 Subject: [PATCH 06/18] fix: format --- apps/staking/src/api.ts | 2 +- apps/staking/src/components/OracleIntegrityStaking/index.tsx | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/apps/staking/src/api.ts b/apps/staking/src/api.ts index 53b3966719..51f17819f6 100644 --- a/apps/staking/src/api.ts +++ b/apps/staking/src/api.ts @@ -229,7 +229,7 @@ export const loadData = async ( const apyHistory = publisherData.apyHistory.map(({ epoch, apy }) => ({ date: epochToDate(epoch + 1n), apy: Number(apy), - })) + })); return { apyHistory, lastApy: apyHistory.at(-1)?.apy ?? 0, diff --git a/apps/staking/src/components/OracleIntegrityStaking/index.tsx b/apps/staking/src/components/OracleIntegrityStaking/index.tsx index 6f3f457680..8942df9130 100644 --- a/apps/staking/src/components/OracleIntegrityStaking/index.tsx +++ b/apps/staking/src/components/OracleIntegrityStaking/index.tsx @@ -518,10 +518,7 @@ const Publisher = ({ -
- {publisher.lastApy} - % -
+
{publisher.lastApy}%
From 26819534548e1b475495359a8c7742746b4e7fed Mon Sep 17 00:00:00 2001 From: keyvan Date: Tue, 10 Sep 2024 09:48:56 -0700 Subject: [PATCH 07/18] feat: add apy --- apps/staking/src/api.ts | 67 ++++++++++------ .../src/components/Dashboard/index.tsx | 3 + .../OracleIntegrityStaking/index.tsx | 78 +++++++++++++++---- 3 files changed, 111 insertions(+), 37 deletions(-) diff --git a/apps/staking/src/api.ts b/apps/staking/src/api.ts index 51f17819f6..859a72483b 100644 --- a/apps/staking/src/api.ts +++ b/apps/staking/src/api.ts @@ -47,6 +47,7 @@ type Data = { cooldown: bigint; cooldown2: bigint; }; + yieldRate: bigint; integrityStakingPublishers: { name: string | undefined; publicKey: PublicKey; @@ -56,7 +57,6 @@ type Data = { poolUtilization: bigint; numFeeds: number; qualityRanking: number; - lastApy: number; apyHistory: { date: Date; apy: number }[]; positions?: | { @@ -162,19 +162,26 @@ export const loadData = async ( stakeAccountCustody, poolData, ownerAtaAccount, - currentEpoch, unlockSchedule, + poolConfig, + currentEpoch, + publisherRankingsResponse, + publisherCaps, ] = await Promise.all([ client.getStakeAccountCustody(stakeAccount.address), client.getPoolDataAccount(), client.getOwnerPythAtaAccount(), - getCurrentEpoch(client.connection), client.getUnlockSchedule(stakeAccount.address), + client.getPoolConfigAccount(), + getCurrentEpoch(client.connection), + fetch("/api/publishers-ranking"), + hermesClient.getLatestPublisherCaps({ + parsed: true, + }), ]); const publishers = extractPublisherData(poolData); - const publisherRankingsResponse = await fetch("/api/publishers-ranking"); const publisherRankings = (await publisherRankingsResponse.json()) as PublisherRankings; @@ -197,10 +204,6 @@ export const loadData = async ( epoch: currentEpoch, }); - const publisherCaps = await hermesClient.getLatestPublisherCaps({ - parsed: true, - }); - const getPublisherCap = (publisher: PublicKey) => BigInt( publisherCaps.parsed?.[0]?.publisher_stake_caps.find( @@ -213,6 +216,7 @@ export const loadData = async ( availableRewards: 0n, // TODO expiringRewards: undefined, // TODO total: stakeAccountCustody.amount, + yieldRate: poolConfig.y, governance: { warmup: filterGovernancePositions(PositionState.LOCKING), staked: filterGovernancePositions(PositionState.LOCKED), @@ -232,7 +236,6 @@ export const loadData = async ( })); return { apyHistory, - lastApy: apyHistory.at(-1)?.apy ?? 0, isSelf: publisherData.stakeAccount?.equals(stakeAccount.address) ?? false, name: undefined, // TODO @@ -347,19 +350,39 @@ export const unstakeIntegrityStaking = async ( throw new NotImplementedError(); }; -export const calculateApy = ( - poolCapacity: bigint, - poolUtilization: bigint, - isSelf: boolean, -) => { - const maxApy = isSelf ? 25 : 20; - const minApy = isSelf ? 10 : 5; - return Math.min( - Math.max( - maxApy - Number((poolUtilization - poolCapacity) / 100_000_000n), - minApy, - ), - maxApy, +export const calculateApy = (options: { + selfStake: bigint; + poolUtilization: bigint; + poolCapacity: bigint; + yieldRate: bigint; + isSelf: boolean; +}) => { + const { selfStake, poolUtilization, poolCapacity, yieldRate, isSelf } = + options; + const eligibleSelfStake = selfStake > poolCapacity ? poolCapacity : selfStake; + + const apyPercentage = (Number(yieldRate) * 52 * 100) / 1_000_000; + + if (isSelf) { + if (selfStake === 0n) { + return apyPercentage; + } + return (apyPercentage * Number(eligibleSelfStake)) / Number(selfStake); + } + + const delegatorPoolUtilization = poolUtilization - selfStake; + const delegatorPoolCapacity = poolCapacity - eligibleSelfStake; + const eligibleStake = + delegatorPoolUtilization > delegatorPoolCapacity + ? delegatorPoolCapacity + : delegatorPoolUtilization; + + if (poolUtilization === selfStake) { + return apyPercentage; + } + + return ( + (apyPercentage * Number(eligibleStake)) / Number(delegatorPoolUtilization) ); }; diff --git a/apps/staking/src/components/Dashboard/index.tsx b/apps/staking/src/components/Dashboard/index.tsx index df026f1720..39c5220948 100644 --- a/apps/staking/src/components/Dashboard/index.tsx +++ b/apps/staking/src/components/Dashboard/index.tsx @@ -33,6 +33,7 @@ type Props = { cooldown: bigint; cooldown2: bigint; }; + yieldRate: bigint; integrityStakingPublishers: ComponentProps< typeof OracleIntegrityStaking >["publishers"]; @@ -48,6 +49,7 @@ export const Dashboard = ({ integrityStakingPublishers, locked, unlockSchedule, + yieldRate, }: Props) => { const availableToStakeGovernance = useMemo( () => @@ -156,6 +158,7 @@ export const Dashboard = ({ cooldown={integrityStakingCooldown} cooldown2={integrityStakingCooldown2} publishers={integrityStakingPublishers} + yieldRate={yieldRate} /> diff --git a/apps/staking/src/components/OracleIntegrityStaking/index.tsx b/apps/staking/src/components/OracleIntegrityStaking/index.tsx index 8942df9130..8c2f4854bc 100644 --- a/apps/staking/src/components/OracleIntegrityStaking/index.tsx +++ b/apps/staking/src/components/OracleIntegrityStaking/index.tsx @@ -47,6 +47,7 @@ type Props = { cooldown: bigint; cooldown2: bigint; publishers: PublisherProps["publisher"][]; + yieldRate: bigint; }; export const OracleIntegrityStaking = ({ @@ -57,6 +58,7 @@ export const OracleIntegrityStaking = ({ cooldown, cooldown2, publishers, + yieldRate, }: Props) => { const self = useMemo( () => publishers.find((publisher) => publisher.isSelf), @@ -115,6 +117,7 @@ export const OracleIntegrityStaking = ({ availableToStake={availableToStake} publisher={self} totalStaked={staked} + yieldRate={yieldRate} /> @@ -132,6 +135,7 @@ export const OracleIntegrityStaking = ({ availableToStake={availableToStake} publishers={otherPublishers} totalStaked={staked} + yieldRate={yieldRate} />
@@ -143,6 +147,7 @@ type PublisherListProps = { availableToStake: bigint; totalStaked: bigint; publishers: PublisherProps["publisher"][]; + yieldRate: bigint; }; const PublisherList = ({ @@ -150,6 +155,7 @@ const PublisherList = ({ availableToStake, publishers, totalStaked, + yieldRate, }: PublisherListProps) => { const [search, setSearch] = useState(""); const [sort, setSort] = useState({ @@ -173,10 +179,22 @@ const PublisherList = ({ b.name ?? b.publicKey.toBase58(), ); } - case SortField.LastEpochAPY: { + case SortField.APY: { return ( - calculateApy(a.poolCapacity, a.poolUtilization, false) - - calculateApy(b.poolCapacity, b.poolUtilization, false) + calculateApy({ + isSelf: false, + selfStake: a.selfStake, + poolCapacity: a.poolCapacity, + poolUtilization: a.poolUtilization, + yieldRate, + }) - + calculateApy({ + isSelf: false, + selfStake: b.selfStake, + poolCapacity: b.poolCapacity, + poolUtilization: b.poolUtilization, + yieldRate, + }) ); } case SortField.NumberOfFeeds: { @@ -197,7 +215,7 @@ const PublisherList = ({ } }); return sort.descending ? sorted.reverse() : sorted; - }, [publishers, search, sort.field, sort.descending, filter]); + }, [publishers, search, sort.field, sort.descending, filter, yieldRate]); const paginatedPublishers = useMemo( () => @@ -276,11 +294,11 @@ const PublisherList = ({ Pool - Last epoch APY + APY Historical APY ))} @@ -416,7 +435,6 @@ type PublisherProps = { numFeeds: number; qualityRanking: number; apyHistory: { date: Date; apy: number }[]; - lastApy: number; positions?: | { warmup?: bigint | undefined; @@ -426,6 +444,7 @@ type PublisherProps = { } | undefined; }; + yieldRate: bigint; }; const Publisher = ({ @@ -433,6 +452,7 @@ const Publisher = ({ availableToStake, totalStaked, isSelf, + yieldRate, }: PublisherProps) => { const warmup = useMemo( () => @@ -518,7 +538,16 @@ const Publisher = ({
-
{publisher.lastApy}%
+
+ {calculateApy({ + isSelf: publisher.isSelf, + selfStake: publisher.selfStake, + poolCapacity: publisher.poolCapacity, + poolUtilization: publisher.poolUtilization, + yieldRate, + })} + % +
@@ -551,6 +580,8 @@ const Publisher = ({ publisherKey={publisher.publicKey} publisherName={publisher.name} isSelf={publisher.isSelf} + selfStake={publisher.selfStake} + yieldRate={yieldRate} /> )} @@ -635,6 +666,8 @@ type StakeToPublisherButtonProps = { poolCapacity: bigint; poolUtilization: bigint; isSelf: boolean; + selfStake: bigint; + yieldRate: bigint; }; const StakeToPublisherButton = ({ @@ -644,6 +677,8 @@ const StakeToPublisherButton = ({ poolUtilization, availableToStake, isSelf, + selfStake, + yieldRate, }: StakeToPublisherButtonProps) => { const delegate = useTransferActionForPublisher( delegateIntegrityStaking, @@ -663,12 +698,25 @@ const StakeToPublisherButton = ({
APY after staking
- {calculateApy( - poolCapacity, - poolUtilization + - (amount.type === AmountType.Valid ? amount.amount : 0n), - isSelf, - )} + {isSelf + ? calculateApy({ + isSelf, + selfStake: + selfStake + + (amount.type === AmountType.Valid ? amount.amount : 0n), + poolCapacity, + poolUtilization, + yieldRate, + }) + : calculateApy({ + isSelf, + selfStake, + poolCapacity, + poolUtilization: + poolUtilization + + (amount.type === AmountType.Valid ? amount.amount : 0n), + yieldRate, + })} %
@@ -706,7 +754,7 @@ const hasAnyPositions = ({ positions }: PublisherProps["publisher"]) => enum SortField { PublisherName, PoolUtilization, - LastEpochAPY, + APY, SelfStake, NumberOfFeeds, QualityRanking, From ce0cc0dd207c0750f601d7b8d5f8d841119d897e Mon Sep 17 00:00:00 2001 From: keyvan Date: Tue, 10 Sep 2024 10:54:44 -0700 Subject: [PATCH 08/18] refactor: calculate apy --- apps/staking/src/api.ts | 36 ------------------- .../OracleIntegrityStaking/index.tsx | 4 +-- governance/pyth_staking_sdk/src/index.ts | 1 + governance/pyth_staking_sdk/src/utils/apy.ts | 36 +++++++++++++++++++ 4 files changed, 38 insertions(+), 39 deletions(-) create mode 100644 governance/pyth_staking_sdk/src/utils/apy.ts diff --git a/apps/staking/src/api.ts b/apps/staking/src/api.ts index 859a72483b..846a52d593 100644 --- a/apps/staking/src/api.ts +++ b/apps/staking/src/api.ts @@ -350,42 +350,6 @@ export const unstakeIntegrityStaking = async ( throw new NotImplementedError(); }; -export const calculateApy = (options: { - selfStake: bigint; - poolUtilization: bigint; - poolCapacity: bigint; - yieldRate: bigint; - isSelf: boolean; -}) => { - const { selfStake, poolUtilization, poolCapacity, yieldRate, isSelf } = - options; - const eligibleSelfStake = selfStake > poolCapacity ? poolCapacity : selfStake; - - const apyPercentage = (Number(yieldRate) * 52 * 100) / 1_000_000; - - if (isSelf) { - if (selfStake === 0n) { - return apyPercentage; - } - return (apyPercentage * Number(eligibleSelfStake)) / Number(selfStake); - } - - const delegatorPoolUtilization = poolUtilization - selfStake; - const delegatorPoolCapacity = poolCapacity - eligibleSelfStake; - const eligibleStake = - delegatorPoolUtilization > delegatorPoolCapacity - ? delegatorPoolCapacity - : delegatorPoolUtilization; - - if (poolUtilization === selfStake) { - return apyPercentage; - } - - return ( - (apyPercentage * Number(eligibleStake)) / Number(delegatorPoolUtilization) - ); -}; - export const getUpcomingEpoch = (): Date => { const d = new Date(); d.setUTCDate(d.getUTCDate() + ((5 + 7 - d.getUTCDay()) % 7 || 7)); diff --git a/apps/staking/src/components/OracleIntegrityStaking/index.tsx b/apps/staking/src/components/OracleIntegrityStaking/index.tsx index 8c2f4854bc..cab6815bae 100644 --- a/apps/staking/src/components/OracleIntegrityStaking/index.tsx +++ b/apps/staking/src/components/OracleIntegrityStaking/index.tsx @@ -3,7 +3,7 @@ import { XMarkIcon, MagnifyingGlassIcon, } from "@heroicons/react/24/outline"; -import type { PythStakingClient } from "@pythnetwork/staking-sdk"; +import { calculateApy, type PythStakingClient } from "@pythnetwork/staking-sdk"; import { PublicKey } from "@solana/web3.js"; import clsx from "clsx"; import { @@ -27,7 +27,6 @@ import { delegateIntegrityStaking, cancelWarmupIntegrityStaking, unstakeIntegrityStaking, - calculateApy, } from "../../api"; import { Button } from "../Button"; import { ProgramSection } from "../ProgramSection"; @@ -705,7 +704,6 @@ const StakeToPublisherButton = ({ selfStake + (amount.type === AmountType.Valid ? amount.amount : 0n), poolCapacity, - poolUtilization, yieldRate, }) : calculateApy({ diff --git a/governance/pyth_staking_sdk/src/index.ts b/governance/pyth_staking_sdk/src/index.ts index ff078d6ca7..19f5f2281b 100644 --- a/governance/pyth_staking_sdk/src/index.ts +++ b/governance/pyth_staking_sdk/src/index.ts @@ -2,4 +2,5 @@ export * from "./pyth-staking-client"; export * from "./utils/clock"; export * from "./utils/position"; export * from "./utils/pool"; +export * from "./utils/apy"; export * from "./types"; diff --git a/governance/pyth_staking_sdk/src/utils/apy.ts b/governance/pyth_staking_sdk/src/utils/apy.ts new file mode 100644 index 0000000000..b182f698ae --- /dev/null +++ b/governance/pyth_staking_sdk/src/utils/apy.ts @@ -0,0 +1,36 @@ +export const calculateApy = ( + options: { + selfStake: bigint; + poolCapacity: bigint; + yieldRate: bigint; + } & ({ isSelf: true } | { isSelf: false; poolUtilization: bigint }), +) => { + const { selfStake, poolCapacity, yieldRate, isSelf } = options; + const eligibleSelfStake = selfStake > poolCapacity ? poolCapacity : selfStake; + + const apyPercentage = (Number(yieldRate) * 52 * 100) / 1_000_000; + + if (isSelf) { + if (selfStake === 0n) { + return apyPercentage; + } + return (apyPercentage * Number(eligibleSelfStake)) / Number(selfStake); + } + + const { poolUtilization } = options; + + const delegatorPoolUtilization = poolUtilization - selfStake; + const delegatorPoolCapacity = poolCapacity - eligibleSelfStake; + const eligibleStake = + delegatorPoolUtilization > delegatorPoolCapacity + ? delegatorPoolCapacity + : delegatorPoolUtilization; + + if (poolUtilization === selfStake) { + return apyPercentage; + } + + return ( + (apyPercentage * Number(eligibleStake)) / Number(delegatorPoolUtilization) + ); +}; From e1000ea6b42a2d151835fb55b0541b8567e5382b Mon Sep 17 00:00:00 2001 From: keyvan Date: Tue, 10 Sep 2024 11:53:06 -0700 Subject: [PATCH 09/18] add unstake governance --- apps/staking/src/api.ts | 16 ++--- .../src/pyth-staking-client.ts | 58 ++++++++++++++++--- 2 files changed, 59 insertions(+), 15 deletions(-) diff --git a/apps/staking/src/api.ts b/apps/staking/src/api.ts index 846a52d593..c220dc9d7b 100644 --- a/apps/staking/src/api.ts +++ b/apps/staking/src/api.ts @@ -308,19 +308,19 @@ export const stakeGovernance = async ( }; export const cancelWarmupGovernance = async ( - _client: PythStakingClient, - _stakeAccount: PublicKey, - _amount: bigint, + client: PythStakingClient, + stakeAccount: PublicKey, + amount: bigint, ): Promise => { - throw new NotImplementedError(); + await client.unstakeFromGovernance(stakeAccount, PositionState.LOCKING, amount); }; export const unstakeGovernance = async ( - _client: PythStakingClient, - _stakeAccount: PublicKey, - _amount: bigint, + client: PythStakingClient, + stakeAccount: PublicKey, + amount: bigint, ): Promise => { - throw new NotImplementedError(); + await client.unstakeFromGovernance(stakeAccount, PositionState.LOCKED, amount); }; export const delegateIntegrityStaking = async ( diff --git a/governance/pyth_staking_sdk/src/pyth-staking-client.ts b/governance/pyth_staking_sdk/src/pyth-staking-client.ts index f7ee6d4690..b911282da2 100644 --- a/governance/pyth_staking_sdk/src/pyth-staking-client.ts +++ b/governance/pyth_staking_sdk/src/pyth-staking-client.ts @@ -6,7 +6,7 @@ import { getAssociatedTokenAddress, } from "@solana/spl-token"; import type { AnchorWallet } from "@solana/wallet-adapter-react"; -import { Connection, PublicKey } from "@solana/web3.js"; +import { Connection, PublicKey, TransactionInstruction } from "@solana/web3.js"; import { getConfigAddress, @@ -14,15 +14,17 @@ import { getStakeAccountCustodyAddress, getStakeAccountMetadataAddress, } from "./pdas"; -import type { - GlobalConfig, - PoolConfig, - PoolDataAccount, - StakeAccountPositions, +import { + PositionState, + type GlobalConfig, + type PoolConfig, + type PoolDataAccount, + type StakeAccountPositions, } from "./types"; import { convertBigIntToBN, convertBNToBigInt } from "./utils/bn"; +import { getCurrentEpoch } from "./utils/clock"; import { extractPublisherData } from "./utils/pool"; -import { deserializeStakeAccountPositions } from "./utils/position"; +import { deserializeStakeAccountPositions, getPositionState } from "./utils/position"; import { sendTransaction } from "./utils/transaction"; import { getUnlockSchedule } from "./utils/vesting"; import * as IntegrityPoolIdl from "../idl/integrity-pool.json"; @@ -210,6 +212,48 @@ export class PythStakingClient { return sendTransaction([instruction], this.connection, this.wallet); } + public async unstakeFromGovernance( + stakeAccountPositions: PublicKey, + positionState: PositionState.LOCKED | PositionState.LOCKING, + amount: bigint, + ) { + const stakeAccountPositionsData = await this.getStakeAccountPositions( + stakeAccountPositions, + ); + const currentEpoch = await getCurrentEpoch(this.connection); + + let remainingAmount = amount; + const instructionPromises: Promise[] = []; + + const eligiblePositions = stakeAccountPositionsData.data.positions + .map((p, i) => ({position: p, index: i})) + .reverse() + .filter( + ({position}) => position.targetWithParameters.voting !== undefined && + positionState === getPositionState(position, currentEpoch)) + + for (const {position, index} of eligiblePositions) { + if (position.amount < remainingAmount) { + instructionPromises.push( + this.stakingProgram.methods.closePosition(index, convertBigIntToBN(position.amount), {voting: {}}).accounts({ + stakeAccountPositions, + }).instruction() + ); + remainingAmount -= position.amount; + } else { + instructionPromises.push( + this.stakingProgram.methods.closePosition(index, convertBigIntToBN(remainingAmount), {voting: {}}).accounts({ + stakeAccountPositions, + }).instruction() + ); + break; + } + } + + const instructions = await Promise.all(instructionPromises); + return sendTransaction(instructions, this.connection, this.wallet); + } + public async depositTokensToStakeAccountCustody( stakeAccountPositions: PublicKey, amount: bigint, From c69d80d48606de25ee4403c21d11b7dbe84ab6ab Mon Sep 17 00:00:00 2001 From: keyvan Date: Tue, 10 Sep 2024 12:38:47 -0700 Subject: [PATCH 10/18] add unstake to publisher --- apps/staking/src/api.ts | 20 ++-- .../src/pyth-staking-client.ts | 100 +++++++++++++++--- governance/pyth_staking_sdk/src/types.ts | 8 +- 3 files changed, 97 insertions(+), 31 deletions(-) diff --git a/apps/staking/src/api.ts b/apps/staking/src/api.ts index c220dc9d7b..be5b9cce44 100644 --- a/apps/staking/src/api.ts +++ b/apps/staking/src/api.ts @@ -333,21 +333,21 @@ export const delegateIntegrityStaking = async ( }; export const cancelWarmupIntegrityStaking = async ( - _client: PythStakingClient, - _stakeAccount: PublicKey, - _publisherKey: PublicKey, - _amount: bigint, + client: PythStakingClient, + stakeAccount: PublicKey, + publisherKey: PublicKey, + amount: bigint, ): Promise => { - throw new NotImplementedError(); + await client.unstakeFromPublisher(stakeAccount, publisherKey, PositionState.LOCKING, amount); }; export const unstakeIntegrityStaking = async ( - _client: PythStakingClient, - _stakeAccount: PublicKey, - _publisherKey: PublicKey, - _amount: bigint, + client: PythStakingClient, + stakeAccount: PublicKey, + publisherKey: PublicKey, + amount: bigint, ): Promise => { - throw new NotImplementedError(); + await client.unstakeFromPublisher(stakeAccount, publisherKey, PositionState.LOCKED, amount); }; export const getUpcomingEpoch = (): Date => { diff --git a/governance/pyth_staking_sdk/src/pyth-staking-client.ts b/governance/pyth_staking_sdk/src/pyth-staking-client.ts index b911282da2..1fe00b1595 100644 --- a/governance/pyth_staking_sdk/src/pyth-staking-client.ts +++ b/governance/pyth_staking_sdk/src/pyth-staking-client.ts @@ -24,7 +24,10 @@ import { import { convertBigIntToBN, convertBNToBigInt } from "./utils/bn"; import { getCurrentEpoch } from "./utils/clock"; import { extractPublisherData } from "./utils/pool"; -import { deserializeStakeAccountPositions, getPositionState } from "./utils/position"; +import { + deserializeStakeAccountPositions, + getPositionState, +} from "./utils/position"; import { sendTransaction } from "./utils/transaction"; import { getUnlockSchedule } from "./utils/vesting"; import * as IntegrityPoolIdl from "../idl/integrity-pool.json"; @@ -221,30 +224,99 @@ export class PythStakingClient { stakeAccountPositions, ); const currentEpoch = await getCurrentEpoch(this.connection); - + let remainingAmount = amount; const instructionPromises: Promise[] = []; const eligiblePositions = stakeAccountPositionsData.data.positions - .map((p, i) => ({position: p, index: i})) - .reverse() - .filter( - ({position}) => position.targetWithParameters.voting !== undefined && - positionState === getPositionState(position, currentEpoch)) + .map((p, i) => ({ position: p, index: i })) + .reverse() + .filter( + ({ position }) => + position.targetWithParameters.voting !== undefined && + positionState === getPositionState(position, currentEpoch), + ); - for (const {position, index} of eligiblePositions) { + for (const { position, index } of eligiblePositions) { if (position.amount < remainingAmount) { instructionPromises.push( - this.stakingProgram.methods.closePosition(index, convertBigIntToBN(position.amount), {voting: {}}).accounts({ - stakeAccountPositions, - }).instruction() + this.stakingProgram.methods + .closePosition(index, convertBigIntToBN(position.amount), { + voting: {}, + }) + .accounts({ + stakeAccountPositions, + }) + .instruction(), ); remainingAmount -= position.amount; } else { instructionPromises.push( - this.stakingProgram.methods.closePosition(index, convertBigIntToBN(remainingAmount), {voting: {}}).accounts({ - stakeAccountPositions, - }).instruction() + this.stakingProgram.methods + .closePosition(index, convertBigIntToBN(remainingAmount), { + voting: {}, + }) + .accounts({ + stakeAccountPositions, + }) + .instruction(), + ); + break; + } + } + + const instructions = await Promise.all(instructionPromises); + return sendTransaction(instructions, this.connection, this.wallet); + } + + public async unstakeFromPublisher( + stakeAccountPositions: PublicKey, + publisher: PublicKey, + positionState: PositionState.LOCKED | PositionState.LOCKING, + amount: bigint, + ) { + const stakeAccountPositionsData = await this.getStakeAccountPositions( + stakeAccountPositions, + ); + const currentEpoch = await getCurrentEpoch(this.connection); + + let remainingAmount = amount; + const instructionPromises: Promise[] = []; + + const eligiblePositions = stakeAccountPositionsData.data.positions + .map((p, i) => ({ position: p, index: i })) + .reverse() + .filter( + ({ position }) => + position.targetWithParameters.integrityPool?.publisher !== + undefined && + position.targetWithParameters.integrityPool.publisher.equals( + publisher, + ) && + positionState === getPositionState(position, currentEpoch), + ); + + for (const { position, index } of eligiblePositions) { + if (position.amount < remainingAmount) { + instructionPromises.push( + this.integrityPoolProgram.methods + .undelegate(index, convertBigIntToBN(position.amount)) + .accounts({ + stakeAccountPositions, + publisher, + }) + .instruction(), + ); + remainingAmount -= position.amount; + } else { + instructionPromises.push( + this.integrityPoolProgram.methods + .undelegate(index, convertBigIntToBN(remainingAmount)) + .accounts({ + stakeAccountPositions, + publisher, + }) + .instruction(), ); break; } diff --git a/governance/pyth_staking_sdk/src/types.ts b/governance/pyth_staking_sdk/src/types.ts index f9e859dee8..ddd27972b9 100644 --- a/governance/pyth_staking_sdk/src/types.ts +++ b/governance/pyth_staking_sdk/src/types.ts @@ -11,13 +11,7 @@ export type Convert = T extends From ? Convert[] : T extends Record ? { - [K in keyof T]: T[K] extends From - ? To - : T[K] extends From | null - ? To | null - : T[K] extends Record | unknown[] - ? Convert - : T[K]; + [K in keyof T]: Convert; } : T; From 401600ffc70b64d99cd669f60f83fe32b40a6275 Mon Sep 17 00:00:00 2001 From: keyvan Date: Tue, 10 Sep 2024 12:43:07 -0700 Subject: [PATCH 11/18] fix --- apps/staking/src/api.ts | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/apps/staking/src/api.ts b/apps/staking/src/api.ts index be5b9cce44..40ab2b0409 100644 --- a/apps/staking/src/api.ts +++ b/apps/staking/src/api.ts @@ -1,5 +1,5 @@ // TODO remove these disables when moving off the mock APIs -/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/require-await */ +/* eslint-disable @typescript-eslint/no-unused-vars */ import type { HermesClient } from "@pythnetwork/hermes-client"; import { @@ -312,7 +312,11 @@ export const cancelWarmupGovernance = async ( stakeAccount: PublicKey, amount: bigint, ): Promise => { - await client.unstakeFromGovernance(stakeAccount, PositionState.LOCKING, amount); + await client.unstakeFromGovernance( + stakeAccount, + PositionState.LOCKING, + amount, + ); }; export const unstakeGovernance = async ( @@ -320,7 +324,11 @@ export const unstakeGovernance = async ( stakeAccount: PublicKey, amount: bigint, ): Promise => { - await client.unstakeFromGovernance(stakeAccount, PositionState.LOCKED, amount); + await client.unstakeFromGovernance( + stakeAccount, + PositionState.LOCKED, + amount, + ); }; export const delegateIntegrityStaking = async ( @@ -338,7 +346,12 @@ export const cancelWarmupIntegrityStaking = async ( publisherKey: PublicKey, amount: bigint, ): Promise => { - await client.unstakeFromPublisher(stakeAccount, publisherKey, PositionState.LOCKING, amount); + await client.unstakeFromPublisher( + stakeAccount, + publisherKey, + PositionState.LOCKING, + amount, + ); }; export const unstakeIntegrityStaking = async ( @@ -347,7 +360,12 @@ export const unstakeIntegrityStaking = async ( publisherKey: PublicKey, amount: bigint, ): Promise => { - await client.unstakeFromPublisher(stakeAccount, publisherKey, PositionState.LOCKED, amount); + await client.unstakeFromPublisher( + stakeAccount, + publisherKey, + PositionState.LOCKED, + amount, + ); }; export const getUpcomingEpoch = (): Date => { @@ -406,10 +424,3 @@ const mkMockHistory = (): AccountHistory => [ locked: 0n, }, ]; - -class NotImplementedError extends Error { - constructor() { - super("Not yet implemented!"); - this.name = "NotImplementedError"; - } -} From eba95e24e6f8b4c5075ee6bfa8b594f33b0d0785 Mon Sep 17 00:00:00 2001 From: keyvan Date: Tue, 10 Sep 2024 15:30:45 -0700 Subject: [PATCH 12/18] claimable placeholder --- apps/staking/src/api.ts | 4 ++- .../src/pyth-staking-client.ts | 32 ++++++++++++++++--- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/apps/staking/src/api.ts b/apps/staking/src/api.ts index 40ab2b0409..ee5ef45721 100644 --- a/apps/staking/src/api.ts +++ b/apps/staking/src/api.ts @@ -164,6 +164,7 @@ export const loadData = async ( ownerAtaAccount, unlockSchedule, poolConfig, + claimableRewards, currentEpoch, publisherRankingsResponse, publisherCaps, @@ -173,6 +174,7 @@ export const loadData = async ( client.getOwnerPythAtaAccount(), client.getUnlockSchedule(stakeAccount.address), client.getPoolConfigAccount(), + client.getClaimableRewards(stakeAccount.address), getCurrentEpoch(client.connection), fetch("/api/publishers-ranking"), hermesClient.getLatestPublisherCaps({ @@ -213,7 +215,7 @@ export const loadData = async ( return { lastSlash: undefined, // TODO - availableRewards: 0n, // TODO + availableRewards: claimableRewards, expiringRewards: undefined, // TODO total: stakeAccountCustody.amount, yieldRate: poolConfig.y, diff --git a/governance/pyth_staking_sdk/src/pyth-staking-client.ts b/governance/pyth_staking_sdk/src/pyth-staking-client.ts index 1fe00b1595..947f406b56 100644 --- a/governance/pyth_staking_sdk/src/pyth-staking-client.ts +++ b/governance/pyth_staking_sdk/src/pyth-staking-client.ts @@ -6,7 +6,7 @@ import { getAssociatedTokenAddress, } from "@solana/spl-token"; import type { AnchorWallet } from "@solana/wallet-adapter-react"; -import { Connection, PublicKey, TransactionInstruction } from "@solana/web3.js"; +import { Connection, PublicKey, Transaction, TransactionInstruction } from "@solana/web3.js"; import { getConfigAddress, @@ -410,14 +410,22 @@ export class PythStakingClient { }); } - public async advanceDelegationRecord(stakeAccountPositions: PublicKey) { - // TODO: optimize to only send transactions for publishers that have positive rewards + async getAdvanceDelegationRecordInstructions(stakeAccountPositions: PublicKey) { const poolData = await this.getPoolDataAccount(); - const publishers = extractPublisherData(poolData); + const stakeAccountPositionsData = await this.getStakeAccountPositions( + stakeAccountPositions, + ); + const allPublishers = extractPublisherData(poolData); + const publishers = allPublishers.filter(({ pubkey }) => + stakeAccountPositionsData.data.positions.some( + ({ targetWithParameters }) => + targetWithParameters.integrityPool?.publisher.equals(pubkey), + ), + ); // anchor does not calculate the correct pda for other programs // therefore we need to manually calculate the pdas - const instructions = await Promise.all( + return Promise.all( publishers.map(({ pubkey, stakeAccount }) => this.integrityPoolProgram.methods .advanceDelegationRecord() @@ -436,7 +444,21 @@ export class PythStakingClient { .instruction(), ), ); + } + + public async advanceDelegationRecord(stakeAccountPositions: PublicKey) { + const instructions = await this.getAdvanceDelegationRecordInstructions(stakeAccountPositions); return sendTransaction(instructions, this.connection, this.wallet); } + + public async getClaimableRewards(stakeAccountPositions: PublicKey) { + const instructions = await this.getAdvanceDelegationRecordInstructions(stakeAccountPositions); + + for (const instruction of instructions) { + await this.connection.simulateTransaction(new Transaction().add(instruction)); + } + + return 1n; + } } From 63a7b7b06f74dddc8d530445df54a7b165e2d471 Mon Sep 17 00:00:00 2001 From: keyvan Date: Tue, 10 Sep 2024 15:43:05 -0700 Subject: [PATCH 13/18] use rewrites for publisher rankings --- apps/staking/next.config.js | 9 +++++++++ apps/staking/src/app/api/publishers-ranking/route.tsx | 10 ---------- 2 files changed, 9 insertions(+), 10 deletions(-) delete mode 100644 apps/staking/src/app/api/publishers-ranking/route.tsx diff --git a/apps/staking/next.config.js b/apps/staking/next.config.js index aaf916ed81..7b55dd3e1d 100644 --- a/apps/staking/next.config.js +++ b/apps/staking/next.config.js @@ -52,4 +52,13 @@ export default { ], }, ], + + async rewrites() { + return [ + { + source: '/api/publishers-ranking', + destination: 'https://web-api.pyth.network/publishers_ranking?cluster=pythnet', + }, + ] + }, }; diff --git a/apps/staking/src/app/api/publishers-ranking/route.tsx b/apps/staking/src/app/api/publishers-ranking/route.tsx deleted file mode 100644 index 37bab519c1..0000000000 --- a/apps/staking/src/app/api/publishers-ranking/route.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { NextResponse } from "next/server"; - -export async function GET() { - const publisherRankingsResponse = await fetch( - "https://www.pyth.network/api/publishers-ranking?cluster=pythnet", - ); - - const publisherRankings = (await publisherRankingsResponse.json()) as JSON; - return NextResponse.json(publisherRankings); -} From 13c300da7598d32fcc6542bba62b4f6c5ca16f1a Mon Sep 17 00:00:00 2001 From: keyvan Date: Tue, 10 Sep 2024 15:50:25 -0700 Subject: [PATCH 14/18] prase ranking with zod --- apps/staking/package.json | 9 ++-- apps/staking/src/api.ts | 16 ++++--- pnpm-lock.yaml | 95 ++++++++++++++++++++++++++------------- 3 files changed, 78 insertions(+), 42 deletions(-) diff --git a/apps/staking/package.json b/apps/staking/package.json index 564ff9d999..1402bbb48e 100644 --- a/apps/staking/package.json +++ b/apps/staking/package.json @@ -29,20 +29,21 @@ "@pythnetwork/hermes-client": "workspace:*", "@pythnetwork/staking-sdk": "workspace:*", "@solana/wallet-adapter-base": "^0.9.20", - "@solana/wallet-adapter-react-ui": "^0.9.27", "@solana/wallet-adapter-react": "^0.15.28", + "@solana/wallet-adapter-react-ui": "^0.9.27", "@solana/wallet-adapter-wallets": "0.19.10", "@solana/web3.js": "^1.95.2", "clsx": "^2.1.1", "dnum": "^2.13.1", "next": "^14.2.5", "pino": "^9.3.2", - "react-aria-components": "^1.3.3", + "react": "^18.3.1", "react-aria": "^3.34.3", + "react-aria-components": "^1.3.3", "react-dom": "^18.3.1", - "react": "^18.3.1", "recharts": "^2.12.7", - "swr": "^2.2.5" + "swr": "^2.2.5", + "zod": "^3.23.8" }, "devDependencies": { "@axe-core/react": "^4.9.1", diff --git a/apps/staking/src/api.ts b/apps/staking/src/api.ts index ee5ef45721..a4547bc498 100644 --- a/apps/staking/src/api.ts +++ b/apps/staking/src/api.ts @@ -12,13 +12,15 @@ import { type StakeAccountPositions, } from "@pythnetwork/staking-sdk"; import { PublicKey } from "@solana/web3.js"; +import { z } from "zod"; + +const publishersRankingSchema = z.object({ + publisher: z.string(), + rank: z.number(), + numSymbols: z.number(), + timestamp: z.string(), +}).array(); -type PublisherRankings = { - publisher: string; - rank: number; - numSymbols: number; - timestamp: string; -}[]; type Data = { total: bigint; @@ -185,7 +187,7 @@ export const loadData = async ( const publishers = extractPublisherData(poolData); const publisherRankings = - (await publisherRankingsResponse.json()) as PublisherRankings; + publishersRankingSchema.parse(await publisherRankingsResponse.json()); const filterGovernancePositions = (positionState: PositionState) => getAmountByTargetAndState({ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ae38b9afb2..b9d556c0c7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -111,7 +111,7 @@ importers: version: 4.9.1 '@cprussin/eslint-config': specifier: ^3.0.0 - version: 3.0.0(@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2))(eslint@9.5.0)(typescript@5.5.2))(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2))(jest@29.7.0(@types/node@20.14.7)(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2)))(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2))(typescript@5.5.2) + version: 3.0.0(@typescript-eslint/eslint-plugin@7.13.1(eslint@9.5.0)(typescript@5.5.2))(jest@29.7.0(@types/node@20.14.7)(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2)))(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2))(typescript@5.5.2) '@cprussin/jest-config': specifier: ^1.4.1 version: 1.4.1(@babel/core@7.24.7)(@jest/globals@29.7.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(@types/jest@29.5.12)(@types/node@20.14.7)(babel-jest@29.7.0(@babel/core@7.24.7))(bufferutil@4.0.8)(eslint@9.5.0)(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2))(utf-8-validate@5.0.10) @@ -384,13 +384,16 @@ importers: swr: specifier: ^2.2.5 version: 2.2.5(react@18.3.1) + zod: + specifier: ^3.23.8 + version: 3.23.8 devDependencies: '@axe-core/react': specifier: ^4.9.1 version: 4.9.1 '@cprussin/eslint-config': specifier: ^3.0.0 - version: 3.0.0(@typescript-eslint/eslint-plugin@7.13.1(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4))(jest@29.7.0(@types/node@22.2.0)(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4)))(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4))(typescript@5.5.4) + version: 3.0.0(@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4))(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(jest@29.7.0(@types/node@22.2.0)(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4)))(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4))(typescript@5.5.4) '@cprussin/jest-config': specifier: ^1.4.1 version: 1.4.1(@babel/core@7.24.7)(@jest/globals@29.7.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(@types/jest@29.5.12)(@types/node@22.2.0)(babel-jest@29.7.0(@babel/core@7.24.7))(bufferutil@4.0.8)(eslint@9.9.0(jiti@1.21.0))(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4))(utf-8-validate@5.0.10) @@ -24324,7 +24327,7 @@ snapshots: transitivePeerDependencies: - debug - '@cprussin/eslint-config@3.0.0(@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2))(eslint@9.5.0)(typescript@5.5.2))(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2))(jest@29.7.0(@types/node@20.14.7)(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2)))(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2))(typescript@5.5.2)': + '@cprussin/eslint-config@3.0.0(@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4))(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(jest@29.7.0(@types/node@22.2.0)(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4)))(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4))(typescript@5.5.4)': dependencies: '@babel/core': 7.24.7 '@babel/eslint-parser': 7.24.7(@babel/core@7.24.7)(eslint@9.5.0) @@ -24336,22 +24339,22 @@ snapshots: eslint: 9.5.0 eslint-config-prettier: 9.1.0(eslint@9.5.0) eslint-config-turbo: 1.13.4(eslint@9.5.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2))(eslint@9.5.0) - eslint-plugin-jest: 28.6.0(@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2))(eslint@9.5.0)(typescript@5.5.2))(eslint@9.5.0)(jest@29.7.0(@types/node@20.14.7)(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2)))(typescript@5.5.2) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(eslint@9.5.0) + eslint-plugin-jest: 28.6.0(@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4))(eslint@9.5.0)(jest@29.7.0(@types/node@22.2.0)(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4)))(typescript@5.5.4) eslint-plugin-jest-dom: 5.4.0(eslint@9.5.0) eslint-plugin-jsonc: 2.16.0(eslint@9.5.0) eslint-plugin-jsx-a11y: 6.8.0(eslint@9.5.0) eslint-plugin-n: 17.9.0(eslint@9.5.0) eslint-plugin-react: 7.34.2(eslint@9.5.0) eslint-plugin-react-hooks: 4.6.2(eslint@9.5.0) - eslint-plugin-storybook: 0.8.0(eslint@9.5.0)(typescript@5.5.2) - eslint-plugin-tailwindcss: 3.17.3(tailwindcss@3.4.4(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2))) - eslint-plugin-testing-library: 6.2.2(eslint@9.5.0)(typescript@5.5.2) + eslint-plugin-storybook: 0.8.0(eslint@9.5.0)(typescript@5.5.4) + eslint-plugin-tailwindcss: 3.17.3(tailwindcss@3.4.4(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4))) + eslint-plugin-testing-library: 6.2.2(eslint@9.5.0)(typescript@5.5.4) eslint-plugin-tsdoc: 0.3.0 eslint-plugin-unicorn: 53.0.0(eslint@9.5.0) globals: 15.6.0 - tailwindcss: 3.4.4(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2)) - typescript-eslint: 7.13.1(eslint@9.5.0)(typescript@5.5.2) + tailwindcss: 3.4.4(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4)) + typescript-eslint: 7.13.1(eslint@9.5.0)(typescript@5.5.4) transitivePeerDependencies: - '@testing-library/dom' - '@typescript-eslint/eslint-plugin' @@ -24402,7 +24405,7 @@ snapshots: - ts-node - typescript - '@cprussin/eslint-config@3.0.0(@typescript-eslint/eslint-plugin@7.13.1(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4))(jest@29.7.0(@types/node@22.2.0)(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4)))(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4))(typescript@5.5.4)': + '@cprussin/eslint-config@3.0.0(@typescript-eslint/eslint-plugin@7.13.1(eslint@9.5.0)(typescript@5.5.2))(jest@29.7.0(@types/node@20.14.7)(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2)))(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2))(typescript@5.5.2)': dependencies: '@babel/core': 7.24.7 '@babel/eslint-parser': 7.24.7(@babel/core@7.24.7)(eslint@9.5.0) @@ -24414,22 +24417,22 @@ snapshots: eslint: 9.5.0 eslint-config-prettier: 9.1.0(eslint@9.5.0) eslint-config-turbo: 1.13.4(eslint@9.5.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2))(eslint@9.5.0) - eslint-plugin-jest: 28.6.0(@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@8.3.0(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4))(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4))(eslint@9.5.0)(jest@29.7.0(@types/node@22.2.0)(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4)))(typescript@5.5.4) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(eslint@9.5.0) + eslint-plugin-jest: 28.6.0(@typescript-eslint/eslint-plugin@7.13.1(eslint@9.5.0)(typescript@5.5.2))(eslint@9.5.0)(jest@29.7.0(@types/node@20.14.7)(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2)))(typescript@5.5.2) eslint-plugin-jest-dom: 5.4.0(eslint@9.5.0) eslint-plugin-jsonc: 2.16.0(eslint@9.5.0) eslint-plugin-jsx-a11y: 6.8.0(eslint@9.5.0) eslint-plugin-n: 17.9.0(eslint@9.5.0) eslint-plugin-react: 7.34.2(eslint@9.5.0) eslint-plugin-react-hooks: 4.6.2(eslint@9.5.0) - eslint-plugin-storybook: 0.8.0(eslint@9.5.0)(typescript@5.5.4) - eslint-plugin-tailwindcss: 3.17.3(tailwindcss@3.4.4(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4))) - eslint-plugin-testing-library: 6.2.2(eslint@9.5.0)(typescript@5.5.4) + eslint-plugin-storybook: 0.8.0(eslint@9.5.0)(typescript@5.5.2) + eslint-plugin-tailwindcss: 3.17.3(tailwindcss@3.4.4(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2))) + eslint-plugin-testing-library: 6.2.2(eslint@9.5.0)(typescript@5.5.2) eslint-plugin-tsdoc: 0.3.0 eslint-plugin-unicorn: 53.0.0(eslint@9.5.0) globals: 15.6.0 - tailwindcss: 3.4.4(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4)) - typescript-eslint: 7.13.1(eslint@9.5.0)(typescript@5.5.4) + tailwindcss: 3.4.4(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2)) + typescript-eslint: 7.13.1(eslint@9.5.0)(typescript@5.5.2) transitivePeerDependencies: - '@testing-library/dom' - '@typescript-eslint/eslint-plugin' @@ -33970,7 +33973,7 @@ snapshots: '@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(eslint@9.5.0)(typescript@5.5.4)': dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 7.13.1(eslint@9.5.0)(typescript@5.5.4) + '@typescript-eslint/parser': 7.13.1(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4) '@typescript-eslint/scope-manager': 7.13.1 '@typescript-eslint/type-utils': 7.13.1(eslint@9.5.0)(typescript@5.5.4) '@typescript-eslint/utils': 7.13.1(eslint@9.5.0)(typescript@5.5.4) @@ -33985,6 +33988,25 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4)': + dependencies: + '@eslint-community/regexpp': 4.10.0 + '@typescript-eslint/parser': 7.13.1(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4) + '@typescript-eslint/scope-manager': 7.13.1 + '@typescript-eslint/type-utils': 7.13.1(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4) + '@typescript-eslint/utils': 7.13.1(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 7.13.1 + eslint: 9.9.0(jiti@1.21.0) + graphemer: 1.4.0 + ignore: 5.3.1 + natural-compare: 1.4.0 + ts-api-utils: 1.3.0(typescript@5.5.4) + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + optional: true + '@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@8.3.0(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4))(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4)': dependencies: '@eslint-community/regexpp': 4.10.0 @@ -34133,14 +34155,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4)': + '@typescript-eslint/parser@7.13.1(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4)': dependencies: '@typescript-eslint/scope-manager': 7.13.1 '@typescript-eslint/types': 7.13.1 '@typescript-eslint/typescript-estree': 7.13.1(typescript@5.5.4) '@typescript-eslint/visitor-keys': 7.13.1 debug: 4.3.5 - eslint: 9.5.0 + eslint: 9.9.0(jiti@1.21.0) optionalDependencies: typescript: 5.5.4 transitivePeerDependencies: @@ -39492,11 +39514,11 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint@9.5.0): + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint@9.5.0): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 7.13.1(eslint@9.5.0)(typescript@5.5.2) + '@typescript-eslint/parser': 7.13.1(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4) eslint: 9.5.0 eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: @@ -39519,7 +39541,7 @@ snapshots: eslint: 9.5.0 eslint-compat-utils: 0.5.1(eslint@9.5.0) - eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2))(eslint@9.5.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(eslint@9.5.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -39529,7 +39551,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.5.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint@9.5.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint@9.5.0) hasown: 2.0.2 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -39540,7 +39562,7 @@ snapshots: semver: 6.3.1 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 7.13.1(eslint@9.5.0)(typescript@5.5.2) + '@typescript-eslint/parser': 7.13.1(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -39606,13 +39628,13 @@ snapshots: eslint: 9.5.0 requireindex: 1.2.0 - eslint-plugin-jest@28.6.0(@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2))(eslint@9.5.0)(typescript@5.5.2))(eslint@9.5.0)(jest@29.7.0(@types/node@20.14.7)(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2)))(typescript@5.5.2): + eslint-plugin-jest@28.6.0(@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4))(eslint@9.5.0)(jest@29.7.0(@types/node@22.2.0)(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4)))(typescript@5.5.4): dependencies: - '@typescript-eslint/utils': 7.7.1(eslint@9.5.0)(typescript@5.5.2) + '@typescript-eslint/utils': 7.7.1(eslint@9.5.0)(typescript@5.5.4) eslint: 9.5.0 optionalDependencies: - '@typescript-eslint/eslint-plugin': 7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2))(eslint@9.5.0)(typescript@5.5.2) - jest: 29.7.0(@types/node@20.14.7)(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2)) + '@typescript-eslint/eslint-plugin': 7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4) + jest: 29.7.0(@types/node@22.2.0)(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4)) transitivePeerDependencies: - supports-color - typescript @@ -39628,6 +39650,17 @@ snapshots: - supports-color - typescript + eslint-plugin-jest@28.6.0(@typescript-eslint/eslint-plugin@7.13.1(eslint@9.5.0)(typescript@5.5.2))(eslint@9.5.0)(jest@29.7.0(@types/node@20.14.7)(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2)))(typescript@5.5.2): + dependencies: + '@typescript-eslint/utils': 7.7.1(eslint@9.5.0)(typescript@5.5.2) + eslint: 9.5.0 + optionalDependencies: + '@typescript-eslint/eslint-plugin': 7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2))(eslint@9.5.0)(typescript@5.5.2) + jest: 29.7.0(@types/node@20.14.7)(ts-node@10.9.2(@types/node@20.14.7)(typescript@5.5.2)) + transitivePeerDependencies: + - supports-color + - typescript + eslint-plugin-jsonc@2.16.0(eslint@9.5.0): dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@9.5.0) @@ -50860,7 +50893,7 @@ snapshots: typescript-eslint@7.13.1(eslint@9.5.0)(typescript@5.5.4): dependencies: '@typescript-eslint/eslint-plugin': 7.13.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.4))(eslint@9.5.0)(typescript@5.5.4) - '@typescript-eslint/parser': 7.13.1(eslint@9.5.0)(typescript@5.5.4) + '@typescript-eslint/parser': 7.13.1(eslint@9.9.0(jiti@1.21.0))(typescript@5.5.4) '@typescript-eslint/utils': 7.13.1(eslint@9.5.0)(typescript@5.5.4) eslint: 9.5.0 optionalDependencies: From b75165d8f208305673a558d3f5acce54740f9ee2 Mon Sep 17 00:00:00 2001 From: keyvan Date: Tue, 10 Sep 2024 15:51:31 -0700 Subject: [PATCH 15/18] fix --- apps/staking/next.config.js | 7 ++++--- apps/staking/src/api.ts | 20 +++++++++++--------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/apps/staking/next.config.js b/apps/staking/next.config.js index 7b55dd3e1d..12bcd51ba7 100644 --- a/apps/staking/next.config.js +++ b/apps/staking/next.config.js @@ -56,9 +56,10 @@ export default { async rewrites() { return [ { - source: '/api/publishers-ranking', - destination: 'https://web-api.pyth.network/publishers_ranking?cluster=pythnet', + source: "/api/publishers-ranking", + destination: + "https://web-api.pyth.network/publishers_ranking?cluster=pythnet", }, - ] + ]; }, }; diff --git a/apps/staking/src/api.ts b/apps/staking/src/api.ts index a4547bc498..0bf24eb546 100644 --- a/apps/staking/src/api.ts +++ b/apps/staking/src/api.ts @@ -14,13 +14,14 @@ import { import { PublicKey } from "@solana/web3.js"; import { z } from "zod"; -const publishersRankingSchema = z.object({ - publisher: z.string(), - rank: z.number(), - numSymbols: z.number(), - timestamp: z.string(), -}).array(); - +const publishersRankingSchema = z + .object({ + publisher: z.string(), + rank: z.number(), + numSymbols: z.number(), + timestamp: z.string(), + }) + .array(); type Data = { total: bigint; @@ -186,8 +187,9 @@ export const loadData = async ( const publishers = extractPublisherData(poolData); - const publisherRankings = - publishersRankingSchema.parse(await publisherRankingsResponse.json()); + const publisherRankings = publishersRankingSchema.parse( + await publisherRankingsResponse.json(), + ); const filterGovernancePositions = (positionState: PositionState) => getAmountByTargetAndState({ From 9f602c4c931f0f4be7dca565c2092f255dcb6bf5 Mon Sep 17 00:00:00 2001 From: keyvan Date: Tue, 10 Sep 2024 15:51:56 -0700 Subject: [PATCH 16/18] fix --- .../src/pyth-staking-client.ts | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/governance/pyth_staking_sdk/src/pyth-staking-client.ts b/governance/pyth_staking_sdk/src/pyth-staking-client.ts index 947f406b56..743557da4f 100644 --- a/governance/pyth_staking_sdk/src/pyth-staking-client.ts +++ b/governance/pyth_staking_sdk/src/pyth-staking-client.ts @@ -6,7 +6,12 @@ import { getAssociatedTokenAddress, } from "@solana/spl-token"; import type { AnchorWallet } from "@solana/wallet-adapter-react"; -import { Connection, PublicKey, Transaction, TransactionInstruction } from "@solana/web3.js"; +import { + Connection, + PublicKey, + Transaction, + TransactionInstruction, +} from "@solana/web3.js"; import { getConfigAddress, @@ -410,7 +415,9 @@ export class PythStakingClient { }); } - async getAdvanceDelegationRecordInstructions(stakeAccountPositions: PublicKey) { + async getAdvanceDelegationRecordInstructions( + stakeAccountPositions: PublicKey, + ) { const poolData = await this.getPoolDataAccount(); const stakeAccountPositionsData = await this.getStakeAccountPositions( stakeAccountPositions, @@ -447,16 +454,22 @@ export class PythStakingClient { } public async advanceDelegationRecord(stakeAccountPositions: PublicKey) { - const instructions = await this.getAdvanceDelegationRecordInstructions(stakeAccountPositions); + const instructions = await this.getAdvanceDelegationRecordInstructions( + stakeAccountPositions, + ); return sendTransaction(instructions, this.connection, this.wallet); } public async getClaimableRewards(stakeAccountPositions: PublicKey) { - const instructions = await this.getAdvanceDelegationRecordInstructions(stakeAccountPositions); + const instructions = await this.getAdvanceDelegationRecordInstructions( + stakeAccountPositions, + ); for (const instruction of instructions) { - await this.connection.simulateTransaction(new Transaction().add(instruction)); + await this.connection.simulateTransaction( + new Transaction().add(instruction), + ); } return 1n; From 6439c6a61c956fe74c75ed013aa4e7ca5a8a2ce3 Mon Sep 17 00:00:00 2001 From: keyvan Date: Tue, 10 Sep 2024 15:57:57 -0700 Subject: [PATCH 17/18] fiz pyth balance --- apps/staking/src/api.ts | 6 +++--- governance/pyth_staking_sdk/src/pyth-staking-client.ts | 9 +++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/staking/src/api.ts b/apps/staking/src/api.ts index 0bf24eb546..bfc0cd2bd5 100644 --- a/apps/staking/src/api.ts +++ b/apps/staking/src/api.ts @@ -164,7 +164,7 @@ export const loadData = async ( const [ stakeAccountCustody, poolData, - ownerAtaAccount, + ownerPythBalance, unlockSchedule, poolConfig, claimableRewards, @@ -174,7 +174,7 @@ export const loadData = async ( ] = await Promise.all([ client.getStakeAccountCustody(stakeAccount.address), client.getPoolDataAccount(), - client.getOwnerPythAtaAccount(), + client.getOwnerPythBalance(), client.getUnlockSchedule(stakeAccount.address), client.getPoolConfigAccount(), client.getClaimableRewards(stakeAccount.address), @@ -231,7 +231,7 @@ export const loadData = async ( }, unlockSchedule, locked: unlockSchedule.reduce((sum, { amount }) => sum + amount, 0n), - walletAmount: ownerAtaAccount.amount, + walletAmount: ownerPythBalance, integrityStakingPublishers: publishers.map((publisherData) => { const publisherRanking = publisherRankings.find( (ranking) => ranking.publisher === publisherData.pubkey.toBase58(), diff --git a/governance/pyth_staking_sdk/src/pyth-staking-client.ts b/governance/pyth_staking_sdk/src/pyth-staking-client.ts index 743557da4f..3034a7a04d 100644 --- a/governance/pyth_staking_sdk/src/pyth-staking-client.ts +++ b/governance/pyth_staking_sdk/src/pyth-staking-client.ts @@ -185,6 +185,15 @@ export class PythStakingClient { ); } + public async getOwnerPythBalance(): Promise { + try { + const ataAccount = await this.getOwnerPythAtaAccount(); + return ataAccount.amount; + } catch { + return 0n; + } + } + public async getPoolConfigAccount(): Promise { const poolConfigAnchor = await this.integrityPoolProgram.account.poolConfig.fetch( From e022d8adb172541b3e695fafd3def1019bcbf37d Mon Sep 17 00:00:00 2001 From: keyvan Date: Tue, 10 Sep 2024 16:00:20 -0700 Subject: [PATCH 18/18] fix --- apps/staking/src/api.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/staking/src/api.ts b/apps/staking/src/api.ts index bfc0cd2bd5..f14cb160c4 100644 --- a/apps/staking/src/api.ts +++ b/apps/staking/src/api.ts @@ -233,8 +233,9 @@ export const loadData = async ( locked: unlockSchedule.reduce((sum, { amount }) => sum + amount, 0n), walletAmount: ownerPythBalance, integrityStakingPublishers: publishers.map((publisherData) => { + const publisherPubkeyString = publisherData.pubkey.toBase58(); const publisherRanking = publisherRankings.find( - (ranking) => ranking.publisher === publisherData.pubkey.toBase58(), + (ranking) => ranking.publisher === publisherPubkeyString, ); const apyHistory = publisherData.apyHistory.map(({ epoch, apy }) => ({ date: epochToDate(epoch + 1n),