diff --git a/apps/staking/src/api.ts b/apps/staking/src/api.ts index 7791855601..3f408d0337 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/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/require-await */ import { getAmountByTargetAndState, @@ -8,14 +8,7 @@ import { PythStakingClient, type StakeAccountPositions, } from "@pythnetwork/staking-sdk"; -import type { AnchorWallet } from "@solana/wallet-adapter-react"; -import { PublicKey, type Connection } from "@solana/web3.js"; - -export type Context = { - connection: Connection; - wallet: AnchorWallet; - stakeAccount: StakeAccountPositions; -}; +import { PublicKey } from "@solana/web3.js"; type Data = { total: bigint; @@ -46,7 +39,7 @@ type Data = { }; integrityStakingPublishers: { name: string; - publicKey: string; + publicKey: PublicKey; isSelf: boolean; selfStake: bigint; poolCapacity: bigint; @@ -145,50 +138,42 @@ type AccountHistory = { }[]; export const getStakeAccounts = async ( - connection: Connection, - wallet: AnchorWallet, -): Promise => { - const pythStakingClient = new PythStakingClient({ connection, wallet }); - return pythStakingClient.getAllStakeAccountPositions(wallet.publicKey); -}; - -export const loadData = async (context: Context): Promise => { - await new Promise((resolve) => setTimeout(resolve, MOCK_DELAY)); - // While using mocks we need to clone the MOCK_DATA object every time - // `loadData` is called so that swr treats the response as changed and - // triggers a rerender. - - const pythStakingClient = new PythStakingClient({ - connection: context.connection, - wallet: context.wallet, - }); - const stakeAccountPositions = context.stakeAccount; - - const [stakeAccountCustody, publishers, ownerAtaAccount, currentEpoch] = - await Promise.all([ - pythStakingClient.getStakeAccountCustody(stakeAccountPositions.address), - pythStakingClient.getPublishers(), - pythStakingClient.getOwnerPythAtaAccount(), - getCurrentEpoch(context.connection), - ]); - - const unlockSchedule = await pythStakingClient.getUnlockSchedule({ - stakeAccountPositions: stakeAccountPositions.address, - }); + client: PythStakingClient, +): Promise => + client.getAllStakeAccountPositions(client.wallet.publicKey); + +export const loadData = async ( + client: PythStakingClient, + stakeAccount: StakeAccountPositions, +): Promise => { + const [ + stakeAccountCustody, + publishers, + ownerAtaAccount, + currentEpoch, + unlockSchedule, + ] = await Promise.all([ + client.getStakeAccountCustody(stakeAccount.address), + client.getPublishers(), + client.getOwnerPythAtaAccount(), + getCurrentEpoch(client.connection), + client.getUnlockSchedule(stakeAccount.address), + ]); const filterGovernancePositions = (positionState: PositionState) => getAmountByTargetAndState({ - stakeAccountPositions, + stakeAccountPositions: stakeAccount, targetWithParameters: { voting: {} }, positionState, epoch: currentEpoch, }); + const filterOISPositions = ( publisher: PublicKey, positionState: PositionState, ) => getAmountByTargetAndState({ - stakeAccountPositions, + stakeAccountPositions: stakeAccount, targetWithParameters: { integrityPool: { publisher } }, positionState, epoch: currentEpoch, @@ -215,7 +200,7 @@ export const loadData = async (context: Context): Promise => { numFeeds: 0, // TODO poolCapacity: 100n, // TODO poolUtilization: 0n, // TODO - publicKey: publisher.toString(), + publicKey: publisher, qualityRanking: 0, // TODO selfStake: 0n, // TODO positions: { @@ -229,135 +214,85 @@ export const loadData = async (context: Context): Promise => { }; export const loadAccountHistory = async ( - _context: Context, + _client: PythStakingClient, + _stakeAccount: PublicKey, ): Promise => { await new Promise((resolve) => setTimeout(resolve, MOCK_DELAY)); - return MOCK_HISTORY["0x000000"]!; + return mkMockHistory(); }; export const deposit = async ( - context: Context, + client: PythStakingClient, + stakeAccount: PublicKey, amount: bigint, ): Promise => { - const pythStakingClient = new PythStakingClient({ - connection: context.connection, - wallet: context.wallet, - }); - await pythStakingClient.depositTokensToStakeAccountCustody( - context.stakeAccount.address, - amount, - ); + await client.depositTokensToStakeAccountCustody(stakeAccount, amount); }; export const withdraw = async ( - context: Context, + client: PythStakingClient, + stakeAccount: PublicKey, amount: bigint, ): Promise => { - const pythStakingClient = new PythStakingClient({ - connection: context.connection, - wallet: context.wallet, - }); - await pythStakingClient.withdrawTokensFromStakeAccountCustody( - context.stakeAccount.address, - amount, - ); + await client.withdrawTokensFromStakeAccountCustody(stakeAccount, amount); }; -export const claim = async (context: Context): Promise => { - const pythStakingClient = new PythStakingClient({ - connection: context.connection, - wallet: context.wallet, - }); - await pythStakingClient.advanceDelegationRecord({ - stakeAccountPositions: context.stakeAccount.address, - }); +export const claim = async ( + client: PythStakingClient, + stakeAccount: PublicKey, +): Promise => { + await client.advanceDelegationRecord(stakeAccount); }; export const stakeGovernance = async ( - context: Context, + client: PythStakingClient, + stakeAccount: PublicKey, amount: bigint, ): Promise => { - const pythStakingClient = new PythStakingClient({ - connection: context.connection, - wallet: context.wallet, - }); - await pythStakingClient.stakeToGovernance( - context.stakeAccount.address, - amount, - ); + await client.stakeToGovernance(stakeAccount, amount); }; export const cancelWarmupGovernance = async ( - _context: Context, + _client: PythStakingClient, + _stakeAccount: PublicKey, _amount: bigint, ): Promise => { - await new Promise((resolve) => setTimeout(resolve, MOCK_DELAY)); + throw new NotImplementedError(); }; export const unstakeGovernance = async ( - _context: Context, + _client: PythStakingClient, + _stakeAccount: PublicKey, _amount: bigint, ): Promise => { - await new Promise((resolve) => setTimeout(resolve, MOCK_DELAY)); + throw new NotImplementedError(); }; export const delegateIntegrityStaking = async ( - context: Context, - publisherKey: string, + client: PythStakingClient, + stakeAccount: PublicKey, + publisherKey: PublicKey, amount: bigint, ): Promise => { - const pythStakingClient = new PythStakingClient({ - connection: context.connection, - wallet: context.wallet, - }); - - await pythStakingClient.stakeToPublisher({ - stakeAccountPositions: context.stakeAccount.address, - publisher: new PublicKey(publisherKey), - amount, - }); + await client.stakeToPublisher(stakeAccount, publisherKey, amount); }; export const cancelWarmupIntegrityStaking = async ( - context: Context, - publisherKey: string, - amount: bigint, + _client: PythStakingClient, + _stakeAccount: PublicKey, + _publisherKey: PublicKey, + _amount: bigint, ): Promise => { - await new Promise((resolve) => setTimeout(resolve, MOCK_DELAY)); - const publisher = MOCK_DATA[ - context.stakeAccount.address.toString() - ]!.integrityStakingPublishers.find( - (publisher) => publisher.publicKey === publisherKey, - ); - if (publisher) { - if (publisher.positions?.warmup) { - publisher.positions.warmup -= amount; - } - } else { - throw new Error(`Invalid publisher key: "${publisherKey}"`); - } + throw new NotImplementedError(); }; export const unstakeIntegrityStaking = async ( - context: Context, - publisherKey: string, - amount: bigint, + _client: PythStakingClient, + _stakeAccount: PublicKey, + _publisherKey: PublicKey, + _amount: bigint, ): Promise => { - await new Promise((resolve) => setTimeout(resolve, MOCK_DELAY)); - const publisher = MOCK_DATA[ - context.stakeAccount.address.toString() - ]!.integrityStakingPublishers.find( - (publisher) => publisher.publicKey === publisherKey, - ); - if (publisher) { - if (publisher.positions?.staked) { - publisher.positions.staked -= amount; - publisher.positions.cooldown = - (publisher.positions.cooldown ?? 0n) + amount; - } - } else { - throw new Error(`Invalid publisher key: "${publisherKey}"`); - } + throw new NotImplementedError(); }; export const calculateApy = ( @@ -394,115 +329,6 @@ export const getNextFullEpoch = (): Date => { const MOCK_DELAY = 500; -const mkMockData = (isDouro: boolean): Data => ({ - total: 15_000_000n, - availableRewards: 156_000n, - lastSlash: isDouro - ? undefined - : { - amount: 2147n, - date: new Date("2024-05-04T00:00:00Z"), - }, - expiringRewards: { - amount: 56_000n, - expiry: new Date("2025-08-01T00:00:00Z"), - }, - locked: isDouro ? 3_000_000n : 0n, - unlockSchedule: isDouro - ? [ - { - amount: 1_000_000n, - date: new Date("2025-08-01T00:00:00Z"), - }, - { - amount: 2_000_000n, - date: new Date("2025-09-01T00:00:00Z"), - }, - ] - : [], - walletAmount: 5_000_000_000_000n, - governance: { - warmup: 2_670_000n, - staked: 4_150_000n, - cooldown: 1_850_000n, - cooldown2: 4_765_000n, - }, - integrityStakingPublishers: [ - { - name: "Douro Labs", - publicKey: "0xF00", - isSelf: isDouro, - selfStake: 5_000_000_000n, - poolCapacity: 500_000_000n, - poolUtilization: 200_000_000n, - numFeeds: 42, - qualityRanking: 1, - apyHistory: [ - { date: new Date("2024-07-22"), apy: 5 }, - { date: new Date("2024-07-23"), apy: 10 }, - { date: new Date("2024-07-24"), apy: 25 }, - { date: new Date("2024-07-25"), apy: 20 }, - ], - positions: { - warmup: 5_000_000n, - staked: 4_000_000n, - cooldown: 1_000_000n, - cooldown2: 460_000n, - }, - }, - { - name: "Jump Trading", - publicKey: "0xBA4", - isSelf: false, - selfStake: 400_000_000n, - poolCapacity: 500_000_000n, - poolUtilization: 750_000_000n, - numFeeds: 84, - qualityRanking: 2, - apyHistory: [ - { date: new Date("2024-07-24"), apy: 5 }, - { date: new Date("2024-07-25"), apy: 10 }, - ], - positions: { - staked: 1_000_000n, - }, - }, - { - name: "Cboe", - publicKey: "0xAA", - isSelf: false, - selfStake: 200_000_000n, - poolCapacity: 600_000_000n, - poolUtilization: 450_000_000n, - numFeeds: 17, - qualityRanking: 5, - apyHistory: [ - { date: new Date("2024-07-24"), apy: 5 }, - { date: new Date("2024-07-25"), apy: 10 }, - ], - }, - { - name: "Raydium", - publicKey: "0x111", - isSelf: false, - selfStake: 400_000_000n, - poolCapacity: 500_000_000n, - poolUtilization: 750_000_000n, - numFeeds: 84, - qualityRanking: 3, - apyHistory: [ - { date: new Date("2024-07-24"), apy: 5 }, - { date: new Date("2024-07-25"), apy: 10 }, - ], - }, - ], -}); - -const MOCK_DATA: Record = { - "0x000000": mkMockData(true), - "0x111111": mkMockData(false), -}; - const mkMockHistory = (): AccountHistory => [ { timestamp: new Date("2024-06-10T00:00:00Z"), @@ -542,7 +368,9 @@ const mkMockHistory = (): AccountHistory => [ }, ]; -const MOCK_HISTORY: Record = { - "0x000000": mkMockHistory(), - "0x111111": mkMockHistory(), -}; +class NotImplementedError extends Error { + constructor() { + super("Not yet implemented!"); + this.name = "NotImplementedError"; + } +} diff --git a/apps/staking/src/components/OracleIntegrityStaking/index.tsx b/apps/staking/src/components/OracleIntegrityStaking/index.tsx index b33a53861d..1f1c0b374e 100644 --- a/apps/staking/src/components/OracleIntegrityStaking/index.tsx +++ b/apps/staking/src/components/OracleIntegrityStaking/index.tsx @@ -1,8 +1,9 @@ +import type { PythStakingClient } from "@pythnetwork/staking-sdk"; +import { PublicKey } from "@solana/web3.js"; import clsx from "clsx"; import { useMemo, useCallback } from "react"; import { - type Context, delegateIntegrityStaking, cancelWarmupIntegrityStaking, unstakeIntegrityStaking, @@ -127,7 +128,7 @@ export const OracleIntegrityStaking = ({ {otherPublishers.map((publisher) => ( Promise, - publicKey: string, + publisher: PublicKey, ) => useCallback( - (context: Context, amount: bigint) => action(context, publicKey, amount), - [action, publicKey], + (client: PythStakingClient, stakingAccount: PublicKey, amount: bigint) => + action(client, stakingAccount, publisher, amount), + [action, publisher], ); diff --git a/apps/staking/src/components/TransferButton/index.tsx b/apps/staking/src/components/TransferButton/index.tsx index 4c6229bccc..a94afc1bae 100644 --- a/apps/staking/src/components/TransferButton/index.tsx +++ b/apps/staking/src/components/TransferButton/index.tsx @@ -1,4 +1,6 @@ import { Field, Input, Label } from "@headlessui/react"; +import type { PythStakingClient } from "@pythnetwork/staking-sdk"; +import type { PublicKey } from "@solana/web3.js"; import { type ChangeEvent, type ComponentProps, @@ -8,7 +10,6 @@ import { useState, } from "react"; -import type { Context } from "../../api"; import { useLogger } from "../../hooks/use-logger"; import { StateType, useTransfer } from "../../hooks/use-transfer"; import { stringToTokens, tokensToString } from "../../tokens"; @@ -28,7 +29,11 @@ type Props = { | ReactNode | ReactNode[] | undefined; - transfer: (context: Context, amount: bigint) => Promise; + transfer: ( + client: PythStakingClient, + stakingAccount: PublicKey, + amount: bigint, + ) => Promise; className?: string | undefined; secondary?: boolean | undefined; small?: boolean | undefined; @@ -51,9 +56,9 @@ export const TransferButton = ({ const { amountInput, setAmount, updateAmount, resetAmount, amount } = useAmountInput(max); const doTransfer = useCallback( - (context: Context) => + (client: PythStakingClient, stakingAccount: PublicKey) => amount.type === AmountType.Valid - ? transfer(context, amount.amount) + ? transfer(client, stakingAccount, amount.amount) : Promise.reject(new InvalidAmountError()), [amount, transfer], ); diff --git a/apps/staking/src/config/server.ts b/apps/staking/src/config/server.ts index c129b5564a..4643f40a4d 100644 --- a/apps/staking/src/config/server.ts +++ b/apps/staking/src/config/server.ts @@ -12,7 +12,7 @@ const demand = (key: string): string => { if (value && value !== "") { return value; } else { - throw new Error(`Missing environment variable ${key}!`); + throw new MissingEnvironmentError(key); } }; @@ -36,3 +36,10 @@ export const WALLETCONNECT_PROJECT_ID = demandInProduction( ); export const RPC = process.env.RPC; export const IS_MAINNET = process.env.IS_MAINNET !== undefined; + +class MissingEnvironmentError extends Error { + constructor(name: string) { + super(`Missing environment variable: ${name}!`); + this.name = "MissingEnvironmentError"; + } +} diff --git a/apps/staking/src/hooks/use-account-history.ts b/apps/staking/src/hooks/use-account-history.ts index 4415a7371e..951030798a 100644 --- a/apps/staking/src/hooks/use-account-history.ts +++ b/apps/staking/src/hooks/use-account-history.ts @@ -1,23 +1,22 @@ +import { PublicKey } from "@solana/web3.js"; import useSWR from "swr"; -import { useApiContext } from "./use-api-context"; +import { useSelectedStakeAccount } from "./use-stake-account"; import { loadAccountHistory } from "../api"; const ONE_SECOND_IN_MS = 1000; const ONE_MINUTE_IN_MS = 60 * ONE_SECOND_IN_MS; const REFRESH_INTERVAL = 1 * ONE_MINUTE_IN_MS; -export const getCacheKey = ({ - stakeAccount, -}: ReturnType) => - `${stakeAccount.address.toBase58()}/history`; +export const getCacheKey = (stakeAccount: PublicKey) => + `${stakeAccount.toBase58()}/history`; export const useAccountHistory = () => { - const apiContext = useApiContext(); + const { client, account } = useSelectedStakeAccount(); const { data, isLoading, ...rest } = useSWR( - getCacheKey(apiContext), - () => loadAccountHistory(apiContext), + getCacheKey(account.address), + () => loadAccountHistory(client, account.address), { refreshInterval: REFRESH_INTERVAL, }, diff --git a/apps/staking/src/hooks/use-api-context.ts b/apps/staking/src/hooks/use-api-context.ts deleted file mode 100644 index 5d87a7ca2d..0000000000 --- a/apps/staking/src/hooks/use-api-context.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { useAnchorWallet, useConnection } from "@solana/wallet-adapter-react"; -import { useMemo } from "react"; - -import { StateType, useStakeAccount } from "./use-stake-account"; -import type { Context } from "../api"; - -export const useApiContext = (): Context => { - const wallet = useAnchorWallet(); - const { connection } = useConnection(); - const stakeAccount = useStakeAccount(); - - if (wallet === undefined) { - throw new NoWalletConnectedError(); - } - - if (stakeAccount.type !== StateType.Loaded) { - throw new NoStakeAccountSelectedError(); - } - - return useMemo( - () => ({ wallet, connection, stakeAccount: stakeAccount.account }), - [wallet, connection, stakeAccount], - ); -}; - -class NoWalletConnectedError extends Error { - constructor() { - super( - "The `useApiContext` hook cannot be called if a wallet isn't connected! Ensure all components that use this hook are only rendered if a wallet is connected!", - ); - } -} - -class NoStakeAccountSelectedError extends Error { - constructor() { - super( - "The `useApiContext` hook cannot be called before stake accounts have loaded! Ensure all components that use this hook are only rendered if `useStakeAccount` returns a loaded state!", - ); - } -} diff --git a/apps/staking/src/hooks/use-dashboard-data.ts b/apps/staking/src/hooks/use-dashboard-data.ts index 3055e99830..1c507094cc 100644 --- a/apps/staking/src/hooks/use-dashboard-data.ts +++ b/apps/staking/src/hooks/use-dashboard-data.ts @@ -1,23 +1,22 @@ +import { PublicKey } from "@solana/web3.js"; import { useCallback } from "react"; import useSWR from "swr"; -import { useApiContext } from "./use-api-context"; +import { useSelectedStakeAccount } from "./use-stake-account"; import { loadData } from "../api"; const ONE_SECOND_IN_MS = 1000; const ONE_MINUTE_IN_MS = 60 * ONE_SECOND_IN_MS; const REFRESH_INTERVAL = 1 * ONE_MINUTE_IN_MS; -export const getCacheKey = ({ - stakeAccount, -}: ReturnType) => stakeAccount.address.toBase58(); +export const getCacheKey = (stakeAccount: PublicKey) => stakeAccount.toBase58(); export const useDashboardData = () => { - const apiContext = useApiContext(); + const { client, account } = useSelectedStakeAccount(); const { data, isLoading, mutate, ...rest } = useSWR( - getCacheKey(apiContext), - () => loadData(apiContext), + getCacheKey(account.address), + () => loadData(client, 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 b87d58774d..3efd0d44d9 100644 --- a/apps/staking/src/hooks/use-stake-account.tsx +++ b/apps/staking/src/hooks/use-stake-account.tsx @@ -1,6 +1,7 @@ "use client"; import type { StakeAccountPositions } from "@pythnetwork/staking-sdk"; +import { PythStakingClient } from "@pythnetwork/staking-sdk"; import { useConnection, useWallet } from "@solana/wallet-adapter-react"; import { type ComponentProps, @@ -25,21 +26,36 @@ export enum StateType { const State = { Initialized: () => ({ type: StateType.Initialized as const }), + NoWallet: () => ({ type: StateType.NoWallet as const }), + Loading: () => ({ type: StateType.Loading as const }), - NoAccounts: () => ({ type: StateType.NoAccounts as const }), + + NoAccounts: (client: PythStakingClient) => ({ + type: StateType.NoAccounts as const, + client, + }), + Loaded: ( + client: PythStakingClient, account: StakeAccountPositions, allAccounts: [StakeAccountPositions, ...StakeAccountPositions[]], selectAccount: (account: StakeAccountPositions) => void, ) => ({ type: StateType.Loaded as const, + client, account, allAccounts, selectAccount, }), - ErrorState: (error: LoadStakeAccountError, reset: () => void) => ({ + + ErrorState: ( + client: PythStakingClient, + error: LoadStakeAccountError, + reset: () => void, + ) => ({ type: StateType.Error as const, + client, error, reset, }), @@ -67,7 +83,7 @@ const useStakeAccountState = () => { (account: StakeAccountPositions) => { setState((cur) => cur.type === StateType.Loaded - ? State.Loaded(account, cur.allAccounts, setAccount) + ? State.Loaded(cur.client, account, cur.allAccounts, setAccount) : cur, ); }, @@ -85,27 +101,34 @@ const useStakeAccountState = () => { ) { throw new WalletConnectedButInvalidError(); } - getStakeAccounts(connection, { - publicKey: wallet.publicKey, - signAllTransactions: wallet.signAllTransactions, - signTransaction: wallet.signTransaction, - }) + const client = new PythStakingClient({ + connection, + wallet: { + publicKey: wallet.publicKey, + signAllTransactions: wallet.signAllTransactions, + signTransaction: wallet.signTransaction, + }, + }); + getStakeAccounts(client) .then((accounts) => { const [firstAccount, ...otherAccounts] = accounts; if (firstAccount) { setState( State.Loaded( + client, firstAccount, [firstAccount, ...otherAccounts], setAccount, ), ); } else { - setState(State.NoAccounts()); + setState(State.NoAccounts(client)); } }) .catch((error: unknown) => { - setState(State.ErrorState(new LoadStakeAccountError(error), reset)); + setState( + State.ErrorState(client, new LoadStakeAccountError(error), reset), + ); }) .finally(() => { loading.current = false; @@ -129,6 +152,15 @@ export const useStakeAccount = () => { } }; +export const useSelectedStakeAccount = () => { + const state = useStakeAccount(); + if (state.type === StateType.Loaded) { + return state; + } else { + throw new InvalidStateError(); + } +}; + class LoadStakeAccountError extends Error { constructor(cause: unknown) { super(cause instanceof Error ? cause.message : ""); @@ -152,3 +184,12 @@ class WalletConnectedButInvalidError extends Error { ); } } + +class InvalidStateError extends Error { + constructor() { + super( + "Cannot use `useSelectedStakeAccount` when stake accounts aren't loaded or a stake account isn't selected! Ensure this hook is only called when a stake account is selected.", + ); + this.name = "InvalidStateError"; + } +} diff --git a/apps/staking/src/hooks/use-transfer.ts b/apps/staking/src/hooks/use-transfer.ts index 23253dd4c5..d73db5a1b8 100644 --- a/apps/staking/src/hooks/use-transfer.ts +++ b/apps/staking/src/hooks/use-transfer.ts @@ -1,14 +1,19 @@ +import type { PythStakingClient } from "@pythnetwork/staking-sdk"; +import type { PublicKey } from "@solana/web3.js"; import { useState, useCallback } from "react"; import { useSWRConfig } from "swr"; import { getCacheKey as getAccountHistoryCacheKey } from "./use-account-history"; -import { useApiContext } from "./use-api-context"; import { getCacheKey as getDashboardDataCacheKey } from "./use-dashboard-data"; +import { useSelectedStakeAccount } from "./use-stake-account"; export const useTransfer = ( - transfer: (context: ReturnType) => Promise, + transfer: ( + client: PythStakingClient, + stakingAccount: PublicKey, + ) => Promise, ) => { - const context = useApiContext(); + const { client, account } = useSelectedStakeAccount(); const [state, setState] = useState(State.Base()); const { mutate } = useSWRConfig(); @@ -19,19 +24,19 @@ export const useTransfer = ( setState(State.Submitting()); try { - await transfer(context); + await transfer(client, account.address); // TODO enable mutate without awaiting? // Prob by changing `api.ts` to encode the change & history item along with each update? await Promise.all([ - mutate(getDashboardDataCacheKey(context)), - mutate(getAccountHistoryCacheKey(context)), + mutate(getDashboardDataCacheKey(account.address)), + mutate(getAccountHistoryCacheKey(account.address)), ]); setState(State.Complete()); } catch (error: unknown) { setState(State.ErrorState(error)); throw error; } - }, [state, context, transfer, setState, mutate]); + }, [state, client, account.address, transfer, setState, mutate]); return { state, execute }; }; @@ -58,5 +63,6 @@ type State = ReturnType<(typeof State)[keyof typeof State]>; class DuplicateSubmitError extends Error { constructor() { super("Attempted to submit a transaction when one is already in process"); + this.name = "DuplicateSubmitError"; } } diff --git a/governance/pyth_staking_sdk/package.json b/governance/pyth_staking_sdk/package.json index 9d8a1c104c..165491e5da 100644 --- a/governance/pyth_staking_sdk/package.json +++ b/governance/pyth_staking_sdk/package.json @@ -8,14 +8,14 @@ }, "scripts": { "build": "tsc", - "test": "echo disabled # pnpm run test:format && pnpm run test:lint && pnpm run test:integration && pnpm run test:types", + "test": "pnpm run test:format && pnpm run test:lint && pnpm run test:integration && pnpm run test:types", "fix": "pnpm fix:lint && pnpm fix:format", "fix:format": "prettier --write .", "fix:lint": "eslint --fix .", "test:format": "prettier --check .", "test:lint": "jest --selectProjects lint", "test:types": "tsc", - "test:integration": "jest --selectProjects integration" + "test:integration": "echo disabled # jest --selectProjects integration" }, "devDependencies": { "@cprussin/eslint-config": "^3.0.0", diff --git a/governance/pyth_staking_sdk/src/pyth-staking-client.ts b/governance/pyth_staking_sdk/src/pyth-staking-client.ts index 2cdb282e3f..59a6f80cd6 100644 --- a/governance/pyth_staking_sdk/src/pyth-staking-client.ts +++ b/governance/pyth_staking_sdk/src/pyth-staking-client.ts @@ -274,12 +274,11 @@ export class PythStakingClient { return sendTransaction([instruction], this.connection, this.wallet); } - public async stakeToPublisher(options: { - stakeAccountPositions: PublicKey; - publisher: PublicKey; - amount: bigint; - }) { - const { stakeAccountPositions, publisher, amount } = options; + public async stakeToPublisher( + stakeAccountPositions: PublicKey, + publisher: PublicKey, + amount: bigint, + ) { const instruction = await this.integrityPoolProgram.methods .delegate(convertBigIntToBN(amount)) .accounts({ @@ -292,10 +291,7 @@ export class PythStakingClient { return sendTransaction([instruction], this.connection, this.wallet); } - public async getUnlockSchedule(options: { - stakeAccountPositions: PublicKey; - }) { - const { stakeAccountPositions } = options; + public async getUnlockSchedule(stakeAccountPositions: PublicKey) { const stakeAccountMetadataAddress = getStakeAccountMetadataAddress( stakeAccountPositions, ); @@ -317,11 +313,8 @@ export class PythStakingClient { }); } - public async advanceDelegationRecord(options: { - stakeAccountPositions: PublicKey; - }) { + public async advanceDelegationRecord(stakeAccountPositions: PublicKey) { // TODO: optimize to only send transactions for publishers that have positive rewards - const { stakeAccountPositions } = options; const publishers = await this.getPublishers(); // anchor does not calculate the correct pda for other programs