@exponent-labs/exponent-sdk 0.9.0 → 0.9.2

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 (155) hide show
  1. package/build/client/vaults/index.d.ts +2 -0
  2. package/build/client/vaults/index.js +2 -0
  3. package/build/client/vaults/index.js.map +1 -1
  4. package/build/client/vaults/types/index.d.ts +2 -0
  5. package/build/client/vaults/types/index.js +2 -0
  6. package/build/client/vaults/types/index.js.map +1 -1
  7. package/build/client/vaults/types/kaminoFarmEntry.d.ts +15 -0
  8. package/build/client/vaults/types/kaminoFarmEntry.js +17 -0
  9. package/build/client/vaults/types/kaminoFarmEntry.js.map +1 -0
  10. package/build/client/vaults/types/kaminoObligationEntry.d.ts +21 -4
  11. package/build/client/vaults/types/kaminoObligationEntry.js +2 -1
  12. package/build/client/vaults/types/kaminoObligationEntry.js.map +1 -1
  13. package/build/client/vaults/types/positionUpdate.d.ts +9 -0
  14. package/build/client/vaults/types/positionUpdate.js +23 -0
  15. package/build/client/vaults/types/positionUpdate.js.map +1 -1
  16. package/build/client/vaults/types/proposalAction.js +0 -3
  17. package/build/client/vaults/types/proposalAction.js.map +1 -1
  18. package/build/client/vaults/types/reserveFarmMapping.d.ts +19 -0
  19. package/build/client/vaults/types/reserveFarmMapping.js +18 -0
  20. package/build/client/vaults/types/reserveFarmMapping.js.map +1 -0
  21. package/build/client/vaults/types/strategyPosition.d.ts +5 -0
  22. package/build/client/vaults/types/strategyPosition.js +5 -0
  23. package/build/client/vaults/types/strategyPosition.js.map +1 -1
  24. package/build/exponentVaults/aumCalculator.d.ts +25 -4
  25. package/build/exponentVaults/aumCalculator.js +236 -15
  26. package/build/exponentVaults/aumCalculator.js.map +1 -1
  27. package/build/exponentVaults/fetcher.d.ts +52 -0
  28. package/build/exponentVaults/fetcher.js +199 -0
  29. package/build/exponentVaults/fetcher.js.map +1 -0
  30. package/build/exponentVaults/index.d.ts +10 -9
  31. package/build/exponentVaults/index.js +26 -8
  32. package/build/exponentVaults/index.js.map +1 -1
  33. package/build/exponentVaults/kamino-farms.d.ts +144 -0
  34. package/build/exponentVaults/kamino-farms.js +396 -0
  35. package/build/exponentVaults/kamino-farms.js.map +1 -0
  36. package/build/exponentVaults/loopscale/client.d.ts +240 -0
  37. package/build/exponentVaults/loopscale/client.js +590 -0
  38. package/build/exponentVaults/loopscale/client.js.map +1 -0
  39. package/build/exponentVaults/loopscale/client.test.d.ts +1 -0
  40. package/build/exponentVaults/loopscale/client.test.js +183 -0
  41. package/build/exponentVaults/loopscale/client.test.js.map +1 -0
  42. package/build/exponentVaults/loopscale/helpers.d.ts +29 -0
  43. package/build/exponentVaults/loopscale/helpers.js +119 -0
  44. package/build/exponentVaults/loopscale/helpers.js.map +1 -0
  45. package/build/exponentVaults/loopscale/index.d.ts +3 -0
  46. package/build/exponentVaults/loopscale/index.js +12 -0
  47. package/build/exponentVaults/loopscale/index.js.map +1 -0
  48. package/build/exponentVaults/loopscale/prepared-transactions.d.ts +13 -0
  49. package/build/exponentVaults/loopscale/prepared-transactions.js +271 -0
  50. package/build/exponentVaults/loopscale/prepared-transactions.js.map +1 -0
  51. package/build/exponentVaults/loopscale/prepared-transactions.test.d.ts +1 -0
  52. package/build/exponentVaults/loopscale/prepared-transactions.test.js +400 -0
  53. package/build/exponentVaults/loopscale/prepared-transactions.test.js.map +1 -0
  54. package/build/exponentVaults/loopscale/prepared-types.d.ts +62 -0
  55. package/build/exponentVaults/loopscale/prepared-types.js +3 -0
  56. package/build/exponentVaults/loopscale/prepared-types.js.map +1 -0
  57. package/build/exponentVaults/loopscale/response-plan.d.ts +69 -0
  58. package/build/exponentVaults/loopscale/response-plan.js +141 -0
  59. package/build/exponentVaults/loopscale/response-plan.js.map +1 -0
  60. package/build/exponentVaults/loopscale/response-plan.test.d.ts +1 -0
  61. package/build/exponentVaults/loopscale/response-plan.test.js +139 -0
  62. package/build/exponentVaults/loopscale/response-plan.test.js.map +1 -0
  63. package/build/exponentVaults/loopscale/send-plan.d.ts +75 -0
  64. package/build/exponentVaults/loopscale/send-plan.js +235 -0
  65. package/build/exponentVaults/loopscale/send-plan.js.map +1 -0
  66. package/build/exponentVaults/loopscale/types.d.ts +443 -0
  67. package/build/exponentVaults/loopscale/types.js +3 -0
  68. package/build/exponentVaults/loopscale/types.js.map +1 -0
  69. package/build/exponentVaults/loopscale-client.d.ts +113 -524
  70. package/build/exponentVaults/loopscale-client.js +296 -539
  71. package/build/exponentVaults/loopscale-client.js.map +1 -1
  72. package/build/exponentVaults/loopscale-client.test.d.ts +1 -0
  73. package/build/exponentVaults/loopscale-client.test.js +162 -0
  74. package/build/exponentVaults/loopscale-client.test.js.map +1 -0
  75. package/build/exponentVaults/loopscale-client.types.d.ts +425 -0
  76. package/build/exponentVaults/loopscale-client.types.js +3 -0
  77. package/build/exponentVaults/loopscale-client.types.js.map +1 -0
  78. package/build/exponentVaults/loopscale-execution.d.ts +125 -0
  79. package/build/exponentVaults/loopscale-execution.js +341 -0
  80. package/build/exponentVaults/loopscale-execution.js.map +1 -0
  81. package/build/exponentVaults/loopscale-execution.test.d.ts +1 -0
  82. package/build/exponentVaults/loopscale-execution.test.js +139 -0
  83. package/build/exponentVaults/loopscale-execution.test.js.map +1 -0
  84. package/build/exponentVaults/loopscale-vault.d.ts +115 -0
  85. package/build/exponentVaults/loopscale-vault.js +275 -0
  86. package/build/exponentVaults/loopscale-vault.js.map +1 -0
  87. package/build/exponentVaults/loopscale-vault.test.d.ts +1 -0
  88. package/build/exponentVaults/loopscale-vault.test.js +102 -0
  89. package/build/exponentVaults/loopscale-vault.test.js.map +1 -0
  90. package/build/exponentVaults/policyBuilders.d.ts +62 -0
  91. package/build/exponentVaults/policyBuilders.js +119 -2
  92. package/build/exponentVaults/policyBuilders.js.map +1 -1
  93. package/build/exponentVaults/pricePathResolver.d.ts +45 -0
  94. package/build/exponentVaults/pricePathResolver.js +198 -0
  95. package/build/exponentVaults/pricePathResolver.js.map +1 -0
  96. package/build/exponentVaults/pricePathResolver.test.d.ts +1 -0
  97. package/build/exponentVaults/pricePathResolver.test.js +369 -0
  98. package/build/exponentVaults/pricePathResolver.test.js.map +1 -0
  99. package/build/exponentVaults/syncTransaction.js +4 -1
  100. package/build/exponentVaults/syncTransaction.js.map +1 -1
  101. package/build/exponentVaults/titan-quote.js +170 -36
  102. package/build/exponentVaults/titan-quote.js.map +1 -1
  103. package/build/exponentVaults/vault-instruction-types.d.ts +363 -0
  104. package/build/exponentVaults/vault-instruction-types.js +128 -0
  105. package/build/exponentVaults/vault-instruction-types.js.map +1 -0
  106. package/build/exponentVaults/vault-interaction.d.ts +203 -343
  107. package/build/exponentVaults/vault-interaction.js +1894 -426
  108. package/build/exponentVaults/vault-interaction.js.map +1 -1
  109. package/build/exponentVaults/vault-interaction.kamino-vault.test.d.ts +1 -0
  110. package/build/exponentVaults/vault-interaction.kamino-vault.test.js +143 -0
  111. package/build/exponentVaults/vault-interaction.kamino-vault.test.js.map +1 -0
  112. package/build/exponentVaults/vault.d.ts +51 -2
  113. package/build/exponentVaults/vault.js +324 -48
  114. package/build/exponentVaults/vault.js.map +1 -1
  115. package/build/exponentVaults/vaultTransactionBuilder.d.ts +100 -134
  116. package/build/exponentVaults/vaultTransactionBuilder.js +383 -285
  117. package/build/exponentVaults/vaultTransactionBuilder.js.map +1 -1
  118. package/build/exponentVaults/vaultTransactionBuilder.test.d.ts +1 -0
  119. package/build/exponentVaults/vaultTransactionBuilder.test.js +297 -0
  120. package/build/exponentVaults/vaultTransactionBuilder.test.js.map +1 -0
  121. package/build/marketThree.d.ts +6 -2
  122. package/build/marketThree.js +10 -8
  123. package/build/marketThree.js.map +1 -1
  124. package/package.json +34 -32
  125. package/src/client/vaults/index.ts +2 -0
  126. package/src/client/vaults/types/index.ts +2 -0
  127. package/src/client/vaults/types/kaminoFarmEntry.ts +32 -0
  128. package/src/client/vaults/types/kaminoObligationEntry.ts +6 -3
  129. package/src/client/vaults/types/positionUpdate.ts +62 -0
  130. package/src/client/vaults/types/proposalAction.ts +0 -3
  131. package/src/client/vaults/types/reserveFarmMapping.ts +35 -0
  132. package/src/client/vaults/types/strategyPosition.ts +18 -1
  133. package/src/exponentVaults/aumCalculator.ts +353 -16
  134. package/src/exponentVaults/fetcher.ts +257 -0
  135. package/src/exponentVaults/index.ts +65 -40
  136. package/src/exponentVaults/kamino-farms.ts +538 -0
  137. package/src/exponentVaults/loopscale/client.ts +808 -0
  138. package/src/exponentVaults/loopscale/helpers.ts +172 -0
  139. package/src/exponentVaults/loopscale/index.ts +57 -0
  140. package/src/exponentVaults/loopscale/prepared-transactions.ts +435 -0
  141. package/src/exponentVaults/loopscale/prepared-types.ts +73 -0
  142. package/src/exponentVaults/loopscale/types.ts +466 -0
  143. package/src/exponentVaults/policyBuilders.ts +170 -0
  144. package/src/exponentVaults/pricePathResolver.test.ts +466 -0
  145. package/src/exponentVaults/pricePathResolver.ts +273 -0
  146. package/src/exponentVaults/syncTransaction.ts +6 -1
  147. package/src/exponentVaults/titan-quote.ts +231 -45
  148. package/src/exponentVaults/vault-instruction-types.ts +493 -0
  149. package/src/exponentVaults/vault-interaction.kamino-vault.test.ts +149 -0
  150. package/src/exponentVaults/vault-interaction.ts +2818 -799
  151. package/src/exponentVaults/vault.ts +474 -63
  152. package/src/exponentVaults/vaultTransactionBuilder.test.ts +349 -0
  153. package/src/exponentVaults/vaultTransactionBuilder.ts +581 -433
  154. package/src/marketThree.ts +14 -6
  155. package/src/exponentVaults/loopscale-client.ts +0 -1373
