@perena/vault-sdk 1.0.23 → 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.
Files changed (3) hide show
  1. package/dist/index.d.ts +322 -157
  2. package/dist/index.js +1479 -284
  3. package/package.json +5 -1
package/dist/index.js CHANGED
@@ -250,9 +250,9 @@ var require_meta = __commonJS({
250
250
  exports2.toCustomAccountMeta = toCustomAccountMeta;
251
251
  exports2.toCustomAccountMetaFromWeb3AccountMeta = toCustomAccountMetaFromWeb3AccountMeta;
252
252
  var roles_1 = require_roles();
253
- function toCustomAccountMeta(address10, isWritable, requiredAtaDetails, isSigner) {
253
+ function toCustomAccountMeta(address12, isWritable, requiredAtaDetails, isSigner) {
254
254
  const role = (0, roles_1.getAccountRole)(isWritable ?? false, isSigner ?? false);
255
- return { address: address10, role, isRequiredAta: requiredAtaDetails };
255
+ return { address: address12, role, isRequiredAta: requiredAtaDetails };
256
256
  }
257
257
  function toCustomAccountMetaFromWeb3AccountMeta(web3AccountMeta) {
258
258
  return toCustomAccountMeta(web3AccountMeta.pubkey.toBase58(), web3AccountMeta.isWritable, void 0, web3AccountMeta.isSigner);
@@ -611,12 +611,12 @@ var require_transaction = __commonJS({
611
611
  async function sendVersionedTransaction({ connection, payer, instructions: instructions2, lookupTables }) {
612
612
  const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash("confirmed");
613
613
  const lookupTableAccounts = await fetchLookupTables(connection, lookupTables);
614
- const message = new web3_js_1.TransactionMessage({
614
+ const message2 = new web3_js_1.TransactionMessage({
615
615
  payerKey: payer.publicKey,
616
616
  recentBlockhash: blockhash,
617
617
  instructions: instructions2
618
618
  }).compileToV0Message(lookupTableAccounts);
619
- const tx = new web3_js_1.VersionedTransaction(message);
619
+ const tx = new web3_js_1.VersionedTransaction(message2);
620
620
  tx.sign([payer]);
621
621
  const signature = await connection.sendRawTransaction(tx.serialize(), {
622
622
  skipPreflight: true
@@ -1200,8 +1200,8 @@ var require_cpiClient = __commonJS({
1200
1200
  var kit_1 = require("@solana/kit");
1201
1201
  var common_1 = require_dist();
1202
1202
  var constants_1 = require_constants3();
1203
- function account(address10, role) {
1204
- return { address: address10, role };
1203
+ function account(address12, role) {
1204
+ return { address: address12, role };
1205
1205
  }
1206
1206
  function requiredAddress(value, label) {
1207
1207
  if (!value) {
@@ -1290,7 +1290,7 @@ var require_cpiClient = __commonJS({
1290
1290
  });
1291
1291
  const bankAndOracleRemainingAccounts = [
1292
1292
  account(this.accounts.bank, kit_1.AccountRole.READONLY),
1293
- ...this.accounts.oracleAccounts.map((address10) => account(address10, kit_1.AccountRole.READONLY))
1293
+ ...this.accounts.oracleAccounts.map((address12) => account(address12, kit_1.AccountRole.READONLY))
1294
1294
  ];
1295
1295
  return {
1296
1296
  cpiType: params.cpiType ?? common_1.CpiTypes.MARGINFI_WITHDRAW,
@@ -1414,11 +1414,17 @@ __export(index_exports, {
1414
1414
  JupiterPriceSource: () => JupiterPriceSource,
1415
1415
  JupiterSwapBuilder: () => JupiterSwapBuilder,
1416
1416
  KaminoPositionProvider: () => KaminoPositionProvider,
1417
+ LOCAL_PROTOCOL_ADMIN: () => LOCAL_PROTOCOL_ADMIN,
1418
+ LivePriceSource: () => LivePriceSource,
1417
1419
  MAX_PRICE_STALENESS_THRESHOLD_SECS: () => MAX_PRICE_STALENESS_THRESHOLD_SECS,
1418
1420
  ManagerRedepositAssetBuilder: () => ManagerRedepositAssetBuilder,
1419
1421
  ManagerWithdrawAssetBuilder: () => ManagerWithdrawAssetBuilder,
1420
1422
  MarginfiPositionProvider: () => MarginfiPositionProvider,
1421
1423
  MockYieldTracker: () => MockYieldTracker,
1424
+ NEST_API_BASE_URL: () => NEST_API_BASE_URL,
1425
+ NEST_RWA_SHARE_MINT: () => NEST_RWA_SHARE_MINT,
1426
+ NEST_VAULT_SLUG: () => NEST_VAULT_SLUG,
1427
+ NestPriceSource: () => NestPriceSource,
1422
1428
  OracleService: () => OracleService,
1423
1429
  PROGRAM_FEE_EXEMPT_OWNER_WHITELIST: () => PROGRAM_FEE_EXEMPT_OWNER_WHITELIST,
1424
1430
  PROTOCOL_ADMIN: () => PROTOCOL_ADMIN,
@@ -1461,6 +1467,7 @@ __export(index_exports, {
1461
1467
  WithdrawalQueueService: () => WithdrawalQueueService,
1462
1468
  addCashflowUpdate: () => addCashflowUpdate,
1463
1469
  address: () => import_kit10.address,
1470
+ applyEffectiveApy: () => applyEffectiveApy,
1464
1471
  confirmYieldPayment: () => confirmYieldPayment,
1465
1472
  createAccount: () => createAccount,
1466
1473
  createLiveConsensusOracleDeps: () => createLiveConsensusOracleDeps,
@@ -1472,7 +1479,9 @@ __export(index_exports, {
1472
1479
  dateToStr: () => dateToStr,
1473
1480
  defaultKeypairPath: () => defaultKeypairPath,
1474
1481
  deleteAccount: () => deleteAccount,
1482
+ fetchNestTokenPrice: () => fetchNestTokenPrice,
1475
1483
  findSquadsWalletRoute: () => findSquadsWalletRoute,
1484
+ formatSettlementSimulation: () => formatSettlementSimulation,
1476
1485
  fromUiAmount: () => fromUiAmount,
1477
1486
  getAccounts: () => getAccounts,
1478
1487
  getBalance: () => getBalance,
@@ -1493,6 +1502,7 @@ __export(index_exports, {
1493
1502
  resolveSquadsWalletRoute: () => resolveSquadsWalletRoute,
1494
1503
  roundToNextUtcMidnight: () => roundToNextUtcMidnight,
1495
1504
  runLiveConsensusOracle: () => runLiveConsensusOracle,
1505
+ simulateConsensusOracleSettlement: () => simulateConsensusOracleSettlement,
1496
1506
  systemClock: () => systemClock,
1497
1507
  toUiAmount: () => toUiAmount,
1498
1508
  updateAccount: () => updateAccount,
@@ -1511,6 +1521,9 @@ var VAULT_PROGRAM_ID = (0, import_kit.address)(
1511
1521
  );
1512
1522
  var VAULT_PROGRAM_PUBLIC_KEY = new import_web3.PublicKey(VAULT_PROGRAM_ID);
1513
1523
  var PROTOCOL_ADMIN = (0, import_kit.address)(
1524
+ "4jMRBkpWrfhhwFYTwG3o7hmsXQwbJUwdyUX8htB6KwLJ"
1525
+ );
1526
+ var LOCAL_PROTOCOL_ADMIN = (0, import_kit.address)(
1514
1527
  "BQJZAhDnvgSGHjdAbzQEXRBYzSBuWgSYqDzhDnUSRWGR"
1515
1528
  );
1516
1529
  var PROTOCOL_FEE_RECIPIENT = (0, import_kit.address)(
@@ -1519,10 +1532,11 @@ var PROTOCOL_FEE_RECIPIENT = (0, import_kit.address)(
1519
1532
  var PROGRAM_FEE_EXEMPT_OWNER_WHITELIST = [
1520
1533
  (0, import_kit.address)("Hobbakk1LmW2DhE4nAKnCeU1iK7V7pKHTq3ihaE2i8qG"),
1521
1534
  (0, import_kit.address)("7C39GeKrqLygmgV9D4bicUaiyFhKrX6Eevk6PDACwuj7"),
1522
- (0, import_kit.address)("LYC8YiiSzQfPpxUW2tpxfuPKGZwywAJhXKUfDP2B66f")
1535
+ (0, import_kit.address)("LYC8YiiSzQfPpxUW2tpxfuPKGZwywAJhXKUfDP2B66f"),
1536
+ (0, import_kit.address)("soBBAHNhFyWF4QBCK9BJLVkX6d6NZt37ozNaPV5aq3H")
1523
1537
  ];
1524
1538
  var VAULT_CREATOR_WHITELIST = [
1525
- (0, import_kit.address)("11111111111111111111111111111111")
1539
+ (0, import_kit.address)("4jMRBkpWrfhhwFYTwG3o7hmsXQwbJUwdyUX8htB6KwLJ")
1526
1540
  ];
1527
1541
  var TOKEN_PROGRAM_ID = (0, import_kit.address)(
1528
1542
  import_spl_token.TOKEN_2022_PROGRAM_ID.toBase58()
@@ -1768,8 +1782,7 @@ var IDL = {
1768
1782
  {
1769
1783
  "name": "fee_asset_mint",
1770
1784
  "docs": [
1771
- "Base-asset mint used to pay performance fees on settlement. Must match",
1772
- "an `is_base` holding; choose a mint whose vault ATA has liquidity."
1785
+ "Legacy fee-asset account retained in this instruction's account schema."
1773
1786
  ]
1774
1787
  },
1775
1788
  {
@@ -1832,7 +1845,7 @@ var IDL = {
1832
1845
  {
1833
1846
  "name": "fee_vault",
1834
1847
  "docs": [
1835
- "Per-vault fee-vault PDA: token authority + accounting for accrued fees."
1848
+ "Legacy fee-vault account retained in this instruction's account schema."
1836
1849
  ],
1837
1850
  "writable": true
1838
1851
  },
@@ -1926,84 +1939,18 @@ var IDL = {
1926
1939
  "signer": true
1927
1940
  },
1928
1941
  {
1929
- "name": "vault"
1930
- },
1931
- {
1932
- "name": "fee_asset_mint",
1933
- "docs": [
1934
- "Vault base asset whose fee-vault ATA should be swept. Must match an",
1935
- "`is_base` holding with a fresh price."
1936
- ]
1937
- },
1938
- {
1939
- "name": "fee_vault",
1940
- "docs": [
1941
- "Per-vault fee-vault PDA: token authority + accounting for accrued fees."
1942
- ],
1942
+ "name": "vault",
1943
1943
  "writable": true
1944
1944
  },
1945
1945
  {
1946
- "name": "fee_vault_ata",
1947
- "writable": true,
1948
- "pda": {
1949
- "seeds": [
1950
- {
1951
- "kind": "account",
1952
- "path": "fee_vault"
1953
- },
1954
- {
1955
- "kind": "account",
1956
- "path": "fee_asset_token_program"
1957
- },
1958
- {
1959
- "kind": "account",
1960
- "path": "fee_asset_mint"
1961
- }
1962
- ],
1963
- "program": {
1964
- "kind": "const",
1965
- "value": [
1966
- 140,
1967
- 151,
1968
- 37,
1969
- 143,
1970
- 78,
1971
- 36,
1972
- 137,
1973
- 241,
1974
- 187,
1975
- 61,
1976
- 16,
1977
- 41,
1978
- 20,
1979
- 142,
1980
- 13,
1981
- 131,
1982
- 11,
1983
- 90,
1984
- 19,
1985
- 153,
1986
- 218,
1987
- 255,
1988
- 16,
1989
- 132,
1990
- 4,
1991
- 142,
1992
- 123,
1993
- 216,
1994
- 219,
1995
- 233,
1996
- 248,
1997
- 89
1998
- ]
1999
- }
2000
- }
1946
+ "name": "share_mint",
1947
+ "writable": true
2001
1948
  },
2002
1949
  {
2003
1950
  "name": "fee_collector"
2004
1951
  },
2005
1952
  {
2006
- "name": "fee_collector_fee_asset_ata",
1953
+ "name": "fee_collector_share_ata",
2007
1954
  "writable": true,
2008
1955
  "pda": {
2009
1956
  "seeds": [
@@ -2013,11 +1960,11 @@ var IDL = {
2013
1960
  },
2014
1961
  {
2015
1962
  "kind": "account",
2016
- "path": "fee_asset_token_program"
1963
+ "path": "share_token_program"
2017
1964
  },
2018
1965
  {
2019
1966
  "kind": "account",
2020
- "path": "fee_asset_mint"
1967
+ "path": "share_mint"
2021
1968
  }
2022
1969
  ],
2023
1970
  "program": {
@@ -2063,7 +2010,7 @@ var IDL = {
2063
2010
  "name": "protocol_fee_collector"
2064
2011
  },
2065
2012
  {
2066
- "name": "protocol_fee_collector_fee_asset_ata",
2013
+ "name": "protocol_fee_collector_share_ata",
2067
2014
  "writable": true,
2068
2015
  "pda": {
2069
2016
  "seeds": [
@@ -2073,11 +2020,11 @@ var IDL = {
2073
2020
  },
2074
2021
  {
2075
2022
  "kind": "account",
2076
- "path": "fee_asset_token_program"
2023
+ "path": "share_token_program"
2077
2024
  },
2078
2025
  {
2079
2026
  "kind": "account",
2080
- "path": "fee_asset_mint"
2027
+ "path": "share_mint"
2081
2028
  }
2082
2029
  ],
2083
2030
  "program": {
@@ -2120,7 +2067,7 @@ var IDL = {
2120
2067
  }
2121
2068
  },
2122
2069
  {
2123
- "name": "fee_asset_token_program"
2070
+ "name": "share_token_program"
2124
2071
  },
2125
2072
  {
2126
2073
  "name": "associated_token_program",
@@ -3003,7 +2950,9 @@ var IDL = {
3003
2950
  {
3004
2951
  "name": "share_mint",
3005
2952
  "docs": [
3006
- "Curator-supplied share mint. Its decimals must match `asset_mint`.",
2953
+ "Curator-supplied share mint. Its decimals define the vault accounting",
2954
+ "precision. `asset_mint` may use a different precision, which is stored",
2955
+ "on its holding and handled by holding-price conversion.",
3007
2956
  "Unless `for_migration`, it must have zero supply with mint authority on",
3008
2957
  "the curator; authority is transferred to the vault PDA on success.",
3009
2958
  "Migration uses the legacy bank mint and defers transfer."
@@ -3227,8 +3176,8 @@ var IDL = {
3227
3176
  {
3228
3177
  "name": "fee_vault",
3229
3178
  "docs": [
3230
- "Per-vault fee-vault PDA: token authority + accounting. The mint fee",
3231
- "accrues here and is swept to the fee collector by the permissionless crank."
3179
+ "Legacy fee-vault account retained in this instruction's account schema.",
3180
+ "New fees accrue as pending vault shares."
3232
3181
  ],
3233
3182
  "writable": true
3234
3183
  },
@@ -3522,8 +3471,8 @@ var IDL = {
3522
3471
  {
3523
3472
  "name": "fee_vault",
3524
3473
  "docs": [
3525
- "Per-vault fee-vault PDA: token authority + accounting. The mint fee",
3526
- "accrues here and is swept to the fee collector by the permissionless crank."
3474
+ "Legacy fee-vault account retained in this instruction's account schema.",
3475
+ "New fees accrue as pending vault shares."
3527
3476
  ],
3528
3477
  "writable": true
3529
3478
  },
@@ -4018,8 +3967,8 @@ var IDL = {
4018
3967
  {
4019
3968
  "name": "fee_vault",
4020
3969
  "docs": [
4021
- "Per-vault fee-vault PDA: token authority + accounting. Mint fees",
4022
- "accrue here and are swept by the permissionless fee crank."
3970
+ "Legacy fee-vault account retained in this instruction's account schema.",
3971
+ "New fees accrue as pending vault shares."
4023
3972
  ],
4024
3973
  "writable": true
4025
3974
  },
@@ -4320,8 +4269,8 @@ var IDL = {
4320
4269
  {
4321
4270
  "name": "fee_vault",
4322
4271
  "docs": [
4323
- "Per-vault fee-vault PDA: token authority + accounting. Mint fees",
4324
- "accrue here and are swept by the permissionless fee crank."
4272
+ "Legacy fee-vault account retained in this instruction's account schema.",
4273
+ "New fees accrue as pending vault shares."
4325
4274
  ],
4326
4275
  "writable": true
4327
4276
  },
@@ -5111,8 +5060,8 @@ var IDL = {
5111
5060
  {
5112
5061
  "name": "fee_vault",
5113
5062
  "docs": [
5114
- "Per-vault fee-vault PDA: token authority + accounting. The burn fee accrues",
5115
- "here and is swept to the fee collector by the permissionless crank."
5063
+ "Legacy fee-vault account retained in this instruction's account schema.",
5064
+ "New fees accrue as pending vault shares."
5116
5065
  ],
5117
5066
  "writable": true
5118
5067
  },
@@ -5400,8 +5349,8 @@ var IDL = {
5400
5349
  {
5401
5350
  "name": "fee_vault",
5402
5351
  "docs": [
5403
- "Per-vault fee-vault PDA: token authority + accounting. The burn fee accrues",
5404
- "here and is swept to the fee collector by the permissionless crank."
5352
+ "Legacy fee-vault account retained in this instruction's account schema.",
5353
+ "New fees accrue as pending vault shares."
5405
5354
  ],
5406
5355
  "writable": true
5407
5356
  },
@@ -5695,8 +5644,8 @@ var IDL = {
5695
5644
  {
5696
5645
  "name": "fee_vault",
5697
5646
  "docs": [
5698
- "Per-vault fee-vault PDA: token authority + accounting. The burn fee accrues",
5699
- "here and is swept to the fee collector by the permissionless crank."
5647
+ "Legacy fee-vault account retained in this instruction's account schema.",
5648
+ "New fees accrue as pending vault shares."
5700
5649
  ],
5701
5650
  "writable": true
5702
5651
  },
@@ -6000,8 +5949,8 @@ var IDL = {
6000
5949
  {
6001
5950
  "name": "fee_vault",
6002
5951
  "docs": [
6003
- "Per-vault fee-vault PDA: token authority + accounting. The burn fee accrues",
6004
- "here and is swept to the fee collector by the permissionless crank."
5952
+ "Legacy fee-vault account retained in this instruction's account schema.",
5953
+ "New fees accrue as pending vault shares."
6005
5954
  ],
6006
5955
  "writable": true
6007
5956
  },
@@ -7422,9 +7371,6 @@ var IDL = {
7422
7371
  },
7423
7372
  {
7424
7373
  "name": "idle_bank_tranche",
7425
- "docs": [
7426
- "path rejects initialized state because it cannot represent junior claims."
7427
- ],
7428
7374
  "pda": {
7429
7375
  "seeds": [
7430
7376
  {
@@ -7583,7 +7529,7 @@ var IDL = {
7583
7529
  {
7584
7530
  "name": "yielding_bank_tranche",
7585
7531
  "docs": [
7586
- "state so an active senior-price regime cannot be hidden by omission."
7532
+ "tranche was already consumed by its regular vault migrations."
7587
7533
  ],
7588
7534
  "pda": {
7589
7535
  "seeds": [
@@ -7761,9 +7707,6 @@ var IDL = {
7761
7707
  },
7762
7708
  {
7763
7709
  "name": "vault_tranche_state",
7764
- "docs": [
7765
- "be initialized and consistent with destination.tranching_enabled."
7766
- ],
7767
7710
  "writable": true,
7768
7711
  "pda": {
7769
7712
  "seeds": [
@@ -9787,6 +9730,69 @@ var IDL = {
9787
9730
  }
9788
9731
  ]
9789
9732
  },
9733
+ {
9734
+ "name": "trigger_bank_circuit_breaker",
9735
+ "discriminator": [
9736
+ 250,
9737
+ 236,
9738
+ 165,
9739
+ 211,
9740
+ 181,
9741
+ 241,
9742
+ 74,
9743
+ 84
9744
+ ],
9745
+ "accounts": [
9746
+ {
9747
+ "name": "bank_state",
9748
+ "writable": true,
9749
+ "pda": {
9750
+ "seeds": [
9751
+ {
9752
+ "kind": "const",
9753
+ "value": [
9754
+ 66,
9755
+ 65,
9756
+ 78,
9757
+ 75
9758
+ ]
9759
+ },
9760
+ {
9761
+ "kind": "account",
9762
+ "path": "bank_state"
9763
+ }
9764
+ ]
9765
+ }
9766
+ },
9767
+ {
9768
+ "name": "bank_manager",
9769
+ "writable": true,
9770
+ "signer": true
9771
+ },
9772
+ {
9773
+ "name": "system_program",
9774
+ "address": "11111111111111111111111111111111"
9775
+ },
9776
+ {
9777
+ "name": "token_program",
9778
+ "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
9779
+ }
9780
+ ],
9781
+ "args": [
9782
+ {
9783
+ "name": "is_halted",
9784
+ "type": "bool"
9785
+ },
9786
+ {
9787
+ "name": "is_halted_deposit",
9788
+ "type": "bool"
9789
+ },
9790
+ {
9791
+ "name": "is_halted_withdrawal",
9792
+ "type": "bool"
9793
+ }
9794
+ ]
9795
+ },
9790
9796
  {
9791
9797
  "name": "trigger_vault_circuit_breaker",
9792
9798
  "discriminator": [
@@ -10458,11 +10464,11 @@ var IDL = {
10458
10464
  "name": "vault_oracle",
10459
10465
  "docs": [
10460
10466
  "Required when enabling tranching organically (`for_migration == false`)",
10461
- "on a vault that still has main shareholders accruing fixed APY: that APY",
10462
- "is only folded into `mint_share_price` while tranching is off, so a fresh",
10463
- "NAV must have been settled just before the switch or the accrued APY owed",
10464
- "to those shareholders is forfeited. Not needed on the migration path,",
10465
- "whose accounting is reconstructed wholesale from the legacy state.",
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.",
10466
10472
  "",
10467
10473
  "Kept LAST (a trailing optional account) so legacy clients that predate it",
10468
10474
  "\u2014 e.g. the migration path building against the frozen bankineco IDL \u2014",
@@ -10548,8 +10554,8 @@ var IDL = {
10548
10554
  {
10549
10555
  "name": "destination_fee_vault",
10550
10556
  "docs": [
10551
- "Destination vault's fee-vault PDA: the mint fee accrues here and is swept",
10552
- "to the fee collector by the permissionless crank."
10557
+ "Legacy fee-vault account retained in this instruction's account schema.",
10558
+ "New fees accrue as pending vault shares."
10553
10559
  ],
10554
10560
  "writable": true
10555
10561
  },
@@ -11866,21 +11872,11 @@ var IDL = {
11866
11872
  {
11867
11873
  "name": "FeeVault",
11868
11874
  "docs": [
11869
- "Per-vault fee custody + accounting account.",
11870
- "",
11871
- "The PDA is both the token authority over the fee-asset ATAs that physically",
11872
- "hold accrued fees and the ledger of what is owed to whom. All four fee types",
11873
- "\u2014 mint, burn, performance, and protocol \u2014 accrue here.",
11874
- "",
11875
- "Ledger amounts (`*_owed` / `*_collected`) are denominated in the vault's",
11876
- "**accounting unit** (the same unit as TVL / holding prices), not raw token",
11877
- "units. Token custody may still be split across ATAs for different base-asset",
11878
- "mints; the permissionless fee crank converts each ATA's balance to accounting",
11879
- "value via that mint's fresh holding price, pays out, and reduces these",
11880
- "counters by the accounting value cleared.",
11875
+ "Legacy per-vault fee custody account.",
11881
11876
  "",
11882
- "Mint/burn/performance go to `vault.roles.fee_collector`, protocol goes to the",
11883
- "hardcoded protocol fee recipient."
11877
+ "New fee accrual is denominated in canonical vault shares and stored on",
11878
+ "`VaultAccounting`. This account remains in existing instruction schemas for",
11879
+ "compatibility; its historical accounting-unit counters are no longer updated."
11884
11880
  ],
11885
11881
  "serialization": "bytemuck",
11886
11882
  "repr": {
@@ -12105,8 +12101,9 @@ var IDL = {
12105
12101
  {
12106
12102
  "name": "migrate_amount",
12107
12103
  "docs": [
12108
- "Reserved for partial migration; partial migration is unsupported, so this",
12109
- "must be `None` (a full vault migration). A `Some(_)` value is rejected."
12104
+ "Partial migration remains unsupported. `None` performs a regular full",
12105
+ "vault migration; `Some(0)` explicitly marks the final in-scope vault and",
12106
+ "permits bounded terminal source-accounting reconciliation."
12110
12107
  ],
12111
12108
  "type": {
12112
12109
  "option": "u64"
@@ -13412,8 +13409,7 @@ var IDL = {
13412
13409
  {
13413
13410
  "name": "_padding_bool",
13414
13411
  "docs": [
13415
- "Reserved padding (previously `apy_settle_skip`). Kept as a field to",
13416
- "preserve the on-chain account layout for already-deployed vaults."
13412
+ "Reserved byte retained for on-chain account layout compatibility."
13417
13413
  ],
13418
13414
  "type": {
13419
13415
  "defined": {
@@ -13724,7 +13720,7 @@ var IDL = {
13724
13720
  {
13725
13721
  "name": "tvl_limit",
13726
13722
  "docs": [
13727
- "Maximum TVL in deposit-asset base units. `0` disables the cap."
13723
+ "Maximum TVL in vault accounting units. `0` disables the cap."
13728
13724
  ],
13729
13725
  "type": "u64"
13730
13726
  },
@@ -14784,7 +14780,7 @@ var IDL = {
14784
14780
  {
14785
14781
  "name": "total_mint_supply",
14786
14782
  "docs": [
14787
- "Total supply of the vault's share mint."
14783
+ "Effective share supply: physical mint supply plus unminted fee shares."
14788
14784
  ],
14789
14785
  "type": "u64"
14790
14786
  },
@@ -14838,12 +14834,26 @@ var IDL = {
14838
14834
  }
14839
14835
  }
14840
14836
  },
14837
+ {
14838
+ "name": "pending_curator_fee_shares",
14839
+ "docs": [
14840
+ "Vault shares owed to the curator but not yet minted."
14841
+ ],
14842
+ "type": "u64"
14843
+ },
14844
+ {
14845
+ "name": "pending_protocol_fee_shares",
14846
+ "docs": [
14847
+ "Vault shares owed to the protocol but not yet minted."
14848
+ ],
14849
+ "type": "u64"
14850
+ },
14841
14851
  {
14842
14852
  "name": "_padding1",
14843
14853
  "type": {
14844
14854
  "array": [
14845
14855
  "u64",
14846
- 32
14856
+ 30
14847
14857
  ]
14848
14858
  }
14849
14859
  },
@@ -14892,8 +14902,8 @@ var IDL = {
14892
14902
  {
14893
14903
  "name": "asset_decimals",
14894
14904
  "docs": [
14895
- "Decimals of the deposit asset, cached so deposit/withdraw paths don't",
14896
- "need to deserialize the mint account."
14905
+ "Vault accounting precision. This matches the vault share mint; each",
14906
+ "holding stores its own mint precision independently."
14897
14907
  ],
14898
14908
  "type": "u8"
14899
14909
  },
@@ -14925,7 +14935,7 @@ var IDL = {
14925
14935
  {
14926
14936
  "name": "tvl_limit",
14927
14937
  "docs": [
14928
- "Maximum TVL in deposit-asset base units. `0` disables the cap."
14938
+ "Maximum TVL in vault accounting units. `0` disables the cap."
14929
14939
  ],
14930
14940
  "type": "u64"
14931
14941
  },
@@ -15709,8 +15719,8 @@ function coerceBool(value) {
15709
15719
  if (value && typeof value.toBool === "function") {
15710
15720
  return value.toBool();
15711
15721
  }
15712
- const record = value;
15713
- return Boolean(record?.value ?? record?.[0]);
15722
+ const record2 = value;
15723
+ return Boolean(record2?.value ?? record2?.[0]);
15714
15724
  }
15715
15725
 
15716
15726
  // src/client/builders/base.ts
@@ -15746,8 +15756,8 @@ function coerceBool2(value) {
15746
15756
  if (value && typeof value.toBool === "function") {
15747
15757
  return value.toBool();
15748
15758
  }
15749
- const record = value;
15750
- return Boolean(record?.value ?? record?.[0]);
15759
+ const record2 = value;
15760
+ return Boolean(record2?.value ?? record2?.[0]);
15751
15761
  }
15752
15762
  function pubkeyToAddress(value) {
15753
15763
  if (typeof value === "string") return (0, import_kit5.address)(value);
@@ -15757,8 +15767,8 @@ function pubkeyToAddress(value) {
15757
15767
  return (0, import_kit5.address)(String(value));
15758
15768
  }
15759
15769
  function tokenProgramAddressFromIdlEnum(value) {
15760
- const record = value;
15761
- if (record && "token2022" in record) {
15770
+ const record2 = value;
15771
+ if (record2 && "token2022" in record2) {
15762
15772
  return TOKEN_PROGRAM_ID;
15763
15773
  }
15764
15774
  return (0, import_kit5.address)(import_spl_token2.TOKEN_PROGRAM_ID.toBase58());
@@ -16132,7 +16142,7 @@ var CreateVaultBuilder = class extends VaultBuilderBase {
16132
16142
  const [vault] = await this.pda.deriveVaultPda(vaultId);
16133
16143
  const [vaultOracle] = await this.pda.deriveVaultOraclePda(vault);
16134
16144
  const [feeVault] = await this.pda.deriveFeeAuthorityPda(vault);
16135
- const assetDecimals = (await (0, import_token.fetchMint)(this.rpc, txArgs.strictAssetMint)).data.decimals;
16145
+ const accountingDecimals = (await (0, import_token.fetchMint)(this.rpc, txArgs.shareMint)).data.decimals;
16136
16146
  return {
16137
16147
  curator: txArgs.curator,
16138
16148
  vault,
@@ -16157,7 +16167,7 @@ var CreateVaultBuilder = class extends VaultBuilderBase {
16157
16167
  initialMintSharePrice: null,
16158
16168
  basePriceOracleType: txArgs.basePriceOracleType ?? 1,
16159
16169
  basePrice: new import_core3.BN(
16160
- (txArgs.basePrice ?? 10n ** BigInt(assetDecimals)).toString()
16170
+ (txArgs.basePrice ?? 10n ** BigInt(accountingDecimals)).toString()
16161
16171
  ),
16162
16172
  basePriceOracleAccount: txArgs.basePriceOracleAccount ?? DEFAULT_PRICE_ORACLE_ACCOUNT,
16163
16173
  forMigration: txArgs.forMigration ?? false
@@ -16333,8 +16343,9 @@ var SetProtocolFeeBuilder = class extends VaultBuilderBase {
16333
16343
  const [derivedVaultOracle] = await this.pda.deriveVaultOraclePda(
16334
16344
  txArgs.vault
16335
16345
  );
16346
+ const defaultProtocolAuthority = this.program.programId.toBase58() === VAULT_PROGRAM_ID ? LOCAL_PROTOCOL_ADMIN : PROTOCOL_ADMIN;
16336
16347
  return {
16337
- protocolAuthority: txArgs.protocolAuthority ?? PROTOCOL_ADMIN,
16348
+ protocolAuthority: txArgs.protocolAuthority ?? defaultProtocolAuthority,
16338
16349
  vault: txArgs.vault,
16339
16350
  vaultOracle: txArgs.vaultOracle ?? derivedVaultOracle,
16340
16351
  args: {
@@ -16531,82 +16542,53 @@ var CrankPerformanceFeesBuilder = class extends VaultBuilderBase {
16531
16542
  return this.program.methods.crankPerformanceFees().accountsPartial({
16532
16543
  cranker: (0, import_common18.toWeb3Pk)(args.cranker),
16533
16544
  vault: (0, import_common18.toWeb3Pk)(args.vault),
16534
- feeAssetMint: (0, import_common18.toWeb3Pk)(args.feeAssetMint),
16535
- feeVault: (0, import_common18.toWeb3Pk)(args.feeVault),
16536
- feeVaultAta: (0, import_common18.toWeb3Pk)(args.feeVaultAta),
16545
+ shareMint: (0, import_common18.toWeb3Pk)(args.shareMint),
16537
16546
  feeCollector: (0, import_common18.toWeb3Pk)(args.feeCollector),
16538
- feeCollectorFeeAssetAta: (0, import_common18.toWeb3Pk)(args.feeCollectorFeeAssetAta),
16547
+ feeCollectorShareAta: (0, import_common18.toWeb3Pk)(args.feeCollectorShareAta),
16539
16548
  protocolFeeCollector: (0, import_common18.toWeb3Pk)(args.protocolFeeCollector),
16540
- protocolFeeCollectorFeeAssetAta: (0, import_common18.toWeb3Pk)(
16541
- args.protocolFeeCollectorFeeAssetAta
16549
+ protocolFeeCollectorShareAta: (0, import_common18.toWeb3Pk)(
16550
+ args.protocolFeeCollectorShareAta
16542
16551
  ),
16543
- feeAssetTokenProgram: (0, import_common18.toWeb3Pk)(args.feeAssetTokenProgram),
16552
+ shareTokenProgram: (0, import_common18.toWeb3Pk)(args.shareTokenProgram),
16544
16553
  associatedTokenProgram: import_spl_token6.ASSOCIATED_TOKEN_PROGRAM_ID,
16545
16554
  systemProgram: import_web38.SystemProgram.programId
16546
16555
  }).instruction().then(import_common18.toKitInstruction);
16547
16556
  }
16548
16557
  async deriveIxArgs(txArgs) {
16549
- const feeAssetMint = await resolveVaultBaseAssetMint(
16558
+ const vaultState = await this.account.fetchVault(txArgs.vault);
16559
+ const shareMint = (0, import_common18.fromWeb3Pk)(vaultState.mint);
16560
+ const shareTokenProgram = txArgs.shareTokenProgram ?? tokenProgramAddressFromIdlEnum(vaultState.mintTokenProgram);
16561
+ const feeCollector = await resolveVaultFeeCollector(
16550
16562
  this.account,
16551
16563
  txArgs.vault,
16552
- txArgs.feeAssetMint
16564
+ txArgs.feeCollector
16553
16565
  );
16554
- const [feeVault] = await this.pda.deriveFeeAuthorityPda(txArgs.vault);
16555
- const [feeCollector, feeAssetTokenProgram] = await Promise.all([
16556
- resolveVaultFeeCollector(this.account, txArgs.vault, txArgs.feeCollector),
16557
- resolveFeeAssetTokenProgram(
16558
- this.account,
16559
- txArgs.vault,
16560
- feeAssetMint,
16561
- txArgs.feeAssetTokenProgram
16562
- )
16563
- ]);
16564
16566
  const protocolFeeCollector = resolveProtocolFeeRecipient(
16565
16567
  txArgs.protocolFeeCollector
16566
16568
  );
16567
16569
  return {
16568
16570
  cranker: txArgs.cranker,
16569
16571
  vault: txArgs.vault,
16570
- feeAssetMint,
16571
- feeVault,
16572
- feeVaultAta: (0, import_common18.getAtaAddress)(
16573
- feeAssetMint,
16574
- feeVault,
16575
- feeAssetTokenProgram,
16576
- true
16577
- ),
16572
+ shareMint,
16578
16573
  feeCollector,
16579
- feeCollectorFeeAssetAta: (0, import_common18.getAtaAddress)(
16580
- feeAssetMint,
16574
+ feeCollectorShareAta: (0, import_common18.getAtaAddress)(
16575
+ shareMint,
16581
16576
  feeCollector,
16582
- feeAssetTokenProgram
16577
+ shareTokenProgram
16583
16578
  ),
16584
16579
  protocolFeeCollector,
16585
16580
  // The protocol fee recipient is a hardcoded protocol-controlled address
16586
16581
  // that may be off-curve (e.g. a PDA / multisig vault), so allow an
16587
16582
  // off-curve owner when deriving its ATA.
16588
- protocolFeeCollectorFeeAssetAta: (0, import_common18.getAtaAddress)(
16589
- feeAssetMint,
16583
+ protocolFeeCollectorShareAta: (0, import_common18.getAtaAddress)(
16584
+ shareMint,
16590
16585
  protocolFeeCollector,
16591
- feeAssetTokenProgram,
16586
+ shareTokenProgram,
16592
16587
  true
16593
16588
  ),
16594
- feeAssetTokenProgram
16589
+ shareTokenProgram
16595
16590
  };
16596
16591
  }
16597
- async buildPreambleInstructions(args) {
16598
- const payer = (0, import_common18.toWeb3Pk)(args.cranker);
16599
- return [
16600
- (0, import_spl_token6.createAssociatedTokenAccountIdempotentInstruction)(
16601
- payer,
16602
- (0, import_common18.toWeb3Pk)(args.feeVaultAta),
16603
- (0, import_common18.toWeb3Pk)(args.feeVault),
16604
- (0, import_common18.toWeb3Pk)(args.feeAssetMint),
16605
- (0, import_common18.toWeb3Pk)(args.feeAssetTokenProgram),
16606
- import_spl_token6.ASSOCIATED_TOKEN_PROGRAM_ID
16607
- )
16608
- ].map(import_common18.toKitInstruction);
16609
- }
16610
16592
  async buildPlanExtras(args) {
16611
16593
  return {
16612
16594
  postSuccessCacheInvalidations: [{ vaultPda: args.vault }]
@@ -16614,11 +16596,10 @@ var CrankPerformanceFeesBuilder = class extends VaultBuilderBase {
16614
16596
  }
16615
16597
  async getTx(txArgs) {
16616
16598
  const args = await this.deriveIxArgs(txArgs);
16617
- const preambleInstructions = await this.buildPreambleInstructions(args);
16618
16599
  const instruction = await this.getIx(args);
16619
16600
  const extras = await this.buildPlanExtras(args);
16620
16601
  return {
16621
- instructions: [...preambleInstructions, instruction],
16602
+ instructions: [instruction],
16622
16603
  ...extras ?? {}
16623
16604
  };
16624
16605
  }
@@ -19287,6 +19268,70 @@ var JupiterPriceSource = class {
19287
19268
  }
19288
19269
  };
19289
19270
 
19271
+ // src/services/consensusOracle/nestPriceSource.ts
19272
+ var import_kit12 = require("@solana/kit");
19273
+ var NEST_API_BASE_URL = "https://api.nest.credit/v1";
19274
+ var NEST_VAULT_SLUG = "nest-perena-vault";
19275
+ var NEST_RWA_SHARE_MINT = (0, import_kit12.address)(
19276
+ "VyXKJnVhkSkB6KAozSZCiLQbuB1k1r6Nu4etNYVo5LJ"
19277
+ );
19278
+ async function fetchNestTokenPrice(nestVaultSlug = NEST_VAULT_SLUG, opts = {}) {
19279
+ const baseUrl = (opts.baseUrl ?? NEST_API_BASE_URL).replace(/\/$/, "");
19280
+ const response = await (opts.fetchFn ?? fetch)(
19281
+ `${baseUrl}/vaults/${encodeURIComponent(nestVaultSlug)}/price`
19282
+ );
19283
+ if (!response.ok) {
19284
+ throw new Error(
19285
+ `Nest price API request failed: ${response.status} ${response.statusText}`
19286
+ );
19287
+ }
19288
+ const body = await response.json();
19289
+ const price = [body.data?.interpolatedPrice, body.data?.price].map(Number).find((candidate) => Number.isFinite(candidate) && candidate > 0);
19290
+ if (price === void 0) {
19291
+ throw new Error("Nest price API returned no positive share price");
19292
+ }
19293
+ return price;
19294
+ }
19295
+ var NestPriceSource = class {
19296
+ constructor(opts = {}) {
19297
+ this.shareMint = opts.shareMint ?? NEST_RWA_SHARE_MINT;
19298
+ this.nestVaultSlug = opts.nestVaultSlug ?? NEST_VAULT_SLUG;
19299
+ this.options = { baseUrl: opts.baseUrl, fetchFn: opts.fetchFn };
19300
+ }
19301
+ async fetchUsdPrices(mints) {
19302
+ if (!mints.some((mint) => mint.toString() === this.shareMint.toString())) {
19303
+ return {};
19304
+ }
19305
+ return {
19306
+ [this.shareMint.toString()]: await fetchNestTokenPrice(
19307
+ this.nestVaultSlug,
19308
+ this.options
19309
+ )
19310
+ };
19311
+ }
19312
+ };
19313
+
19314
+ // src/services/consensusOracle/livePriceSource.ts
19315
+ var LivePriceSource = class {
19316
+ constructor(opts = {}) {
19317
+ this.jupiter = opts.jupiter ?? new JupiterPriceSource();
19318
+ this.nest = opts.nest ?? new NestPriceSource();
19319
+ }
19320
+ async fetchUsdPrices(mints) {
19321
+ const unique = [
19322
+ ...new Map(mints.map((mint) => [mint.toString(), mint])).values()
19323
+ ];
19324
+ const nestMint = this.nest.shareMint.toString();
19325
+ const jupiterMints = unique.filter((mint) => mint.toString() !== nestMint);
19326
+ const nestMints = unique.filter((mint) => mint.toString() === nestMint);
19327
+ const [jupiterPrices, nestPrices] = await Promise.all([
19328
+ this.jupiter.fetchUsdPrices(jupiterMints),
19329
+ this.nest.fetchUsdPrices(nestMints)
19330
+ ]);
19331
+ return { ...jupiterPrices, ...nestPrices };
19332
+ }
19333
+ };
19334
+
19290
19335
  // src/services/consensusOracle/jupiterBalanceSource.ts
19291
19336
  var import_web323 = require("@solana/web3.js");
19292
19337
  var import_spl_token19 = require("@solana/spl-token");
@@ -19395,12 +19440,12 @@ var StaticPositionProvider = class {
19395
19440
  };
19396
19441
 
19397
19442
  // src/services/consensusOracle/kaminoPositionProvider.ts
19398
- var import_kit12 = require("@solana/kit");
19443
+ var import_kit13 = require("@solana/kit");
19399
19444
  var import_web324 = require("@solana/web3.js");
19400
19445
  var import_klend_sdk = require("@kamino-finance/klend-sdk");
19401
19446
  async function readKaminoBalance(manager, vault, sharesHolder) {
19402
19447
  const userShares = await manager.getUserSharesBalanceSingleVault(
19403
- (0, import_kit12.address)(sharesHolder.toBase58()),
19448
+ (0, import_kit13.address)(sharesHolder.toBase58()),
19404
19449
  vault
19405
19450
  );
19406
19451
  const tokensPerShare = await manager.getTokensPerShareSingleVault(vault);
@@ -19416,7 +19461,7 @@ var KaminoPositionProvider = class {
19416
19461
  async positionsFor(ctx) {
19417
19462
  const refs = ctx.refs.filter((r) => r.kind === "kamino");
19418
19463
  if (refs.length === 0) return [];
19419
- const rpc = (0, import_kit12.createSolanaRpc)(this.rpcUrl);
19464
+ const rpc = (0, import_kit13.createSolanaRpc)(this.rpcUrl);
19420
19465
  const slotDuration = await (0, import_klend_sdk.getMedianSlotDurationInMsFromLastEpochs)();
19421
19466
  const manager = new import_klend_sdk.KaminoManager(rpc, slotDuration);
19422
19467
  const out = [];
@@ -19429,7 +19474,7 @@ var KaminoPositionProvider = class {
19429
19474
  );
19430
19475
  continue;
19431
19476
  }
19432
- const kVault = new import_klend_sdk.KaminoVault(rpc, (0, import_kit12.address)(vaultState));
19477
+ const kVault = new import_klend_sdk.KaminoVault(rpc, (0, import_kit13.address)(vaultState));
19433
19478
  const amount = await readKaminoBalance(
19434
19479
  manager,
19435
19480
  kVault,
@@ -19544,17 +19589,21 @@ var MarginfiPositionProvider = class {
19544
19589
  };
19545
19590
 
19546
19591
  // src/services/consensusOracle/liveConsensusOracleDeps.ts
19592
+ var YIELD_TRACKER_DECIMALS = 6;
19547
19593
  function createLiveConsensusOracleDeps(connection, opts = {}) {
19548
19594
  const log = opts.log ?? (() => {
19549
19595
  });
19550
19596
  const deps = {
19551
- priceSource: new JupiterPriceSource(),
19597
+ priceSource: new LivePriceSource(),
19552
19598
  balanceSource: new JupiterBalanceSource(connection, {
19553
19599
  rpcOnly: opts.rpcOnly ?? false,
19554
19600
  log
19555
19601
  }),
19556
19602
  yieldTracker: {
19557
- getTotalOutstandingYield,
19603
+ async getTotalOutstandingYield(accountNames, start, end) {
19604
+ const raw = await getTotalOutstandingYield(accountNames, start, end);
19605
+ return raw / 10 ** YIELD_TRACKER_DECIMALS;
19606
+ },
19558
19607
  getYieldPayments
19559
19608
  }
19560
19609
  };
@@ -19571,41 +19620,977 @@ function createLiveConsensusOracleDeps(connection, opts = {}) {
19571
19620
  }
19572
19621
 
19573
19622
  // src/services/consensusOracle/consensusOracleService.ts
19574
- var import_web326 = require("@solana/web3.js");
19623
+ var import_web327 = require("@solana/web3.js");
19575
19624
  var import_common41 = __toESM(require_dist());
19576
- var SYSTEM_PROGRAM2 = "11111111111111111111111111111111";
19577
- var CONSENSUS_ORACLE_VARIANT = "consensusoracle";
19578
- function variantName2(value) {
19579
- if (value && typeof value === "object") return Object.keys(value)[0] ?? "";
19580
- return String(value ?? "");
19581
- }
19582
- function coerceBool3(value) {
19583
- if (typeof value === "boolean") return value;
19584
- if (typeof value === "number") return value !== 0;
19585
- const record = value;
19586
- return Boolean(record?.value ?? record?.[0]);
19587
- }
19588
- function priceInAccountingUnit(usd, baseUsd, assetDecimals) {
19589
- return BigInt(Math.round(usd / baseUsd * 10 ** assetDecimals));
19590
- }
19591
- function sumPositionsByMint(positions) {
19592
- const byMint = /* @__PURE__ */ new Map();
19593
- for (const pos of positions) {
19594
- const key = pos.mint.toString();
19595
- byMint.set(key, (byMint.get(key) ?? 0n) + pos.amount);
19625
+
19626
+ // src/services/consensusOracle/settlementSimulator.ts
19627
+ var import_kit14 = require("@solana/kit");
19628
+ var import_web326 = require("@solana/web3.js");
19629
+ var DEFAULT_SHARE_PRICE = 1000000n;
19630
+ var BPS_DENOMINATOR2 = 10000n;
19631
+ var SECONDS_PER_YEAR = 31536000n;
19632
+ var DEFAULT_STALENESS_SECS = 86400n;
19633
+ var CONSENSUS_MAX_DIFF_BPS = 8n;
19634
+ var MIN_CONSENSUS_SIGNERS = 2;
19635
+ var MAX_HOLDINGS = 12;
19636
+ var MAX_SIGNERS = 4;
19637
+ var ENTRY_SIZE = 272;
19638
+ var ENTRIES_OFFSET = 40;
19639
+ var PRICE_OFFSET = 40;
19640
+ var EXTERNAL_OFFSET = PRICE_OFFSET + MAX_HOLDINGS * 8;
19641
+ var APY_SCALE = 1000000000000000000n;
19642
+ var APY_LN_2 = 693147180559945309n;
19643
+ var APY_SERIES_TERMS = 32;
19644
+ function simulateConsensusOracleSettlement(args) {
19645
+ const warnings = [];
19646
+ const blockers = [];
19647
+ const vaultAccounting = record(args.vault.accounting);
19648
+ const feesConfig = record(args.vault.config.fees);
19649
+ const apyConfig = record(args.vault.config.apy);
19650
+ const tranchingEnabled = bool(args.vault.tranchingEnabled);
19651
+ const currentTvl = bigint(vaultAccounting.tvl);
19652
+ const pendingCuratorShares = bigint(vaultAccounting.pendingCuratorFeeShares);
19653
+ const pendingProtocolShares = bigint(
19654
+ vaultAccounting.pendingProtocolFeeShares
19655
+ );
19656
+ const cachedSupply = bigint(vaultAccounting.totalMintSupply);
19657
+ const reconciledSupply = args.physicalShareSupply === void 0 ? cachedSupply : args.physicalShareSupply + pendingCuratorShares + pendingProtocolShares;
19658
+ if (args.physicalShareSupply === void 0) {
19659
+ warnings.push(
19660
+ "regular share-mint supply was unavailable; using cached effective supply"
19661
+ );
19662
+ } else if (reconciledSupply !== cachedSupply) {
19663
+ warnings.push(
19664
+ `crank supply reconciliation changes regular supply ${cachedSupply} -> ${reconciledSupply}`
19665
+ );
19596
19666
  }
19597
- return byMint;
19598
- }
19599
- function parseExternalLiquidityRefs(vaultState) {
19600
- const slots = vaultState.externalLiquidity;
19601
- if (!Array.isArray(slots)) return [];
19602
- const refs = [];
19603
- for (const slot of slots) {
19604
- const data = slot?.data;
19605
- if (!data || data.length < 40) continue;
19606
- if (data[0] !== 1) continue;
19667
+ const holdings = args.vault.holdings.map(decodeHolding);
19668
+ const oracle = decodeOracleData(args.oracleData, args.nowTs);
19669
+ const signerEntry = oracle.entries.find(
19670
+ (entry) => entry.signer === args.signer.toString()
19671
+ );
19672
+ if (!signerEntry) {
19673
+ blockers.push(`signer ${args.signer} is not registered in the oracle`);
19674
+ } else {
19675
+ signerEntry.lastUpdateTs = args.nowTs;
19676
+ for (const update of args.updates) {
19677
+ signerEntry.prices[update.holdingIndex] = update.price;
19678
+ signerEntry.externalAmounts[update.holdingIndex] = update.externalAmount;
19679
+ }
19680
+ }
19681
+ const consensus = applyConsensus(
19682
+ holdings,
19683
+ oracle.entries,
19684
+ args.nowTs,
19685
+ oracle.stalenessThresholdSecs
19686
+ );
19687
+ const proposedUpdatesLanded = args.updates.filter(
19688
+ (update) => consensus.find(
19689
+ (result) => result.holdingIndex === update.holdingIndex && result.wouldUpdateHolding
19690
+ )
19691
+ ).length;
19692
+ const consensusSummary = {
19693
+ allProposedUpdatesLand: proposedUpdatesLanded === args.updates.length && args.updates.length > 0,
19694
+ proposedUpdatesLanded,
19695
+ proposedUpdatesTotal: args.updates.length
19696
+ };
19697
+ const currentTranche = tranchingEnabled ? decodeTrancheState(args.trancheState, args, warnings, blockers) : void 0;
19698
+ const current = snapshot(
19699
+ currentTvl,
19700
+ reconciledSupply,
19701
+ bigint(vaultAccounting.mintSharePrice),
19702
+ currentTranche
19703
+ );
19704
+ let grossNav;
19705
+ try {
19706
+ grossNav = grossNavFromHoldings(
19707
+ holdings,
19708
+ args.nowTs,
19709
+ positiveOrDefault(
19710
+ bigint(args.vault.config.priceStalenessThresholdSecs),
19711
+ DEFAULT_STALENESS_SECS
19712
+ )
19713
+ );
19714
+ } catch (error) {
19715
+ blockers.push(message(error));
19716
+ }
19717
+ if (grossNav === void 0 || blockers.length > 0) {
19718
+ return {
19719
+ nowTs: args.nowTs,
19720
+ assetDecimals: args.vault.config.assetDecimals,
19721
+ ...consensusSummary,
19722
+ canSettle: false,
19723
+ blockers,
19724
+ warnings,
19725
+ consensus,
19726
+ grossNav,
19727
+ current
19728
+ };
19729
+ }
19730
+ try {
19731
+ const settlement = settleVault({
19732
+ grossNav,
19733
+ nowTs: args.nowTs,
19734
+ currentTvl,
19735
+ currentSharePrice: bigint(vaultAccounting.mintSharePrice),
19736
+ totalSupply: reconciledSupply,
19737
+ lossesEnabled: bool(args.vault.lossesEnabled),
19738
+ accountingLastUpdateTs: bigint(vaultAccounting.lastUpdateTs),
19739
+ priorSettledNavTs: oracle.settledNavTs,
19740
+ performanceFeeBps: number(feesConfig.performanceFeeBps),
19741
+ protocolFeeBps: number(feesConfig.protocolFeeBps),
19742
+ hwmSharePrice: bigint(vaultAccounting.hwmSharePrice),
19743
+ fixedApyBps: number(apyConfig.fixedApyBps),
19744
+ maxApyBps: number(apyConfig.maxApyBps),
19745
+ accruedApyBalance: bigint(apyConfig.accruedApyBalance),
19746
+ apyAnchorSharePrice: bigint(apyConfig.anchorSharePrice),
19747
+ apyAnchorTs: bigint(apyConfig.anchorTs),
19748
+ tranche: currentTranche
19749
+ });
19750
+ return {
19751
+ nowTs: args.nowTs,
19752
+ assetDecimals: args.vault.config.assetDecimals,
19753
+ ...consensusSummary,
19754
+ canSettle: true,
19755
+ blockers,
19756
+ warnings,
19757
+ consensus,
19758
+ grossNav,
19759
+ settledAccountingNav: settlement.settledAccountingNav,
19760
+ current,
19761
+ projected: settlement.projected,
19762
+ performanceFees: settlement.performanceFees
19763
+ };
19764
+ } catch (error) {
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
+ }
19811
+ return {
19812
+ nowTs: args.nowTs,
19813
+ assetDecimals: args.vault.config.assetDecimals,
19814
+ ...consensusSummary,
19815
+ canSettle: false,
19816
+ blockers,
19817
+ warnings,
19818
+ consensus,
19819
+ grossNav,
19820
+ current
19821
+ };
19822
+ }
19823
+ }
19824
+ function applyConsensus(holdings, entries, nowTs, stalenessThresholdSecs) {
19825
+ const results = [];
19826
+ for (let index = 0; index < holdings.length; index += 1) {
19827
+ const holding = holdings[index];
19828
+ if (holding.cleared || holding.priceOracleType !== "consensusoracle") {
19829
+ continue;
19830
+ }
19831
+ const reports = entries.filter(
19832
+ (entry) => entry.signer !== import_web326.PublicKey.default.toBase58() && isFresh(entry.lastUpdateTs, nowTs, stalenessThresholdSecs) && (entry.prices[index] ?? 0n) > 0n
19833
+ );
19834
+ const prices = reports.map((entry) => entry.prices[index] ?? 0n);
19835
+ const amounts = reports.map((entry) => entry.externalAmounts[index] ?? 0n);
19836
+ let reason;
19837
+ if (reports.length < MIN_CONSENSUS_SIGNERS) {
19838
+ reason = `only ${reports.length} fresh priced report(s); need ${MIN_CONSENSUS_SIGNERS}`;
19839
+ } else if (!withinDeviation(prices)) {
19840
+ reason = `price reports exceed ${CONSENSUS_MAX_DIFF_BPS}bps pairwise deviation`;
19841
+ } else if (!withinDeviation(amounts)) {
19842
+ reason = `external-amount reports exceed ${CONSENSUS_MAX_DIFF_BPS}bps pairwise deviation`;
19843
+ }
19844
+ if (reason) {
19845
+ results.push({
19846
+ holdingIndex: index,
19847
+ mint: holding.mint,
19848
+ reportCount: reports.length,
19849
+ wouldUpdateHolding: false,
19850
+ reason
19851
+ });
19852
+ continue;
19853
+ }
19854
+ const settledPrice = average(prices);
19855
+ const settledExternalAmount = average(amounts);
19856
+ const latestTs = reports.reduce(
19857
+ (latest, entry) => entry.lastUpdateTs > latest ? entry.lastUpdateTs : latest,
19858
+ 0n
19859
+ );
19860
+ const wouldUpdateHolding = latestTs > holding.lastUpdateTs && latestTs <= nowTs;
19861
+ if (wouldUpdateHolding) {
19862
+ holding.price = settledPrice;
19863
+ holding.externalAmount = settledExternalAmount;
19864
+ holding.lastUpdateTs = latestTs;
19865
+ if (!holding.isBase && holding.localAmount === 0n && holding.externalAmount === 0n) {
19866
+ holding.cleared = true;
19867
+ }
19868
+ }
19869
+ results.push({
19870
+ holdingIndex: index,
19871
+ mint: holding.mint,
19872
+ reportCount: reports.length,
19873
+ settledPrice,
19874
+ settledExternalAmount,
19875
+ wouldUpdateHolding,
19876
+ reason: wouldUpdateHolding ? void 0 : `aggregate timestamp ${latestTs} is not newer than holding timestamp ${holding.lastUpdateTs}`
19877
+ });
19878
+ }
19879
+ return results;
19880
+ }
19881
+ function settleVault(args) {
19882
+ const regularSurplusBefore = args.tranche ? max(args.accruedApyBalance, 0n) : 0n;
19883
+ const physicalNavBefore = args.currentTvl + regularSurplusBefore;
19884
+ if (args.grossNav < physicalNavBefore && !args.lossesEnabled) {
19885
+ throw new Error(
19886
+ `crank would fail LossesDisabled: gross NAV ${args.grossNav} < physical NAV ${physicalNavBefore}`
19887
+ );
19888
+ }
19889
+ const priorTs = args.priorSettledNavTs > 0n ? args.priorSettledNavTs : max(args.accountingLastUpdateTs, args.apyAnchorTs);
19890
+ const nextApyValue = args.tranche ? args.grossNav : sharePriceForValue(args.grossNav, args.totalSupply);
19891
+ if (nextApyValue !== void 0) {
19892
+ validateRealizedApy(
19893
+ args.tranche ? physicalNavBefore : args.currentSharePrice,
19894
+ priorTs,
19895
+ nextApyValue,
19896
+ args.nowTs,
19897
+ args.maxApyBps
19898
+ );
19899
+ }
19900
+ let regularValue;
19901
+ let regularSharePrice = args.currentSharePrice;
19902
+ let curatorFee = 0n;
19903
+ let protocolFee = 0n;
19904
+ let settledAccountingNav;
19905
+ if (args.tranche) {
19906
+ const regularGrossValue = settleTrancheNav(
19907
+ args.tranche,
19908
+ physicalNavBefore,
19909
+ args.grossNav,
19910
+ args.nowTs
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;
19922
+ const juniorFees = settleShareClassPerformanceFee(
19923
+ args.tranche.junior,
19924
+ args.performanceFeeBps,
19925
+ args.protocolFeeBps
19926
+ );
19927
+ const regularFees = settleRegularPerformanceFee(
19928
+ regularValue,
19929
+ args.totalSupply,
19930
+ args.hwmSharePrice,
19931
+ args.performanceFeeBps,
19932
+ args.protocolFeeBps
19933
+ );
19934
+ regularValue = regularFees.netValue;
19935
+ regularSharePrice = sharePriceForValue(regularValue, args.totalSupply) ?? regularSharePrice;
19936
+ curatorFee = juniorFees.curator + regularFees.fees.curator;
19937
+ protocolFee = juniorFees.protocol + regularFees.fees.protocol;
19938
+ } else {
19939
+ settledAccountingNav = settleFixedApyNav({
19940
+ currentSharePrice: args.currentSharePrice,
19941
+ reportedNav: args.grossNav,
19942
+ nowTs: args.nowTs,
19943
+ totalSupply: args.totalSupply,
19944
+ fixedApyBps: args.fixedApyBps,
19945
+ anchorSharePrice: args.apyAnchorSharePrice,
19946
+ anchorTs: args.apyAnchorTs
19947
+ });
19948
+ const regularFees = settleRegularPerformanceFee(
19949
+ settledAccountingNav,
19950
+ args.totalSupply,
19951
+ args.hwmSharePrice,
19952
+ args.performanceFeeBps,
19953
+ args.protocolFeeBps
19954
+ );
19955
+ regularValue = regularFees.netValue;
19956
+ regularSharePrice = sharePriceForValue(regularValue, args.totalSupply) ?? regularSharePrice;
19957
+ curatorFee = regularFees.fees.curator;
19958
+ protocolFee = regularFees.fees.protocol;
19959
+ }
19960
+ const totalFees = curatorFee + protocolFee;
19961
+ const netRegularBacking = mulDiv2(
19962
+ args.totalSupply,
19963
+ regularSharePrice,
19964
+ DEFAULT_SHARE_PRICE
19965
+ );
19966
+ let mintedShares = 0n;
19967
+ if (totalFees > 0n) {
19968
+ mintedShares = args.totalSupply === 0n ? totalFees : mulDiv2(totalFees, args.totalSupply, netRegularBacking);
19969
+ if (mintedShares === 0n) {
19970
+ throw new Error(
19971
+ "crank would fail ZeroAssetAmount while accruing fee shares"
19972
+ );
19973
+ }
19974
+ }
19975
+ const projectedSupply = args.totalSupply + mintedShares;
19976
+ const projected = snapshot(
19977
+ // Fee value is carved out, then added back when converted to vault shares.
19978
+ settledAccountingNav,
19979
+ projectedSupply,
19980
+ regularSharePrice,
19981
+ args.tranche
19982
+ );
19983
+ return {
19984
+ settledAccountingNav,
19985
+ projected,
19986
+ performanceFees: {
19987
+ curator: curatorFee,
19988
+ protocol: protocolFee,
19989
+ total: totalFees,
19990
+ mintedShares
19991
+ }
19992
+ };
19993
+ }
19994
+ function settleTrancheNav(tranche, currentTvl, nextTvl, nowTs) {
19995
+ const before = tranche.junior.value + tranche.senior.value;
19996
+ if (before === 0n) {
19997
+ tranche.lastSettledTs = nowTs;
19998
+ return nextTvl;
19999
+ }
20000
+ if (currentTvl <= 0n || before > currentTvl) {
20001
+ throw new Error(
20002
+ `crank would fail InvalidArgument: tranche value ${before}, current TVL ${currentTvl}`
20003
+ );
20004
+ }
20005
+ const after = mulDiv2(before, nextTvl, currentTvl);
20006
+ if (after >= before) {
20007
+ let gain = after - before;
20008
+ const entitlement = seniorEntitlement(tranche, nowTs);
20009
+ const seniorGain = entitlement < gain ? entitlement : gain;
20010
+ tranche.senior.value += seniorGain;
20011
+ gain -= seniorGain;
20012
+ if (gain > 0n) {
20013
+ if (tranche.junior.totalSupply > 0n) tranche.junior.value += gain;
20014
+ else tranche.senior.value += gain;
20015
+ }
20016
+ } else {
20017
+ const loss = before - after;
20018
+ const juniorLoss = tranche.junior.value < loss ? tranche.junior.value : loss;
20019
+ tranche.junior.value -= juniorLoss;
20020
+ tranche.senior.value -= loss - juniorLoss;
20021
+ }
20022
+ refreshShareClass(tranche.junior);
20023
+ refreshShareClass(tranche.senior);
20024
+ tranche.lastSettledTs = nowTs;
20025
+ return nextTvl - tranche.junior.value - tranche.senior.value;
20026
+ }
20027
+ function seniorEntitlement(tranche, nowTs) {
20028
+ if (tranche.seniorApyAnchorTs <= 0n) {
20029
+ tranche.seniorApyAnchorSharePrice = tranche.senior.sharePrice;
20030
+ tranche.seniorApyAnchorTs = tranche.lastSettledTs > 0n ? tranche.lastSettledTs : nowTs;
20031
+ }
20032
+ if (tranche.seniorFixedApyBps === 0 || tranche.seniorApyAnchorSharePrice === 0n || tranche.senior.totalSupply === 0n || tranche.seniorApyAnchorTs <= 0n || nowTs <= tranche.seniorApyAnchorTs) {
20033
+ return 0n;
20034
+ }
20035
+ const targetPrice = applyEffectiveApy(
20036
+ tranche.seniorApyAnchorSharePrice,
20037
+ tranche.seniorFixedApyBps,
20038
+ nowTs - tranche.seniorApyAnchorTs
20039
+ );
20040
+ const targetValue = mulDiv2(
20041
+ targetPrice,
20042
+ tranche.senior.totalSupply,
20043
+ DEFAULT_SHARE_PRICE
20044
+ );
20045
+ return targetValue > tranche.senior.value ? targetValue - tranche.senior.value : 0n;
20046
+ }
20047
+ function settleShareClassPerformanceFee(shareClass, performanceFeeBps, protocolFeeBps) {
20048
+ refreshShareClass(shareClass);
20049
+ const grossPrice = shareClass.sharePrice;
20050
+ if (shareClass.hwmSharePrice === 0n) {
20051
+ shareClass.hwmSharePrice = grossPrice;
20052
+ return noFees();
20053
+ }
20054
+ if (performanceFeeBps + protocolFeeBps === 0 || shareClass.totalSupply === 0n || grossPrice <= shareClass.hwmSharePrice) {
20055
+ return noFees();
20056
+ }
20057
+ const gainValue = mulDiv2(
20058
+ grossPrice - shareClass.hwmSharePrice,
20059
+ shareClass.totalSupply,
20060
+ DEFAULT_SHARE_PRICE
20061
+ );
20062
+ const fees = feeAmounts(gainValue, performanceFeeBps, protocolFeeBps);
20063
+ const total = fees.curator + fees.protocol;
20064
+ if (total > 0n) {
20065
+ shareClass.value -= total;
20066
+ refreshShareClass(shareClass);
20067
+ shareClass.hwmSharePrice = shareClass.sharePrice;
20068
+ }
20069
+ return fees;
20070
+ }
20071
+ function settleRegularPerformanceFee(grossValue, supply, hwmSharePrice, performanceFeeBps, protocolFeeBps) {
20072
+ if (supply === 0n) return { netValue: grossValue, fees: noFees() };
20073
+ const grossPrice = sharePriceForValue(grossValue, supply);
20074
+ if (hwmSharePrice === 0n || grossPrice <= hwmSharePrice || performanceFeeBps + protocolFeeBps === 0) {
20075
+ return { netValue: grossValue, fees: noFees() };
20076
+ }
20077
+ const gainValue = mulDiv2(
20078
+ supply,
20079
+ grossPrice - hwmSharePrice,
20080
+ DEFAULT_SHARE_PRICE
20081
+ );
20082
+ const fees = feeAmounts(gainValue, performanceFeeBps, protocolFeeBps);
20083
+ return {
20084
+ netValue: grossValue - fees.curator - fees.protocol,
20085
+ fees
20086
+ };
20087
+ }
20088
+ function settleFixedApyNav(args) {
20089
+ if (args.totalSupply === 0n) return 0n;
20090
+ if (args.fixedApyBps === 0 || args.currentSharePrice === 0n || args.anchorSharePrice === 0n || args.anchorTs <= 0n) {
20091
+ return args.reportedNav;
20092
+ }
20093
+ if (args.nowTs <= args.anchorTs) {
20094
+ const anchoredNav = mulDiv2(
20095
+ args.totalSupply,
20096
+ args.anchorSharePrice,
20097
+ DEFAULT_SHARE_PRICE
20098
+ );
20099
+ if (args.reportedNav !== anchoredNav) {
20100
+ throw new Error("crank would fail InvalidOracleUpdate at APY anchor");
20101
+ }
20102
+ return args.reportedNav;
20103
+ }
20104
+ const fixedPrice = applyEffectiveApy(
20105
+ args.anchorSharePrice,
20106
+ args.fixedApyBps,
20107
+ args.nowTs - args.anchorTs
20108
+ );
20109
+ const fixedNav = mulDiv2(args.totalSupply, fixedPrice, DEFAULT_SHARE_PRICE);
20110
+ return args.reportedNav >= fixedNav ? fixedNav : args.reportedNav;
20111
+ }
20112
+ function validateRealizedApy(priorValue, priorTs, nextValue, nextTs, maxApyBps) {
20113
+ if (maxApyBps === 0 || priorValue === 0n || priorTs <= 0n || nextValue <= priorValue) {
20114
+ return;
20115
+ }
20116
+ const elapsed = nextTs - priorTs;
20117
+ if (elapsed <= 0n) throw new Error("crank would fail MaxApyExceeded");
20118
+ const lhs = (nextValue - priorValue) * BPS_DENOMINATOR2 * SECONDS_PER_YEAR;
20119
+ const rhs = BigInt(maxApyBps) * priorValue * elapsed;
20120
+ if (lhs > rhs) {
20121
+ const implied = lhs / (priorValue * elapsed);
20122
+ throw new Error(
20123
+ `crank would fail MaxApyExceeded: implied ${implied}bps > ${maxApyBps}bps`
20124
+ );
20125
+ }
20126
+ }
20127
+ function grossNavFromHoldings(holdings, nowTs, stalenessSecs) {
20128
+ let nav = 0n;
20129
+ for (const holding of holdings) {
20130
+ if (holding.cleared) continue;
20131
+ const amount = holding.localAmount + holding.externalAmount;
20132
+ if (amount === 0n && !holding.isBase) continue;
20133
+ if (holding.price <= 0n) {
20134
+ throw new Error(`crank blocked: holding ${holding.mint} is unpriced`);
20135
+ }
20136
+ if (holding.priceOracleType !== "none" && (holding.lastUpdateTs <= 0n || nowTs - holding.lastUpdateTs < 0n || nowTs - holding.lastUpdateTs > stalenessSecs)) {
20137
+ throw new Error(
20138
+ `crank blocked: holding ${holding.mint} price is stale (ts=${holding.lastUpdateTs})`
20139
+ );
20140
+ }
20141
+ if (amount > 0n) {
20142
+ nav += mulDiv2(amount, holding.price, 10n ** BigInt(holding.decimals));
20143
+ }
20144
+ }
20145
+ return nav;
20146
+ }
20147
+ function decodeOracleData(data, nowTs) {
20148
+ if (data.length < ENTRIES_OFFSET + MAX_SIGNERS * ENTRY_SIZE) {
20149
+ throw new Error(`oracle payload is too short (${data.length} bytes)`);
20150
+ }
20151
+ const entries = [];
20152
+ for (let signerIndex = 0; signerIndex < MAX_SIGNERS; signerIndex += 1) {
20153
+ const offset = ENTRIES_OFFSET + signerIndex * ENTRY_SIZE;
20154
+ const signerBytes = new Uint8Array(data.slice(offset, offset + 32));
20155
+ const prices = Array.from(
20156
+ { length: MAX_HOLDINGS },
20157
+ (_, holdingIndex) => readU64(data, offset + PRICE_OFFSET + holdingIndex * 8)
20158
+ );
20159
+ const externalAmounts = Array.from(
20160
+ { length: MAX_HOLDINGS },
20161
+ (_, holdingIndex) => readU64(data, offset + EXTERNAL_OFFSET + holdingIndex * 8)
20162
+ );
20163
+ entries.push({
20164
+ signer: new import_web326.PublicKey(signerBytes).toBase58(),
20165
+ lastUpdateTs: readI64(data, offset + 32),
20166
+ prices,
20167
+ externalAmounts
20168
+ });
20169
+ }
20170
+ const pendingOffset = ENTRIES_OFFSET + MAX_SIGNERS * ENTRY_SIZE;
20171
+ const pendingEffectiveTs = readI64(data, pendingOffset + 128);
20172
+ const pendingCount = data[pendingOffset + 136] ?? 0;
20173
+ if (pendingCount > 0 && pendingCount <= MAX_SIGNERS && nowTs >= pendingEffectiveTs) {
20174
+ for (let index = 0; index < MAX_SIGNERS; index += 1) {
20175
+ const signerBytes = new Uint8Array(
20176
+ data.slice(pendingOffset + index * 32, pendingOffset + (index + 1) * 32)
20177
+ );
20178
+ entries[index] = {
20179
+ signer: index < pendingCount ? new import_web326.PublicKey(signerBytes).toBase58() : import_web326.PublicKey.default.toBase58(),
20180
+ lastUpdateTs: 0n,
20181
+ prices: Array(MAX_HOLDINGS).fill(0n),
20182
+ externalAmounts: Array(MAX_HOLDINGS).fill(0n)
20183
+ };
20184
+ }
20185
+ }
20186
+ return {
20187
+ settledNavTs: readI64(data, 8),
20188
+ stalenessThresholdSecs: positiveOrDefault(
20189
+ readI64(data, 32),
20190
+ DEFAULT_STALENESS_SECS
20191
+ ),
20192
+ entries
20193
+ };
20194
+ }
20195
+ function decodeHolding(value) {
20196
+ const holding = record(value);
20197
+ const mintString = pubkeyString3(holding.mint);
20198
+ return {
20199
+ mint: (0, import_kit14.address)(mintString),
20200
+ decimals: number(holding.decimals),
20201
+ priceOracleType: variant(holding.priceOracleType),
20202
+ isBase: bool(holding.isBase),
20203
+ localAmount: bigint(holding.localAmount),
20204
+ externalAmount: bigint(holding.externalAmount),
20205
+ price: bigint(holding.price),
20206
+ lastUpdateTs: bigint(holding.lastUpdateTs),
20207
+ cleared: mintString === import_web326.PublicKey.default.toBase58()
20208
+ };
20209
+ }
20210
+ function decodeTrancheState(decoded, args, warnings, blockers) {
20211
+ if (!decoded) {
20212
+ blockers.push("tranching is enabled but the tranche state was unavailable");
20213
+ return void 0;
20214
+ }
20215
+ const junior = decodeShareClass(decoded.junior);
20216
+ const senior = decodeShareClass(decoded.senior);
20217
+ if (args.physicalJuniorSupply === void 0) {
20218
+ warnings.push(
20219
+ "junior mint supply unavailable; using cached tranche supply"
20220
+ );
20221
+ } else {
20222
+ junior.totalSupply = args.physicalJuniorSupply;
20223
+ refreshShareClass(junior);
20224
+ }
20225
+ if (args.physicalSeniorSupply === void 0) {
20226
+ warnings.push(
20227
+ "senior mint supply unavailable; using cached tranche supply"
20228
+ );
20229
+ } else {
20230
+ senior.totalSupply = args.physicalSeniorSupply;
20231
+ refreshShareClass(senior);
20232
+ }
20233
+ return {
20234
+ junior,
20235
+ senior,
20236
+ seniorFixedApyBps: number(decoded.config.seniorFixedApyBps),
20237
+ lastSettledTs: bigint(decoded.lastSettledTs),
20238
+ seniorApyAnchorSharePrice: bigint(decoded.seniorApyAnchorSharePrice),
20239
+ seniorApyAnchorTs: bigint(decoded.seniorApyAnchorTs)
20240
+ };
20241
+ }
20242
+ function decodeShareClass(value) {
20243
+ const decoded = record(value);
20244
+ return {
20245
+ value: bigint(decoded.value),
20246
+ totalSupply: bigint(decoded.totalSupply),
20247
+ sharePrice: bigint(decoded.sharePrice),
20248
+ hwmSharePrice: bigint(decoded.hwmSharePrice)
20249
+ };
20250
+ }
20251
+ function snapshot(tvl, regularSupply, regularSharePrice, tranche) {
20252
+ const regularValue = mulDiv2(
20253
+ regularSupply,
20254
+ regularSharePrice,
20255
+ DEFAULT_SHARE_PRICE
20256
+ );
20257
+ return {
20258
+ tvl,
20259
+ regular: {
20260
+ value: regularValue,
20261
+ totalSupply: regularSupply,
20262
+ sharePrice: regularSharePrice
20263
+ },
20264
+ ...tranche ? {
20265
+ junior: publicShareClass(tranche.junior),
20266
+ senior: publicShareClass(tranche.senior)
20267
+ } : {}
20268
+ };
20269
+ }
20270
+ function publicShareClass(value) {
20271
+ return {
20272
+ value: value.value,
20273
+ totalSupply: value.totalSupply,
20274
+ sharePrice: value.sharePrice
20275
+ };
20276
+ }
20277
+ function refreshShareClass(value) {
20278
+ value.sharePrice = value.totalSupply === 0n ? DEFAULT_SHARE_PRICE : mulDiv2(value.value, DEFAULT_SHARE_PRICE, value.totalSupply);
20279
+ }
20280
+ function sharePriceForValue(value, supply) {
20281
+ return supply === 0n ? void 0 : mulDiv2(value, DEFAULT_SHARE_PRICE, supply);
20282
+ }
20283
+ function feeAmounts(value, performanceFeeBps, protocolFeeBps) {
20284
+ return {
20285
+ curator: mulDiv2(value, BigInt(performanceFeeBps), BPS_DENOMINATOR2),
20286
+ protocol: mulDiv2(value, BigInt(protocolFeeBps), BPS_DENOMINATOR2)
20287
+ };
20288
+ }
20289
+ function noFees() {
20290
+ return { curator: 0n, protocol: 0n };
20291
+ }
20292
+ function withinDeviation(values) {
20293
+ for (let i = 0; i < values.length; i += 1) {
20294
+ for (let j = i + 1; j < values.length; j += 1) {
20295
+ const a = values[i];
20296
+ const b = values[j];
20297
+ if (a === b) continue;
20298
+ if (a === 0n || b === 0n) return false;
20299
+ const diff = abs(a * BPS_DENOMINATOR2 / b - BPS_DENOMINATOR2);
20300
+ if (diff > CONSENSUS_MAX_DIFF_BPS) return false;
20301
+ }
20302
+ }
20303
+ return true;
20304
+ }
20305
+ function average(values) {
20306
+ return values.reduce((sum, value) => sum + value, 0n) / BigInt(values.length);
20307
+ }
20308
+ function isFresh(lastTs, nowTs, staleness) {
20309
+ if (lastTs <= 0n) return false;
20310
+ const age = nowTs - lastTs;
20311
+ return age >= 0n && age <= staleness;
20312
+ }
20313
+ function applyEffectiveApy(anchor, apyBps, elapsedSecs) {
20314
+ if (anchor === 0n || apyBps === 0 || elapsedSecs === 0n) return anchor;
20315
+ if (apyBps < 0 || apyBps > 1e4) throw new Error("invalid APY bps");
20316
+ const base = APY_SCALE + mulDiv2(BigInt(apyBps), APY_SCALE, BPS_DENOMINATOR2);
20317
+ const wholeYears = elapsedSecs / SECONDS_PER_YEAR;
20318
+ const remainder = elapsedSecs % SECONDS_PER_YEAR;
20319
+ const wholeMultiplier = fixedPointPow(base, wholeYears);
20320
+ const fractionalMultiplier = remainder === 0n ? APY_SCALE : fixedPointExp(
20321
+ mulDiv2(
20322
+ fixedPointLnBetweenOneAndTwo(base),
20323
+ remainder,
20324
+ SECONDS_PER_YEAR
20325
+ )
20326
+ );
20327
+ return mulDiv2(
20328
+ anchor,
20329
+ fixedPointMul(wholeMultiplier, fractionalMultiplier),
20330
+ APY_SCALE
20331
+ );
20332
+ }
20333
+ function fixedPointMul(lhs, rhs) {
20334
+ const lhsWhole = lhs / APY_SCALE;
20335
+ const lhsRemainder = lhs % APY_SCALE;
20336
+ const rhsWhole = rhs / APY_SCALE;
20337
+ const rhsRemainder = rhs % APY_SCALE;
20338
+ return lhsWhole * rhs + lhsRemainder * rhsWhole + lhsRemainder * rhsRemainder / APY_SCALE;
20339
+ }
20340
+ function fixedPointPow(baseInput, exponentInput) {
20341
+ let base = baseInput;
20342
+ let exponent = exponentInput;
20343
+ let result = APY_SCALE;
20344
+ while (exponent > 0n) {
20345
+ if ((exponent & 1n) === 1n) result = fixedPointMul(result, base);
20346
+ exponent >>= 1n;
20347
+ if (exponent > 0n) base = fixedPointMul(base, base);
20348
+ }
20349
+ return result;
20350
+ }
20351
+ function fixedPointLnBetweenOneAndTwo(value) {
20352
+ if (value < APY_SCALE || value > 2n * APY_SCALE) {
20353
+ throw new Error("APY logarithm input out of range");
20354
+ }
20355
+ const z = mulDiv2(value - APY_SCALE, APY_SCALE, value + APY_SCALE);
20356
+ const zSquared = fixedPointMul(z, z);
20357
+ let power = z;
20358
+ let sum = 0n;
20359
+ for (let index = 0; index < APY_SERIES_TERMS; index += 1) {
20360
+ sum += power / BigInt(2 * index + 1);
20361
+ power = fixedPointMul(power, zSquared);
20362
+ }
20363
+ return sum * 2n;
20364
+ }
20365
+ function fixedPointExp(value) {
20366
+ if (value > APY_LN_2) throw new Error("APY exponent input out of range");
20367
+ let sum = APY_SCALE;
20368
+ let term = APY_SCALE;
20369
+ for (let divisor = 1; divisor <= APY_SERIES_TERMS; divisor += 1) {
20370
+ term = fixedPointMul(term, value) / BigInt(divisor);
20371
+ if (term === 0n) break;
20372
+ sum += term;
20373
+ }
20374
+ return sum;
20375
+ }
20376
+ function readU64(data, offset) {
20377
+ let value = 0n;
20378
+ for (let index = 0; index < 8; index += 1) {
20379
+ value |= BigInt(data[offset + index] ?? 0) << BigInt(index * 8);
20380
+ }
20381
+ return value;
20382
+ }
20383
+ function readI64(data, offset) {
20384
+ const value = readU64(data, offset);
20385
+ return value >= 0x8000000000000000n ? value - 0x10000000000000000n : value;
20386
+ }
20387
+ function mulDiv2(a, b, c) {
20388
+ if (c === 0n) throw new Error("division by zero in settlement simulation");
20389
+ return a * b / c;
20390
+ }
20391
+ function record(value) {
20392
+ return value && typeof value === "object" ? value : {};
20393
+ }
20394
+ function bigint(value) {
20395
+ if (value === void 0 || value === null) return 0n;
20396
+ if (typeof value === "bigint") return value;
20397
+ if (typeof value === "number" || typeof value === "string") {
20398
+ return BigInt(value);
20399
+ }
20400
+ return BigInt(String(value));
20401
+ }
20402
+ function number(value) {
20403
+ return Number(bigint(value));
20404
+ }
20405
+ function bool(value) {
20406
+ if (typeof value === "boolean") return value;
20407
+ if (typeof value === "number") return value !== 0;
20408
+ if (value && typeof value.toBool === "function") {
20409
+ return value.toBool();
20410
+ }
20411
+ const pod = value;
20412
+ return Boolean(pod?.value ?? pod?.[0]);
20413
+ }
20414
+ function variant(value) {
20415
+ return (value && typeof value === "object" ? Object.keys(value)[0] ?? "" : String(value ?? "")).toLowerCase();
20416
+ }
20417
+ function pubkeyString3(value) {
20418
+ if (typeof value === "string") return value;
20419
+ if (value && typeof value.toBase58 === "function") {
20420
+ return value.toBase58();
20421
+ }
20422
+ return String(value);
20423
+ }
20424
+ function positiveOrDefault(value, fallback) {
20425
+ return value > 0n ? value : fallback;
20426
+ }
20427
+ function max(a, b) {
20428
+ return a > b ? a : b;
20429
+ }
20430
+ function abs(value) {
20431
+ return value < 0n ? -value : value;
20432
+ }
20433
+ function message(error) {
20434
+ return error instanceof Error ? error.message : String(error);
20435
+ }
20436
+ function formatSettlementSimulation(simulation) {
20437
+ const lines = [];
20438
+ lines.push(
20439
+ ` proposed oracle values: ${simulation.allProposedUpdatesLand ? "WOULD REACH CONSENSUS" : "WOULD NOT ALL LAND"} (${simulation.proposedUpdatesLanded}/${simulation.proposedUpdatesTotal} holdings)`
20440
+ );
20441
+ lines.push(
20442
+ ` crank simulation: ${simulation.canSettle ? "WOULD SETTLE" : "BLOCKED"}`
20443
+ );
20444
+ for (const holding of simulation.consensus) {
20445
+ const detail = holding.settledPrice ? `price=${holding.settledPrice} external=${holding.settledExternalAmount}` : holding.reason ?? "no aggregate";
20446
+ lines.push(
20447
+ ` consensus #${holding.holdingIndex} ${holding.mint}: ${holding.reportCount} report(s), ${holding.wouldUpdateHolding ? "lands" : "unchanged"} (${detail})`
20448
+ );
20449
+ }
20450
+ if (simulation.grossNav !== void 0) {
20451
+ lines.push(
20452
+ ` gross NAV: ${formatUnits(
20453
+ simulation.grossNav,
20454
+ simulation.assetDecimals
20455
+ )} [${simulation.grossNav}]`
20456
+ );
20457
+ }
20458
+ if (simulation.settledAccountingNav !== void 0 && simulation.settledAccountingNav !== simulation.grossNav) {
20459
+ lines.push(
20460
+ ` accounting NAV after fixed APY: ${formatUnits(
20461
+ simulation.settledAccountingNav,
20462
+ simulation.assetDecimals
20463
+ )} [${simulation.settledAccountingNav}]`
20464
+ );
20465
+ }
20466
+ if (simulation.projected) {
20467
+ if (simulation.projectionRequiresMaxApyDisabled) {
20468
+ lines.push(" projection with max APY disabled (max_apy_bps=0):");
20469
+ }
20470
+ lines.push(
20471
+ ` TVL: ${formatTransition(
20472
+ simulation.current.tvl,
20473
+ simulation.projected.tvl,
20474
+ simulation.assetDecimals
20475
+ )}`
20476
+ );
20477
+ lines.push(
20478
+ ` regular: ${formatTransition(
20479
+ simulation.current.regular.sharePrice,
20480
+ simulation.projected.regular.sharePrice,
20481
+ 6
20482
+ )} per share`
20483
+ );
20484
+ lines.push(
20485
+ ` supply ${simulation.current.regular.totalSupply} -> ${simulation.projected.regular.totalSupply}, backing ${simulation.current.regular.value} -> ${simulation.projected.regular.value}`
20486
+ );
20487
+ if (simulation.current.junior && simulation.projected.junior) {
20488
+ lines.push(
20489
+ ` junior: ${formatTransition(
20490
+ simulation.current.junior.sharePrice,
20491
+ simulation.projected.junior.sharePrice,
20492
+ 6
20493
+ )} per share`
20494
+ );
20495
+ lines.push(
20496
+ ` supply ${simulation.current.junior.totalSupply} -> ${simulation.projected.junior.totalSupply}, value ${simulation.current.junior.value} -> ${simulation.projected.junior.value}`
20497
+ );
20498
+ }
20499
+ if (simulation.current.senior && simulation.projected.senior) {
20500
+ lines.push(
20501
+ ` senior: ${formatTransition(
20502
+ simulation.current.senior.sharePrice,
20503
+ simulation.projected.senior.sharePrice,
20504
+ 6
20505
+ )} per share`
20506
+ );
20507
+ lines.push(
20508
+ ` supply ${simulation.current.senior.totalSupply} -> ${simulation.projected.senior.totalSupply}, value ${simulation.current.senior.value} -> ${simulation.projected.senior.value}`
20509
+ );
20510
+ }
20511
+ }
20512
+ if (simulation.performanceFees) {
20513
+ lines.push(
20514
+ ` performance fees: curator=${simulation.performanceFees.curator} protocol=${simulation.performanceFees.protocol} total=${simulation.performanceFees.total} fee_shares=${simulation.performanceFees.mintedShares}`
20515
+ );
20516
+ }
20517
+ for (const blocker of simulation.blockers)
20518
+ lines.push(` BLOCKER: ${blocker}`);
20519
+ for (const warning of simulation.warnings)
20520
+ lines.push(` warning: ${warning}`);
20521
+ return lines;
20522
+ }
20523
+ function formatTransition(before, after, decimals) {
20524
+ return `${formatUnits(before, decimals)} [${before}] -> ${formatUnits(
20525
+ after,
20526
+ decimals
20527
+ )} [${after}]`;
20528
+ }
20529
+ function formatUnits(value, decimals) {
20530
+ const negative = value < 0n;
20531
+ const absolute = negative ? -value : value;
20532
+ const scale = 10n ** BigInt(decimals);
20533
+ const whole = absolute / scale;
20534
+ const fraction = (absolute % scale).toString().padStart(decimals, "0").replace(/0+$/, "");
20535
+ return `${negative ? "-" : ""}${whole}${fraction ? `.${fraction}` : ""}`;
20536
+ }
20537
+
20538
+ // src/services/consensusOracle/consensusOracleService.ts
20539
+ var SYSTEM_PROGRAM2 = "11111111111111111111111111111111";
20540
+ var PRICE_ORACLE_TYPES_BY_INDEX2 = [
20541
+ "none",
20542
+ "consensusOracle",
20543
+ "pyth",
20544
+ "perenaVault"
20545
+ ];
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;
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
+ }
20557
+ if (value && typeof value === "object") return Object.keys(value)[0] ?? "";
20558
+ return String(value ?? "");
20559
+ }
20560
+ function coerceBool3(value) {
20561
+ if (typeof value === "boolean") return value;
20562
+ if (typeof value === "number") return value !== 0;
20563
+ const record2 = value;
20564
+ return Boolean(record2?.value ?? record2?.[0]);
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
+ }
20573
+ function priceInAccountingUnit(usd, baseUsd, assetDecimals) {
20574
+ return BigInt(Math.round(usd / baseUsd * 10 ** assetDecimals));
20575
+ }
20576
+ function sumPositionsByMint(positions) {
20577
+ const byMint = /* @__PURE__ */ new Map();
20578
+ for (const pos of positions) {
20579
+ const key = pos.mint.toString();
20580
+ byMint.set(key, (byMint.get(key) ?? 0n) + pos.amount);
20581
+ }
20582
+ return byMint;
20583
+ }
20584
+ function parseExternalLiquidityRefs(vaultState) {
20585
+ const slots = vaultState.externalLiquidity;
20586
+ if (!Array.isArray(slots)) return [];
20587
+ const refs = [];
20588
+ for (const slot of slots) {
20589
+ const data = slot?.data;
20590
+ if (!data || data.length < 40) continue;
20591
+ if (data[0] !== 1) continue;
19607
20592
  const pubkeyBytes = new Uint8Array(data.slice(8, 40));
19608
- const userAccount = new import_web326.PublicKey(pubkeyBytes).toBase58();
20593
+ const userAccount = new import_web327.PublicKey(pubkeyBytes).toBase58();
19609
20594
  refs.push({
19610
20595
  kind: "marginfi",
19611
20596
  mint: "",
@@ -19629,24 +20614,71 @@ var ConsensusOracleService = class {
19629
20614
  const log = opts.log ?? (() => {
19630
20615
  });
19631
20616
  const dryRun = opts.dryRun ?? false;
19632
- const vaultState = await this.fetchDecodedVault(target.vault);
19633
- const consensusHoldings = selectConsensusHoldings(vaultState.holdings);
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
+ }
20628
+ const receiptMints = await this.fetchReceiptMints(target.vault, vaultState);
20629
+ const consensusHoldings = selectConsensusHoldings(
20630
+ vaultState.holdings,
20631
+ receiptMints
20632
+ );
20633
+ const filteredCount = vaultState.holdings.filter(
20634
+ (holding) => receiptMints.has((0, import_common41.fromWeb3Pk)(holding.mint).toString())
20635
+ ).length;
20636
+ if (filteredCount > 0) {
20637
+ log(
20638
+ `vault ${target.vault}: excluded ${filteredCount} vault/tranche receipt mint holding(s)`
20639
+ );
20640
+ }
19634
20641
  if (consensusHoldings.length === 0) {
19635
20642
  log(`vault ${target.vault}: no consensus holdings, skipping`);
19636
20643
  return { vault: target.vault, updates: [], dryRun };
19637
20644
  }
19638
- await this.assertNoUnconfirmedYieldPayments(target);
20645
+ const filteredTarget = filterTargetHoldings(target, receiptMints);
20646
+ await this.assertNoUnconfirmedYieldPayments(filteredTarget);
19639
20647
  const inputs = await this.gatherPricingInputs(
19640
- target,
20648
+ filteredTarget,
19641
20649
  vaultState,
19642
20650
  consensusHoldings
19643
20651
  );
19644
20652
  const updates = await this.buildUpdates(consensusHoldings, inputs);
19645
20653
  if (dryRun) {
19646
20654
  log(`vault ${target.vault}: dry run, ${updates.length} holding(s)`);
19647
- return { vault: target.vault, updates, dryRun: true };
20655
+ try {
20656
+ const settlementSimulation = await this.simulateDryRunSettlement(
20657
+ signer,
20658
+ target.vault,
20659
+ vaultState,
20660
+ updates
20661
+ );
20662
+ return {
20663
+ vault: target.vault,
20664
+ updates,
20665
+ dryRun: true,
20666
+ settlementSimulation
20667
+ };
20668
+ } catch (error) {
20669
+ const settlementSimulationError = error instanceof Error ? error.message : String(error);
20670
+ log(
20671
+ `vault ${target.vault}: settlement simulation unavailable: ${settlementSimulationError}`
20672
+ );
20673
+ return {
20674
+ vault: target.vault,
20675
+ updates,
20676
+ dryRun: true,
20677
+ settlementSimulationError
20678
+ };
20679
+ }
19648
20680
  }
19649
- const { updateSignature, crankSignature, crankSkippedReason } = await this.settleVault(signer, target, updates, vaultState, log);
20681
+ const { updateSignature, crankSignature, crankSkippedReason } = await this.settleVault(signer, filteredTarget, updates, vaultState, log);
19650
20682
  return {
19651
20683
  vault: target.vault,
19652
20684
  updates,
@@ -19656,6 +20688,43 @@ var ConsensusOracleService = class {
19656
20688
  crankSkippedReason
19657
20689
  };
19658
20690
  }
20691
+ /** Read canonical supplies and replay the exact on-chain settlement path. */
20692
+ async simulateDryRunSettlement(signer, vault, vaultState, updates) {
20693
+ const tranchingEnabled = coerceBool3(vaultState.tranchingEnabled);
20694
+ const [oracleState, trancheState] = await Promise.all([
20695
+ this.client.account.fetchVaultOracleForVault(vault, { fresh: true }),
20696
+ tranchingEnabled ? this.client.account.fetchVaultTrancheStateForVault(vault, {
20697
+ fresh: true
20698
+ }) : Promise.resolve(void 0)
20699
+ ]);
20700
+ const mintKeys = [new import_web327.PublicKey(vaultState.mint)];
20701
+ if (trancheState) {
20702
+ mintKeys.push(
20703
+ new import_web327.PublicKey(trancheState.config.juniorMint),
20704
+ new import_web327.PublicKey(trancheState.config.seniorMint)
20705
+ );
20706
+ }
20707
+ const mintAccounts = await this.client.provider.connection.getMultipleAccountsInfo(
20708
+ mintKeys,
20709
+ "confirmed"
20710
+ );
20711
+ const supplies = mintAccounts.map((info, index) => {
20712
+ if (!info)
20713
+ throw new Error(`share mint ${mintKeys[index]} does not exist`);
20714
+ return readSplMintSupply(info.data);
20715
+ });
20716
+ return simulateConsensusOracleSettlement({
20717
+ vault: vaultState,
20718
+ oracleData: oracleState.data,
20719
+ trancheState,
20720
+ updates,
20721
+ signer: (0, import_common41.fromWeb3Pk)(signer.publicKey),
20722
+ nowTs: BigInt(Math.floor(this.clock.now() / 1e3)),
20723
+ physicalShareSupply: supplies[0],
20724
+ physicalJuniorSupply: tranchingEnabled ? supplies[1] : void 0,
20725
+ physicalSeniorSupply: tranchingEnabled ? supplies[2] : void 0
20726
+ });
20727
+ }
19659
20728
  /**
19660
20729
  * Fail closed while any configured yield account has an unsettled payment.
19661
20730
  * The payout must be confirmed only after it is visible in the same wallet
@@ -19777,6 +20846,86 @@ var ConsensusOracleService = class {
19777
20846
  fresh: true
19778
20847
  });
19779
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
+ }
20907
+ /**
20908
+ * Receipt mints issued by this vault are liabilities/shares, not underlying
20909
+ * assets for its own NAV. Exclude them even if they were registered as
20910
+ * consensus-priced holdings.
20911
+ */
20912
+ async fetchReceiptMints(vault, vaultState) {
20913
+ const receiptMints = /* @__PURE__ */ new Set();
20914
+ if (vaultState.mint) {
20915
+ receiptMints.add((0, import_common41.fromWeb3Pk)(vaultState.mint).toString());
20916
+ }
20917
+ if (!coerceBool3(vaultState.tranchingEnabled)) return receiptMints;
20918
+ const trancheState = await this.client.account.fetchVaultTrancheStateForVault(vault, {
20919
+ fresh: true
20920
+ });
20921
+ receiptMints.add(
20922
+ (0, import_common41.fromWeb3Pk)(trancheState.config.juniorMint).toString()
20923
+ );
20924
+ receiptMints.add(
20925
+ (0, import_common41.fromWeb3Pk)(trancheState.config.seniorMint).toString()
20926
+ );
20927
+ return receiptMints;
20928
+ }
19780
20929
  /** Fetch every external input the per-holding updates draw on, in parallel-ish. */
19781
20930
  async gatherPricingInputs(target, vaultState, consensusHoldings) {
19782
20931
  const manager = (0, import_common41.fromWeb3Pk)(vaultState.roles.manager);
@@ -19922,12 +21071,13 @@ var ConsensusOracleService = class {
19922
21071
  };
19923
21072
  }
19924
21073
  /**
19925
- * Fetch the current settled NAV from the oracle account and log the APY
19926
- * implied by the prospective update, so MaxApyExceeded failures are diagnosable.
21074
+ * Fetch the current settlement timestamp and log the APY implied by the
21075
+ * prospective update, so MaxApyExceeded failures are diagnosable.
19927
21076
  *
19928
21077
  * NAV is expressed in the vault's accounting unit (scaled by assetDecimals).
19929
- * prospective NAV = Σ ((localAmount_i + externalAmount_i) * price_i)
19930
- * / 10^assetDecimals
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.
19931
21081
  */
19932
21082
  async logProspectiveApy(vault, updates, vaultState, log) {
19933
21083
  try {
@@ -19936,22 +21086,38 @@ var ConsensusOracleService = class {
19936
21086
  });
19937
21087
  const data = oracle.data;
19938
21088
  if (!data || data.length < 16) return;
19939
- const settledNav = readI64LE(data, 0);
19940
21089
  const settledNavTs = readI64LE(data, 8);
19941
- if (settledNav <= 0n || settledNavTs <= 0n) return;
19942
- const scale = BigInt(10 ** vaultState.config.assetDecimals);
19943
- const prospectiveNav = updates.reduce((sum, u) => {
19944
- const holding = vaultState.holdings[u.holdingIndex];
19945
- const localAmount = BigInt(holding?.localAmount?.toString() ?? "0");
19946
- return sum + (localAmount + u.externalAmount) * u.price / scale;
19947
- }, 0n);
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;
19948
21114
  const nowSecs = BigInt(Math.floor(this.clock.now() / 1e3));
19949
21115
  const elapsedSecs = nowSecs - settledNavTs;
19950
21116
  if (elapsedSecs <= 0n) return;
19951
- const SECONDS_PER_YEAR2 = 365n * 24n * 3600n;
19952
- const apyBps = (prospectiveNav - settledNav) * SECONDS_PER_YEAR2 * 10000n / (settledNav * elapsedSecs);
21117
+ const SECONDS_PER_YEAR3 = 365n * 24n * 3600n;
21118
+ const apyBps = (prospectiveNav - physicalNavBefore) * SECONDS_PER_YEAR3 * 10000n / (physicalNavBefore * elapsedSecs);
19953
21119
  log(
19954
- `vault ${vault}: prospective NAV ${prospectiveNav} vs settled ${settledNav} (elapsed ${elapsedSecs}s) \u2192 implied APY ${apyBps}bps`
21120
+ `vault ${vault}: prospective NAV ${prospectiveNav} vs physical baseline ${physicalNavBefore} (elapsed ${elapsedSecs}s) \u2192 implied APY ${apyBps}bps`
19955
21121
  );
19956
21122
  } catch {
19957
21123
  }
@@ -19965,13 +21131,23 @@ function readI64LE(data, offset) {
19965
21131
  if (value >= 0x8000000000000000n) value -= 0x10000000000000000n;
19966
21132
  return value;
19967
21133
  }
19968
- var ENTRY_SIZE = 272;
19969
- var ENTRIES_OFFSET = 40;
21134
+ function readSplMintSupply(data) {
21135
+ if (data.length < 44) {
21136
+ throw new Error(`invalid SPL mint account: expected at least 44 bytes`);
21137
+ }
21138
+ let supply = 0n;
21139
+ for (let i = 0; i < 8; i += 1) {
21140
+ supply |= BigInt(data[36 + i]) << BigInt(8 * i);
21141
+ }
21142
+ return supply;
21143
+ }
21144
+ var ENTRY_SIZE2 = 272;
21145
+ var ENTRIES_OFFSET2 = 40;
19970
21146
  var MAX_CONSENSUS_SIGNERS = 4;
19971
21147
  function signerInOracleData(data, signerBytes) {
19972
21148
  const ZERO_PUBKEY = new Uint8Array(32);
19973
21149
  for (let i = 0; i < MAX_CONSENSUS_SIGNERS; i++) {
19974
- const off = ENTRIES_OFFSET + i * ENTRY_SIZE;
21150
+ const off = ENTRIES_OFFSET2 + i * ENTRY_SIZE2;
19975
21151
  const slice = data.slice(off, off + 32);
19976
21152
  if (slice.length < 32) break;
19977
21153
  const bytes = new Uint8Array(slice);
@@ -19981,12 +21157,21 @@ function signerInOracleData(data, signerBytes) {
19981
21157
  }
19982
21158
  return false;
19983
21159
  }
19984
- function selectConsensusHoldings(holdings) {
21160
+ function selectConsensusHoldings(holdings, excludedMints = /* @__PURE__ */ new Set()) {
19985
21161
  return holdings.map((holding, holdingIndex) => ({ holding, holdingIndex })).filter(({ holding }) => {
19986
21162
  const mint = (0, import_common41.fromWeb3Pk)(holding.mint);
19987
- return mint !== SYSTEM_PROGRAM2 && variantName2(holding.priceOracleType).toLowerCase() === CONSENSUS_ORACLE_VARIANT;
21163
+ return mint !== SYSTEM_PROGRAM2 && !excludedMints.has(mint.toString()) && variantName2(holding.priceOracleType).toLowerCase() === CONSENSUS_ORACLE_VARIANT;
19988
21164
  });
19989
21165
  }
21166
+ function filterTargetHoldings(target, excludedMints) {
21167
+ if (!target.holdings?.length || excludedMints.size === 0) return target;
21168
+ return {
21169
+ ...target,
21170
+ holdings: target.holdings.filter(
21171
+ (holding) => !excludedMints.has(holding.mint.toString())
21172
+ )
21173
+ };
21174
+ }
19990
21175
 
19991
21176
  // src/services/consensusOracle/runLiveConsensusOracle.ts
19992
21177
  async function runLiveConsensusOracle(env, oracleSigner, opts = {}) {
@@ -20010,7 +21195,7 @@ async function runLiveConsensusOracle(env, oracleSigner, opts = {}) {
20010
21195
  }
20011
21196
 
20012
21197
  // src/services/consensusOracle/mockYieldTracker.ts
20013
- var SECONDS_PER_YEAR = 365 * 24 * 60 * 60;
21198
+ var SECONDS_PER_YEAR2 = 365 * 24 * 60 * 60;
20014
21199
  var MockYieldTracker = class {
20015
21200
  constructor(opts = {}) {
20016
21201
  this.accounts = /* @__PURE__ */ new Map();
@@ -20065,12 +21250,12 @@ var MockYieldTracker = class {
20065
21250
  let segStart = windowStartMs;
20066
21251
  for (const cf of boundaries) {
20067
21252
  const dtSecs = (cf.ts - segStart) / 1e3;
20068
- totalYield += principal * apr * (dtSecs / SECONDS_PER_YEAR);
21253
+ totalYield += principal * apr * (dtSecs / SECONDS_PER_YEAR2);
20069
21254
  principal += cf.amount;
20070
21255
  segStart = cf.ts;
20071
21256
  }
20072
21257
  const tailSecs = Math.max(0, (windowEndMs - segStart) / 1e3);
20073
- totalYield += principal * apr * (tailSecs / SECONDS_PER_YEAR);
21258
+ totalYield += principal * apr * (tailSecs / SECONDS_PER_YEAR2);
20074
21259
  const paidYield = state.yieldPayments.reduce((sum, payment) => {
20075
21260
  return payment.status === "confirmed" && payment.effectiveAt > windowStartMs && payment.effectiveAt <= windowEndMs ? sum + payment.amount : sum;
20076
21261
  }, 0);
@@ -20090,22 +21275,22 @@ var MockYieldTracker = class {
20090
21275
  async getTotalYield(accountNames, start, end) {
20091
21276
  let total = 0;
20092
21277
  for (const name of accountNames) {
20093
- const snapshot = await this.getYield(name, start, end);
20094
- total += snapshot.totalYield;
21278
+ const snapshot2 = await this.getYield(name, start, end);
21279
+ total += snapshot2.totalYield;
20095
21280
  }
20096
21281
  return total;
20097
21282
  }
20098
21283
  async getTotalOutstandingYield(accountNames, start, end) {
20099
21284
  let total = 0;
20100
21285
  for (const name of accountNames) {
20101
- const snapshot = await this.getYield(name, start, end);
20102
- total += snapshot.outstandingYield;
21286
+ const snapshot2 = await this.getYield(name, start, end);
21287
+ total += snapshot2.outstandingYield;
20103
21288
  }
20104
21289
  return total;
20105
21290
  }
20106
21291
  async getBalance(accountName, atTime) {
20107
- const snapshot = await this.getYield(accountName, void 0, atTime);
20108
- return snapshot.principalAtEnd + snapshot.outstandingYield;
21292
+ const snapshot2 = await this.getYield(accountName, void 0, atTime);
21293
+ return snapshot2.principalAtEnd + snapshot2.outstandingYield;
20109
21294
  }
20110
21295
  async addCashflow(params) {
20111
21296
  const state = this.require(params.accountName);
@@ -20157,12 +21342,12 @@ var MockYieldTracker = class {
20157
21342
  let cumulativePaid = 0;
20158
21343
  for (const candidate of chronologicalPayments) {
20159
21344
  cumulativePaid += candidate.amount;
20160
- const snapshot = await this.getYield(
21345
+ const snapshot2 = await this.getYield(
20161
21346
  params.accountName,
20162
21347
  void 0,
20163
21348
  new Date(candidate.effectiveAt)
20164
21349
  );
20165
- if (cumulativePaid > snapshot.totalYield + 1e-9) {
21350
+ if (cumulativePaid > snapshot2.totalYield + 1e-9) {
20166
21351
  throw new Error("Confirmed payment exceeds accrued unpaid yield");
20167
21352
  }
20168
21353
  }
@@ -20172,8 +21357,8 @@ var MockYieldTracker = class {
20172
21357
  };
20173
21358
 
20174
21359
  // src/services/externalLiquidityIntegrityService.ts
20175
- var import_kit13 = require("@solana/kit");
20176
- var import_web327 = require("@solana/web3.js");
21360
+ var import_kit15 = require("@solana/kit");
21361
+ var import_web328 = require("@solana/web3.js");
20177
21362
  var import_common42 = __toESM(require_dist());
20178
21363
  var import_marginfi2 = __toESM(require_dist2());
20179
21364
  function parseActiveSlots(externalLiquidity) {
@@ -20186,12 +21371,12 @@ function parseActiveSlots(externalLiquidity) {
20186
21371
  if (discriminant === 0) continue;
20187
21372
  if (discriminant !== 1) continue;
20188
21373
  const pubkeyBytes = new Uint8Array(data.slice(8, 40));
20189
- const userAccount = (0, import_common42.toAddress)(new import_web327.PublicKey(pubkeyBytes));
21374
+ const userAccount = (0, import_common42.toAddress)(new import_web328.PublicKey(pubkeyBytes));
20190
21375
  results.push({ index: i, source: "marginfi", userAccount });
20191
21376
  }
20192
21377
  return results;
20193
21378
  }
20194
- function toBigInt2(value) {
21379
+ function toBigInt3(value) {
20195
21380
  if (typeof value === "bigint") return value;
20196
21381
  if (value !== null && value !== void 0) return BigInt(String(value));
20197
21382
  return 0n;
@@ -20252,7 +21437,7 @@ var ExternalLiquidityIntegrityService = class {
20252
21437
  return result;
20253
21438
  }
20254
21439
  const hwManagerAddress = (0, import_common42.fromWeb3Pk)(hwManager.publicKey);
20255
- const vaultPk = new import_web327.PublicKey(vault.toString());
21440
+ const vaultPk = new import_web328.PublicKey(vault.toString());
20256
21441
  const registeredHwManager = pubkeyStr(
20257
21442
  vaultState.roles.hwManager
20258
21443
  );
@@ -20271,7 +21456,7 @@ var ExternalLiquidityIntegrityService = class {
20271
21456
  }
20272
21457
  const holdings = vaultState.holdings.filter((h) => {
20273
21458
  const mint = pubkeyStr(h.mint);
20274
- return !isZeroPubkey2(mint) && toBigInt2(h.localAmount) > 0n;
21459
+ return !isZeroPubkey2(mint) && toBigInt3(h.localAmount) > 0n;
20275
21460
  });
20276
21461
  if (holdings.length === 0) {
20277
21462
  log(`Vault ${vault} slot ${slot.index}: no local balance to deploy`);
@@ -20279,8 +21464,8 @@ var ExternalLiquidityIntegrityService = class {
20279
21464
  continue;
20280
21465
  }
20281
21466
  for (const holding of holdings) {
20282
- const mintAddress = (0, import_kit13.address)(pubkeyStr(holding.mint));
20283
- const localAmount = toBigInt2(holding.localAmount);
21467
+ const mintAddress = (0, import_kit15.address)(pubkeyStr(holding.mint));
21468
+ const localAmount = toBigInt3(holding.localAmount);
20284
21469
  const marginfiAccounts = (0, import_marginfi2.marginfiAccountsForMint)(mintAddress);
20285
21470
  if (!marginfiAccounts) {
20286
21471
  log(
@@ -20395,11 +21580,17 @@ var ExternalLiquidityIntegrityService = class {
20395
21580
  JupiterPriceSource,
20396
21581
  JupiterSwapBuilder,
20397
21582
  KaminoPositionProvider,
21583
+ LOCAL_PROTOCOL_ADMIN,
21584
+ LivePriceSource,
20398
21585
  MAX_PRICE_STALENESS_THRESHOLD_SECS,
20399
21586
  ManagerRedepositAssetBuilder,
20400
21587
  ManagerWithdrawAssetBuilder,
20401
21588
  MarginfiPositionProvider,
20402
21589
  MockYieldTracker,
21590
+ NEST_API_BASE_URL,
21591
+ NEST_RWA_SHARE_MINT,
21592
+ NEST_VAULT_SLUG,
21593
+ NestPriceSource,
20403
21594
  OracleService,
20404
21595
  PROGRAM_FEE_EXEMPT_OWNER_WHITELIST,
20405
21596
  PROTOCOL_ADMIN,
@@ -20442,6 +21633,7 @@ var ExternalLiquidityIntegrityService = class {
20442
21633
  WithdrawalQueueService,
20443
21634
  addCashflowUpdate,
20444
21635
  address,
21636
+ applyEffectiveApy,
20445
21637
  confirmYieldPayment,
20446
21638
  createAccount,
20447
21639
  createLiveConsensusOracleDeps,
@@ -20453,7 +21645,9 @@ var ExternalLiquidityIntegrityService = class {
20453
21645
  dateToStr,
20454
21646
  defaultKeypairPath,
20455
21647
  deleteAccount,
21648
+ fetchNestTokenPrice,
20456
21649
  findSquadsWalletRoute,
21650
+ formatSettlementSimulation,
20457
21651
  fromUiAmount,
20458
21652
  getAccounts,
20459
21653
  getBalance,
@@ -20474,6 +21668,7 @@ var ExternalLiquidityIntegrityService = class {
20474
21668
  resolveSquadsWalletRoute,
20475
21669
  roundToNextUtcMidnight,
20476
21670
  runLiveConsensusOracle,
21671
+ simulateConsensusOracleSettlement,
20477
21672
  systemClock,
20478
21673
  toUiAmount,
20479
21674
  updateAccount,