@exponent-labs/generic-sy-sdk 0.9.20 → 0.9.22

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.
@@ -0,0 +1,125 @@
1
+ import { BN, BorshAccountsCoder, type Idl, web3 } from "@coral-xyz/anchor"
2
+
3
+ import { IDL as EXPONENT_TRANCHING_IDL, PROGRAM_ID } from "@exponent-labs/exponent-tranching-idl"
4
+
5
+ const EXPONENT_TRANCHING_PROGRAM_ID = new web3.PublicKey(PROGRAM_ID)
6
+ const UPDATE_MARKET_DISCRIMINATOR = Buffer.from([153, 39, 2, 197, 179, 50, 199, 217])
7
+ const NUMBER_DENOMINATOR = 1_000_000_000_000n
8
+
9
+ type TranchingNumber = { 0: BN[] }
10
+
11
+ type CpiInterfaceContext = {
12
+ alt_index: number
13
+ is_signer: boolean
14
+ is_writable: boolean
15
+ }
16
+
17
+ type ExponentTranchingMarketAccount = {
18
+ address_lookup_table: web3.PublicKey
19
+ sy_program: web3.PublicKey
20
+ mint_lp_senior: web3.PublicKey
21
+ mint_lp_junior: web3.PublicKey
22
+ return_model_storage: web3.PublicKey
23
+ financials: {
24
+ sr_effective_net_asset: TranchingNumber
25
+ jr_effective_net_asset: TranchingNumber
26
+ }
27
+ tranche_supply_state: {
28
+ total_senior_lp_supply: BN
29
+ total_junior_lp_supply: BN
30
+ }
31
+ sy_cpi_accounts: {
32
+ get_sy_state: CpiInterfaceContext[]
33
+ }
34
+ }
35
+
36
+ const accountCoder = new BorshAccountsCoder(EXPONENT_TRANCHING_IDL as Idl)
37
+
38
+ function tranchingNumberToRaw(value: TranchingNumber): bigint {
39
+ if (value[0].length !== 4) {
40
+ throw new Error("Invalid Exponent Tranching number")
41
+ }
42
+
43
+ let raw = 0n
44
+ for (let index = 3; index >= 0; index--) {
45
+ raw = (raw << 64n) + BigInt(value[0][index]!.toString())
46
+ }
47
+ return raw
48
+ }
49
+
50
+ function calculateLpRate(effectiveNetAsset: TranchingNumber, totalLpSupply: BN): number {
51
+ const priceRaw =
52
+ (tranchingNumberToRaw(effectiveNetAsset) + NUMBER_DENOMINATOR) / (BigInt(totalLpSupply.toString()) + 1n)
53
+ return Number(priceRaw) / Number(NUMBER_DENOMINATOR)
54
+ }
55
+
56
+ async function fetchMarket(
57
+ connection: web3.Connection,
58
+ marketAddress: web3.PublicKey,
59
+ ): Promise<ExponentTranchingMarketAccount> {
60
+ const accountInfo = await connection.getAccountInfo(marketAddress)
61
+ if (!accountInfo) {
62
+ throw new Error(`Exponent Tranching market not found: ${marketAddress.toBase58()}`)
63
+ }
64
+ if (!accountInfo.owner.equals(EXPONENT_TRANCHING_PROGRAM_ID)) {
65
+ throw new Error(`Invalid Exponent Tranching market owner: ${accountInfo.owner.toBase58()}`)
66
+ }
67
+
68
+ return accountCoder.decode("ExponentTranchingMarket", accountInfo.data) as ExponentTranchingMarketAccount
69
+ }
70
+
71
+ /** Reads the selected tranche's virtual-supply NAV from an Exponent Tranching market. */
72
+ export async function fetchExponentTranchingRate(params: {
73
+ connection: web3.Connection
74
+ marketAddress: web3.PublicKey
75
+ yieldBearingMint: web3.PublicKey
76
+ }): Promise<number> {
77
+ const market = await fetchMarket(params.connection, params.marketAddress)
78
+
79
+ if (params.yieldBearingMint.equals(market.mint_lp_senior)) {
80
+ return calculateLpRate(market.financials.sr_effective_net_asset, market.tranche_supply_state.total_senior_lp_supply)
81
+ }
82
+ if (params.yieldBearingMint.equals(market.mint_lp_junior)) {
83
+ return calculateLpRate(market.financials.jr_effective_net_asset, market.tranche_supply_state.total_junior_lp_supply)
84
+ }
85
+
86
+ throw new Error(`Tranching market does not contain yield-bearing mint ${params.yieldBearingMint.toBase58()}`)
87
+ }
88
+
89
+ /** Builds the permissionless refresh required before reading a slot-fresh tranching NAV. */
90
+ export async function buildExponentTranchingUpdateMarketInstruction(params: {
91
+ connection: web3.Connection
92
+ marketAddress: web3.PublicKey
93
+ }): Promise<web3.TransactionInstruction> {
94
+ const market = await fetchMarket(params.connection, params.marketAddress)
95
+ const lookupTable = await params.connection.getAddressLookupTable(market.address_lookup_table)
96
+ if (!lookupTable.value) {
97
+ throw new Error(`Exponent Tranching lookup table not found: ${market.address_lookup_table.toBase58()}`)
98
+ }
99
+
100
+ const getSyStateAccounts = market.sy_cpi_accounts.get_sy_state.map((context) => {
101
+ const pubkey = lookupTable.value!.state.addresses[context.alt_index]
102
+ if (!pubkey) {
103
+ throw new Error(`Exponent Tranching get_sy_state ALT index ${context.alt_index} is out of bounds`)
104
+ }
105
+ return { pubkey, isSigner: context.is_signer, isWritable: context.is_writable }
106
+ })
107
+ const [eventAuthority] = web3.PublicKey.findProgramAddressSync(
108
+ [Buffer.from("__event_authority")],
109
+ EXPONENT_TRANCHING_PROGRAM_ID,
110
+ )
111
+
112
+ return new web3.TransactionInstruction({
113
+ programId: EXPONENT_TRANCHING_PROGRAM_ID,
114
+ data: UPDATE_MARKET_DISCRIMINATOR,
115
+ keys: [
116
+ { pubkey: params.marketAddress, isSigner: false, isWritable: true },
117
+ { pubkey: market.return_model_storage, isSigner: false, isWritable: true },
118
+ { pubkey: market.address_lookup_table, isSigner: false, isWritable: false },
119
+ { pubkey: market.sy_program, isSigner: false, isWritable: false },
120
+ { pubkey: eventAuthority, isSigner: false, isWritable: false },
121
+ { pubkey: EXPONENT_TRANCHING_PROGRAM_ID, isSigner: false, isWritable: false },
122
+ ...getSyStateAccounts,
123
+ ],
124
+ })
125
+ }
@@ -0,0 +1,55 @@
1
+ import { web3 } from "@coral-xyz/anchor"
2
+
3
+ const GLOW_VAULT_PROGRAM_ID = new web3.PublicKey("gwv1ybUe2JVEpjdWARK1PjZUVY5xdNUCRhu24tgYtxa")
4
+ const GLOW_VAULT_DISCRIMINATOR = Buffer.from([211, 8, 232, 43, 2, 152, 117, 119])
5
+ const GLOW_VAULT_ACCOUNT_SIZE = 8 + 552
6
+ const SHARE_MINT_OFFSET = 8 + 160
7
+ const DEPOSIT_TOKENS_OFFSET = 8 + 320
8
+ const DEPOSIT_SHARES_OFFSET = 8 + 336
9
+ const UNCOLLECTED_MANAGEMENT_FEES_OFFSET = 8 + 344
10
+ const MINIMUM_SHARES_DUST_THRESHOLD_OFFSET = 8 + 408
11
+ const REALISED_PERFORMANCE_FEES_OFFSET = 8 + 488
12
+ const NUMBER_DENOMINATOR = 1_000_000_000_000n
13
+
14
+ /** Reads a Glow vault's net underlying-token value per share. */
15
+ export async function fetchGlowVaultRate(params: {
16
+ connection: web3.Connection
17
+ vaultAddress: web3.PublicKey
18
+ expectedShareMint: web3.PublicKey
19
+ }): Promise<number> {
20
+ const accountInfo = await params.connection.getAccountInfo(params.vaultAddress)
21
+ if (!accountInfo) {
22
+ throw new Error(`Glow vault not found: ${params.vaultAddress.toBase58()}`)
23
+ }
24
+ if (!accountInfo.owner.equals(GLOW_VAULT_PROGRAM_ID)) {
25
+ throw new Error(`Invalid Glow vault owner: ${accountInfo.owner.toBase58()}`)
26
+ }
27
+ if (accountInfo.data.length < GLOW_VAULT_ACCOUNT_SIZE) {
28
+ throw new Error(`Glow vault account is too short: ${accountInfo.data.length}`)
29
+ }
30
+ if (!accountInfo.data.subarray(0, 8).equals(GLOW_VAULT_DISCRIMINATOR)) {
31
+ throw new Error("Invalid Glow vault discriminator")
32
+ }
33
+
34
+ const shareMint = new web3.PublicKey(accountInfo.data.subarray(SHARE_MINT_OFFSET, SHARE_MINT_OFFSET + 32))
35
+ if (!shareMint.equals(params.expectedShareMint)) {
36
+ throw new Error(
37
+ `Glow vault share mint ${shareMint.toBase58()} does not match ${params.expectedShareMint.toBase58()}`,
38
+ )
39
+ }
40
+
41
+ const depositShares = accountInfo.data.readBigUInt64LE(DEPOSIT_SHARES_OFFSET)
42
+ const minimumSharesDustThreshold = accountInfo.data.readBigUInt64LE(MINIMUM_SHARES_DUST_THRESHOLD_OFFSET)
43
+ if (depositShares <= minimumSharesDustThreshold) return 1
44
+
45
+ const depositTokens = accountInfo.data.readBigUInt64LE(DEPOSIT_TOKENS_OFFSET)
46
+ const uncollectedManagementFees = accountInfo.data.readBigUInt64LE(UNCOLLECTED_MANAGEMENT_FEES_OFFSET)
47
+ const realisedPerformanceFees = accountInfo.data.readBigUInt64LE(REALISED_PERFORMANCE_FEES_OFFSET)
48
+ const netAfterManagementFees =
49
+ depositTokens > uncollectedManagementFees ? depositTokens - uncollectedManagementFees : 0n
50
+ const netTokens =
51
+ netAfterManagementFees > realisedPerformanceFees ? netAfterManagementFees - realisedPerformanceFees : 0n
52
+ const rateRaw = (netTokens * NUMBER_DENOMINATOR) / depositShares
53
+
54
+ return Number(rateRaw) / Number(NUMBER_DENOMINATOR)
55
+ }
package/tsconfig.json CHANGED
@@ -25,6 +25,9 @@
25
25
  {
26
26
  "path": "../exponent-fetcher"
27
27
  },
28
+ {
29
+ "path": "../exponent-tranching-idl"
30
+ },
28
31
  {
29
32
  "path": "../generic-sy-pda"
30
33
  },