Skip to content

Commit 0935702

Browse files
authored
Merge pull request #1874 from cprussin/move-staking-client-to-context
chore(staking): initialize pyth staking client only once
2 parents 2aef1ca + 045bcac commit 0935702

File tree

11 files changed

+186
-345
lines changed

11 files changed

+186
-345
lines changed

apps/staking/src/api.ts

Lines changed: 71 additions & 243 deletions
Large diffs are not rendered by default.

apps/staking/src/components/OracleIntegrityStaking/index.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import type { PythStakingClient } from "@pythnetwork/staking-sdk";
2+
import { PublicKey } from "@solana/web3.js";
13
import clsx from "clsx";
24
import { useMemo, useCallback } from "react";
35

46
import {
5-
type Context,
67
delegateIntegrityStaking,
78
cancelWarmupIntegrityStaking,
89
unstakeIntegrityStaking,
@@ -127,7 +128,7 @@ export const OracleIntegrityStaking = ({
127128
<tbody className="bg-white/5">
128129
{otherPublishers.map((publisher) => (
129130
<Publisher
130-
key={publisher.publicKey}
131+
key={publisher.publicKey.toBase58()}
131132
availableToStake={availableToStake}
132133
publisher={publisher}
133134
totalStaked={staked}
@@ -152,7 +153,7 @@ type PublisherProps = {
152153
isSelf?: boolean;
153154
publisher: {
154155
name: string;
155-
publicKey: string;
156+
publicKey: PublicKey;
156157
isSelf: boolean;
157158
selfStake: bigint;
158159
poolCapacity: bigint;
@@ -372,7 +373,7 @@ const PublisherTableCell = Styled("td", "py-4 px-5 whitespace-nowrap");
372373

373374
type StakeToPublisherButtonProps = {
374375
publisherName: string;
375-
publisherKey: string;
376+
publisherKey: PublicKey;
376377
availableToStake: bigint;
377378
poolCapacity: bigint;
378379
poolUtilization: bigint;
@@ -423,13 +424,15 @@ const StakeToPublisherButton = ({
423424

424425
const useTransferActionForPublisher = (
425426
action: (
426-
context: Context,
427-
publicKey: string,
427+
client: PythStakingClient,
428+
stakingAccount: PublicKey,
429+
publisher: PublicKey,
428430
amount: bigint,
429431
) => Promise<void>,
430-
publicKey: string,
432+
publisher: PublicKey,
431433
) =>
432434
useCallback(
433-
(context: Context, amount: bigint) => action(context, publicKey, amount),
434-
[action, publicKey],
435+
(client: PythStakingClient, stakingAccount: PublicKey, amount: bigint) =>
436+
action(client, stakingAccount, publisher, amount),
437+
[action, publisher],
435438
);

apps/staking/src/components/TransferButton/index.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { Field, Input, Label } from "@headlessui/react";
2+
import type { PythStakingClient } from "@pythnetwork/staking-sdk";
3+
import type { PublicKey } from "@solana/web3.js";
24
import {
35
type ChangeEvent,
46
type ComponentProps,
@@ -8,7 +10,6 @@ import {
810
useState,
911
} from "react";
1012

11-
import type { Context } from "../../api";
1213
import { useLogger } from "../../hooks/use-logger";
1314
import { StateType, useTransfer } from "../../hooks/use-transfer";
1415
import { stringToTokens, tokensToString } from "../../tokens";
@@ -28,7 +29,11 @@ type Props = {
2829
| ReactNode
2930
| ReactNode[]
3031
| undefined;
31-
transfer: (context: Context, amount: bigint) => Promise<void>;
32+
transfer: (
33+
client: PythStakingClient,
34+
stakingAccount: PublicKey,
35+
amount: bigint,
36+
) => Promise<void>;
3237
className?: string | undefined;
3338
secondary?: boolean | undefined;
3439
small?: boolean | undefined;
@@ -51,9 +56,9 @@ export const TransferButton = ({
5156
const { amountInput, setAmount, updateAmount, resetAmount, amount } =
5257
useAmountInput(max);
5358
const doTransfer = useCallback(
54-
(context: Context) =>
59+
(client: PythStakingClient, stakingAccount: PublicKey) =>
5560
amount.type === AmountType.Valid
56-
? transfer(context, amount.amount)
61+
? transfer(client, stakingAccount, amount.amount)
5762
: Promise.reject(new InvalidAmountError()),
5863
[amount, transfer],
5964
);

apps/staking/src/config/server.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const demand = (key: string): string => {
1212
if (value && value !== "") {
1313
return value;
1414
} else {
15-
throw new Error(`Missing environment variable ${key}!`);
15+
throw new MissingEnvironmentError(key);
1616
}
1717
};
1818

@@ -36,3 +36,10 @@ export const WALLETCONNECT_PROJECT_ID = demandInProduction(
3636
);
3737
export const RPC = process.env.RPC;
3838
export const IS_MAINNET = process.env.IS_MAINNET !== undefined;
39+
40+
class MissingEnvironmentError extends Error {
41+
constructor(name: string) {
42+
super(`Missing environment variable: ${name}!`);
43+
this.name = "MissingEnvironmentError";
44+
}
45+
}

apps/staking/src/hooks/use-account-history.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
1+
import { PublicKey } from "@solana/web3.js";
12
import useSWR from "swr";
23

3-
import { useApiContext } from "./use-api-context";
4+
import { useSelectedStakeAccount } from "./use-stake-account";
45
import { loadAccountHistory } from "../api";
56

67
const ONE_SECOND_IN_MS = 1000;
78
const ONE_MINUTE_IN_MS = 60 * ONE_SECOND_IN_MS;
89
const REFRESH_INTERVAL = 1 * ONE_MINUTE_IN_MS;
910

10-
export const getCacheKey = ({
11-
stakeAccount,
12-
}: ReturnType<typeof useApiContext>) =>
13-
`${stakeAccount.address.toBase58()}/history`;
11+
export const getCacheKey = (stakeAccount: PublicKey) =>
12+
`${stakeAccount.toBase58()}/history`;
1413

1514
export const useAccountHistory = () => {
16-
const apiContext = useApiContext();
15+
const { client, account } = useSelectedStakeAccount();
1716

1817
const { data, isLoading, ...rest } = useSWR(
19-
getCacheKey(apiContext),
20-
() => loadAccountHistory(apiContext),
18+
getCacheKey(account.address),
19+
() => loadAccountHistory(client, account.address),
2120
{
2221
refreshInterval: REFRESH_INTERVAL,
2322
},

apps/staking/src/hooks/use-api-context.ts

Lines changed: 0 additions & 40 deletions
This file was deleted.

apps/staking/src/hooks/use-dashboard-data.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
1+
import { PublicKey } from "@solana/web3.js";
12
import { useCallback } from "react";
23
import useSWR from "swr";
34

4-
import { useApiContext } from "./use-api-context";
5+
import { useSelectedStakeAccount } from "./use-stake-account";
56
import { loadData } from "../api";
67

78
const ONE_SECOND_IN_MS = 1000;
89
const ONE_MINUTE_IN_MS = 60 * ONE_SECOND_IN_MS;
910
const REFRESH_INTERVAL = 1 * ONE_MINUTE_IN_MS;
1011

11-
export const getCacheKey = ({
12-
stakeAccount,
13-
}: ReturnType<typeof useApiContext>) => stakeAccount.address.toBase58();
12+
export const getCacheKey = (stakeAccount: PublicKey) => stakeAccount.toBase58();
1413

1514
export const useDashboardData = () => {
16-
const apiContext = useApiContext();
15+
const { client, account } = useSelectedStakeAccount();
1716

1817
const { data, isLoading, mutate, ...rest } = useSWR(
19-
getCacheKey(apiContext),
20-
() => loadData(apiContext),
18+
getCacheKey(account.address),
19+
() => loadData(client, account),
2120
{
2221
refreshInterval: REFRESH_INTERVAL,
2322
},

apps/staking/src/hooks/use-stake-account.tsx

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"use client";
22

33
import type { StakeAccountPositions } from "@pythnetwork/staking-sdk";
4+
import { PythStakingClient } from "@pythnetwork/staking-sdk";
45
import { useConnection, useWallet } from "@solana/wallet-adapter-react";
56
import {
67
type ComponentProps,
@@ -25,21 +26,36 @@ export enum StateType {
2526

2627
const State = {
2728
Initialized: () => ({ type: StateType.Initialized as const }),
29+
2830
NoWallet: () => ({ type: StateType.NoWallet as const }),
31+
2932
Loading: () => ({ type: StateType.Loading as const }),
30-
NoAccounts: () => ({ type: StateType.NoAccounts as const }),
33+
34+
NoAccounts: (client: PythStakingClient) => ({
35+
type: StateType.NoAccounts as const,
36+
client,
37+
}),
38+
3139
Loaded: (
40+
client: PythStakingClient,
3241
account: StakeAccountPositions,
3342
allAccounts: [StakeAccountPositions, ...StakeAccountPositions[]],
3443
selectAccount: (account: StakeAccountPositions) => void,
3544
) => ({
3645
type: StateType.Loaded as const,
46+
client,
3747
account,
3848
allAccounts,
3949
selectAccount,
4050
}),
41-
ErrorState: (error: LoadStakeAccountError, reset: () => void) => ({
51+
52+
ErrorState: (
53+
client: PythStakingClient,
54+
error: LoadStakeAccountError,
55+
reset: () => void,
56+
) => ({
4257
type: StateType.Error as const,
58+
client,
4359
error,
4460
reset,
4561
}),
@@ -67,7 +83,7 @@ const useStakeAccountState = () => {
6783
(account: StakeAccountPositions) => {
6884
setState((cur) =>
6985
cur.type === StateType.Loaded
70-
? State.Loaded(account, cur.allAccounts, setAccount)
86+
? State.Loaded(cur.client, account, cur.allAccounts, setAccount)
7187
: cur,
7288
);
7389
},
@@ -85,27 +101,34 @@ const useStakeAccountState = () => {
85101
) {
86102
throw new WalletConnectedButInvalidError();
87103
}
88-
getStakeAccounts(connection, {
89-
publicKey: wallet.publicKey,
90-
signAllTransactions: wallet.signAllTransactions,
91-
signTransaction: wallet.signTransaction,
92-
})
104+
const client = new PythStakingClient({
105+
connection,
106+
wallet: {
107+
publicKey: wallet.publicKey,
108+
signAllTransactions: wallet.signAllTransactions,
109+
signTransaction: wallet.signTransaction,
110+
},
111+
});
112+
getStakeAccounts(client)
93113
.then((accounts) => {
94114
const [firstAccount, ...otherAccounts] = accounts;
95115
if (firstAccount) {
96116
setState(
97117
State.Loaded(
118+
client,
98119
firstAccount,
99120
[firstAccount, ...otherAccounts],
100121
setAccount,
101122
),
102123
);
103124
} else {
104-
setState(State.NoAccounts());
125+
setState(State.NoAccounts(client));
105126
}
106127
})
107128
.catch((error: unknown) => {
108-
setState(State.ErrorState(new LoadStakeAccountError(error), reset));
129+
setState(
130+
State.ErrorState(client, new LoadStakeAccountError(error), reset),
131+
);
109132
})
110133
.finally(() => {
111134
loading.current = false;
@@ -129,6 +152,15 @@ export const useStakeAccount = () => {
129152
}
130153
};
131154

155+
export const useSelectedStakeAccount = () => {
156+
const state = useStakeAccount();
157+
if (state.type === StateType.Loaded) {
158+
return state;
159+
} else {
160+
throw new InvalidStateError();
161+
}
162+
};
163+
132164
class LoadStakeAccountError extends Error {
133165
constructor(cause: unknown) {
134166
super(cause instanceof Error ? cause.message : "");
@@ -152,3 +184,12 @@ class WalletConnectedButInvalidError extends Error {
152184
);
153185
}
154186
}
187+
188+
class InvalidStateError extends Error {
189+
constructor() {
190+
super(
191+
"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.",
192+
);
193+
this.name = "InvalidStateError";
194+
}
195+
}

0 commit comments

Comments
 (0)