@perena/vault-sdk 1.0.24 → 1.0.25
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/dist/index.d.ts +26 -18
- package/dist/index.js +209 -43
- package/package.json +3 -1
package/dist/index.d.ts
CHANGED
|
@@ -8957,11 +8957,11 @@ type Bankineco = {
|
|
|
8957
8957
|
"name": "vaultOracle";
|
|
8958
8958
|
"docs": [
|
|
8959
8959
|
"Required when enabling tranching organically (`for_migration == false`)",
|
|
8960
|
-
"on a vault that still has main shareholders accruing fixed APY
|
|
8961
|
-
"
|
|
8962
|
-
"
|
|
8963
|
-
"
|
|
8964
|
-
"
|
|
8960
|
+
"on a vault that still has main shareholders accruing fixed APY. A fresh",
|
|
8961
|
+
"settlement establishes the regular sleeve's booked NAV and physical",
|
|
8962
|
+
"surplus immediately before the new tranche sleeve is introduced. Not",
|
|
8963
|
+
"needed on the migration path, whose accounting is reconstructed wholesale",
|
|
8964
|
+
"from the legacy state.",
|
|
8965
8965
|
"",
|
|
8966
8966
|
"Kept LAST (a trailing optional account) so legacy clients that predate it",
|
|
8967
8967
|
"— e.g. the migration path building against the frozen bankineco IDL —",
|
|
@@ -11900,16 +11900,9 @@ type Bankineco = {
|
|
|
11900
11900
|
};
|
|
11901
11901
|
},
|
|
11902
11902
|
{
|
|
11903
|
-
"name": "
|
|
11903
|
+
"name": "paddingBool";
|
|
11904
11904
|
"docs": [
|
|
11905
|
-
"
|
|
11906
|
-
"and receipt-supply accounting. Oracle mutation and NAV settlement stay",
|
|
11907
|
-
"disabled until every configured receipt mint authority has moved to this vault",
|
|
11908
|
-
"and migration finalization clears the flag.",
|
|
11909
|
-
"",
|
|
11910
|
-
"This occupies the former `apy_settle_skip` padding byte, preserving the",
|
|
11911
|
-
"on-chain account layout for already-deployed vaults. Existing vaults read",
|
|
11912
|
-
"it as false."
|
|
11905
|
+
"Reserved byte retained for on-chain account layout compatibility."
|
|
11913
11906
|
];
|
|
11914
11907
|
"type": {
|
|
11915
11908
|
"defined": {
|
|
@@ -15955,6 +15948,8 @@ interface DecodedHolding {
|
|
|
15955
15948
|
mint: unknown;
|
|
15956
15949
|
decimals: number;
|
|
15957
15950
|
priceOracleType: unknown;
|
|
15951
|
+
/** Bound Pyth price-update or nested-vault account for live-priced holdings. */
|
|
15952
|
+
priceOracleAccount?: unknown;
|
|
15958
15953
|
isBase: unknown;
|
|
15959
15954
|
localAmount?: unknown;
|
|
15960
15955
|
externalAmount?: unknown;
|
|
@@ -16035,6 +16030,12 @@ interface SettlementSimulation {
|
|
|
16035
16030
|
grossNav?: bigint;
|
|
16036
16031
|
settledAccountingNav?: bigint;
|
|
16037
16032
|
current: SettlementSimulationSnapshot;
|
|
16033
|
+
/**
|
|
16034
|
+
* True when `projected` was calculated by replaying the otherwise-identical
|
|
16035
|
+
* settlement with the max-APY guard disabled. The real crank remains blocked
|
|
16036
|
+
* until the vault config is updated.
|
|
16037
|
+
*/
|
|
16038
|
+
projectionRequiresMaxApyDisabled?: boolean;
|
|
16038
16039
|
projected?: SettlementSimulationSnapshot;
|
|
16039
16040
|
performanceFees?: {
|
|
16040
16041
|
curator: bigint;
|
|
@@ -16341,6 +16342,12 @@ declare class ConsensusOracleService {
|
|
|
16341
16342
|
log?: (msg: string) => void;
|
|
16342
16343
|
}): Promise<ConsensusOracleTarget[]>;
|
|
16343
16344
|
private fetchDecodedVault;
|
|
16345
|
+
/**
|
|
16346
|
+
* Keep active permissionless price sources warm so they cannot block the NAV
|
|
16347
|
+
* crank after the consensus report lands. Prices are refreshed at least once
|
|
16348
|
+
* per hour, or at the vault's shorter staleness interval when configured.
|
|
16349
|
+
*/
|
|
16350
|
+
private refreshLiveOraclePrices;
|
|
16344
16351
|
/**
|
|
16345
16352
|
* Receipt mints issued by this vault are liabilities/shares, not underlying
|
|
16346
16353
|
* assets for its own NAV. Exclude them even if they were registered as
|
|
@@ -16358,12 +16365,13 @@ declare class ConsensusOracleService {
|
|
|
16358
16365
|
/** Push the consensus report for all holdings, then settle NAV. */
|
|
16359
16366
|
private settleVault;
|
|
16360
16367
|
/**
|
|
16361
|
-
* Fetch the current
|
|
16362
|
-
*
|
|
16368
|
+
* Fetch the current settlement timestamp and log the APY implied by the
|
|
16369
|
+
* prospective update, so MaxApyExceeded failures are diagnosable.
|
|
16363
16370
|
*
|
|
16364
16371
|
* NAV is expressed in the vault's accounting unit (scaled by assetDecimals).
|
|
16365
|
-
*
|
|
16366
|
-
*
|
|
16372
|
+
* Mirrors `gross_nav_from_holdings`: consensus updates overlay their target
|
|
16373
|
+
* slots, while live-priced and otherwise unchanged holdings retain their
|
|
16374
|
+
* current price/external amount.
|
|
16367
16375
|
*/
|
|
16368
16376
|
private logProspectiveApy;
|
|
16369
16377
|
}
|
package/dist/index.js
CHANGED
|
@@ -1532,7 +1532,8 @@ var PROTOCOL_FEE_RECIPIENT = (0, import_kit.address)(
|
|
|
1532
1532
|
var PROGRAM_FEE_EXEMPT_OWNER_WHITELIST = [
|
|
1533
1533
|
(0, import_kit.address)("Hobbakk1LmW2DhE4nAKnCeU1iK7V7pKHTq3ihaE2i8qG"),
|
|
1534
1534
|
(0, import_kit.address)("7C39GeKrqLygmgV9D4bicUaiyFhKrX6Eevk6PDACwuj7"),
|
|
1535
|
-
(0, import_kit.address)("LYC8YiiSzQfPpxUW2tpxfuPKGZwywAJhXKUfDP2B66f")
|
|
1535
|
+
(0, import_kit.address)("LYC8YiiSzQfPpxUW2tpxfuPKGZwywAJhXKUfDP2B66f"),
|
|
1536
|
+
(0, import_kit.address)("soBBAHNhFyWF4QBCK9BJLVkX6d6NZt37ozNaPV5aq3H")
|
|
1536
1537
|
];
|
|
1537
1538
|
var VAULT_CREATOR_WHITELIST = [
|
|
1538
1539
|
(0, import_kit.address)("4jMRBkpWrfhhwFYTwG3o7hmsXQwbJUwdyUX8htB6KwLJ")
|
|
@@ -10463,11 +10464,11 @@ var IDL = {
|
|
|
10463
10464
|
"name": "vault_oracle",
|
|
10464
10465
|
"docs": [
|
|
10465
10466
|
"Required when enabling tranching organically (`for_migration == false`)",
|
|
10466
|
-
"on a vault that still has main shareholders accruing fixed APY
|
|
10467
|
-
"
|
|
10468
|
-
"
|
|
10469
|
-
"
|
|
10470
|
-
"
|
|
10467
|
+
"on a vault that still has main shareholders accruing fixed APY. A fresh",
|
|
10468
|
+
"settlement establishes the regular sleeve's booked NAV and physical",
|
|
10469
|
+
"surplus immediately before the new tranche sleeve is introduced. Not",
|
|
10470
|
+
"needed on the migration path, whose accounting is reconstructed wholesale",
|
|
10471
|
+
"from the legacy state.",
|
|
10471
10472
|
"",
|
|
10472
10473
|
"Kept LAST (a trailing optional account) so legacy clients that predate it",
|
|
10473
10474
|
"\u2014 e.g. the migration path building against the frozen bankineco IDL \u2014",
|
|
@@ -13406,16 +13407,9 @@ var IDL = {
|
|
|
13406
13407
|
}
|
|
13407
13408
|
},
|
|
13408
13409
|
{
|
|
13409
|
-
"name": "
|
|
13410
|
+
"name": "_padding_bool",
|
|
13410
13411
|
"docs": [
|
|
13411
|
-
"
|
|
13412
|
-
"and receipt-supply accounting. Oracle mutation and NAV settlement stay",
|
|
13413
|
-
"disabled until every configured receipt mint authority has moved to this vault",
|
|
13414
|
-
"and migration finalization clears the flag.",
|
|
13415
|
-
"",
|
|
13416
|
-
"This occupies the former `apy_settle_skip` padding byte, preserving the",
|
|
13417
|
-
"on-chain account layout for already-deployed vaults. Existing vaults read",
|
|
13418
|
-
"it as false."
|
|
13412
|
+
"Reserved byte retained for on-chain account layout compatibility."
|
|
13419
13413
|
],
|
|
13420
13414
|
"type": {
|
|
13421
13415
|
"defined": {
|
|
@@ -19595,6 +19589,7 @@ var MarginfiPositionProvider = class {
|
|
|
19595
19589
|
};
|
|
19596
19590
|
|
|
19597
19591
|
// src/services/consensusOracle/liveConsensusOracleDeps.ts
|
|
19592
|
+
var YIELD_TRACKER_DECIMALS = 6;
|
|
19598
19593
|
function createLiveConsensusOracleDeps(connection, opts = {}) {
|
|
19599
19594
|
const log = opts.log ?? (() => {
|
|
19600
19595
|
});
|
|
@@ -19605,7 +19600,10 @@ function createLiveConsensusOracleDeps(connection, opts = {}) {
|
|
|
19605
19600
|
log
|
|
19606
19601
|
}),
|
|
19607
19602
|
yieldTracker: {
|
|
19608
|
-
getTotalOutstandingYield,
|
|
19603
|
+
async getTotalOutstandingYield(accountNames, start, end) {
|
|
19604
|
+
const raw = await getTotalOutstandingYield(accountNames, start, end);
|
|
19605
|
+
return raw / 10 ** YIELD_TRACKER_DECIMALS;
|
|
19606
|
+
},
|
|
19609
19607
|
getYieldPayments
|
|
19610
19608
|
}
|
|
19611
19609
|
};
|
|
@@ -19744,6 +19742,7 @@ function simulateConsensusOracleSettlement(args) {
|
|
|
19744
19742
|
hwmSharePrice: bigint(vaultAccounting.hwmSharePrice),
|
|
19745
19743
|
fixedApyBps: number(apyConfig.fixedApyBps),
|
|
19746
19744
|
maxApyBps: number(apyConfig.maxApyBps),
|
|
19745
|
+
accruedApyBalance: bigint(apyConfig.accruedApyBalance),
|
|
19747
19746
|
apyAnchorSharePrice: bigint(apyConfig.anchorSharePrice),
|
|
19748
19747
|
apyAnchorTs: bigint(apyConfig.anchorTs),
|
|
19749
19748
|
tranche: currentTranche
|
|
@@ -19763,7 +19762,52 @@ function simulateConsensusOracleSettlement(args) {
|
|
|
19763
19762
|
performanceFees: settlement.performanceFees
|
|
19764
19763
|
};
|
|
19765
19764
|
} catch (error) {
|
|
19766
|
-
|
|
19765
|
+
const blocker = message(error);
|
|
19766
|
+
blockers.push(blocker);
|
|
19767
|
+
if (blocker.includes("MaxApyExceeded")) {
|
|
19768
|
+
try {
|
|
19769
|
+
const settlement = settleVault({
|
|
19770
|
+
grossNav,
|
|
19771
|
+
nowTs: args.nowTs,
|
|
19772
|
+
currentTvl,
|
|
19773
|
+
currentSharePrice: bigint(vaultAccounting.mintSharePrice),
|
|
19774
|
+
totalSupply: reconciledSupply,
|
|
19775
|
+
lossesEnabled: bool(args.vault.lossesEnabled),
|
|
19776
|
+
accountingLastUpdateTs: bigint(vaultAccounting.lastUpdateTs),
|
|
19777
|
+
priorSettledNavTs: oracle.settledNavTs,
|
|
19778
|
+
performanceFeeBps: number(feesConfig.performanceFeeBps),
|
|
19779
|
+
protocolFeeBps: number(feesConfig.protocolFeeBps),
|
|
19780
|
+
hwmSharePrice: bigint(vaultAccounting.hwmSharePrice),
|
|
19781
|
+
fixedApyBps: number(apyConfig.fixedApyBps),
|
|
19782
|
+
maxApyBps: 0,
|
|
19783
|
+
accruedApyBalance: bigint(apyConfig.accruedApyBalance),
|
|
19784
|
+
apyAnchorSharePrice: bigint(apyConfig.anchorSharePrice),
|
|
19785
|
+
apyAnchorTs: bigint(apyConfig.anchorTs),
|
|
19786
|
+
tranche: currentTranche
|
|
19787
|
+
});
|
|
19788
|
+
return {
|
|
19789
|
+
nowTs: args.nowTs,
|
|
19790
|
+
assetDecimals: args.vault.config.assetDecimals,
|
|
19791
|
+
...consensusSummary,
|
|
19792
|
+
canSettle: false,
|
|
19793
|
+
blockers,
|
|
19794
|
+
warnings,
|
|
19795
|
+
consensus,
|
|
19796
|
+
grossNav,
|
|
19797
|
+
settledAccountingNav: settlement.settledAccountingNav,
|
|
19798
|
+
current,
|
|
19799
|
+
projectionRequiresMaxApyDisabled: true,
|
|
19800
|
+
projected: settlement.projected,
|
|
19801
|
+
performanceFees: settlement.performanceFees
|
|
19802
|
+
};
|
|
19803
|
+
} catch (projectionError) {
|
|
19804
|
+
warnings.push(
|
|
19805
|
+
`could not calculate the uncapped projection: ${message(
|
|
19806
|
+
projectionError
|
|
19807
|
+
)}`
|
|
19808
|
+
);
|
|
19809
|
+
}
|
|
19810
|
+
}
|
|
19767
19811
|
return {
|
|
19768
19812
|
nowTs: args.nowTs,
|
|
19769
19813
|
assetDecimals: args.vault.config.assetDecimals,
|
|
@@ -19835,16 +19879,18 @@ function applyConsensus(holdings, entries, nowTs, stalenessThresholdSecs) {
|
|
|
19835
19879
|
return results;
|
|
19836
19880
|
}
|
|
19837
19881
|
function settleVault(args) {
|
|
19838
|
-
|
|
19882
|
+
const regularSurplusBefore = args.tranche ? max(args.accruedApyBalance, 0n) : 0n;
|
|
19883
|
+
const physicalNavBefore = args.currentTvl + regularSurplusBefore;
|
|
19884
|
+
if (args.grossNav < physicalNavBefore && !args.lossesEnabled) {
|
|
19839
19885
|
throw new Error(
|
|
19840
|
-
`crank would fail LossesDisabled: gross NAV ${args.grossNav} <
|
|
19886
|
+
`crank would fail LossesDisabled: gross NAV ${args.grossNav} < physical NAV ${physicalNavBefore}`
|
|
19841
19887
|
);
|
|
19842
19888
|
}
|
|
19843
19889
|
const priorTs = args.priorSettledNavTs > 0n ? args.priorSettledNavTs : max(args.accountingLastUpdateTs, args.apyAnchorTs);
|
|
19844
19890
|
const nextApyValue = args.tranche ? args.grossNav : sharePriceForValue(args.grossNav, args.totalSupply);
|
|
19845
19891
|
if (nextApyValue !== void 0) {
|
|
19846
19892
|
validateRealizedApy(
|
|
19847
|
-
args.tranche ?
|
|
19893
|
+
args.tranche ? physicalNavBefore : args.currentSharePrice,
|
|
19848
19894
|
priorTs,
|
|
19849
19895
|
nextApyValue,
|
|
19850
19896
|
args.nowTs,
|
|
@@ -19857,13 +19903,22 @@ function settleVault(args) {
|
|
|
19857
19903
|
let protocolFee = 0n;
|
|
19858
19904
|
let settledAccountingNav;
|
|
19859
19905
|
if (args.tranche) {
|
|
19860
|
-
|
|
19861
|
-
regularValue = settleTrancheNav(
|
|
19906
|
+
const regularGrossValue = settleTrancheNav(
|
|
19862
19907
|
args.tranche,
|
|
19863
|
-
|
|
19864
|
-
|
|
19908
|
+
physicalNavBefore,
|
|
19909
|
+
args.grossNav,
|
|
19865
19910
|
args.nowTs
|
|
19866
19911
|
);
|
|
19912
|
+
regularValue = settleFixedApyNav({
|
|
19913
|
+
currentSharePrice: args.currentSharePrice,
|
|
19914
|
+
reportedNav: regularGrossValue,
|
|
19915
|
+
nowTs: args.nowTs,
|
|
19916
|
+
totalSupply: args.totalSupply,
|
|
19917
|
+
fixedApyBps: args.fixedApyBps,
|
|
19918
|
+
anchorSharePrice: args.apyAnchorSharePrice,
|
|
19919
|
+
anchorTs: args.apyAnchorTs
|
|
19920
|
+
});
|
|
19921
|
+
settledAccountingNav = regularValue + args.tranche.junior.value + args.tranche.senior.value;
|
|
19867
19922
|
const juniorFees = settleShareClassPerformanceFee(
|
|
19868
19923
|
args.tranche.junior,
|
|
19869
19924
|
args.performanceFeeBps,
|
|
@@ -20031,7 +20086,8 @@ function settleRegularPerformanceFee(grossValue, supply, hwmSharePrice, performa
|
|
|
20031
20086
|
};
|
|
20032
20087
|
}
|
|
20033
20088
|
function settleFixedApyNav(args) {
|
|
20034
|
-
if (args.
|
|
20089
|
+
if (args.totalSupply === 0n) return 0n;
|
|
20090
|
+
if (args.fixedApyBps === 0 || args.currentSharePrice === 0n || args.anchorSharePrice === 0n || args.anchorTs <= 0n) {
|
|
20035
20091
|
return args.reportedNav;
|
|
20036
20092
|
}
|
|
20037
20093
|
if (args.nowTs <= args.anchorTs) {
|
|
@@ -20408,6 +20464,9 @@ function formatSettlementSimulation(simulation) {
|
|
|
20408
20464
|
);
|
|
20409
20465
|
}
|
|
20410
20466
|
if (simulation.projected) {
|
|
20467
|
+
if (simulation.projectionRequiresMaxApyDisabled) {
|
|
20468
|
+
lines.push(" projection with max APY disabled (max_apy_bps=0):");
|
|
20469
|
+
}
|
|
20411
20470
|
lines.push(
|
|
20412
20471
|
` TVL: ${formatTransition(
|
|
20413
20472
|
simulation.current.tvl,
|
|
@@ -20478,8 +20537,23 @@ function formatUnits(value, decimals) {
|
|
|
20478
20537
|
|
|
20479
20538
|
// src/services/consensusOracle/consensusOracleService.ts
|
|
20480
20539
|
var SYSTEM_PROGRAM2 = "11111111111111111111111111111111";
|
|
20540
|
+
var PRICE_ORACLE_TYPES_BY_INDEX2 = [
|
|
20541
|
+
"none",
|
|
20542
|
+
"consensusOracle",
|
|
20543
|
+
"pyth",
|
|
20544
|
+
"perenaVault"
|
|
20545
|
+
];
|
|
20481
20546
|
var CONSENSUS_ORACLE_VARIANT = "consensusoracle";
|
|
20547
|
+
var LIVE_ORACLE_VARIANTS = /* @__PURE__ */ new Set(["pyth", "perenavault"]);
|
|
20548
|
+
var LIVE_PRICE_REFRESH_INTERVAL_SECS = 60n * 60n;
|
|
20549
|
+
var DEFAULT_PRICE_STALENESS_THRESHOLD_SECS3 = 24n * 60n * 60n;
|
|
20482
20550
|
function variantName2(value) {
|
|
20551
|
+
if (typeof value === "number") {
|
|
20552
|
+
return PRICE_ORACLE_TYPES_BY_INDEX2[value] ?? String(value);
|
|
20553
|
+
}
|
|
20554
|
+
if (typeof value === "bigint") {
|
|
20555
|
+
return PRICE_ORACLE_TYPES_BY_INDEX2[Number(value)] ?? String(value);
|
|
20556
|
+
}
|
|
20483
20557
|
if (value && typeof value === "object") return Object.keys(value)[0] ?? "";
|
|
20484
20558
|
return String(value ?? "");
|
|
20485
20559
|
}
|
|
@@ -20489,6 +20563,13 @@ function coerceBool3(value) {
|
|
|
20489
20563
|
const record2 = value;
|
|
20490
20564
|
return Boolean(record2?.value ?? record2?.[0]);
|
|
20491
20565
|
}
|
|
20566
|
+
function toBigInt2(value) {
|
|
20567
|
+
if (typeof value === "bigint") return value;
|
|
20568
|
+
if (typeof value === "number") return BigInt(Math.trunc(value));
|
|
20569
|
+
if (typeof value === "string" && value.length > 0) return BigInt(value);
|
|
20570
|
+
if (value !== null && value !== void 0) return BigInt(String(value));
|
|
20571
|
+
return 0n;
|
|
20572
|
+
}
|
|
20492
20573
|
function priceInAccountingUnit(usd, baseUsd, assetDecimals) {
|
|
20493
20574
|
return BigInt(Math.round(usd / baseUsd * 10 ** assetDecimals));
|
|
20494
20575
|
}
|
|
@@ -20533,7 +20614,17 @@ var ConsensusOracleService = class {
|
|
|
20533
20614
|
const log = opts.log ?? (() => {
|
|
20534
20615
|
});
|
|
20535
20616
|
const dryRun = opts.dryRun ?? false;
|
|
20536
|
-
|
|
20617
|
+
let vaultState = await this.fetchDecodedVault(target.vault);
|
|
20618
|
+
const refreshedLivePrices = await this.refreshLiveOraclePrices(
|
|
20619
|
+
signer,
|
|
20620
|
+
target.vault,
|
|
20621
|
+
vaultState,
|
|
20622
|
+
log,
|
|
20623
|
+
dryRun
|
|
20624
|
+
);
|
|
20625
|
+
if (refreshedLivePrices) {
|
|
20626
|
+
vaultState = await this.fetchDecodedVault(target.vault);
|
|
20627
|
+
}
|
|
20537
20628
|
const receiptMints = await this.fetchReceiptMints(target.vault, vaultState);
|
|
20538
20629
|
const consensusHoldings = selectConsensusHoldings(
|
|
20539
20630
|
vaultState.holdings,
|
|
@@ -20755,6 +20846,64 @@ var ConsensusOracleService = class {
|
|
|
20755
20846
|
fresh: true
|
|
20756
20847
|
});
|
|
20757
20848
|
}
|
|
20849
|
+
/**
|
|
20850
|
+
* Keep active permissionless price sources warm so they cannot block the NAV
|
|
20851
|
+
* crank after the consensus report lands. Prices are refreshed at least once
|
|
20852
|
+
* per hour, or at the vault's shorter staleness interval when configured.
|
|
20853
|
+
*/
|
|
20854
|
+
async refreshLiveOraclePrices(signer, vault, vaultState, log, dryRun) {
|
|
20855
|
+
const nowSecs = BigInt(Math.floor(this.clock.now() / 1e3));
|
|
20856
|
+
const configuredStaleness = toBigInt2(
|
|
20857
|
+
vaultState.config.priceStalenessThresholdSecs
|
|
20858
|
+
);
|
|
20859
|
+
const stalenessThreshold = configuredStaleness > 0n ? configuredStaleness : DEFAULT_PRICE_STALENESS_THRESHOLD_SECS3;
|
|
20860
|
+
const refreshInterval = stalenessThreshold < LIVE_PRICE_REFRESH_INTERVAL_SECS ? stalenessThreshold : LIVE_PRICE_REFRESH_INTERVAL_SECS;
|
|
20861
|
+
const targets = [];
|
|
20862
|
+
for (const holding of vaultState.holdings) {
|
|
20863
|
+
const oracleType = variantName2(holding.priceOracleType).toLowerCase();
|
|
20864
|
+
if (!LIVE_ORACLE_VARIANTS.has(oracleType)) continue;
|
|
20865
|
+
const mint = (0, import_common41.fromWeb3Pk)(holding.mint);
|
|
20866
|
+
if (mint.toString() === SYSTEM_PROGRAM2) continue;
|
|
20867
|
+
const activeAmount = toBigInt2(holding.localAmount) + toBigInt2(holding.externalAmount);
|
|
20868
|
+
if (activeAmount === 0n) continue;
|
|
20869
|
+
const lastUpdateTs = toBigInt2(holding.lastUpdateTs);
|
|
20870
|
+
const ageSecs = nowSecs - lastUpdateTs;
|
|
20871
|
+
if (lastUpdateTs > 0n && ageSecs >= 0n && ageSecs < refreshInterval) {
|
|
20872
|
+
continue;
|
|
20873
|
+
}
|
|
20874
|
+
if (!holding.priceOracleAccount) {
|
|
20875
|
+
log(
|
|
20876
|
+
`vault ${vault}: cannot refresh ${mint}; ${oracleType} holding has no oracle account`
|
|
20877
|
+
);
|
|
20878
|
+
continue;
|
|
20879
|
+
}
|
|
20880
|
+
const oracleAccount = (0, import_common41.fromWeb3Pk)(holding.priceOracleAccount);
|
|
20881
|
+
if (oracleAccount.toString() === SYSTEM_PROGRAM2) {
|
|
20882
|
+
log(
|
|
20883
|
+
`vault ${vault}: cannot refresh ${mint}; ${oracleType} holding has no oracle account`
|
|
20884
|
+
);
|
|
20885
|
+
continue;
|
|
20886
|
+
}
|
|
20887
|
+
targets.push({ mint, oracleAccount });
|
|
20888
|
+
}
|
|
20889
|
+
if (targets.length === 0) return false;
|
|
20890
|
+
if (dryRun) {
|
|
20891
|
+
log(
|
|
20892
|
+
`vault ${vault}: dry run, would refresh ${targets.length} live asset price(s)`
|
|
20893
|
+
);
|
|
20894
|
+
return false;
|
|
20895
|
+
}
|
|
20896
|
+
log(
|
|
20897
|
+
`vault ${vault}: refreshing ${targets.length} live asset price(s) due for hourly refresh`
|
|
20898
|
+
);
|
|
20899
|
+
const results = await this.oracle.refreshAssetPrices(
|
|
20900
|
+
signer,
|
|
20901
|
+
vault,
|
|
20902
|
+
targets,
|
|
20903
|
+
log
|
|
20904
|
+
);
|
|
20905
|
+
return results.some((result) => Boolean(result.signature));
|
|
20906
|
+
}
|
|
20758
20907
|
/**
|
|
20759
20908
|
* Receipt mints issued by this vault are liabilities/shares, not underlying
|
|
20760
20909
|
* assets for its own NAV. Exclude them even if they were registered as
|
|
@@ -20922,12 +21071,13 @@ var ConsensusOracleService = class {
|
|
|
20922
21071
|
};
|
|
20923
21072
|
}
|
|
20924
21073
|
/**
|
|
20925
|
-
* Fetch the current
|
|
20926
|
-
*
|
|
21074
|
+
* Fetch the current settlement timestamp and log the APY implied by the
|
|
21075
|
+
* prospective update, so MaxApyExceeded failures are diagnosable.
|
|
20927
21076
|
*
|
|
20928
21077
|
* NAV is expressed in the vault's accounting unit (scaled by assetDecimals).
|
|
20929
|
-
*
|
|
20930
|
-
*
|
|
21078
|
+
* Mirrors `gross_nav_from_holdings`: consensus updates overlay their target
|
|
21079
|
+
* slots, while live-priced and otherwise unchanged holdings retain their
|
|
21080
|
+
* current price/external amount.
|
|
20931
21081
|
*/
|
|
20932
21082
|
async logProspectiveApy(vault, updates, vaultState, log) {
|
|
20933
21083
|
try {
|
|
@@ -20936,22 +21086,38 @@ var ConsensusOracleService = class {
|
|
|
20936
21086
|
});
|
|
20937
21087
|
const data = oracle.data;
|
|
20938
21088
|
if (!data || data.length < 16) return;
|
|
20939
|
-
const settledNav = readI64LE(data, 0);
|
|
20940
21089
|
const settledNavTs = readI64LE(data, 8);
|
|
20941
|
-
if (
|
|
20942
|
-
const
|
|
20943
|
-
|
|
20944
|
-
|
|
20945
|
-
|
|
20946
|
-
|
|
20947
|
-
|
|
21090
|
+
if (settledNavTs <= 0n) return;
|
|
21091
|
+
const updatesByIndex = new Map(
|
|
21092
|
+
updates.map((update) => [update.holdingIndex, update])
|
|
21093
|
+
);
|
|
21094
|
+
const prospectiveNav = vaultState.holdings.reduce(
|
|
21095
|
+
(sum, holding, holdingIndex) => {
|
|
21096
|
+
const mint = (0, import_common41.fromWeb3Pk)(holding.mint).toString();
|
|
21097
|
+
if (mint === SYSTEM_PROGRAM2) return sum;
|
|
21098
|
+
const update = updatesByIndex.get(holdingIndex);
|
|
21099
|
+
const localAmount = toBigInt2(holding.localAmount);
|
|
21100
|
+
const externalAmount = update ? update.externalAmount : toBigInt2(holding.externalAmount);
|
|
21101
|
+
const amount = localAmount + externalAmount;
|
|
21102
|
+
if (amount === 0n) return sum;
|
|
21103
|
+
const price = update ? update.price : toBigInt2(holding.price);
|
|
21104
|
+
if (price <= 0n) return sum;
|
|
21105
|
+
const scale = 10n ** BigInt(holding.decimals);
|
|
21106
|
+
return sum + amount * price / scale;
|
|
21107
|
+
},
|
|
21108
|
+
0n
|
|
21109
|
+
);
|
|
21110
|
+
const currentTvl = toBigInt2(vaultState.accounting?.tvl);
|
|
21111
|
+
const accruedApyBalance = coerceBool3(vaultState.tranchingEnabled) ? toBigInt2(vaultState.config.apy?.accruedApyBalance) : 0n;
|
|
21112
|
+
const physicalNavBefore = currentTvl + (accruedApyBalance > 0n ? accruedApyBalance : 0n);
|
|
21113
|
+
if (physicalNavBefore <= 0n) return;
|
|
20948
21114
|
const nowSecs = BigInt(Math.floor(this.clock.now() / 1e3));
|
|
20949
21115
|
const elapsedSecs = nowSecs - settledNavTs;
|
|
20950
21116
|
if (elapsedSecs <= 0n) return;
|
|
20951
21117
|
const SECONDS_PER_YEAR3 = 365n * 24n * 3600n;
|
|
20952
|
-
const apyBps = (prospectiveNav -
|
|
21118
|
+
const apyBps = (prospectiveNav - physicalNavBefore) * SECONDS_PER_YEAR3 * 10000n / (physicalNavBefore * elapsedSecs);
|
|
20953
21119
|
log(
|
|
20954
|
-
`vault ${vault}: prospective NAV ${prospectiveNav} vs
|
|
21120
|
+
`vault ${vault}: prospective NAV ${prospectiveNav} vs physical baseline ${physicalNavBefore} (elapsed ${elapsedSecs}s) \u2192 implied APY ${apyBps}bps`
|
|
20955
21121
|
);
|
|
20956
21122
|
} catch {
|
|
20957
21123
|
}
|
|
@@ -21210,7 +21376,7 @@ function parseActiveSlots(externalLiquidity) {
|
|
|
21210
21376
|
}
|
|
21211
21377
|
return results;
|
|
21212
21378
|
}
|
|
21213
|
-
function
|
|
21379
|
+
function toBigInt3(value) {
|
|
21214
21380
|
if (typeof value === "bigint") return value;
|
|
21215
21381
|
if (value !== null && value !== void 0) return BigInt(String(value));
|
|
21216
21382
|
return 0n;
|
|
@@ -21290,7 +21456,7 @@ var ExternalLiquidityIntegrityService = class {
|
|
|
21290
21456
|
}
|
|
21291
21457
|
const holdings = vaultState.holdings.filter((h) => {
|
|
21292
21458
|
const mint = pubkeyStr(h.mint);
|
|
21293
|
-
return !isZeroPubkey2(mint) &&
|
|
21459
|
+
return !isZeroPubkey2(mint) && toBigInt3(h.localAmount) > 0n;
|
|
21294
21460
|
});
|
|
21295
21461
|
if (holdings.length === 0) {
|
|
21296
21462
|
log(`Vault ${vault} slot ${slot.index}: no local balance to deploy`);
|
|
@@ -21299,7 +21465,7 @@ var ExternalLiquidityIntegrityService = class {
|
|
|
21299
21465
|
}
|
|
21300
21466
|
for (const holding of holdings) {
|
|
21301
21467
|
const mintAddress = (0, import_kit15.address)(pubkeyStr(holding.mint));
|
|
21302
|
-
const localAmount =
|
|
21468
|
+
const localAmount = toBigInt3(holding.localAmount);
|
|
21303
21469
|
const marginfiAccounts = (0, import_marginfi2.marginfiAccountsForMint)(mintAddress);
|
|
21304
21470
|
if (!marginfiAccounts) {
|
|
21305
21471
|
log(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@perena/vault-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.25",
|
|
4
4
|
"description": "Vault program helpers for Bankineco integrations.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"create-vault": "tsx scripts/createVault.ts",
|
|
39
39
|
"init-vault-atas": "tsx scripts/initVaultAtas.ts",
|
|
40
40
|
"inspect": "tsx scripts/inspect.ts",
|
|
41
|
+
"vault-data": "tsx scripts/fetchVaultData.ts",
|
|
41
42
|
"simulate-withdraw": "tsx scripts/simulate-withdraw.ts",
|
|
42
43
|
"simulate-message": "tsx scripts/simulate-message.ts",
|
|
43
44
|
"run-external-liquidity-service": "tsx scripts/runExternalLiquidityService.ts"
|
|
@@ -56,6 +57,7 @@
|
|
|
56
57
|
},
|
|
57
58
|
"devDependencies": {
|
|
58
59
|
"common": "file:../common",
|
|
60
|
+
"jupiter": "file:../jupiter",
|
|
59
61
|
"marginfi": "file:../marginfi",
|
|
60
62
|
"@types/node": "^24.3.1",
|
|
61
63
|
"prettier": "^2.6.2",
|