@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.
- package/lib/stake.browser.js +12820 -6787
- package/lib/stake.browser.js.map +1 -1
- package/lib/stake.d.ts +353 -209
- package/lib/stake.js +12940 -6886
- package/lib/stake.js.map +1 -1
- package/lib/stake.m.js +12820 -6787
- package/lib/stake.m.js.map +1 -1
- package/package.json +1 -1
- package/src/assets/ethereum/ABI/common/Base58.sol/Base58.dbg.json +4 -0
- package/src/assets/ethereum/ABI/common/Base58.sol/Base58.json +164 -0
- package/src/assets/ethereum/ABI/common/OpenZepArtifacts.sol/__Dummy_OZ_UUPS__.dbg.json +4 -0
- package/src/assets/ethereum/ABI/common/OpenZepArtifacts.sol/__Dummy_OZ_UUPS__.json +76 -0
- package/src/assets/ethereum/ABI/common/RestrictedCallers.sol/RestrictedCallers.dbg.json +4 -0
- package/src/assets/ethereum/ABI/common/RestrictedCallers.sol/RestrictedCallers.json +10 -0
- package/src/assets/ethereum/ABI/common/iodata.sol/iodata.dbg.json +4 -0
- package/src/assets/ethereum/ABI/common/iodata.sol/iodata.json +618 -0
- package/src/assets/ethereum/ABI/common/iodata_util.sol/iodata_util.dbg.json +4 -0
- package/src/assets/ethereum/ABI/common/iodata_util.sol/iodata_util.json +40 -0
- package/src/assets/ethereum/ABI/common/sysio_data.sol/sysio_data.dbg.json +4 -0
- package/src/assets/ethereum/ABI/common/sysio_data.sol/sysio_data.json +10 -0
- package/src/assets/ethereum/ABI/common/sysio_errors.sol/sysio_errors.dbg.json +4 -0
- package/src/assets/ethereum/ABI/common/sysio_errors.sol/sysio_errors.json +10 -0
- package/src/assets/ethereum/ABI/common/sysio_merkle.sol/sysio_merkle.dbg.json +4 -0
- package/src/assets/ethereum/ABI/common/sysio_merkle.sol/sysio_merkle.json +233 -0
- package/src/assets/ethereum/ABI/common/sysio_name.sol/sysio_name.dbg.json +4 -0
- package/src/assets/ethereum/ABI/common/sysio_name.sol/sysio_name.json +49 -0
- package/src/assets/ethereum/ABI/common/sysio_pubkey.sol/sysio_pubkey.dbg.json +4 -0
- package/src/assets/ethereum/ABI/common/sysio_pubkey.sol/sysio_pubkey.json +64 -0
- package/src/assets/ethereum/ABI/common/sysio_read.sol/sysio_read.dbg.json +4 -0
- package/src/assets/ethereum/ABI/common/sysio_read.sol/sysio_read.json +1458 -0
- package/src/assets/ethereum/ABI/common/sysio_tester.sol/SysioTester.dbg.json +4 -0
- package/src/assets/ethereum/ABI/common/sysio_tester.sol/SysioTester.json +1532 -0
- package/src/assets/ethereum/ABI/common/sysio_verify.sol/sysio_verify.dbg.json +4 -0
- package/src/assets/ethereum/ABI/common/sysio_verify.sol/sysio_verify.json +1525 -0
- package/src/assets/ethereum/ABI/common/sysio_write.sol/sysio_write.dbg.json +4 -0
- package/src/assets/ethereum/ABI/common/sysio_write.sol/sysio_write.json +1076 -0
- package/src/assets/ethereum/ABI/liqEth/BeaconState.sol/BeaconState.dbg.json +1 -1
- package/src/assets/ethereum/ABI/liqEth/DepositManager.sol/DepositManager.dbg.json +1 -1
- package/src/assets/ethereum/ABI/liqEth/DepositManager.sol/DepositManager.json +28 -2
- package/src/assets/ethereum/ABI/liqEth/LiqEthAuthority.sol/LiqEthAuthority.dbg.json +1 -1
- package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IAccounting.dbg.json +1 -1
- package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IDepositContract.dbg.json +1 -1
- package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IDepositManager.dbg.json +1 -1
- package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/ILiqEthUpgradeable.dbg.json +1 -1
- package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IRewardsERC20.dbg.json +1 -1
- package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IRewardsERC20Pausable.dbg.json +1 -1
- package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IStakingModule.dbg.json +1 -1
- package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IValidatorBalanceVerifier.dbg.json +1 -1
- package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IWithdrawalRecord.dbg.json +1 -1
- package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/LiqEthCommon.dbg.json +1 -1
- package/src/assets/ethereum/ABI/liqEth/LiqEthManaged.sol/LiqEthManaged.dbg.json +1 -1
- package/src/assets/ethereum/ABI/liqEth/RewardsERC20.sol/RewardsERC20Upgradeable.dbg.json +1 -1
- package/src/assets/ethereum/ABI/liqEth/RewardsERC20Pausable.sol/RewardsERC20PausableUpgradeable.dbg.json +1 -1
- package/src/assets/ethereum/ABI/liqEth/Yield.sol/YieldOracle.dbg.json +1 -1
- package/src/assets/ethereum/ABI/liqEth/Yield.sol/YieldOracle.json +2 -2
- package/src/assets/ethereum/ABI/liqEth/accounting.sol/Accounting.dbg.json +1 -1
- package/src/assets/ethereum/ABI/liqEth/accounting.sol/Accounting.json +2 -15
- package/src/assets/ethereum/ABI/liqEth/liqEth.sol/LiqEthToken.dbg.json +1 -1
- package/src/assets/ethereum/ABI/liqEth/stakingModule.sol/StakingModule.dbg.json +1 -1
- package/src/assets/ethereum/ABI/liqEth/stakingModule.sol/StakingModule.json +6 -25
- package/src/assets/ethereum/ABI/liqEth/withdrawalQueue.sol/WithdrawalQueue.dbg.json +1 -1
- package/src/assets/ethereum/ABI/liqEth/withdrawalVault.sol/Uint64BE.dbg.json +1 -1
- package/src/assets/ethereum/ABI/liqEth/withdrawalVault.sol/Uint64BE.json +2 -2
- package/src/assets/ethereum/ABI/liqEth/withdrawalVault.sol/WithdrawalVault.dbg.json +1 -1
- package/src/assets/ethereum/ABI/liqEth/withdrawalVault.sol/WithdrawalVault.json +6 -25
- package/src/assets/ethereum/ABI/outpost/BAR.sol/BAR.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/Depositor.sol/Depositor.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/Depositor.sol/Depositor.json +26 -26
- package/src/assets/ethereum/ABI/outpost/EthUsdPriceConsumer.sol/AggregatorV3Interface.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/EthUsdPriceConsumer.sol/EthUsdPriceConsumer.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/OPP.sol/OPP.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/OPPCommon.sol/IOPP.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/OPPCommon.sol/IOPPEndpoint.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/OPPCommon.sol/IOPPInbound.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/OPPCommon.sol/IOPPReceiver.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/OPPCommon.sol/IOPPSender.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/OPPCommon.sol/OPPCommon.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/OPPEndpoint.sol/OPPEndpoint.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/OPPEndpointManaged.sol/OPPEndpointManaged.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/OPPEndpointOwnable.sol/OPPEndpointOwnable.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/OPPErrors.sol/OPPErrors.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/OPPInbound.sol/OPPInbound.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/OPPReceiver.sol/OPPReceiver.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/OPPSender.sol/OPPSender.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/OutpostErrors.sol/OutpostErrors.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/OutpostManaged.sol/OutpostManaged.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/OutpostManager.sol/OutpostManager.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/OutpostManagerAuthority.sol/OutpostManagerAuthority.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/OutpostManagerCommon.sol/IOutpostManager.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/OutpostManagerCommon.sol/IOutpostUpgradeable.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/OutpostManagerCommon.sol/OutpostManagerCommon.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/OutpostOwnable.sol/OutpostOwnable.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/Pool.sol/Pool.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/Pool.sol/Pool.json +2 -2
- package/src/assets/ethereum/ABI/outpost/Pretoken.sol/Pretoken.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/Pretoken.sol/Pretoken.json +9 -9
- package/src/assets/ethereum/ABI/outpost/ReceiptNFT.sol/ReceiptNFT.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/interfaces/IPretoken.sol/IPretoken.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/interfaces/IWarrant.sol/IWarrant.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/token/ERC721EthEquivalentVotesUpgradeable.sol/ERC721EthEquivalentVotesUpgradeable.dbg.json +1 -1
- package/src/assets/ethereum/ABI/outpost/token/IERC721EthEquivalent.sol/IERC721EthEquivalent.dbg.json +1 -1
- package/src/assets/solana/idl/liqsol_core.json +132 -182
- package/src/assets/solana/types/liqsol_core.ts +132 -182
- package/src/networks/ethereum/clients/convert.client.ts +2 -2
- package/src/networks/ethereum/clients/pretoken.client.ts +8 -9
- package/src/networks/ethereum/clients/stake.client.ts +4 -7
- package/src/networks/ethereum/contract.ts +112 -59
- package/src/networks/ethereum/ethereum.ts +144 -56
- package/src/networks/ethereum/types.ts +26 -17
- package/src/networks/ethereum/utils.ts +8 -8
- package/src/networks/solana/clients/deposit.client.ts +25 -7
- package/src/networks/solana/clients/distribution.client.ts +119 -1
- package/src/networks/solana/clients/outpost.client.ts +34 -28
- package/src/networks/solana/constants.ts +0 -3
- package/src/networks/solana/solana.ts +145 -12
- package/src/networks/solana/types.ts +132 -9
- package/src/networks/solana/utils.ts +14 -7
- package/src/types.ts +19 -11
- package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/BeaconRoots.dbg.json +0 -4
- package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/BeaconRoots.json +0 -10
- package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/SSZ.dbg.json +0 -4
- package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/SSZ.json +0 -10
- package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/SSZExtras.dbg.json +0 -4
- package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/SSZExtras.json +0 -10
- package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/SSZVec48.dbg.json +0 -4
- package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/SSZVec48.json +0 -10
- package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/ValidatorBalanceVerifier.dbg.json +0 -4
- package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/ValidatorBalanceVerifier.json +0 -291
- 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
|
|
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
|
|
211
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
// -------------------------------------------------------------------------
|
|
@@ -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
|
|
477
|
-
getSystemAPY(): Promise<number> {
|
|
478
|
-
//
|
|
479
|
-
|
|
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
|
-
//
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
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
|
|
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
|
|
391
|
-
|
|
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;
|