@wireio/stake 0.4.3 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/lib/stake.browser.js +12820 -6787
  2. package/lib/stake.browser.js.map +1 -1
  3. package/lib/stake.d.ts +353 -209
  4. package/lib/stake.js +12940 -6886
  5. package/lib/stake.js.map +1 -1
  6. package/lib/stake.m.js +12820 -6787
  7. package/lib/stake.m.js.map +1 -1
  8. package/package.json +1 -1
  9. package/src/assets/ethereum/ABI/common/Base58.sol/Base58.dbg.json +4 -0
  10. package/src/assets/ethereum/ABI/common/Base58.sol/Base58.json +164 -0
  11. package/src/assets/ethereum/ABI/common/OpenZepArtifacts.sol/__Dummy_OZ_UUPS__.dbg.json +4 -0
  12. package/src/assets/ethereum/ABI/common/OpenZepArtifacts.sol/__Dummy_OZ_UUPS__.json +76 -0
  13. package/src/assets/ethereum/ABI/common/RestrictedCallers.sol/RestrictedCallers.dbg.json +4 -0
  14. package/src/assets/ethereum/ABI/common/RestrictedCallers.sol/RestrictedCallers.json +10 -0
  15. package/src/assets/ethereum/ABI/common/iodata.sol/iodata.dbg.json +4 -0
  16. package/src/assets/ethereum/ABI/common/iodata.sol/iodata.json +618 -0
  17. package/src/assets/ethereum/ABI/common/iodata_util.sol/iodata_util.dbg.json +4 -0
  18. package/src/assets/ethereum/ABI/common/iodata_util.sol/iodata_util.json +40 -0
  19. package/src/assets/ethereum/ABI/common/sysio_data.sol/sysio_data.dbg.json +4 -0
  20. package/src/assets/ethereum/ABI/common/sysio_data.sol/sysio_data.json +10 -0
  21. package/src/assets/ethereum/ABI/common/sysio_errors.sol/sysio_errors.dbg.json +4 -0
  22. package/src/assets/ethereum/ABI/common/sysio_errors.sol/sysio_errors.json +10 -0
  23. package/src/assets/ethereum/ABI/common/sysio_merkle.sol/sysio_merkle.dbg.json +4 -0
  24. package/src/assets/ethereum/ABI/common/sysio_merkle.sol/sysio_merkle.json +233 -0
  25. package/src/assets/ethereum/ABI/common/sysio_name.sol/sysio_name.dbg.json +4 -0
  26. package/src/assets/ethereum/ABI/common/sysio_name.sol/sysio_name.json +49 -0
  27. package/src/assets/ethereum/ABI/common/sysio_pubkey.sol/sysio_pubkey.dbg.json +4 -0
  28. package/src/assets/ethereum/ABI/common/sysio_pubkey.sol/sysio_pubkey.json +64 -0
  29. package/src/assets/ethereum/ABI/common/sysio_read.sol/sysio_read.dbg.json +4 -0
  30. package/src/assets/ethereum/ABI/common/sysio_read.sol/sysio_read.json +1458 -0
  31. package/src/assets/ethereum/ABI/common/sysio_tester.sol/SysioTester.dbg.json +4 -0
  32. package/src/assets/ethereum/ABI/common/sysio_tester.sol/SysioTester.json +1532 -0
  33. package/src/assets/ethereum/ABI/common/sysio_verify.sol/sysio_verify.dbg.json +4 -0
  34. package/src/assets/ethereum/ABI/common/sysio_verify.sol/sysio_verify.json +1525 -0
  35. package/src/assets/ethereum/ABI/common/sysio_write.sol/sysio_write.dbg.json +4 -0
  36. package/src/assets/ethereum/ABI/common/sysio_write.sol/sysio_write.json +1076 -0
  37. package/src/assets/ethereum/ABI/liqEth/BeaconState.sol/BeaconState.dbg.json +1 -1
  38. package/src/assets/ethereum/ABI/liqEth/DepositManager.sol/DepositManager.dbg.json +1 -1
  39. package/src/assets/ethereum/ABI/liqEth/DepositManager.sol/DepositManager.json +28 -2
  40. package/src/assets/ethereum/ABI/liqEth/LiqEthAuthority.sol/LiqEthAuthority.dbg.json +1 -1
  41. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IAccounting.dbg.json +1 -1
  42. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IDepositContract.dbg.json +1 -1
  43. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IDepositManager.dbg.json +1 -1
  44. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/ILiqEthUpgradeable.dbg.json +1 -1
  45. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IRewardsERC20.dbg.json +1 -1
  46. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IRewardsERC20Pausable.dbg.json +1 -1
  47. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IStakingModule.dbg.json +1 -1
  48. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IValidatorBalanceVerifier.dbg.json +1 -1
  49. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IWithdrawalRecord.dbg.json +1 -1
  50. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/LiqEthCommon.dbg.json +1 -1
  51. package/src/assets/ethereum/ABI/liqEth/LiqEthManaged.sol/LiqEthManaged.dbg.json +1 -1
  52. package/src/assets/ethereum/ABI/liqEth/RewardsERC20.sol/RewardsERC20Upgradeable.dbg.json +1 -1
  53. package/src/assets/ethereum/ABI/liqEth/RewardsERC20Pausable.sol/RewardsERC20PausableUpgradeable.dbg.json +1 -1
  54. package/src/assets/ethereum/ABI/liqEth/Yield.sol/YieldOracle.dbg.json +1 -1
  55. package/src/assets/ethereum/ABI/liqEth/Yield.sol/YieldOracle.json +2 -2
  56. package/src/assets/ethereum/ABI/liqEth/accounting.sol/Accounting.dbg.json +1 -1
  57. package/src/assets/ethereum/ABI/liqEth/accounting.sol/Accounting.json +2 -15
  58. package/src/assets/ethereum/ABI/liqEth/liqEth.sol/LiqEthToken.dbg.json +1 -1
  59. package/src/assets/ethereum/ABI/liqEth/stakingModule.sol/StakingModule.dbg.json +1 -1
  60. package/src/assets/ethereum/ABI/liqEth/stakingModule.sol/StakingModule.json +6 -25
  61. package/src/assets/ethereum/ABI/liqEth/withdrawalQueue.sol/WithdrawalQueue.dbg.json +1 -1
  62. package/src/assets/ethereum/ABI/liqEth/withdrawalVault.sol/Uint64BE.dbg.json +1 -1
  63. package/src/assets/ethereum/ABI/liqEth/withdrawalVault.sol/Uint64BE.json +2 -2
  64. package/src/assets/ethereum/ABI/liqEth/withdrawalVault.sol/WithdrawalVault.dbg.json +1 -1
  65. package/src/assets/ethereum/ABI/liqEth/withdrawalVault.sol/WithdrawalVault.json +6 -25
  66. package/src/assets/ethereum/ABI/outpost/BAR.sol/BAR.dbg.json +1 -1
  67. package/src/assets/ethereum/ABI/outpost/Depositor.sol/Depositor.dbg.json +1 -1
  68. package/src/assets/ethereum/ABI/outpost/Depositor.sol/Depositor.json +26 -26
  69. package/src/assets/ethereum/ABI/outpost/EthUsdPriceConsumer.sol/AggregatorV3Interface.dbg.json +1 -1
  70. package/src/assets/ethereum/ABI/outpost/EthUsdPriceConsumer.sol/EthUsdPriceConsumer.dbg.json +1 -1
  71. package/src/assets/ethereum/ABI/outpost/OPP.sol/OPP.dbg.json +1 -1
  72. package/src/assets/ethereum/ABI/outpost/OPPCommon.sol/IOPP.dbg.json +1 -1
  73. package/src/assets/ethereum/ABI/outpost/OPPCommon.sol/IOPPEndpoint.dbg.json +1 -1
  74. package/src/assets/ethereum/ABI/outpost/OPPCommon.sol/IOPPInbound.dbg.json +1 -1
  75. package/src/assets/ethereum/ABI/outpost/OPPCommon.sol/IOPPReceiver.dbg.json +1 -1
  76. package/src/assets/ethereum/ABI/outpost/OPPCommon.sol/IOPPSender.dbg.json +1 -1
  77. package/src/assets/ethereum/ABI/outpost/OPPCommon.sol/OPPCommon.dbg.json +1 -1
  78. package/src/assets/ethereum/ABI/outpost/OPPEndpoint.sol/OPPEndpoint.dbg.json +1 -1
  79. package/src/assets/ethereum/ABI/outpost/OPPEndpointManaged.sol/OPPEndpointManaged.dbg.json +1 -1
  80. package/src/assets/ethereum/ABI/outpost/OPPEndpointOwnable.sol/OPPEndpointOwnable.dbg.json +1 -1
  81. package/src/assets/ethereum/ABI/outpost/OPPErrors.sol/OPPErrors.dbg.json +1 -1
  82. package/src/assets/ethereum/ABI/outpost/OPPInbound.sol/OPPInbound.dbg.json +1 -1
  83. package/src/assets/ethereum/ABI/outpost/OPPReceiver.sol/OPPReceiver.dbg.json +1 -1
  84. package/src/assets/ethereum/ABI/outpost/OPPSender.sol/OPPSender.dbg.json +1 -1
  85. package/src/assets/ethereum/ABI/outpost/OutpostErrors.sol/OutpostErrors.dbg.json +1 -1
  86. package/src/assets/ethereum/ABI/outpost/OutpostManaged.sol/OutpostManaged.dbg.json +1 -1
  87. package/src/assets/ethereum/ABI/outpost/OutpostManager.sol/OutpostManager.dbg.json +1 -1
  88. package/src/assets/ethereum/ABI/outpost/OutpostManagerAuthority.sol/OutpostManagerAuthority.dbg.json +1 -1
  89. package/src/assets/ethereum/ABI/outpost/OutpostManagerCommon.sol/IOutpostManager.dbg.json +1 -1
  90. package/src/assets/ethereum/ABI/outpost/OutpostManagerCommon.sol/IOutpostUpgradeable.dbg.json +1 -1
  91. package/src/assets/ethereum/ABI/outpost/OutpostManagerCommon.sol/OutpostManagerCommon.dbg.json +1 -1
  92. package/src/assets/ethereum/ABI/outpost/OutpostOwnable.sol/OutpostOwnable.dbg.json +1 -1
  93. package/src/assets/ethereum/ABI/outpost/Pool.sol/Pool.dbg.json +1 -1
  94. package/src/assets/ethereum/ABI/outpost/Pool.sol/Pool.json +2 -2
  95. package/src/assets/ethereum/ABI/outpost/Pretoken.sol/Pretoken.dbg.json +1 -1
  96. package/src/assets/ethereum/ABI/outpost/Pretoken.sol/Pretoken.json +9 -9
  97. package/src/assets/ethereum/ABI/outpost/ReceiptNFT.sol/ReceiptNFT.dbg.json +1 -1
  98. package/src/assets/ethereum/ABI/outpost/interfaces/IPretoken.sol/IPretoken.dbg.json +1 -1
  99. package/src/assets/ethereum/ABI/outpost/interfaces/IWarrant.sol/IWarrant.dbg.json +1 -1
  100. package/src/assets/ethereum/ABI/outpost/token/ERC721EthEquivalentVotesUpgradeable.sol/ERC721EthEquivalentVotesUpgradeable.dbg.json +1 -1
  101. package/src/assets/ethereum/ABI/outpost/token/IERC721EthEquivalent.sol/IERC721EthEquivalent.dbg.json +1 -1
  102. package/src/assets/solana/idl/liqsol_core.json +132 -182
  103. package/src/assets/solana/types/liqsol_core.ts +132 -182
  104. package/src/networks/ethereum/clients/convert.client.ts +2 -2
  105. package/src/networks/ethereum/clients/pretoken.client.ts +8 -9
  106. package/src/networks/ethereum/clients/stake.client.ts +4 -7
  107. package/src/networks/ethereum/contract.ts +112 -59
  108. package/src/networks/ethereum/ethereum.ts +144 -56
  109. package/src/networks/ethereum/types.ts +26 -17
  110. package/src/networks/ethereum/utils.ts +8 -8
  111. package/src/networks/solana/clients/deposit.client.ts +25 -7
  112. package/src/networks/solana/clients/distribution.client.ts +119 -1
  113. package/src/networks/solana/clients/outpost.client.ts +34 -28
  114. package/src/networks/solana/constants.ts +0 -3
  115. package/src/networks/solana/solana.ts +145 -12
  116. package/src/networks/solana/types.ts +132 -9
  117. package/src/networks/solana/utils.ts +14 -7
  118. package/src/types.ts +19 -11
  119. package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/BeaconRoots.dbg.json +0 -4
  120. package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/BeaconRoots.json +0 -10
  121. package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/SSZ.dbg.json +0 -4
  122. package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/SSZ.json +0 -10
  123. package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/SSZExtras.dbg.json +0 -4
  124. package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/SSZExtras.json +0 -10
  125. package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/SSZVec48.dbg.json +0 -4
  126. package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/SSZVec48.json +0 -10
  127. package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/ValidatorBalanceVerifier.dbg.json +0 -4
  128. package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/ValidatorBalanceVerifier.json +0 -291
  129. package/src/staker/types.ts +0 -62
