Skip to content

Commit faf2a1b

Browse files
feat(staking): add reward expiry (#1915)
1 parent cb427fa commit faf2a1b

File tree

5 files changed

+56
-25
lines changed

5 files changed

+56
-25
lines changed

apps/staking/src/api.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,7 @@ type Data = {
3333
date: Date;
3434
}
3535
| undefined;
36-
expiringRewards:
37-
| {
38-
amount: bigint;
39-
expiry: Date;
40-
}
41-
| undefined;
36+
expiringRewards: Date | undefined;
4237
unlockSchedule: {
4338
date: Date;
4439
amount: bigint;
@@ -235,8 +230,8 @@ const loadDataForStakeAccount = async (
235230
return {
236231
...baseInfo,
237232
lastSlash: undefined, // TODO
238-
availableRewards: claimableRewards,
239-
expiringRewards: undefined, // TODO
233+
availableRewards: claimableRewards.totalRewards,
234+
expiringRewards: claimableRewards.expiry,
240235
total: stakeAccountCustody.amount,
241236
governance: {
242237
warmup: filterGovernancePositions(PositionState.LOCKING),

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

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,7 @@ type Props = {
2929
| undefined;
3030
walletAmount: bigint;
3131
availableRewards: bigint;
32-
expiringRewards:
33-
| {
34-
amount: bigint;
35-
expiry: Date;
36-
}
37-
| undefined;
32+
expiringRewards: Date | undefined;
3833
availableToWithdraw: bigint;
3934
};
4035

@@ -153,11 +148,12 @@ export const AccountSummary = ({
153148
)
154149
}
155150
{...(expiringRewards !== undefined &&
156-
expiringRewards.amount > 0n && {
151+
availableRewards > 0n && {
157152
warning: (
158153
<>
159-
<Tokens>{expiringRewards.amount}</Tokens> will expire on{" "}
160-
{expiringRewards.expiry.toLocaleDateString()}
154+
Rewards expire one year from the epoch in which they were
155+
earned. You have rewards expiring on{" "}
156+
{expiringRewards.toLocaleDateString()}.
161157
</>
162158
),
163159
})}

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

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,7 @@ type Props = {
1919
| undefined;
2020
walletAmount: bigint;
2121
availableRewards: bigint;
22-
expiringRewards:
23-
| {
24-
amount: bigint;
25-
expiry: Date;
26-
}
27-
| undefined;
22+
expiringRewards: Date | undefined;
2823
unlockSchedule: {
2924
amount: bigint;
3025
date: Date;

governance/pyth_staking_sdk/src/pdas.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,17 @@ export const getPoolConfigAddress = () => {
3636
INTEGRITY_POOL_PROGRAM_ADDRESS,
3737
)[0];
3838
};
39+
40+
export const getDelegationRecordAddress = (
41+
stakeAccountPositions: PublicKey,
42+
publisher: PublicKey,
43+
) => {
44+
return PublicKey.findProgramAddressSync(
45+
[
46+
Buffer.from("delegation_record"),
47+
publisher.toBuffer(),
48+
stakeAccountPositions.toBuffer(),
49+
],
50+
INTEGRITY_POOL_PROGRAM_ADDRESS,
51+
)[0];
52+
};

governance/pyth_staking_sdk/src/pyth-staking-client.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
import { GOVERNANCE_ADDRESS, POSITIONS_ACCOUNT_SIZE } from "./constants";
2525
import {
2626
getConfigAddress,
27+
getDelegationRecordAddress,
2728
getPoolConfigAddress,
2829
getStakeAccountCustodyAddress,
2930
getStakeAccountMetadataAddress,
@@ -36,7 +37,7 @@ import {
3637
type StakeAccountPositions,
3738
} from "./types";
3839
import { convertBigIntToBN, convertBNToBigInt } from "./utils/bn";
39-
import { getCurrentEpoch } from "./utils/clock";
40+
import { epochToDate, getCurrentEpoch } from "./utils/clock";
4041
import { extractPublisherData } from "./utils/pool";
4142
import {
4243
deserializeStakeAccountPositions,
@@ -148,6 +149,17 @@ export class PythStakingClient {
148149
);
149150
}
150151

152+
public async getDelegationRecord(
153+
stakeAccountPositions: PublicKey,
154+
publisher: PublicKey,
155+
) {
156+
const delegationRecord =
157+
await this.integrityPoolProgram.account.delegationRecord.fetch(
158+
getDelegationRecordAddress(stakeAccountPositions, publisher),
159+
);
160+
return convertBNToBigInt(delegationRecord);
161+
}
162+
151163
public async getStakeAccountCustody(
152164
stakeAccountPositions: PublicKey,
153165
): Promise<Account> {
@@ -573,6 +585,7 @@ export class PythStakingClient {
573585
return {
574586
advanceDelegationRecordInstructions,
575587
mergePositionsInstruction,
588+
publishers,
576589
};
577590
}
578591

@@ -609,6 +622,24 @@ export class PythStakingClient {
609622
const buffer = Buffer.from(val, "base64").reverse();
610623
totalRewards += BigInt("0x" + buffer.toString("hex"));
611624
}
612-
return totalRewards;
625+
626+
const delegationRecords = await Promise.all(
627+
instructions.publishers.map(({ pubkey }) =>
628+
this.getDelegationRecord(stakeAccountPositions, pubkey),
629+
),
630+
);
631+
632+
let lowestEpoch: bigint | undefined;
633+
for (const record of delegationRecords) {
634+
if (lowestEpoch === undefined || record.lastEpoch < lowestEpoch) {
635+
lowestEpoch = record.lastEpoch;
636+
}
637+
}
638+
639+
return {
640+
totalRewards,
641+
expiry:
642+
lowestEpoch === undefined ? undefined : epochToDate(lowestEpoch + 52n),
643+
};
613644
}
614645
}

0 commit comments

Comments
 (0)