position.ts 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import { BorshCoder } from "@coral-xyz/anchor";
  2. import { PublicKey } from "@solana/web3.js";
  3. import { convertBNToBigInt } from "./bn";
  4. import type { Staking } from "../../types/staking";
  5. import {
  6. POSITION_BUFFER_SIZE,
  7. POSITIONS_ACCOUNT_HEADER_SIZE,
  8. } from "../constants";
  9. import type {
  10. Position,
  11. PositionAnchor,
  12. StakeAccountPositions,
  13. TargetWithParameters,
  14. } from "../types";
  15. import { PositionState } from "../types";
  16. export const getPositionState = (
  17. position: Position,
  18. currentEpoch: bigint,
  19. ): PositionState => {
  20. if (currentEpoch < position.activationEpoch) {
  21. return PositionState.LOCKING;
  22. }
  23. if (!position.unlockingStart) {
  24. return PositionState.LOCKED;
  25. }
  26. const hasActivated = position.activationEpoch <= currentEpoch;
  27. const unlockStarted = position.unlockingStart <= currentEpoch;
  28. const unlockEnded = position.unlockingStart + 1n <= currentEpoch;
  29. if (hasActivated && !unlockStarted) {
  30. return PositionState.PREUNLOCKING;
  31. } else if (unlockStarted && !unlockEnded) {
  32. return PositionState.UNLOCKING;
  33. } else {
  34. return PositionState.UNLOCKED;
  35. }
  36. };
  37. export const getAmountByTargetAndState = (options: {
  38. stakeAccountPositions: StakeAccountPositions;
  39. targetWithParameters: TargetWithParameters;
  40. positionState: PositionState;
  41. epoch: bigint;
  42. }): bigint => {
  43. const { stakeAccountPositions, targetWithParameters, positionState, epoch } =
  44. options;
  45. return stakeAccountPositions.data.positions
  46. .filter((p) => getPositionState(p, epoch) === positionState)
  47. .filter((p) => {
  48. if (targetWithParameters.voting) {
  49. return p.targetWithParameters.voting !== undefined;
  50. }
  51. return (
  52. p.targetWithParameters.integrityPool?.publisher &&
  53. targetWithParameters.integrityPool.publisher.equals(
  54. p.targetWithParameters.integrityPool.publisher,
  55. )
  56. );
  57. })
  58. .map((p) => p.amount)
  59. .reduce((sum, amount) => sum + amount, 0n);
  60. };
  61. export const deserializeStakeAccountPositions = (
  62. address: PublicKey,
  63. data: Buffer,
  64. idl: Staking,
  65. ) => {
  66. const coder = new BorshCoder(idl);
  67. let i = 8; // Skip discriminator
  68. const owner = new PublicKey(data.subarray(i, i + 32));
  69. const numberOfPositions = Math.floor(
  70. (data.length - POSITIONS_ACCOUNT_HEADER_SIZE) / POSITION_BUFFER_SIZE,
  71. );
  72. i += 32;
  73. const positions: PositionAnchor[] = [];
  74. for (let j = 0; j < numberOfPositions; j++) {
  75. if (data[i] === 1) {
  76. positions.push(coder.types.decode("position", data.subarray(i + 1)));
  77. }
  78. i += POSITION_BUFFER_SIZE;
  79. }
  80. return {
  81. address,
  82. data: {
  83. owner,
  84. positions: positions.map((p) => convertBNToBigInt(p)),
  85. },
  86. };
  87. };
  88. export const getVotingTokenAmount = (
  89. stakeAccountPositions: StakeAccountPositions,
  90. epoch: bigint,
  91. ) => {
  92. const positions = stakeAccountPositions.data.positions;
  93. const votingPositions = positions
  94. .filter((p) => p.targetWithParameters.voting)
  95. .filter((p) =>
  96. [PositionState.LOCKED, PositionState.PREUNLOCKING].includes(
  97. getPositionState(p, epoch),
  98. ),
  99. );
  100. const totalVotingTokenAmount = votingPositions.reduce(
  101. (sum, p) => sum + p.amount,
  102. 0n,
  103. );
  104. return totalVotingTokenAmount;
  105. };