@@ -42,7 +42,7 @@ import {
42
42
  deriveLiqReceiptDataPda,
43
43
  deriveGlobalConfigPda,
44
44
  } from '../constants';
45
- import { WalletLike } from '../types';
45
+ import { GlobalAccount, WalletLike } from '../types';
46
46
 
47
47
  export class DepositClient {
48
48
  private program: Program<LiqsolCore>;
@@ -192,23 +192,41 @@ export class DepositClient {
192
192
  );
193
193
 
194
194
  // Distribution / balance-tracking
195
- // user_record is keyed by the user’s liqSOL ATA (same convention as deposit/purchase)
195
+ // user_record is keyed by the user’s liqSOL ATA (same convention
196
+ // as deposit/purchase).
196
197
  const userRecord = deriveUserRecordPda(userAta);
197
198
  const distributionState = deriveDistributionStatePda();
198
199
 
199
200
  // Reserve + stake controller PDAs
200
- const global = deriveWithdrawGlobalPda();
201
+ const global = deriveWithdrawGlobalPda(); // withdraw operator state
201
202
  const reservePool = deriveReservePoolPda();
202
203
  const stakeAllocationState = deriveStakeAllocationStatePda();
203
204
  const stakeMetrics = deriveStakeMetricsPda();
204
205
  const maintenanceLedger = deriveMaintenanceLedgerPda();
205
- const globalConfig = deriveGlobalConfigPda();
206
+ const globalConfig = deriveGlobalConfigPda(); // liqSOL config / roles
206
207
 
207
208
  // -------------------------------------------------------------
208
209
  // Need nextReceiptId from withdraw global state
209
210
  // -------------------------------------------------------------
210
- const globalState = await this.program.account.global.fetch(global);
211
- const receiptId = (globalState.nextReceiptId as BN).toBigInt();
211
+ const globalAcct : GlobalAccount = await this.program.account.global.fetch(global);
212
+
213
+ const rawId = globalAcct.nextReceiptId;
214
+ let receiptId: bigint;
215
+
216
+ if (typeof rawId === 'bigint') {
217
+ // New-style IDL / accounts returning bigint directly
218
+ receiptId = rawId;
219
+ } else if (rawId != null && typeof rawId === 'object' && 'toString' in rawId) {
220
+ // Anchor BN / bn.js or similar – normalize through string
221
+ receiptId = BigInt(rawId.toString());
222
+ } else if (typeof rawId === 'number') {
223
+ // Just in case someone typed it as a JS number in tests
224
+ receiptId = BigInt(rawId);
225
+ } else {
226
+ throw new Error(
227
+ `DepositClient.buildWithdrawTx: unexpected nextReceiptId type (${typeof rawId})`,
228
+ );
229
+ }
212
230
 
213
231
  // -------------------------------------------------------------
214
232
  // NFT receipt PDAs (mint, metadata, data, ATA)
@@ -265,7 +283,7 @@ export class DepositClient {
265
283
  associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
266
284
  systemProgram: SystemProgram.programId,
267
285
  rent: SYSVAR_RENT_PUBKEY,
268
- globalConfig
286
+ globalConfig,
269
287
  })
