فهرست منبع

fix: use key with sol for simulations (#2315)

* fix: use key with sol for simulations

* cleanup

* fix format

* fix format

* add default value

* clean up, choose default value

* format
guibescos 9 ماه پیش
والد
کامیت
49d3887366

+ 4 - 1
apps/staking/src/api.ts

@@ -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)
@@ -114,6 +115,7 @@ export const loadData = async (
         pythnetClient,
         hermesClient,
         stakeAccount,
+        simulationPayer,
       );
 
 const loadDataNoStakeAccount = async (
@@ -149,6 +151,7 @@ const loadDataForStakeAccount = async (
   pythnetClient: PythnetClient,
   hermesClient: HermesClient,
   stakeAccount: PublicKey,
+  simulationPayer?: PublicKey,
 ): Promise<Data> => {
   const [
     { publishers, ...baseInfo },
@@ -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),
   ]);
 

+ 6 - 1
apps/staking/src/components/Root/index.tsx

@@ -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";
@@ -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>

+ 6 - 1
apps/staking/src/config/server.ts

@@ -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}!`);

+ 22 - 4
apps/staking/src/hooks/use-api.tsx

@@ -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";
 
@@ -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"],
@@ -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),
@@ -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();
@@ -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
@@ -235,6 +252,7 @@ const useApiContext = (hermesUrl: string, pythnetRpcUrl: string) => {
                 pythnetClient,
                 hermesClient,
                 selectedAccount ?? firstAccount,
+                simulationPayer,
                 [firstAccount, ...otherAccounts],
                 (account: PublicKey) => {
                   localStorageValue.set(account.toBase58());

+ 8 - 3
governance/pyth_staking_sdk/src/pyth-staking-client.ts

@@ -688,6 +688,7 @@ export class PythStakingClient {
 
   async getAdvanceDelegationRecordInstructions(
     stakeAccountPositions: PublicKey,
+    payer?: PublicKey,
   ) {
     const poolData = await this.getPoolDataAccount();
     const stakeAccountPositionsData = await this.getStakeAccountPositions(
@@ -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
@@ -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) {