Skip to content

fix: use key with sol for simulations #2315

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion apps/staking/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ export const loadData = async (
pythnetClient: PythnetClient,
hermesClient: HermesClient,
stakeAccount?: PublicKey | undefined,
simulationPayer?: PublicKey,
): Promise<Data> =>
stakeAccount === undefined
? loadDataNoStakeAccount(client, pythnetClient, hermesClient)
Expand All @@ -114,6 +115,7 @@ export const loadData = async (
pythnetClient,
hermesClient,
stakeAccount,
simulationPayer,
);

const loadDataNoStakeAccount = async (
Expand Down Expand Up @@ -149,6 +151,7 @@ const loadDataForStakeAccount = async (
pythnetClient: PythnetClient,
hermesClient: HermesClient,
stakeAccount: PublicKey,
simulationPayer?: PublicKey,
): Promise<Data> => {
const [
{ publishers, ...baseInfo },
Expand All @@ -160,7 +163,7 @@ const loadDataForStakeAccount = async (
loadBaseInfo(client, pythnetClient, hermesClient),
client.getStakeAccountCustody(stakeAccount),
client.getUnlockSchedule(stakeAccount),
client.getClaimableRewards(stakeAccount),
client.getClaimableRewards(stakeAccount, simulationPayer),
client.getStakeAccountPositions(stakeAccount),
]);

Expand Down
7 changes: 6 additions & 1 deletion apps/staking/src/components/Root/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
MAINNET_RPC,
HERMES_URL,
PYTHNET_RPC,
SIMULATION_PAYER_ADDRESS,
} from "../../config/server";
import { ApiProvider } from "../../hooks/use-api";
import { LoggerProvider } from "../../hooks/use-logger";
Expand Down Expand Up @@ -82,7 +83,11 @@ const HtmlWithProviders = ({ lang, ...props }: HTMLProps<HTMLHtmlElement>) => (
walletConnectProjectId={WALLETCONNECT_PROJECT_ID}
mainnetRpc={MAINNET_RPC}
>
<ApiProvider hermesUrl={HERMES_URL} pythnetRpcUrl={PYTHNET_RPC}>
<ApiProvider
hermesUrl={HERMES_URL}
pythnetRpcUrl={PYTHNET_RPC}
simulationPayerAddress={SIMULATION_PAYER_ADDRESS}
>
<ToastProvider>
<html lang={lang} {...props} />
</ToastProvider>
Expand Down
7 changes: 6 additions & 1 deletion apps/staking/src/config/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,12 @@ export const GOVERNANCE_ONLY_REGIONS = transformOr(
[],
);
export const PROXYCHECK_API_KEY = demandInProduction("PROXYCHECK_API_KEY");

// This needs to be a public key that has SOL in it all the time, it will be used as a payer in the transaction simulation to compute the claimable rewards
// such simulation fails when the payer has no funds.
export const SIMULATION_PAYER_ADDRESS = getOr(
"SIMULATION_PAYER_ADDRESS",
"E5KR7yfb9UyVB6ZhmhQki1rM1eBcxHvyGKFZakAC5uc",
);
class MissingEnvironmentError extends Error {
constructor(name: string) {
super(`Missing environment variable: ${name}!`);
Expand Down
26 changes: 22 additions & 4 deletions apps/staking/src/hooks/use-api.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { HermesClient } from "@pythnetwork/hermes-client";
import { PythnetClient, PythStakingClient } from "@pythnetwork/staking-sdk";
import { useLocalStorageValue } from "@react-hookz/web";
import { useConnection, useWallet } from "@solana/wallet-adapter-react";
import { Connection, type PublicKey } from "@solana/web3.js";
import { Connection, PublicKey } from "@solana/web3.js";
import { type ComponentProps, createContext, useContext, useMemo } from "react";
import { useSWRConfig } from "swr";

Expand Down Expand Up @@ -65,6 +65,7 @@ const State = {
pythnetClient: PythnetClient,
hermesClient: HermesClient,
account: PublicKey,
simulationPayer: PublicKey,
allAccounts: [PublicKey, ...PublicKey[]],
selectAccount: (account: PublicKey) => void,
mutate: ReturnType<typeof useSWRConfig>["mutate"],
Expand Down Expand Up @@ -95,7 +96,13 @@ const State = {
dashboardDataCacheKey,

loadData: () =>
api.loadData(client, pythnetClient, hermesClient, account),
api.loadData(
client,
pythnetClient,
hermesClient,
account,
simulationPayer,
),

claim: bindApi(api.claim),
deposit: bindApi(api.deposit),
Expand Down Expand Up @@ -131,19 +138,25 @@ type ApiProviderProps = Omit<
> & {
pythnetRpcUrl: string;
hermesUrl: string;
simulationPayerAddress: string;
};

export const ApiProvider = ({
hermesUrl,
pythnetRpcUrl,
simulationPayerAddress,
...props
}: ApiProviderProps) => {
const state = useApiContext(hermesUrl, pythnetRpcUrl);
const state = useApiContext(hermesUrl, pythnetRpcUrl, simulationPayerAddress);

return <ApiContext.Provider value={state} {...props} />;
};

const useApiContext = (hermesUrl: string, pythnetRpcUrl: string) => {
const useApiContext = (
hermesUrl: string,
pythnetRpcUrl: string,
simulationPayerAddress: string,
) => {
const wallet = useWallet();
const { connection } = useConnection();
const { isMainnet } = useNetwork();
Expand All @@ -153,6 +166,10 @@ const useApiContext = (hermesUrl: string, pythnetRpcUrl: string) => {
() => new PythnetClient(new Connection(pythnetRpcUrl)),
[pythnetRpcUrl],
);
const simulationPayer = useMemo(
() => new PublicKey(simulationPayerAddress),
[simulationPayerAddress],
);
const pythStakingClient = useMemo(
() =>
wallet.publicKey && wallet.signAllTransactions && wallet.signTransaction
Expand Down Expand Up @@ -235,6 +252,7 @@ const useApiContext = (hermesUrl: string, pythnetRpcUrl: string) => {
pythnetClient,
hermesClient,
selectedAccount ?? firstAccount,
simulationPayer,
[firstAccount, ...otherAccounts],
(account: PublicKey) => {
localStorageValue.set(account.toBase58());
Expand Down
11 changes: 8 additions & 3 deletions governance/pyth_staking_sdk/src/pyth-staking-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,7 @@ export class PythStakingClient {

async getAdvanceDelegationRecordInstructions(
stakeAccountPositions: PublicKey,
payer?: PublicKey,
) {
const poolData = await this.getPoolDataAccount();
const stakeAccountPositionsData = await this.getStakeAccountPositions(
Expand Down Expand Up @@ -745,7 +746,7 @@ export class PythStakingClient {
this.integrityPoolProgram.methods
.advanceDelegationRecord()
.accountsPartial({
payer: this.wallet.publicKey,
payer: payer ?? this.wallet.publicKey,
publisher: pubkey,
publisherStakeAccountPositions: stakeAccount,
publisherStakeAccountCustody: stakeAccount
Expand Down Expand Up @@ -795,16 +796,20 @@ export class PythStakingClient {
);
}

public async getClaimableRewards(stakeAccountPositions: PublicKey) {
public async getClaimableRewards(
stakeAccountPositions: PublicKey,
simulationPayer?: PublicKey,
) {
const instructions = await this.getAdvanceDelegationRecordInstructions(
stakeAccountPositions,
simulationPayer,
);

let totalRewards = 0n;

for (const instruction of instructions.advanceDelegationRecordInstructions) {
const tx = new Transaction().add(instruction);
tx.feePayer = this.wallet.publicKey;
tx.feePayer = simulationPayer ?? this.wallet.publicKey;
const res = await this.connection.simulateTransaction(tx);
const val = res.value.returnData?.data[0];
if (val === undefined) {
Expand Down
Loading