@@ -0,0 +1,538 @@
1
+ import { PublicKey } from "@solana/web3.js"
2
+
3
+ const U64_MAX = 18_446_744_073_709_551_615n
4
+ const DECIMAL_WAD = 1_000_000_000_000_000_000n
5
+ const BPS_DIV_FACTOR = 10_000n
6
+ const DEFAULT_PUBLIC_KEY = PublicKey.default
7
+
8
+ export const KAMINO_FARM_ACCOUNT_DISCRIMINATOR_LEN = 8
9
+ export const KAMINO_FARM_GLOBAL_CONFIG_OFFSET = 32
10
+ export const KAMINO_FARM_GLOBAL_CONFIG_TREASURY_FEE_BPS_OFFSET = 32
11
+ export const KAMINO_FARM_TOKEN_MINT_OFFSET = 64
12
+ export const KAMINO_FARM_TOKEN_PROGRAM_OFFSET = 104
13
+ export const KAMINO_FARM_REWARD_INFOS_OFFSET = 184
14
+ export const KAMINO_FARM_REWARD_INFO_SIZE = 704
15
+ export const KAMINO_FARM_REWARD_TOKEN_MINT_OFFSET = 0
16
+ export const KAMINO_FARM_REWARD_TOKEN_PROGRAM_OFFSET = 40
17
+ export const KAMINO_FARM_REWARD_VAULT_OFFSET = 120
18
+ export const KAMINO_FARM_REWARDS_AVAILABLE_OFFSET = 152
19
+ export const KAMINO_FARM_REWARD_SCHEDULE_CURVE_OFFSET = 160
20
+ export const KAMINO_FARM_REWARD_SCHEDULE_CURVE_POINTS = 20
21
+ export const KAMINO_FARM_REWARD_SCHEDULE_CURVE_POINT_SIZE = 16
22
+ export const KAMINO_FARM_LAST_ISSUANCE_TS_OFFSET = 488
23
+ export const KAMINO_FARM_REWARDS_ISSUED_UNCLAIMED_OFFSET = 496
24
+ export const KAMINO_FARM_REWARDS_ISSUED_CUMULATIVE_OFFSET = 504
25
+ export const KAMINO_FARM_REWARD_PER_SHARE_SCALED_OFFSET = 512
26
+ export const KAMINO_FARM_REWARD_TYPE_OFFSET = 536
27
+ export const KAMINO_FARM_REWARDS_PER_SECOND_DECIMALS_OFFSET = 537
28
+ export const KAMINO_FARM_NUM_REWARD_TOKENS_OFFSET = 7224
29
+ export const KAMINO_FARM_TOTAL_STAKED_AMOUNT_OFFSET = 7240
30
+ export const KAMINO_FARM_FARM_VAULT_OFFSET = 7248
31
+ export const KAMINO_FARM_FARM_VAULTS_AUTHORITY_OFFSET = 7280
32
+ export const KAMINO_FARM_FARM_VAULTS_AUTHORITY_BUMP_OFFSET = 7312
33
+ export const KAMINO_FARM_DELEGATE_AUTHORITY_OFFSET = 7320
34
+ export const KAMINO_FARM_TIME_UNIT_OFFSET = 7352
35
+ export const KAMINO_FARM_IS_FARM_DELEGATED_OFFSET = 7354
36
+ export const KAMINO_FARM_DEPOSIT_WARMUP_PERIOD_OFFSET = 7392
37
+ export const KAMINO_FARM_WITHDRAWAL_COOLDOWN_PERIOD_OFFSET = 7396
38
+ export const KAMINO_FARM_TOTAL_ACTIVE_STAKE_SCALED_OFFSET = 7400
39
+ export const KAMINO_FARM_TOTAL_PENDING_STAKE_SCALED_OFFSET = 7416
40
+ export const KAMINO_FARM_TOTAL_PENDING_AMOUNT_OFFSET = 7432
41
+ export const KAMINO_FARM_SCOPE_PRICES_OFFSET = 7528
42
+ export const KAMINO_FARM_SCOPE_ORACLE_PRICE_ID_OFFSET = 7560
43
+ export const KAMINO_FARM_SCOPE_ORACLE_MAX_AGE_OFFSET = 7568
44
+
45
+ export const KAMINO_FARM_USER_OWNER_OFFSET = 40
46
+ export const KAMINO_FARM_USER_REWARDS_TALLY_SCALED_OFFSET = 80
47
+ export const KAMINO_FARM_USER_REWARDS_ISSUED_UNCLAIMED_OFFSET = 240
48
+ export const KAMINO_FARM_USER_LAST_CLAIM_TS_OFFSET = 320
49
+ export const KAMINO_FARM_USER_ACTIVE_STAKE_SCALED_OFFSET = 400
50
+ export const KAMINO_FARM_USER_PENDING_DEPOSIT_STAKE_SCALED_OFFSET = 416
51
+ export const KAMINO_FARM_USER_PENDING_WITHDRAWAL_UNSTAKE_SCALED_OFFSET = 440
52
+ export const KAMINO_FARM_USER_DELEGATEE_OFFSET = 472
53
+
54
+ export const KAMINO_SCOPE_ACCOUNT_DISCRIMINATOR_LEN = 8
55
+ export const KAMINO_SCOPE_ORACLE_PRICES_ARRAY_OFFSET = 32
56
+ export const KAMINO_SCOPE_DATED_PRICE_SIZE = 56
57
+ export const KAMINO_SCOPE_PRICE_VALUE_OFFSET = 0
58
+ export const KAMINO_SCOPE_PRICE_EXP_OFFSET = 8
59
+ export const KAMINO_SCOPE_LAST_UPDATED_SLOT_OFFSET = 16
60
+ export const KAMINO_SCOPE_UNIX_TIMESTAMP_OFFSET = 24
61
+
62
+ export enum KaminoFarmTimeUnit {
63
+ Seconds = 0,
64
+ Slots = 1,
65
+ }
66
+
67
+ export enum KaminoFarmRewardType {
68
+ Proportional = 0,
69
+ Constant = 1,
70
+ }
71
+
72
+ export type KaminoFarmRewardSchedulePoint = {
73
+ tsStart: bigint
74
+ rewardPerTimeUnit: bigint
75
+ }
76
+
77
+ export type KaminoFarmRewardInfoView = {
78
+ rewardMint: PublicKey
79
+ tokenProgram: PublicKey
80
+ rewardsVault: PublicKey
81
+ rewardsAvailable: bigint
82
+ rewardScheduleCurve: KaminoFarmRewardSchedulePoint[]
83
+ lastIssuanceTs: bigint
84
+ rewardsIssuedUnclaimed: bigint
85
+ rewardsIssuedCumulative: bigint
86
+ rewardPerShareScaled: bigint
87
+ rewardType: KaminoFarmRewardType
88
+ rewardsPerSecondDecimals: number
89
+ }
90
+
91
+ export type KaminoFarmStateView = {
92
+ globalConfig: PublicKey
93
+ underlyingMint: PublicKey
94
+ tokenProgram: PublicKey
95
+ farmVault: PublicKey
96
+ farmVaultsAuthority: PublicKey
97
+ farmVaultsAuthorityBump: bigint
98
+ delegateAuthority: PublicKey
99
+ timeUnit: KaminoFarmTimeUnit
100
+ isDelegated: boolean
101
+ depositWarmupPeriod: number
102
+ withdrawalCooldownPeriod: number
103
+ totalStakedAmount: bigint
104
+ totalActiveStakeScaled: bigint
105
+ totalPendingStakeScaled: bigint
106
+ totalPendingAmount: bigint
107
+ numRewardTokens: number
108
+ scopePrices: PublicKey
109
+ scopeOraclePriceId: bigint
110
+ scopeOracleMaxAge: bigint
111
+ rewardInfos: KaminoFarmRewardInfoView[]
112
+ }
113
+
114
+ export type KaminoFarmUserStateView = {
115
+ owner: PublicKey
116
+ rewardsTallyScaled: bigint[]
117
+ rewardsIssuedUnclaimed: bigint[]
118
+ lastClaimTs: bigint[]
119
+ activeStakeScaled: bigint
120
+ pendingDepositStakeScaled: bigint
121
+ pendingWithdrawalUnstakeScaled: bigint
122
+ delegatee: PublicKey
123
+ }
124
+
125
+ export type KaminoScopeDatedPrice = {
126
+ value: bigint
127
+ exp: bigint
128
+ lastUpdatedSlot: bigint
129
+ unixTimestamp: bigint
130
+ }
131
+
132
+ export type KaminoFarmProjectedRewards = {
133
+ projectedRewardInfos: KaminoFarmRewardInfoView[]
134
+ projectedRewardsTallyScaled: bigint[]
135
+ projectedRewardsIssuedUnclaimed: bigint[]
136
+ }
137
+
138
+ function readPubkey(data: Buffer, offset: number): PublicKey {
139
+ return new PublicKey(data.subarray(offset, offset + 32))
140
+ }
141
+
142
+ function readU64LE(data: Buffer, offset: number): bigint {
143
+ return data.readBigUInt64LE(offset)
144
+ }
145
+
146
+ export function readU128LE(data: Buffer, offset: number): bigint {
147
+ return data.readBigUInt64LE(offset) + (data.readBigUInt64LE(offset + 8) << 64n)
148
+ }
149
+
150
+ function tenPow(exp: number): bigint {
151
+ const POWERS: bigint[] = [
152
+ 1n,
153
+ 10n,
154
+ 100n,
155
+ 1_000n,
156
+ 10_000n,
157
+ 100_000n,
158
+ 1_000_000n,
159
+ 10_000_000n,
160
+ 100_000_000n,
161
+ 1_000_000_000n,
162
+ 10_000_000_000n,
163
+ 100_000_000_000n,
164
+ 1_000_000_000_000n,
165
+ 10_000_000_000_000n,
166
+ 100_000_000_000_000n,
167
+ 1_000_000_000_000_000n,
168
+ 10_000_000_000_000_000n,
169
+ 100_000_000_000_000_000n,
170
+ 1_000_000_000_000_000_000n,
171
+ 10_000_000_000_000_000_000n,
172
+ ]
173
+ if (exp < 0 || exp >= POWERS.length) {
174
+ throw new Error(`Invalid power-of-ten exponent ${exp}`)
175
+ }
176
+ return POWERS[exp]
177
+ }
178
+
179
+ export function decodeKaminoFarmState(data: Buffer): KaminoFarmStateView {
180
+ const base = KAMINO_FARM_ACCOUNT_DISCRIMINATOR_LEN
181
+ const numRewardTokens = Number(readU64LE(data, base + KAMINO_FARM_NUM_REWARD_TOKENS_OFFSET))
182
+ if (numRewardTokens < 0 || numRewardTokens > 10) {
183
+ throw new Error(`Invalid Kamino farm reward count: ${numRewardTokens}`)
184
+ }
185
+
186
+ const rewardInfos: KaminoFarmRewardInfoView[] = []
187
+ for (let i = 0; i < numRewardTokens; i += 1) {
188
+ const rewardBase = base + KAMINO_FARM_REWARD_INFOS_OFFSET + (i * KAMINO_FARM_REWARD_INFO_SIZE)
189
+ const rewardScheduleCurve: KaminoFarmRewardSchedulePoint[] = []
190
+ for (let pointIndex = 0; pointIndex < KAMINO_FARM_REWARD_SCHEDULE_CURVE_POINTS; pointIndex += 1) {
191
+ const pointBase = rewardBase
192
+ + KAMINO_FARM_REWARD_SCHEDULE_CURVE_OFFSET
193
+ + (pointIndex * KAMINO_FARM_REWARD_SCHEDULE_CURVE_POINT_SIZE)
194
+ rewardScheduleCurve.push({
195
+ tsStart: readU64LE(data, pointBase),
196
+ rewardPerTimeUnit: readU64LE(data, pointBase + 8),
197
+ })
198
+ }
199
+
200
+ rewardInfos.push({
201
+ rewardMint: readPubkey(data, rewardBase + KAMINO_FARM_REWARD_TOKEN_MINT_OFFSET),
202
+ tokenProgram: readPubkey(data, rewardBase + KAMINO_FARM_REWARD_TOKEN_PROGRAM_OFFSET),
203
+ rewardsVault: readPubkey(data, rewardBase + KAMINO_FARM_REWARD_VAULT_OFFSET),
204
+ rewardsAvailable: readU64LE(data, rewardBase + KAMINO_FARM_REWARDS_AVAILABLE_OFFSET),
205
+ rewardScheduleCurve,
206
+ lastIssuanceTs: readU64LE(data, rewardBase + KAMINO_FARM_LAST_ISSUANCE_TS_OFFSET),
207
+ rewardsIssuedUnclaimed: readU64LE(data, rewardBase + KAMINO_FARM_REWARDS_ISSUED_UNCLAIMED_OFFSET),
208
+ rewardsIssuedCumulative: readU64LE(data, rewardBase + KAMINO_FARM_REWARDS_ISSUED_CUMULATIVE_OFFSET),
209
+ rewardPerShareScaled: readU128LE(data, rewardBase + KAMINO_FARM_REWARD_PER_SHARE_SCALED_OFFSET),
210
+ rewardType: data[rewardBase + KAMINO_FARM_REWARD_TYPE_OFFSET] as KaminoFarmRewardType,
211
+ rewardsPerSecondDecimals: data[rewardBase + KAMINO_FARM_REWARDS_PER_SECOND_DECIMALS_OFFSET],
212
+ })
213
+ }
214
+
215
+ const delegateAuthority = readPubkey(data, base + KAMINO_FARM_DELEGATE_AUTHORITY_OFFSET)
216
+ return {
217
+ globalConfig: readPubkey(data, base + KAMINO_FARM_GLOBAL_CONFIG_OFFSET),
218
+ underlyingMint: readPubkey(data, base + KAMINO_FARM_TOKEN_MINT_OFFSET),
219
+ tokenProgram: readPubkey(data, base + KAMINO_FARM_TOKEN_PROGRAM_OFFSET),
220
+ farmVault: readPubkey(data, base + KAMINO_FARM_FARM_VAULT_OFFSET),
221
+ farmVaultsAuthority: readPubkey(data, base + KAMINO_FARM_FARM_VAULTS_AUTHORITY_OFFSET),
222
+ farmVaultsAuthorityBump: readU64LE(data, base + KAMINO_FARM_FARM_VAULTS_AUTHORITY_BUMP_OFFSET),
223
+ delegateAuthority,
224
+ timeUnit: data[base + KAMINO_FARM_TIME_UNIT_OFFSET] as KaminoFarmTimeUnit,
225
+ isDelegated:
226
+ data[base + KAMINO_FARM_IS_FARM_DELEGATED_OFFSET] !== 0
227
+ || !delegateAuthority.equals(DEFAULT_PUBLIC_KEY),
228
+ depositWarmupPeriod: data.readUInt32LE(base + KAMINO_FARM_DEPOSIT_WARMUP_PERIOD_OFFSET),
229
+ withdrawalCooldownPeriod: data.readUInt32LE(base + KAMINO_FARM_WITHDRAWAL_COOLDOWN_PERIOD_OFFSET),
230
+ totalStakedAmount: readU64LE(data, base + KAMINO_FARM_TOTAL_STAKED_AMOUNT_OFFSET),
231
+ totalActiveStakeScaled: readU128LE(data, base + KAMINO_FARM_TOTAL_ACTIVE_STAKE_SCALED_OFFSET),
232
+ totalPendingStakeScaled: readU128LE(data, base + KAMINO_FARM_TOTAL_PENDING_STAKE_SCALED_OFFSET),
233
+ totalPendingAmount: readU64LE(data, base + KAMINO_FARM_TOTAL_PENDING_AMOUNT_OFFSET),
234
+ numRewardTokens,
235
+ scopePrices: readPubkey(data, base + KAMINO_FARM_SCOPE_PRICES_OFFSET),
236
+ scopeOraclePriceId: readU64LE(data, base + KAMINO_FARM_SCOPE_ORACLE_PRICE_ID_OFFSET),
237
+ scopeOracleMaxAge: readU64LE(data, base + KAMINO_FARM_SCOPE_ORACLE_MAX_AGE_OFFSET),
238
+ rewardInfos,
239
+ }
240
+ }
241
+
242
+ export function decodeKaminoFarmUserState(data: Buffer): KaminoFarmUserStateView {
243
+ const base = KAMINO_FARM_ACCOUNT_DISCRIMINATOR_LEN
244
+ const rewardsTallyScaled: bigint[] = []
245
+ const rewardsIssuedUnclaimed: bigint[] = []
246
+ const lastClaimTs: bigint[] = []
247
+ for (let i = 0; i < 10; i += 1) {
248
+ rewardsTallyScaled.push(readU128LE(data, base + KAMINO_FARM_USER_REWARDS_TALLY_SCALED_OFFSET + (i * 16)))
249
+ rewardsIssuedUnclaimed.push(readU64LE(data, base + KAMINO_FARM_USER_REWARDS_ISSUED_UNCLAIMED_OFFSET + (i * 8)))
250
+ lastClaimTs.push(readU64LE(data, base + KAMINO_FARM_USER_LAST_CLAIM_TS_OFFSET + (i * 8)))
251
+ }
252
+
253
+ return {
254
+ owner: readPubkey(data, base + KAMINO_FARM_USER_OWNER_OFFSET),
255
+ rewardsTallyScaled,
256
+ rewardsIssuedUnclaimed,
257
+ lastClaimTs,
258
+ activeStakeScaled: readU128LE(data, base + KAMINO_FARM_USER_ACTIVE_STAKE_SCALED_OFFSET),
259
+ pendingDepositStakeScaled: readU128LE(data, base + KAMINO_FARM_USER_PENDING_DEPOSIT_STAKE_SCALED_OFFSET),
260
+ pendingWithdrawalUnstakeScaled: readU128LE(
261
+ data,
262
+ base + KAMINO_FARM_USER_PENDING_WITHDRAWAL_UNSTAKE_SCALED_OFFSET,
263
+ ),
264
+ delegatee: readPubkey(data, base + KAMINO_FARM_USER_DELEGATEE_OFFSET),
265
+ }
266
+ }
267
+
268
+ export function decodeKaminoScopeDatedPrice(data: Buffer, priceId: bigint): KaminoScopeDatedPrice {
269
+ const priceOffset = KAMINO_SCOPE_ACCOUNT_DISCRIMINATOR_LEN
270
+ + KAMINO_SCOPE_ORACLE_PRICES_ARRAY_OFFSET
271
+ + (Number(priceId) * KAMINO_SCOPE_DATED_PRICE_SIZE)
272
+ return {
273
+ value: readU64LE(data, priceOffset + KAMINO_SCOPE_PRICE_VALUE_OFFSET),
274
+ exp: readU64LE(data, priceOffset + KAMINO_SCOPE_PRICE_EXP_OFFSET),
275
+ lastUpdatedSlot: readU64LE(data, priceOffset + KAMINO_SCOPE_LAST_UPDATED_SLOT_OFFSET),
276
+ unixTimestamp: readU64LE(data, priceOffset + KAMINO_SCOPE_UNIX_TIMESTAMP_OFFSET),
277
+ }
278
+ }
279
+
280
+ export function kaminoFarmUsesScopeOracle(farm: KaminoFarmStateView): boolean {
281
+ return farm.scopeOraclePriceId !== U64_MAX
282
+ }
283
+
284
+ export function getKaminoFarmScopePricesAddress(farm: KaminoFarmStateView): PublicKey | null {
285
+ if (!kaminoFarmUsesScopeOracle(farm) || farm.scopePrices.equals(DEFAULT_PUBLIC_KEY)) {
286
+ return null
287
+ }
288
+ return farm.scopePrices
289
+ }
290
+
291
+ export function convertKaminoStakeToAmount(
292
+ stake: bigint,
293
+ totalStake: bigint,
294
+ totalAmount: bigint,
295
+ ): bigint {
296
+ if (stake === 0n) {
297
+ return 0n
298
+ }
299
+ if (totalStake === 0n) {
300
+ return totalAmount
301
+ }
302
+ return (stake * totalAmount) / totalStake
303
+ }
304
+
305
+ export function calculateKaminoFarmPrincipalAmount(
306
+ farm: KaminoFarmStateView,
307
+ user: KaminoFarmUserStateView,
308
+ ): bigint {
309
+ const activeAmount = convertKaminoStakeToAmount(
310
+ user.activeStakeScaled,
311
+ farm.totalActiveStakeScaled,
312
+ farm.totalStakedAmount,
313
+ )
314
+ const pendingStake = user.pendingDepositStakeScaled + user.pendingWithdrawalUnstakeScaled
315
+ const pendingAmount = convertKaminoStakeToAmount(
316
+ pendingStake,
317
+ farm.totalPendingStakeScaled,
318
+ farm.totalPendingAmount,
319
+ )
320
+ return activeAmount + pendingAmount
321
+ }
322
+
323
+ function getCurrentFarmTs(
324
+ farm: KaminoFarmStateView,
325
+ params: { currentSlot: bigint; currentUnixTimestamp: bigint },
326
+ ): bigint {
327
+ return farm.timeUnit === KaminoFarmTimeUnit.Slots
328
+ ? params.currentSlot
329
+ : params.currentUnixTimestamp
330
+ }
331
+
332
+ // Read-only mirror of KFarms RewardScheduleCurve::most_recent_curve_starting_point and
333
+ // RewardScheduleCurve::get_cumulative_amount_issued_since_last_ts.
334
+ // Source:
335
+ // https://github.com/Kamino-Finance/kfarms/blob/ae4be1b33808bae692edd9b06aecfac24714b100/programs/kfarms/src/state.rs#L344-L397
336
+ // Differences:
337
+ // - operates on decoded JS views
338
+ // - throws JS errors instead of returning Anchor errors
339
+ function getCumulativeAmountIssuedSinceLastTs(
340
+ rewardScheduleCurve: KaminoFarmRewardSchedulePoint[],
341
+ lastIssuedTs: bigint,
342
+ currentTs: bigint,
343
+ ): bigint {
344
+ if (lastIssuedTs > currentTs) {
345
+ throw new Error("Kamino farm reward schedule has lastIssuedTs > currentTs")
346
+ }
347
+
348
+ let startIndex = rewardScheduleCurve.length - 1
349
+ for (let i = 0; i < rewardScheduleCurve.length; i += 1) {
350
+ if (rewardScheduleCurve[i].tsStart > lastIssuedTs) {
351
+ if (i === 0) {
352
+ throw new Error("Kamino farm reward schedule first point starts after lastIssuedTs")
353
+ }
354
+ startIndex = i - 1
355
+ break
356
+ }
357
+ }
358
+
359
+ let cumulativeAmount = 0n
360
+ for (let i = startIndex; i < rewardScheduleCurve.length; i += 1) {
361
+ const point = rewardScheduleCurve[i]
362
+ if (point.tsStart >= currentTs) {
363
+ break
364
+ }
365
+
366
+ const startTs = point.tsStart > lastIssuedTs ? point.tsStart : lastIssuedTs
367
+ const endTs = i < rewardScheduleCurve.length - 1 && rewardScheduleCurve[i + 1].tsStart < currentTs
368
+ ? rewardScheduleCurve[i + 1].tsStart
369
+ : currentTs
370
+ cumulativeAmount += point.rewardPerTimeUnit * (endTs - startTs)
371
+ }
372
+
373
+ return cumulativeAmount
374
+ }
375
+
376
+ function applyKaminoFarmScopePrice(
377
+ farm: KaminoFarmStateView,
378
+ amount: bigint,
379
+ scopePrice: KaminoScopeDatedPrice | null,
380
+ currentTs: bigint,
381
+ ): bigint {
382
+ if (!kaminoFarmUsesScopeOracle(farm)) {
383
+ return amount
384
+ }
385
+ if (!scopePrice) {
386
+ throw new Error(`Missing scope prices for Kamino farm ${farm.scopePrices.toBase58()}`)
387
+ }
388
+ if (currentTs < scopePrice.unixTimestamp) {
389
+ throw new Error("Kamino farm scope price timestamp is in the future")
390
+ }
391
+ if (currentTs - scopePrice.unixTimestamp > farm.scopeOracleMaxAge) {
392
+ throw new Error("Kamino farm scope price is too old")
393
+ }
394
+ return (amount * scopePrice.value) / tenPow(Number(scopePrice.exp))
395
+ }
396
+
397
+ // Read-only mirror of KFarms refresh_global_reward.
398
+ // Source:
399
+ // https://github.com/Kamino-Finance/kfarms/blob/ae4be1b33808bae692edd9b06aecfac24714b100/programs/kfarms/src/farm_operations.rs#L759-L850
400
+ // Differences:
401
+ // - custody farms only
402
+ // - operates on copied views and returns projected reward info
403
+ export function projectKaminoFarmGlobalReward(
404
+ farm: KaminoFarmStateView,
405
+ rewardInfo: KaminoFarmRewardInfoView,
406
+ scopePrice: KaminoScopeDatedPrice | null,
407
+ params: { currentSlot: bigint; currentUnixTimestamp: bigint },
408
+ ): KaminoFarmRewardInfoView {
409
+ const currentTs = getCurrentFarmTs(farm, params)
410
+ if (currentTs === rewardInfo.lastIssuanceTs) {
411
+ return rewardInfo
412
+ }
413
+
414
+ if (farm.totalActiveStakeScaled === 0n) {
415
+ return {
416
+ ...rewardInfo,
417
+ lastIssuanceTs: currentTs,
418
+ }
419
+ }
420
+
421
+ const cumulativeAmount = getCumulativeAmountIssuedSinceLastTs(
422
+ rewardInfo.rewardScheduleCurve,
423
+ rewardInfo.lastIssuanceTs,
424
+ currentTs,
425
+ )
426
+
427
+ const rewardTypeAmount = rewardInfo.rewardType === KaminoFarmRewardType.Constant
428
+ ? cumulativeAmount * farm.totalStakedAmount
429
+ : cumulativeAmount
430
+ const decimalAdjustedAmount = rewardTypeAmount / tenPow(rewardInfo.rewardsPerSecondDecimals)
431
+ const oracleAdjustedAmount = applyKaminoFarmScopePrice(farm, decimalAdjustedAmount, scopePrice, currentTs)
432
+
433
+ if (oracleAdjustedAmount === 0n) {
434
+ return rewardInfo
435
+ }
436
+
437
+ const rewards = oracleAdjustedAmount < rewardInfo.rewardsAvailable
438
+ ? oracleAdjustedAmount
439
+ : rewardInfo.rewardsAvailable
440
+ const rewardTimesWad = rewards * DECIMAL_WAD
441
+ const addedRewardPerShareScaled = farm.isDelegated
442
+ ? rewardTimesWad / farm.totalActiveStakeScaled
443
+ : (rewardTimesWad * DECIMAL_WAD) / farm.totalActiveStakeScaled
444
+
445
+ return {
446
+ ...rewardInfo,
447
+ lastIssuanceTs: currentTs,
448
+ rewardsIssuedUnclaimed: rewardInfo.rewardsIssuedUnclaimed + rewards,
449
+ rewardsIssuedCumulative: rewardInfo.rewardsIssuedCumulative + rewards,
450
+ rewardsAvailable: rewardInfo.rewardsAvailable - rewards,
451
+ rewardPerShareScaled: rewardInfo.rewardPerShareScaled + addedRewardPerShareScaled,
452
+ }
453
+ }
454
+
455
+ // Read-only mirror of KFarms refresh_global_rewards.
456
+ // Source:
457
+ // https://github.com/Kamino-Finance/kfarms/blob/ae4be1b33808bae692edd9b06aecfac24714b100/programs/kfarms/src/farm_operations.rs#L852-L862
458
+ // Differences:
459
+ // - operates on copied reward infos
460
+ export function projectKaminoFarmGlobalRewards(
461
+ farm: KaminoFarmStateView,
462
+ scopePrice: KaminoScopeDatedPrice | null,
463
+ params: { currentSlot: bigint; currentUnixTimestamp: bigint },
464
+ ): KaminoFarmRewardInfoView[] {
465
+ return farm.rewardInfos.map((rewardInfo) =>
466
+ projectKaminoFarmGlobalReward(farm, rewardInfo, scopePrice, params),
467
+ )
468
+ }
469
+
470
+ // Read-only mirror of KFarms user_refresh_reward.
471
+ // Source:
472
+ // https://github.com/Kamino-Finance/kfarms/blob/ae4be1b33808bae692edd9b06aecfac24714b100/programs/kfarms/src/farm_operations.rs#L553-L592
473
+ // Differences:
474
+ // - returns projected tally / rewards instead of mutating UserState
475
+ // - clamps small negative valuation deltas to zero instead of throwing so
476
+ // read-only AUM can tolerate post-harvest rounding drift
477
+ export function projectKaminoFarmUserReward(
478
+ farm: KaminoFarmStateView,
479
+ user: KaminoFarmUserStateView,
480
+ rewardInfo: KaminoFarmRewardInfoView,
481
+ rewardIndex: number,
482
+ ): { rewardsTallyScaled: bigint; rewardsIssuedUnclaimed: bigint } {
483
+ const rewardsTallyScaled = user.rewardsTallyScaled[rewardIndex] ?? 0n
484
+ const rewardsIssuedUnclaimed = user.rewardsIssuedUnclaimed[rewardIndex] ?? 0n
485
+ const rewardPerShareTimesStake = rewardInfo.rewardPerShareScaled * user.activeStakeScaled
486
+ const newRewardTallyScaled = farm.isDelegated
487
+ ? rewardPerShareTimesStake
488
+ : rewardPerShareTimesStake / DECIMAL_WAD
489
+ if (newRewardTallyScaled < rewardsTallyScaled) {
490
+ return {
491
+ rewardsTallyScaled,
492
+ rewardsIssuedUnclaimed,
493
+ }
494
+ }
495
+ const reward = (newRewardTallyScaled - rewardsTallyScaled) / DECIMAL_WAD
496
+ return {
497
+ rewardsTallyScaled: rewardsTallyScaled + (reward * DECIMAL_WAD),
498
+ rewardsIssuedUnclaimed: rewardsIssuedUnclaimed + reward,
499
+ }
500
+ }
501
+
502
+ // Read-only mirror of KFarms user_refresh_all_rewards.
503
+ // Source:
504
+ // https://github.com/Kamino-Finance/kfarms/blob/ae4be1b33808bae692edd9b06aecfac24714b100/programs/kfarms/src/farm_operations.rs#L595-L605
505
+ // Differences:
506
+ // - returns projected arrays instead of mutating UserState
507
+ export function projectKaminoFarmUserRewards(
508
+ farm: KaminoFarmStateView,
509
+ user: KaminoFarmUserStateView,
510
+ projectedRewardInfos: KaminoFarmRewardInfoView[],
511
+ ): KaminoFarmProjectedRewards {
512
+ const projectedRewardsTallyScaled = [...user.rewardsTallyScaled]
513
+ const projectedRewardsIssuedUnclaimed = [...user.rewardsIssuedUnclaimed]
514
+ if (user.activeStakeScaled === 0n) {
515
+ return {
516
+ projectedRewardInfos,
517
+ projectedRewardsTallyScaled,
518
+ projectedRewardsIssuedUnclaimed,
519
+ }
520
+ }
521
+
522
+ for (let rewardIndex = 0; rewardIndex < farm.numRewardTokens; rewardIndex += 1) {
523
+ const projected = projectKaminoFarmUserReward(farm, user, projectedRewardInfos[rewardIndex], rewardIndex)
524
+ projectedRewardsTallyScaled[rewardIndex] = projected.rewardsTallyScaled
525
+ projectedRewardsIssuedUnclaimed[rewardIndex] = projected.rewardsIssuedUnclaimed
526
+ }
527
+
528
+ return {
529
+ projectedRewardInfos,
530
+ projectedRewardsTallyScaled,
531
+ projectedRewardsIssuedUnclaimed,
532
+ }
533
+ }
534
+
535
+ export function applyKaminoFarmTreasuryFee(reward: bigint, treasuryFeeBps: bigint): bigint {
536
+ const rewardTreasury = (reward * treasuryFeeBps) / BPS_DIV_FACTOR
537
+ return reward - rewardTreasury
538
+ }