270
288
  .instruction();
271
289
 
@@ -5,11 +5,14 @@ import { SolanaProgramService } from '../program';
5
5
  import type { LiqsolCore } from '../../../assets/solana/types/liqsol_core';
6
6
  import {
7
7
  deriveDistributionStatePda,
8
+ deriveGlobalConfigPda,
8
9
  deriveLiqsolMintPda,
10
+ derivePayRateHistoryPda,
9
11
  deriveUserRecordPda,
10
12
  } from '../constants';
11
- import type { DistributionState, DistributionUserRecord } from '../types';
13
+ import type { DistributionState, DistributionUserRecord, GlobalConfig, PayRateEntry, PayRateHistory } from '../types';
12
14
  import { getAssociatedTokenAddressSync, TOKEN_2022_PROGRAM_ID } from '@solana/spl-token';
15
+ import { ceilDiv } from '../utils';
13
16
 
14
17
  /**
15
18
  * Distribution client – wraps the distribution portion of the liqsol_core
@@ -55,6 +58,39 @@ export class DistributionClient {
55
58
  }
56
59
  }
57
60
 
61
+
62
+ /**
63
+ * Fetch the global payRateHistory account (circular buffer of scaled pay rates).
64
+ *
65
+ * IDL account name: "payRateHistory"
66
+ */
67
+ async getPayRateHistory(): Promise<PayRateHistory | null> {
68
+ const pda = derivePayRateHistoryPda();
69
+ try {
70
+ // Anchor types map directly onto our PayRateHistory TS type
71
+ return (await this.program.account.payRateHistory.fetch(
72
+ pda,
73
+ )) as PayRateHistory;
74
+ } catch {
75
+ return null;
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Fetch the globalConfig account (contains depositFeeMultiplier, etc).
81
+ *
82
+ * IDL account name: "globalConfig"
83
+ */
84
+ async getGlobalConfig(): Promise<GlobalConfig | null> {
85
+ const pda = deriveGlobalConfigPda();
86
+ try {
87
+ return (await this.program.account.globalConfig.fetch(
88
+ pda,
89
+ ));
90
+ } catch {
91
+ return null;
92
+ }
93
+ }
58
94
  /**
59
95
  * Fetch a user's distribution userRecord (or null if missing).
60
96
  *
@@ -130,4 +166,86 @@ export class DistributionClient {
130
166
 
131
167
  return { shares: userShares, totalShares, ratio };
132
168
  }
169
+
170
+ /**
171
+ * Compute an average scaled pay rate over the most recent `windowSize`
172
+ * valid entries in the pay-rate history circular buffer.
173
+ *
174
+ * Returns a BN scaled by 1e12 (same as on-chain).
175
+ */
176
+ async getAverageScaledPayRate(windowSize = 5): Promise<BN> {
177
+ const history = await this.getPayRateHistory();
178
+ if (!history) {
179
+ return new BN(0);
180
+ }
181
+
182
+ const entries: PayRateEntry[] = history.entries ?? [];
183
+ if (!entries.length) {
184
+ return new BN(0);
185
+ }
186
+
187
+ const maxEntries: number =
188
+ typeof history.maxEntries === 'number'
189
+ ? history.maxEntries
190
+ : entries.length;
191
+
192
+ const rawTotalAdded: any = history.totalEntriesAdded ?? 0;
193
+ const totalAddedBn = new BN(rawTotalAdded.toString());
194
+
195
+ // No valid entries written yet
196
+ if (totalAddedBn.isZero()) {
197
+ return new BN(0);
198
+ }
199
+
200
+ const totalAdded = Math.min(
201
+ totalAddedBn.toNumber(),
202
+ maxEntries,
203
+ entries.length,
204
+ );
205
+
206
+ if (!totalAdded) {
207
+ return new BN(0);
208
+ }
209
+
210
+ const COUNT = Math.max(1, Math.min(windowSize, totalAdded));
211
+
212
+ const currentIndexNum = Number(history.currentIndex ?? 0);
213
+
214
+ // Most recently written entry is at currentIndex - 1 (mod maxEntries)
215
+ let idx =
216
+ currentIndexNum === 0
217
+ ? maxEntries - 1
218
+ : currentIndexNum - 1;
219
+
220
+ let sum = new BN(0);
221
+ let valid = 0;
222
+ const zero = new BN(0);
223
+
224
+ for (let i = 0; i < COUNT; i++) {
225
+ const entry: any = entries[idx];
226
+ if (entry) {
227
+ // Support both camelCase and snake_case (for safety)
228
+ const rawScaled =
229
+ entry.scaledRate ??
230
+ entry.scaled_rate ??
231
+ 0;
232
+
233
+ const rate = new BN(rawScaled.toString());
234
+ if (rate.gt(zero)) {
235
+ sum = sum.add(rate);
236
+ valid += 1;
237
+ }
238
+ }
239
+
240
+ // Walk backwards through the circular buffer
241
+ idx = idx === 0 ? maxEntries - 1 : idx - 1;
242
+ }
243
+
244
+ if (!valid) {
245
+ return new BN(0);
246
+ }
247
+
248
+ // Same behavior as the dashboard: use a ceiling-like average
249
+ return ceilDiv(sum, new BN(valid));
250
+ }
133
251
  }
@@ -100,34 +100,40 @@ export class OutpostClient {
100
100
 
101
101
  const pdas = await this.buildAccounts(userPk);
102
102
 
103
- const [
104
- globalState,
105
- outpostAccount,
106
- distributionState,
107
- userPretokenRecord,
108
- trancheState,
109
- ] = await Promise.all([
110
- this.program.account.globalState.fetch(pdas.globalState),
111
- this.program.account.outpostAccount.fetchNullable(pdas.outpostAccount),
112
- this.program.account.distributionState.fetchNullable(pdas.distributionState),
113
- this.program.account.userPretokenRecord.fetchNullable(pdas.userPretokenRecord),
114
- this.program.account.trancheState.fetchNullable(pdas.trancheState),
115
- ]);
116
-
117
- const [liqsolPoolBalance, userLiqsolBalance] = await Promise.all([
118
- this.getTokenBalance(pdas.liqsolPoolAta),
119
- this.getTokenBalance(pdas.userAta),
120
- ]);
121
-
122
- return {
123
- globalState,
124
- outpostAccount,
125
- distributionState,
126
- trancheState,
127
- userPretokenRecord,
128
- liqsolPoolBalance,
129
- userLiqsolBalance,
130
- };
103
+ try {
104
+ const [
105
+ globalState,
106
+ outpostAccount,
107
+ distributionState,
108
+ userPretokenRecord,
109
+ trancheState,
110
+ ] = await Promise.all([
111
+ this.program.account.globalState.fetch(pdas.globalState),
112
+ this.program.account.outpostAccount.fetchNullable(pdas.outpostAccount),
113
+ this.program.account.distributionState.fetchNullable(pdas.distributionState),
114
+ this.program.account.userPretokenRecord.fetchNullable(pdas.userPretokenRecord),
115
+ this.program.account.trancheState.fetchNullable(pdas.trancheState),
116
+ ]);
117
+
118
+ const [liqsolPoolBalance, userLiqsolBalance] = await Promise.all([
119
+ this.getTokenBalance(pdas.liqsolPoolAta),
120
+ this.getTokenBalance(pdas.userAta),
121
+ ]);
122
+
123
+ return {
124
+ globalState,
125
+ outpostAccount,
126
+ distributionState,
127
+ trancheState,
128
+ userPretokenRecord,
129
+ liqsolPoolBalance,
130
+ userLiqsolBalance,
131
+ };
132
+ }
133
+ catch (err) {
134
+ console.error('Error fetching Outpost wire state:', err);
135
+ throw err;
136
+ }
131
137
  }
132
138
 
133
139
  // -------------------------------------------------------------------------
@@ -89,9 +89,6 @@ export const PDA_SEEDS = {
89
89
  MINT_METADATA: 'mint_metadata',
90
90
  LIQ_RECEIPT_DATA: 'liq_receipt_data',
91
91
  WITHDRAW_MINT: 'mint',
92
-
93
-
94
-
95
92
  } as const;
96
93
 
97
94
  // Global Config PDA
@@ -4,10 +4,11 @@ import {
4
4
  Connection,
5
5
  ConnectionConfig,
6
6
  PublicKey as SolPubKey,
7
+ SystemProgram,
7
8
  Transaction,
8
9
  TransactionSignature,
9
10
  } from '@solana/web3.js';
10
- import { AnchorProvider } from '@coral-xyz/anchor';
11
+ import { AnchorProvider, BN } from '@coral-xyz/anchor';
11
12
  import { BaseSignerWalletAdapter } from '@solana/wallet-adapter-base';
12
13
  import {
13
14
  ASSOCIATED_TOKEN_PROGRAM_ID,
@@ -43,11 +44,14 @@ import {
43
44
  INDEX_SCALE,
44
45
  } from './constants';
45
46
 
46
- import { buildSolanaTrancheSnapshot } from './utils';
47
- import { SolanaTransaction } from './types';
47
+ import { buildSolanaTrancheSnapshot, ceilDiv } from './utils';
48
+ import { GlobalConfig, PayRateEntry, SolanaTransaction } from './types';
49
+ import { SolanaProgramService } from './program';
48
50
 
49
51
  const commitment: Commitment = 'confirmed';
50
52
 
53
+ export const SCALE = new BN('1000000000000');
54
+
51
55
  /**
52
56
  * Solana implementation of IStakingClient.
53
57
  *
@@ -473,17 +477,77 @@ export class SolanaStakingClient implements IStakingClient {
473
477
  // READ-ONLY Public Methods
474
478
  // ---------------------------------------------------------------------
475
479
 
476
- // Estimated total APY for staking yeild
477
- getSystemAPY(): Promise<number> {
478
- // TODO
479
- return Promise.resolve(0);
480
+ // Estimated total APY for staking yield (percent, e.g. 7.23)
481
+ async getSystemAPY(): Promise<number> {
482
+ // Reuse same window as the dashboard (5 most recent valid entries)
483
+ const avgPayRate = await this.distributionClient.getAverageScaledPayRate(5);
484
+
485
+ if (avgPayRate.isZero()) {
486
+ return 0;
487
+ }
488
+
489
+ // 10^12, same scale used on-chain and in the dashboard
490
+ const SCALE = new BN('1000000000000');
491
+ const EPOCHS_PER_YEAR = 365; // matches DEFAULT_PAY_RATE semantics
492
+
493
+ // Safe: pay rate is well below 1e12, so .toNumber() won't overflow
494
+ const ratePerPeriod = avgPayRate.toNumber() / SCALE.toNumber(); // e.g. 0.0001917…
495
+ const apyDecimal = ratePerPeriod * EPOCHS_PER_YEAR; // e.g. ~0.07
496
+ const apyPercent = apyDecimal * 100; // e.g. 7
497
+
498
+ return apyPercent;
480
499
  }
481
500
 
482
- // Protocol fee charged for deposit from Native to LIQ
483
- getDepositFee(amount: bigint): Promise<bigint> {
484
- // Returning 1% for now,
485
- // TODO: fetch real fee from on-chain config
486
- return Promise.resolve((amount * BigInt(1)) / BigInt(100));
501
+ // ---------------------------------------------
502
+ // Deposit fee calculation (SOL -> liqSOL)
503
+ // ---------------------------------------------
504
+
505
+ /**
506
+ * Estimate the protocol deposit fee in lamports for a given SOL amount,
507
+ * based on recent pay rates and globalConfig.depositFeeMultiplier.
508
+ *
509
+ * - amountLamports: deposit notional in lamports
510
+ * - windowSize: how many recent payRate entries to average (default 5)
511
+ *
512
+ * Returns 0n if payRateHistory or globalConfig is missing, or if
513
+ * there are no valid pay-rate entries yet.
514
+ */
515
+ async getDepositFee(
516
+ amountLamports: bigint,
517
+ windowSize = 5,
518
+ ): Promise<bigint> {
519
+ if (amountLamports <= BigInt(0)) {
520
+ return BigInt(0);
521
+ }
522
+
523
+ const [avgPayRate, globalConfig] : [BN, GlobalConfig] = await Promise.all([
524
+ this.distributionClient.getAverageScaledPayRate(windowSize),
525
+ this.distributionClient.getGlobalConfig(),
526
+ ]);
527
+
528
+ if (!globalConfig || avgPayRate.isZero()) {
529
+ return BigInt(0);
530
+ }
531
+
532
+ // depositFeeMultiplier may be BN or number depending on your type
533
+ const rawMultiplier = globalConfig.depositFeeMultiplier;
534
+ const multiplier = new BN(
535
+ rawMultiplier?.toString?.() ?? rawMultiplier ?? 0,
536
+ );
537
+ if (multiplier.isZero()) {
538
+ return BigInt(0);
539
+ }
540
+
541
+ const amountBn = new BN(amountLamports.toString());
542
+
543
+ // 10^12 scale (matches scaledRate / index scale)
544
+
545
+ const feeBn = ceilDiv(
546
+ avgPayRate.mul(multiplier).mul(amountBn),
547
+ SCALE,
548
+ );
549
+
550
+ return BigInt(feeBn.toString());
487
551
  }
488
552
 
489
553
  /**
@@ -526,6 +590,75 @@ export class SolanaStakingClient implements IStakingClient {
526
590
  });
527
591
  }
528
592
 
593
+ /**
594
+ * Estimate a conservative SOL buffer (lamports) to leave in the wallet
595
+ * so the user can pay fees for the current deposit and at least one
596
+ * more transaction, plus a bit extra for future interactions.
597
+ *
598
+ * Intended usage in UI:
599
+ * const bufferLamports = await client.estimateGasBuffer();
600
+ * const maxSpendable = balanceLamports > bufferLamports
601
+ * ? balanceLamports - bufferLamports
602
+ * : 0n;
603
+ *
604
+ * @param options.txCount How many transactions to cover (default 2)
605
+ * @param options.safetyMultiplier Extra safety multiplier (default 3x)
606
+ * @param options.minBufferLamports Optional override minimum buffer (default ~0.01 SOL)
607
+ */
608
+ async estimateGasBuffer(options?: {
609
+ txCount?: number;
610
+ safetyMultiplier?: number;
611
+ minBufferLamports?: bigint;
612
+ }): Promise<bigint> {
613
+ this.ensureUser();
614
+
615
+ const payer = this.solPubKey;
616
+
617
+ // 1) Build a small dummy transaction (self-transfer of 0 lamports).
618
+ const dummyIx = SystemProgram.transfer({
619
+ fromPubkey: payer,
620
+ toPubkey: payer,
621
+ lamports: 0,
622
+ });
623
+
624
+ const tx = new Transaction().add(dummyIx);
625
+ const { blockhash } = await this.connection.getLatestBlockhash(commitment);
626
+ tx.recentBlockhash = blockhash;
627
+ tx.feePayer = payer;
628
+
629
+ // 2) Ask the cluster what it would charge in lamports for this tx.
630
+ const message = tx.compileMessage();
631
+ const feeInfo = await this.connection.getFeeForMessage(message, commitment);
632
+
633
+ // Fallback to a conservative default if RPC doesn't give a value.
634
+ const singleTxFeeLamports = BigInt(feeInfo.value ?? 5000);
635
+
636
+ // 3) How many txs do we want to cover?
637
+ // Default: 2 (deposit + one extra operation).
638
+ const txCount = BigInt(options?.txCount ?? 2);
639
+
640
+ // 4) Apply a safety multiplier (default 3x to be very safe).
641
+ const safetyMultiplier = options?.safetyMultiplier ?? 3;
642
+ const safetyScaled = BigInt(Math.round(safetyMultiplier * 100)); // e.g. 300
643
+
644
+ let bufferLamports =
645
+ (singleTxFeeLamports *
646
+ txCount *
647
+ safetyScaled) /
648
+ BigInt(100);
649
+
650
+ // 5) Enforce a minimum buffer (default ~0.01 SOL).
651
+ const defaultMinBufferLamports = BigInt(10_000_000); // 0.01 SOL (1e9 / 100)
652
+ const minBufferLamports =
653
+ options?.minBufferLamports ?? defaultMinBufferLamports;
654
+
655
+ if (bufferLamports < minBufferLamports) {
656
+ bufferLamports = minBufferLamports;
657
+ }
658
+
659
+ return bufferLamports;
660
+ }
661
+
529
662
  // ---------------------------------------------------------------------
530
663
  // Tx helpers
531
664
  // ---------------------------------------------------------------------
@@ -177,7 +177,7 @@ export interface SharesPreview {
177
177
  * - NFT withdrawal receipts
178
178
  * - Encumbered funds for pending withdrawals
179
179
  */
180
- export type Global = {
180
+ export type GlobalAccount = {
181
181
  /** PDA bump */
182
182
  bump: number;
183
183
 
@@ -210,9 +210,6 @@ export type Global = {
210
210
  * - `wireState` lifecycle (preLaunch/postLaunch/refund)
211
211
  */
212
212
  export type GlobalState = {
213
- /** Admin authority for pretokens/outpost config */
214
- admin: PublicKey;
215
-
216
213
  /** Deployment timestamp (Unix, seconds) */
217
214
  deployedAt: BN;
218
215
 
@@ -271,6 +268,135 @@ export type GlobalState = {
271
268
  bump: number;
272
269
  };
273
270
 
271
+
272
+ /**
273
+ * IDL: `globalConfig`
274
+ *
275
+ * Zero-copy global config PDA.
276
+ * Authority is taken from StakeControllerState, not stored here.
277
+ */
278
+ export type GlobalConfig = {
279
+ /** PDA bump */
280
+ bump: number;
281
+
282
+ /** 7-byte padding (unused) */
283
+ padding: number[]; // u8[7]
284
+
285
+ /** Minimum SOL amount a user can deposit (lamports, u64) */
286
+ minUserDeposit: BN;
287
+
288
+ /** Minimum SOL amount for an unstake/withdrawal request (lamports, u64) */
289
+ minUnstakeRequest: BN;
290
+
291
+ /** Minimum stake delta to trigger a stake rebalance order (lamports, u64) */
292
+ minRebalanceStakeDelta: BN;
293
+
294
+ /** Minimum unstake delta to trigger an unstake rebalance order (lamports, u64) */
295
+ minRebalanceUnstakeDelta: BN;
296
+
297
+ /** Minimum transient stake to include in effective stake calculations (lamports, u64) */
298
+ transientThreshold: BN;
299
+
300
+ /**
301
+ * Minimum slots that must have elapsed in the epoch before late epoch operations can execute (u64)
302
+ */
303
+ minLateEpochSlotGate: BN;
304
+
305
+ /** Reserved u64[2] */
306
+ reservedU64: BN[];
307
+
308
+ /**
309
+ * Epochs a validator must wait in the graveyard before it is booted.
310
+ * This begins after the last recorded state change (u16)
311
+ */
312
+ cooldownEpochs: number;
313
+
314
+ /**
315
+ * Multiplier for deposit fee calculation (u16).
316
+ * Typically: avg_pay_rate * expected_warmup_epochs
317
+ */
318
+ depositFeeMultiplier: number;
319
+
320
+ /**
321
+ * Minimum VPP score required to enter the active validator set,
322
+ * fallback when the validator set is very small (u16)
323
+ */
324
+ minVppEntry: number;
325
+
326
+ /**
327
+ * VPP score threshold below which a validator is removed from active set,
328
+ * fallback threshold (u16)
329
+ */
330
+ minVppExit: number;
331
+
332
+ /**
333
+ * Max validators for "tiny" network band (uses fixed VPP thresholds) (u16)
334
+ */
335
+ tinyNetworkThreshold: number;
336
+
337
+ /**
338
+ * Max validators for "small" network band (uses percentile-based selection) (u16)
339
+ */
340
+ smallNetworkThreshold: number;
341
+
342
+ /**
343
+ * Max validators for "medium" network band (uses percentile-based selection) (u16)
344
+ */
345
+ mediumNetworkThreshold: number;
346
+
347
+ /**
348
+ * Fixed rank threshold to enter active set in large networks (0-indexed, u16)
349
+ */
350
+ largeNetworkEntryRank: number;
351
+
352
+ /**
353
+ * Fixed rank threshold to exit active set in large networks (0-indexed, u16)
354
+ */
355
+ largeNetworkExitRank: number;
356
+
357
+ /** Reserved u16[3] */
358
+ reservedU16: number[];
359
+
360
+ /**
361
+ * Percentile rank required to enter active set in small networks (u8)
362
+ */
363
+ smallNetworkEntryPercent: number;
364
+
365
+ /**
366
+ * Percentile rank below which validators exit in small networks (u8)
367
+ */
368
+ smallNetworkExitPercent: number;
369
+
370
+ /**
371
+ * Percentile rank required to enter active set in medium networks (u8)
372
+ */
373
+ mediumNetworkEntryPercent: number;
374
+
375
+ /**
376
+ * Percentile rank below which validators exit in medium networks (u8)
377
+ */
378
+ mediumNetworkExitPercent: number;
379
+
380
+ /** Reserved u8[2] */
381
+ reservedU8: number[];
382
+
383
+ /**
384
+ * Feature flags (u16 bitfield):
385
+ * Bit 0: DepositsEnabled
386
+ * Bit 1: WithdrawalsEnabled
387
+ * Bit 2: ClaimWithdrawalsEnabled
388
+ * Bit 3: ProcessStakeOrdersEnabled
389
+ * Bit 4: ProcessUnstakeOrdersEnabled
390
+ * Bit 5: ProcessPayCycleEnabled
391
+ * Bit 6: RebalancingEnabled
392
+ * Bits 7–15: Reserved
393
+ */
394
+ featureFlags: number;
395
+
396
+ /** Reserved flags (u16[1]) */
397
+ reservedFlags: number[];
398
+ };
399
+
274
400
  /**
275
401
  * ============================================================
276
402
  * Outpost / Pretoken Accounts
@@ -366,9 +492,6 @@ export type UserPretokenRecord = {
366
492
  * All price/supply fields use 8-decimal precision (SCALE = 1e8).
367
493
  */
368
494
  export type TrancheState = {
369
- /** Admin authority for tranche parameters */
370
- admin: PublicKey;
371
-
372
495
  /** Current tranche number */
373
496
  currentTrancheNumber: BN;
374
497
 
@@ -387,8 +510,8 @@ export type TrancheState = {
387
510
  /** Supply growth in basis points per tranche */
388
511
  supplyGrowthBps: number;
389
512
 
390
- /** Price growth in basis points per tranche */
391
- priceGrowthBps: number;
513
+ /** Price growth in cents per tranche (0.01 USD units) */
514
+ priceGrowthCents: number;
392
515
 
393
516
  /** Minimum valid SOL/USD price for Chainlink validation (8-decimal i128) */
394
517
  minPriceUsd: BN;