@symmetry-hq/sdk 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/dist/src/constants.d.ts +23 -0
  2. package/dist/src/constants.js +38 -0
  3. package/dist/src/index.d.ts +804 -0
  4. package/dist/src/index.js +2097 -0
  5. package/dist/src/instructions/automation/auction.d.ts +6 -0
  6. package/dist/src/instructions/automation/auction.js +40 -0
  7. package/dist/src/instructions/automation/claimBounty.d.ts +12 -0
  8. package/dist/src/instructions/automation/claimBounty.js +44 -0
  9. package/dist/src/instructions/automation/flashSwap.d.ts +21 -0
  10. package/dist/src/instructions/automation/flashSwap.js +74 -0
  11. package/dist/src/instructions/automation/priceUpdate.d.ts +19 -0
  12. package/dist/src/instructions/automation/priceUpdate.js +89 -0
  13. package/dist/src/instructions/automation/rebalanceIntent.d.ts +32 -0
  14. package/dist/src/instructions/automation/rebalanceIntent.js +117 -0
  15. package/dist/src/instructions/automation/rebalanceSwap.d.ts +11 -0
  16. package/dist/src/instructions/automation/rebalanceSwap.js +42 -0
  17. package/dist/src/instructions/management/addBounty.d.ts +7 -0
  18. package/dist/src/instructions/management/addBounty.js +41 -0
  19. package/dist/src/instructions/management/admin.d.ts +9 -0
  20. package/dist/src/instructions/management/admin.js +53 -0
  21. package/dist/src/instructions/management/claimFees.d.ts +15 -0
  22. package/dist/src/instructions/management/claimFees.js +95 -0
  23. package/dist/src/instructions/management/createBasket.d.ts +21 -0
  24. package/dist/src/instructions/management/createBasket.js +98 -0
  25. package/dist/src/instructions/management/edit.d.ts +51 -0
  26. package/dist/src/instructions/management/edit.js +477 -0
  27. package/dist/src/instructions/management/luts.d.ts +30 -0
  28. package/dist/src/instructions/management/luts.js +99 -0
  29. package/dist/src/instructions/pda.d.ts +25 -0
  30. package/dist/src/instructions/pda.js +128 -0
  31. package/dist/src/instructions/user/deposit.d.ts +20 -0
  32. package/dist/src/instructions/user/deposit.js +100 -0
  33. package/dist/src/instructions/user/withdraw.d.ts +8 -0
  34. package/dist/src/instructions/user/withdraw.js +36 -0
  35. package/dist/src/jup.d.ts +49 -0
  36. package/dist/src/jup.js +80 -0
  37. package/dist/src/keeperMonitor.d.ts +52 -0
  38. package/dist/src/keeperMonitor.js +624 -0
  39. package/dist/src/layouts/basket.d.ts +191 -0
  40. package/dist/src/layouts/basket.js +51 -0
  41. package/dist/src/layouts/config.d.ts +281 -0
  42. package/dist/src/layouts/config.js +237 -0
  43. package/dist/src/layouts/fraction.d.ts +20 -0
  44. package/dist/src/layouts/fraction.js +164 -0
  45. package/dist/src/layouts/intents/bounty.d.ts +18 -0
  46. package/dist/src/layouts/intents/bounty.js +19 -0
  47. package/dist/src/layouts/intents/intent.d.ts +209 -0
  48. package/dist/src/layouts/intents/intent.js +97 -0
  49. package/dist/src/layouts/intents/rebalanceIntent.d.ts +212 -0
  50. package/dist/src/layouts/intents/rebalanceIntent.js +94 -0
  51. package/dist/src/layouts/lookupTable.d.ts +7 -0
  52. package/dist/src/layouts/lookupTable.js +10 -0
  53. package/dist/src/layouts/oracle.d.ts +63 -0
  54. package/dist/src/layouts/oracle.js +96 -0
  55. package/dist/src/states/basket.d.ts +14 -0
  56. package/dist/src/states/basket.js +479 -0
  57. package/dist/src/states/config.d.ts +3 -0
  58. package/dist/src/states/config.js +71 -0
  59. package/dist/src/states/intents/intent.d.ts +10 -0
  60. package/dist/src/states/intents/intent.js +316 -0
  61. package/dist/src/states/intents/rebalanceIntent.d.ts +42 -0
  62. package/dist/src/states/intents/rebalanceIntent.js +680 -0
  63. package/dist/src/states/oracles/constants.d.ts +9 -0
  64. package/dist/src/states/oracles/constants.js +15 -0
  65. package/dist/src/states/oracles/oracle.d.ts +24 -0
  66. package/dist/src/states/oracles/oracle.js +168 -0
  67. package/dist/src/states/oracles/pythOracle.d.ts +132 -0
  68. package/dist/src/states/oracles/pythOracle.js +609 -0
  69. package/dist/src/states/oracles/raydiumClmmOracle.d.ts +184 -0
  70. package/dist/src/states/oracles/raydiumClmmOracle.js +843 -0
  71. package/dist/src/states/oracles/raydiumCpmmOracle.d.ts +120 -0
  72. package/dist/src/states/oracles/raydiumCpmmOracle.js +540 -0
  73. package/dist/src/states/oracles/switchboardOracle.d.ts +0 -0
  74. package/dist/src/states/oracles/switchboardOracle.js +1 -0
  75. package/dist/src/states/withdrawBasketFees.d.ts +10 -0
  76. package/dist/src/states/withdrawBasketFees.js +154 -0
  77. package/dist/src/txUtils.d.ts +65 -0
  78. package/dist/src/txUtils.js +306 -0
  79. package/dist/test.d.ts +1 -0
  80. package/dist/test.js +561 -0
  81. package/package.json +31 -0
  82. package/src/constants.ts +40 -0
  83. package/src/index.ts +2431 -0
  84. package/src/instructions/automation/auction.ts +55 -0
  85. package/src/instructions/automation/claimBounty.ts +69 -0
  86. package/src/instructions/automation/flashSwap.ts +104 -0
  87. package/src/instructions/automation/priceUpdate.ts +117 -0
  88. package/src/instructions/automation/rebalanceIntent.ts +181 -0
  89. package/src/instructions/management/addBounty.ts +55 -0
  90. package/src/instructions/management/admin.ts +72 -0
  91. package/src/instructions/management/claimFees.ts +129 -0
  92. package/src/instructions/management/createBasket.ts +138 -0
  93. package/src/instructions/management/edit.ts +602 -0
  94. package/src/instructions/management/luts.ts +157 -0
  95. package/src/instructions/pda.ts +151 -0
  96. package/src/instructions/user/deposit.ts +143 -0
  97. package/src/instructions/user/withdraw.ts +53 -0
  98. package/src/jup.ts +113 -0
  99. package/src/keeperMonitor.ts +585 -0
  100. package/src/layouts/basket.ts +233 -0
  101. package/src/layouts/config.ts +576 -0
  102. package/src/layouts/fraction.ts +164 -0
  103. package/src/layouts/intents/bounty.ts +35 -0
  104. package/src/layouts/intents/intent.ts +324 -0
  105. package/src/layouts/intents/rebalanceIntent.ts +306 -0
  106. package/src/layouts/lookupTable.ts +14 -0
  107. package/src/layouts/oracle.ts +157 -0
  108. package/src/states/basket.ts +527 -0
  109. package/src/states/config.ts +62 -0
  110. package/src/states/intents/intent.ts +311 -0
  111. package/src/states/intents/rebalanceIntent.ts +751 -0
  112. package/src/states/oracles/constants.ts +13 -0
  113. package/src/states/oracles/oracle.ts +212 -0
  114. package/src/states/oracles/pythOracle.ts +874 -0
  115. package/src/states/oracles/raydiumClmmOracle.ts +1193 -0
  116. package/src/states/oracles/raydiumCpmmOracle.ts +784 -0
  117. package/src/states/oracles/switchboardOracle.ts +0 -0
  118. package/src/states/withdrawBasketFees.ts +160 -0
  119. package/src/txUtils.ts +424 -0
  120. package/test.ts +609 -0
  121. package/tsconfig.json +101 -0
@@ -0,0 +1,843 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.RaydiumCLMMOracle = exports.PoolState = void 0;
7
+ exports.i32ToBytes = i32ToBytes;
8
+ exports.getTickArrayStartIndexByTick = getTickArrayStartIndexByTick;
9
+ exports.getNextTickArrayStartIndex = getNextTickArrayStartIndex;
10
+ exports.getPdaTickArrayAddress = getPdaTickArrayAddress;
11
+ const decimal_js_1 = __importDefault(require("decimal.js"));
12
+ const bn_js_1 = __importDefault(require("bn.js"));
13
+ const web3_js_1 = require("@solana/web3.js");
14
+ const constants_1 = require("../../constants");
15
+ const oracle_1 = require("../../layouts/oracle");
16
+ const constants_2 = require("./constants");
17
+ const oracle_2 = require("./oracle");
18
+ const CLMM_PROGRAM_ID = new web3_js_1.PublicKey("CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK");
19
+ const TICK_ARRAY_SEED = Buffer.from("tick_array");
20
+ const MIN_TICK = -443636;
21
+ const MAX_TICK = 443636;
22
+ function i32ToBytes(num) {
23
+ const arr = new ArrayBuffer(4);
24
+ const view = new DataView(arr);
25
+ view.setInt32(0, num, false);
26
+ return new Uint8Array(arr);
27
+ }
28
+ function getTickArrayStartIndexByTick(tickIndex, tickSpacing) {
29
+ const perArray = TICK_ARRAY_SIZE_USIZE * tickSpacing;
30
+ return Math.floor(tickIndex / perArray) * perArray;
31
+ }
32
+ function getNextTickArrayStartIndex(lastStart, tickSpacing, zeroForOne) {
33
+ return zeroForOne ? lastStart - tickSpacing * TICK_ARRAY_SIZE_USIZE : lastStart + tickSpacing * TICK_ARRAY_SIZE_USIZE;
34
+ }
35
+ function getPdaTickArrayAddress(poolId, startIndex) {
36
+ return web3_js_1.PublicKey.findProgramAddressSync([TICK_ARRAY_SEED, poolId.toBuffer(), i32ToBytes(startIndex)], CLMM_PROGRAM_ID)[0];
37
+ }
38
+ class Observation {
39
+ constructor(params) {
40
+ this.blockTimestamp = params.blockTimestamp;
41
+ this.tickCumulative = params.tickCumulative;
42
+ this.padding = params.padding;
43
+ }
44
+ static decode(data, offset = 0) {
45
+ const blockTimestamp = new bn_js_1.default(data.readUInt32LE(offset));
46
+ const tickCumulative = new bn_js_1.default(data.subarray(offset + 4, offset + 12), "le").fromTwos(64);
47
+ const padding = [];
48
+ let cursor = offset + 12;
49
+ for (let i = 0; i < 4; i++) {
50
+ padding.push(new bn_js_1.default(data.subarray(cursor, cursor + 8), "le"));
51
+ cursor += 8;
52
+ }
53
+ return [
54
+ new Observation({
55
+ blockTimestamp,
56
+ tickCumulative,
57
+ padding
58
+ }),
59
+ cursor
60
+ ];
61
+ }
62
+ sub(other) {
63
+ if (!(other instanceof Observation)) {
64
+ throw new TypeError("Subtraction is only supported between Observation instances");
65
+ }
66
+ return new Observation({
67
+ blockTimestamp: this.blockTimestamp.sub(other.blockTimestamp),
68
+ tickCumulative: this.tickCumulative.sub(other.tickCumulative),
69
+ padding: this.padding
70
+ });
71
+ }
72
+ add(other) {
73
+ if (!(other instanceof Observation)) {
74
+ throw new TypeError("Addition is only supported between Observation instances");
75
+ }
76
+ return new Observation({
77
+ blockTimestamp: this.blockTimestamp.add(other.blockTimestamp),
78
+ tickCumulative: this.tickCumulative.add(other.tickCumulative),
79
+ padding: this.padding
80
+ });
81
+ }
82
+ getWeightedObservation(time) {
83
+ // cumT = (self.tickCumulative * time) / self.blockTimestamp
84
+ const cum = this.tickCumulative.mul(time).div(this.blockTimestamp);
85
+ return new Observation({
86
+ blockTimestamp: time,
87
+ tickCumulative: cum,
88
+ padding: this.padding
89
+ });
90
+ }
91
+ adjustToTimestamp(targetTimestamp, observationPrev) {
92
+ // delta = self.sub(prevObservation)
93
+ const delta = this.sub(observationPrev);
94
+ // weightedDelta = delta.getWeightedObservation(targetTimestamp - self.blockTimestamp)
95
+ const timeForWeighted = targetTimestamp.sub(this.blockTimestamp);
96
+ const weightedDelta = delta.getWeightedObservation(timeForWeighted);
97
+ // return self.add(weightedDelta)
98
+ return this.add(weightedDelta);
99
+ }
100
+ }
101
+ class ObservationState {
102
+ constructor(params) {
103
+ this.initialized = params.initialized;
104
+ this.recentEpoch = params.recentEpoch;
105
+ this.observationIndex = params.observationIndex;
106
+ this.poolId = params.poolId;
107
+ this.observations = params.observations;
108
+ this.padding = params.padding;
109
+ }
110
+ static decode(data, offset = 8) {
111
+ let cursor = offset;
112
+ const initialized = data.readUInt8(cursor) !== 0;
113
+ cursor += 1;
114
+ const recentEpoch = new bn_js_1.default(data.subarray(cursor, cursor + 8), "le");
115
+ cursor += 8;
116
+ const observationIndex = data.readUInt16LE(cursor);
117
+ cursor += 2;
118
+ const poolId = new web3_js_1.PublicKey(data.subarray(cursor, cursor + 32));
119
+ cursor += 32;
120
+ const observations = [];
121
+ for (let i = 0; i < 100; i++) {
122
+ let obs;
123
+ [obs, cursor] = Observation.decode(data, cursor);
124
+ observations.push(obs);
125
+ }
126
+ const padding = [];
127
+ for (let i = 0; i < 4; i++) {
128
+ padding.push(new bn_js_1.default(data.subarray(cursor, cursor + 8), "le"));
129
+ cursor += 8;
130
+ }
131
+ return [
132
+ new ObservationState({ initialized, recentEpoch, observationIndex, poolId, observations, padding }),
133
+ cursor
134
+ ];
135
+ }
136
+ }
137
+ class RewardInfo {
138
+ constructor(params) {
139
+ this.rewardState = params.rewardState;
140
+ this.openTime = params.openTime;
141
+ this.endTime = params.endTime;
142
+ this.lastUpdateTime = params.lastUpdateTime;
143
+ this.emissionsPerSecondX64 = params.emissionsPerSecondX64;
144
+ this.rewardTotalEmissioned = params.rewardTotalEmissioned;
145
+ this.rewardClaimed = params.rewardClaimed;
146
+ this.tokenMint = params.tokenMint;
147
+ this.tokenVault = params.tokenVault;
148
+ this.authority = params.authority;
149
+ this.rewardGrowthGlobalX64 = params.rewardGrowthGlobalX64;
150
+ }
151
+ static decode(data, offset = 0) {
152
+ let cursor = offset;
153
+ const rewardState = data.readUInt8(cursor);
154
+ cursor += 1;
155
+ const openTime = new bn_js_1.default(data.subarray(cursor, cursor + 8), "le");
156
+ cursor += 8;
157
+ const endTime = new bn_js_1.default(data.subarray(cursor, cursor + 8), "le");
158
+ cursor += 8;
159
+ const lastUpdateTime = new bn_js_1.default(data.subarray(cursor, cursor + 8), "le");
160
+ cursor += 8;
161
+ const emissionsPerSecondX64 = new bn_js_1.default(data.subarray(cursor, cursor + 16), "le");
162
+ cursor += 16;
163
+ const rewardTotalEmissioned = new bn_js_1.default(data.subarray(cursor, cursor + 8), "le");
164
+ cursor += 8;
165
+ const rewardClaimed = new bn_js_1.default(data.subarray(cursor, cursor + 8), "le");
166
+ cursor += 8;
167
+ const tokenMint = new web3_js_1.PublicKey(data.subarray(cursor, cursor + 32));
168
+ cursor += 32;
169
+ const tokenVault = new web3_js_1.PublicKey(data.subarray(cursor, cursor + 32));
170
+ cursor += 32;
171
+ const authority = new web3_js_1.PublicKey(data.subarray(cursor, cursor + 32));
172
+ cursor += 32;
173
+ const rewardGrowthGlobalX64 = new bn_js_1.default(data.subarray(cursor, cursor + 16), "le");
174
+ cursor += 16;
175
+ return [new RewardInfo({
176
+ rewardState,
177
+ openTime,
178
+ endTime,
179
+ lastUpdateTime,
180
+ emissionsPerSecondX64,
181
+ rewardTotalEmissioned,
182
+ rewardClaimed,
183
+ tokenMint,
184
+ tokenVault,
185
+ authority,
186
+ rewardGrowthGlobalX64
187
+ }), cursor];
188
+ }
189
+ }
190
+ class PoolState {
191
+ constructor(params) {
192
+ this.bump = params.bump;
193
+ this.ammConfig = params.ammConfig;
194
+ this.owner = params.owner;
195
+ this.tokenMint0 = params.tokenMint0;
196
+ this.tokenMint1 = params.tokenMint1;
197
+ this.tokenVault0 = params.tokenVault0;
198
+ this.tokenVault1 = params.tokenVault1;
199
+ this.observationKey = params.observationKey;
200
+ this.mintDecimals0 = params.mintDecimals0;
201
+ this.mintDecimals1 = params.mintDecimals1;
202
+ this.tickSpacing = params.tickSpacing;
203
+ this.liquidity = params.liquidity;
204
+ this.sqrtPriceX64 = params.sqrtPriceX64;
205
+ this.tickCurrent = params.tickCurrent;
206
+ this.padding3 = params.padding3;
207
+ this.padding4 = params.padding4;
208
+ this.feeGrowthGlobal0X64 = params.feeGrowthGlobal0X64;
209
+ this.feeGrowthGlobal1X64 = params.feeGrowthGlobal1X64;
210
+ this.protocolFeesToken0 = params.protocolFeesToken0;
211
+ this.protocolFeesToken1 = params.protocolFeesToken1;
212
+ this.swapInAmountToken0 = params.swapInAmountToken0;
213
+ this.swapOutAmountToken1 = params.swapOutAmountToken1;
214
+ this.swapInAmountToken1 = params.swapInAmountToken1;
215
+ this.swapOutAmountToken0 = params.swapOutAmountToken0;
216
+ this.status = params.status;
217
+ this.padding = params.padding;
218
+ this.rewardInfos = params.rewardInfos;
219
+ this.tickArrayBitmap = params.tickArrayBitmap;
220
+ this.totalFeesToken0 = params.totalFeesToken0;
221
+ this.totalFeesClaimedToken0 = params.totalFeesClaimedToken0;
222
+ this.totalFeesToken1 = params.totalFeesToken1;
223
+ this.totalFeesClaimedToken1 = params.totalFeesClaimedToken1;
224
+ this.fundFeesToken0 = params.fundFeesToken0;
225
+ this.fundFeesToken1 = params.fundFeesToken1;
226
+ this.openTime = params.openTime;
227
+ this.recentEpoch = params.recentEpoch;
228
+ this.padding1 = params.padding1;
229
+ this.padding2 = params.padding2;
230
+ }
231
+ static decode(data, offset = 8) {
232
+ let cursor = offset;
233
+ const bump = [data.readUInt8(cursor)];
234
+ cursor += 1;
235
+ const ammConfig = new web3_js_1.PublicKey(data.subarray(cursor, cursor + 32));
236
+ cursor += 32;
237
+ const owner = new web3_js_1.PublicKey(data.subarray(cursor, cursor + 32));
238
+ cursor += 32;
239
+ const tokenMint0 = new web3_js_1.PublicKey(data.subarray(cursor, cursor + 32));
240
+ cursor += 32;
241
+ const tokenMint1 = new web3_js_1.PublicKey(data.subarray(cursor, cursor + 32));
242
+ cursor += 32;
243
+ const tokenVault0 = new web3_js_1.PublicKey(data.subarray(cursor, cursor + 32));
244
+ cursor += 32;
245
+ const tokenVault1 = new web3_js_1.PublicKey(data.subarray(cursor, cursor + 32));
246
+ cursor += 32;
247
+ const observationKey = new web3_js_1.PublicKey(data.subarray(cursor, cursor + 32));
248
+ cursor += 32;
249
+ const mintDecimals0 = data.readUInt8(cursor);
250
+ cursor += 1;
251
+ const mintDecimals1 = data.readUInt8(cursor);
252
+ cursor += 1;
253
+ const tickSpacing = data.readUInt16LE(cursor);
254
+ cursor += 2;
255
+ const liquidity = new bn_js_1.default(data.subarray(cursor, cursor + 16), "le");
256
+ cursor += 16;
257
+ const sqrtPriceX64 = new bn_js_1.default(data.subarray(cursor, cursor + 16), "le");
258
+ cursor += 16;
259
+ const tickCurrent = data.readInt32LE(cursor);
260
+ cursor += 4;
261
+ const padding3 = data.readUInt16LE(cursor);
262
+ cursor += 2;
263
+ const padding4 = data.readUInt16LE(cursor);
264
+ cursor += 2;
265
+ const feeGrowthGlobal0X64 = new bn_js_1.default(data.subarray(cursor, cursor + 16), "le");
266
+ cursor += 16;
267
+ const feeGrowthGlobal1X64 = new bn_js_1.default(data.subarray(cursor, cursor + 16), "le");
268
+ cursor += 16;
269
+ const protocolFeesToken0 = new bn_js_1.default(data.subarray(cursor, cursor + 8), "le");
270
+ cursor += 8;
271
+ const protocolFeesToken1 = new bn_js_1.default(data.subarray(cursor, cursor + 8), "le");
272
+ cursor += 8;
273
+ const swapInAmountToken0 = new bn_js_1.default(data.subarray(cursor, cursor + 16), "le");
274
+ cursor += 16;
275
+ const swapOutAmountToken1 = new bn_js_1.default(data.subarray(cursor, cursor + 16), "le");
276
+ cursor += 16;
277
+ const swapInAmountToken1 = new bn_js_1.default(data.subarray(cursor, cursor + 16), "le");
278
+ cursor += 16;
279
+ const swapOutAmountToken0 = new bn_js_1.default(data.subarray(cursor, cursor + 16), "le");
280
+ cursor += 16;
281
+ const status = data.readUInt8(cursor);
282
+ cursor += 1;
283
+ const padding = [];
284
+ for (let i = 0; i < 7; i++) {
285
+ padding.push(data.readUInt8(cursor));
286
+ cursor += 1;
287
+ }
288
+ // RewardInfos
289
+ const rewardInfos = [];
290
+ for (let i = 0; i < 3; i++) {
291
+ const decoded = RewardInfo.decode(data, cursor);
292
+ rewardInfos.push(decoded[0]);
293
+ cursor = decoded[1];
294
+ }
295
+ // tickArrayBitmap [u64;16]
296
+ const tickArrayBitmap = [];
297
+ for (let i = 0; i < 16; i++) {
298
+ tickArrayBitmap.push(new bn_js_1.default(data.subarray(cursor, cursor + 8), "le"));
299
+ cursor += 8;
300
+ }
301
+ const totalFeesToken0 = new bn_js_1.default(data.subarray(cursor, cursor + 8), "le");
302
+ cursor += 8;
303
+ const totalFeesClaimedToken0 = new bn_js_1.default(data.subarray(cursor, cursor + 8), "le");
304
+ cursor += 8;
305
+ const totalFeesToken1 = new bn_js_1.default(data.subarray(cursor, cursor + 8), "le");
306
+ cursor += 8;
307
+ const totalFeesClaimedToken1 = new bn_js_1.default(data.subarray(cursor, cursor + 8), "le");
308
+ cursor += 8;
309
+ const fundFeesToken0 = new bn_js_1.default(data.subarray(cursor, cursor + 8), "le");
310
+ cursor += 8;
311
+ const fundFeesToken1 = new bn_js_1.default(data.subarray(cursor, cursor + 8), "le");
312
+ cursor += 8;
313
+ const openTime = new bn_js_1.default(data.subarray(cursor, cursor + 8), "le");
314
+ cursor += 8;
315
+ const recentEpoch = new bn_js_1.default(data.subarray(cursor, cursor + 8), "le");
316
+ cursor += 8;
317
+ const padding1 = [];
318
+ for (let i = 0; i < 24; i++) {
319
+ padding1.push(new bn_js_1.default(data.subarray(cursor, cursor + 8), "le"));
320
+ cursor += 8;
321
+ }
322
+ const padding2 = [];
323
+ for (let i = 0; i < 32; i++) {
324
+ padding2.push(new bn_js_1.default(data.subarray(cursor, cursor + 8), "le"));
325
+ cursor += 8;
326
+ }
327
+ return [
328
+ new PoolState({
329
+ bump,
330
+ ammConfig,
331
+ owner,
332
+ tokenMint0,
333
+ tokenMint1,
334
+ tokenVault0,
335
+ tokenVault1,
336
+ observationKey,
337
+ mintDecimals0,
338
+ mintDecimals1,
339
+ tickSpacing,
340
+ liquidity,
341
+ sqrtPriceX64,
342
+ tickCurrent,
343
+ padding3,
344
+ padding4,
345
+ feeGrowthGlobal0X64,
346
+ feeGrowthGlobal1X64,
347
+ protocolFeesToken0,
348
+ protocolFeesToken1,
349
+ swapInAmountToken0,
350
+ swapOutAmountToken1,
351
+ swapInAmountToken1,
352
+ swapOutAmountToken0,
353
+ status,
354
+ padding,
355
+ rewardInfos,
356
+ tickArrayBitmap,
357
+ totalFeesToken0,
358
+ totalFeesClaimedToken0,
359
+ totalFeesToken1,
360
+ totalFeesClaimedToken1,
361
+ fundFeesToken0,
362
+ fundFeesToken1,
363
+ openTime,
364
+ recentEpoch,
365
+ padding1,
366
+ padding2,
367
+ }),
368
+ cursor,
369
+ ];
370
+ }
371
+ }
372
+ exports.PoolState = PoolState;
373
+ const TICK_ARRAY_SIZE_USIZE = 60;
374
+ const REWARD_NUM = 3;
375
+ class TickState {
376
+ constructor(params) {
377
+ this.tick = params.tick;
378
+ this.liquidityNet = params.liquidityNet;
379
+ this.liquidityGross = params.liquidityGross;
380
+ this.feeGrowthOutside0X64 = params.feeGrowthOutside0X64;
381
+ this.feeGrowthOutside1X64 = params.feeGrowthOutside1X64;
382
+ this.rewardGrowthsOutsideX64 = params.rewardGrowthsOutsideX64;
383
+ this.padding = params.padding;
384
+ }
385
+ static decode(data, offset = 0) {
386
+ let cursor = offset;
387
+ const tick = data.readInt32LE(cursor);
388
+ cursor += 4;
389
+ // liquidity_net: i128 (signed)
390
+ const liquidityNet = new bn_js_1.default(data.subarray(cursor, cursor + 16), "le").fromTwos(128);
391
+ cursor += 16;
392
+ // liquidity_gross: u128
393
+ const liquidityGross = new bn_js_1.default(data.subarray(cursor, cursor + 16), "le");
394
+ cursor += 16;
395
+ const feeGrowthOutside0X64 = new bn_js_1.default(data.subarray(cursor, cursor + 16), "le");
396
+ cursor += 16;
397
+ const feeGrowthOutside1X64 = new bn_js_1.default(data.subarray(cursor, cursor + 16), "le");
398
+ cursor += 16;
399
+ const rewardGrowthsOutsideX64 = [];
400
+ for (let i = 0; i < REWARD_NUM; i++) {
401
+ rewardGrowthsOutsideX64.push(new bn_js_1.default(data.subarray(cursor, cursor + 16), "le"));
402
+ cursor += 16;
403
+ }
404
+ const padding = [];
405
+ for (let i = 0; i < 13; i++) {
406
+ padding.push(data.readUInt32LE(cursor));
407
+ cursor += 4;
408
+ }
409
+ return [
410
+ new TickState({
411
+ tick,
412
+ liquidityNet,
413
+ liquidityGross,
414
+ feeGrowthOutside0X64,
415
+ feeGrowthOutside1X64,
416
+ rewardGrowthsOutsideX64,
417
+ padding
418
+ }),
419
+ cursor
420
+ ];
421
+ }
422
+ }
423
+ class TickArrayState {
424
+ constructor(params) {
425
+ this.poolId = params.poolId;
426
+ this.startTickIndex = params.startTickIndex;
427
+ this.ticks = params.ticks;
428
+ this.initializedTickCount = params.initializedTickCount;
429
+ this.recentEpoch = params.recentEpoch;
430
+ this.padding = params.padding;
431
+ }
432
+ static decode(data, offset = 8) {
433
+ let cursor = offset;
434
+ const poolId = new web3_js_1.PublicKey(data.subarray(cursor, cursor + 32));
435
+ cursor += 32;
436
+ const startTickIndex = data.readInt32LE(cursor);
437
+ cursor += 4;
438
+ const ticks = [];
439
+ for (let i = 0; i < TICK_ARRAY_SIZE_USIZE; i++) {
440
+ const [tickState, next] = TickState.decode(data, cursor);
441
+ ticks.push(tickState);
442
+ cursor = next;
443
+ }
444
+ const initializedTickCount = data.readUInt8(cursor);
445
+ cursor += 1;
446
+ const recentEpoch = new bn_js_1.default(data.subarray(cursor, cursor + 8), "le");
447
+ cursor += 8;
448
+ const padding = [];
449
+ for (let i = 0; i < 107; i++) {
450
+ padding.push(data.readUInt8(cursor));
451
+ cursor += 1;
452
+ }
453
+ return [
454
+ new TickArrayState({
455
+ poolId,
456
+ startTickIndex,
457
+ ticks,
458
+ initializedTickCount,
459
+ recentEpoch,
460
+ padding
461
+ }),
462
+ cursor
463
+ ];
464
+ }
465
+ }
466
+ class RaydiumCLMMOracle {
467
+ static deriveObservationKey(poolId) {
468
+ const seeds = [
469
+ Buffer.from("observation"),
470
+ poolId.toBuffer(),
471
+ ];
472
+ return web3_js_1.PublicKey.findProgramAddressSync(seeds, CLMM_PROGRAM_ID);
473
+ }
474
+ ;
475
+ static getObservationAtIndex(observations, index) {
476
+ return observations[index];
477
+ }
478
+ static getObservationAtTimestamp(timestamp, observations, startIndex) {
479
+ let observationCurrent = this.getObservationAtIndex(observations, startIndex);
480
+ let index = startIndex;
481
+ // const obsIndex = this.observationState.observationIndex;
482
+ // Loop backwards until we find the observation <= timestamp
483
+ while (observationCurrent.blockTimestamp.gt(timestamp)) {
484
+ index = (index - 1 + 100) % 100;
485
+ observationCurrent = this.getObservationAtIndex(observations, index);
486
+ if (index === startIndex) {
487
+ throw Error("Observations do not go back far enough in time");
488
+ break; // looped all the way around
489
+ }
490
+ }
491
+ // Previous index for interpolation
492
+ const prevIndex = (index - 1 + 100) % 100;
493
+ const observationPrev = this.getObservationAtIndex(observations, prevIndex);
494
+ // Adjust current observation to the target timestamp
495
+ const result = observationCurrent.adjustToTimestamp(timestamp, observationPrev);
496
+ return result;
497
+ }
498
+ static getDeltaObservations(currentTime, observations, observationIndex, primarySeconds, secondarySeconds) {
499
+ const observationCurrent = this.getObservationAtTimestamp(currentTime, observations, observationIndex);
500
+ const observationPrimary = this.getObservationAtTimestamp(new bn_js_1.default(currentTime.sub(primarySeconds)), observations, observationIndex);
501
+ const deltaPrimary = observationCurrent.sub(observationPrimary);
502
+ const observationSecondary = this.getObservationAtTimestamp(new bn_js_1.default(currentTime.sub(secondarySeconds)), observations, observationIndex);
503
+ const deltaSecondary = observationCurrent.sub(observationSecondary);
504
+ return [deltaPrimary, deltaSecondary];
505
+ }
506
+ static getSqrtPriceX64FromTick(tick) {
507
+ if (!Number.isInteger(tick)) {
508
+ throw new Error("tick must be integer");
509
+ }
510
+ if (tick < MIN_TICK || tick > MAX_TICK) {
511
+ throw new Error("tick must be in MIN_TICK and MAX_TICK");
512
+ }
513
+ const tickAbs = tick < 0 ? tick * -1 : tick;
514
+ let ratio = (tickAbs & 0x1) != 0 ? new bn_js_1.default("18445821805675395072") : new bn_js_1.default("18446744073709551616");
515
+ if ((tickAbs & 0x2) != 0)
516
+ ratio = mulRightShift(ratio, new bn_js_1.default("18444899583751176192"));
517
+ if ((tickAbs & 0x4) != 0)
518
+ ratio = mulRightShift(ratio, new bn_js_1.default("18443055278223355904"));
519
+ if ((tickAbs & 0x8) != 0)
520
+ ratio = mulRightShift(ratio, new bn_js_1.default("18439367220385607680"));
521
+ if ((tickAbs & 0x10) != 0)
522
+ ratio = mulRightShift(ratio, new bn_js_1.default("18431993317065453568"));
523
+ if ((tickAbs & 0x20) != 0)
524
+ ratio = mulRightShift(ratio, new bn_js_1.default("18417254355718170624"));
525
+ if ((tickAbs & 0x40) != 0)
526
+ ratio = mulRightShift(ratio, new bn_js_1.default("18387811781193609216"));
527
+ if ((tickAbs & 0x80) != 0)
528
+ ratio = mulRightShift(ratio, new bn_js_1.default("18329067761203558400"));
529
+ if ((tickAbs & 0x100) != 0)
530
+ ratio = mulRightShift(ratio, new bn_js_1.default("18212142134806163456"));
531
+ if ((tickAbs & 0x200) != 0)
532
+ ratio = mulRightShift(ratio, new bn_js_1.default("17980523815641700352"));
533
+ if ((tickAbs & 0x400) != 0)
534
+ ratio = mulRightShift(ratio, new bn_js_1.default("17526086738831433728"));
535
+ if ((tickAbs & 0x800) != 0)
536
+ ratio = mulRightShift(ratio, new bn_js_1.default("16651378430235570176"));
537
+ if ((tickAbs & 0x1000) != 0)
538
+ ratio = mulRightShift(ratio, new bn_js_1.default("15030750278694412288"));
539
+ if ((tickAbs & 0x2000) != 0)
540
+ ratio = mulRightShift(ratio, new bn_js_1.default("12247334978884435968"));
541
+ if ((tickAbs & 0x4000) != 0)
542
+ ratio = mulRightShift(ratio, new bn_js_1.default("8131365268886854656"));
543
+ if ((tickAbs & 0x8000) != 0)
544
+ ratio = mulRightShift(ratio, new bn_js_1.default("3584323654725218816"));
545
+ if ((tickAbs & 0x10000) != 0)
546
+ ratio = mulRightShift(ratio, new bn_js_1.default("696457651848324352"));
547
+ if ((tickAbs & 0x20000) != 0)
548
+ ratio = mulRightShift(ratio, new bn_js_1.default("26294789957507116"));
549
+ if ((tickAbs & 0x40000) != 0)
550
+ ratio = mulRightShift(ratio, new bn_js_1.default("37481735321082"));
551
+ if (tick > 0)
552
+ ratio = constants_2.U128_MAX.div(ratio);
553
+ return ratio;
554
+ }
555
+ static sqrtPriceX64ToPrice(sqrtPriceX64, decimalsA, decimalsB) {
556
+ return this.x64ToDecimal(sqrtPriceX64)
557
+ .pow(2)
558
+ .mul(decimal_js_1.default.pow(10, decimalsA - decimalsB));
559
+ }
560
+ static x64ToDecimal(num, decimalPlaces) {
561
+ return new decimal_js_1.default(num.toString()).div(decimal_js_1.default.pow(2, 64)).toDecimalPlaces(decimalPlaces);
562
+ }
563
+ static getSpotPrice(tickCurrent, mintDecimals0, mintDecimals1) {
564
+ let price = this.sqrtPriceX64ToPrice(this.getSqrtPriceX64FromTick(tickCurrent), mintDecimals0, mintDecimals1);
565
+ return price;
566
+ }
567
+ static getTwapPrimary(observations, observationIndex, primarySeconds, secondarySeconds, mintDecimals0, mintDecimals1, currentTime) {
568
+ let observation = this.getDeltaObservations(currentTime, observations, observationIndex, primarySeconds, secondarySeconds)[0];
569
+ let tick = observation.tickCumulative.div(observation.blockTimestamp).toNumber();
570
+ let twap = this.sqrtPriceX64ToPrice(this.getSqrtPriceX64FromTick(tick), mintDecimals0, mintDecimals1);
571
+ return twap;
572
+ }
573
+ static getTwapSecondary(observations, observationIndex, primarySeconds, secondarySeconds, mintDecimals0, mintDecimals1, currentTime) {
574
+ let observation = this.getDeltaObservations(currentTime, observations, observationIndex, primarySeconds, secondarySeconds)[1];
575
+ let tick = observation.tickCumulative.div(observation.blockTimestamp).toNumber();
576
+ let twap = this.sqrtPriceX64ToPrice(this.getSqrtPriceX64FromTick(tick), mintDecimals0, mintDecimals1);
577
+ return twap;
578
+ }
579
+ static getConfidence(observations, observationIndex, primarySeconds, secondarySeconds, tickCurrent, mintDecimals0, mintDecimals1, currentTime) {
580
+ let primary = this.getTwapPrimary(observations, observationIndex, primarySeconds, secondarySeconds, mintDecimals0, mintDecimals1, currentTime);
581
+ let secondary = this.getTwapSecondary(observations, observationIndex, primarySeconds, secondarySeconds, mintDecimals0, mintDecimals1, currentTime);
582
+ let spot = this.getSpotPrice(tickCurrent, mintDecimals0, mintDecimals1);
583
+ let first = primary.sub(spot).abs();
584
+ let second = secondary.sub(spot).abs();
585
+ let third = primary.sub(secondary).abs();
586
+ let conf = decimal_js_1.default.max(first, second, third);
587
+ return conf;
588
+ }
589
+ static calculateMaxPriceImpactForMinLiquidity(oracleParams, poolState, prevTickArray, currentTickArray, nextTickArray, solPrice, usdPrice) {
590
+ // local helpers / aliases
591
+ const sStart = this.x64ToDecimal(poolState.sqrtPriceX64);
592
+ const Lstart = new decimal_js_1.default(poolState.liquidity.toString());
593
+ const spotPrice = this.sqrtPriceX64ToPrice(poolState.sqrtPriceX64, poolState.mintDecimals0, poolState.mintDecimals1);
594
+ const minLiquidityUsd = new decimal_js_1.default(oracleParams.minLiquidity.toString());
595
+ let amountQuote;
596
+ if (oracleParams.quote === oracle_1.Quote.Usdc) {
597
+ amountQuote = minLiquidityUsd.mul(new decimal_js_1.default(10).pow(6)).div(usdPrice.price);
598
+ }
599
+ else {
600
+ amountQuote = minLiquidityUsd.mul(new decimal_js_1.default(10).pow(9)).div(solPrice.price);
601
+ }
602
+ const tickSpacing = poolState.tickSpacing;
603
+ // Build fast lookup structures once:
604
+ // - tickMap: absoluteTick -> TickState
605
+ // - arrays of initialized tick indices per region (prev/current/next)
606
+ const tickMap = new Map();
607
+ const prevIdxs = [];
608
+ const currIdxs = [];
609
+ const nextIdxs = [];
610
+ const pushTicks = (ta, outArr) => {
611
+ const start = ta.startTickIndex;
612
+ for (let i = 0; i < ta.ticks.length; i++) {
613
+ const tickVal = start + i * tickSpacing;
614
+ const t = ta.ticks[i];
615
+ // store in map regardless, to speed up findTickState
616
+ tickMap.set(tickVal, t);
617
+ if (t.liquidityGross.gt(new bn_js_1.default(0)))
618
+ outArr.push(tickVal);
619
+ }
620
+ };
621
+ pushTicks(prevTickArray, prevIdxs);
622
+ pushTicks(currentTickArray, currIdxs);
623
+ pushTicks(nextTickArray, nextIdxs);
624
+ // findTickState map lookup
625
+ const findTickState = (idx) => {
626
+ var _a;
627
+ if (idx === null)
628
+ return null;
629
+ return (_a = tickMap.get(idx)) !== null && _a !== void 0 ? _a : null;
630
+ };
631
+ // find next initialized tick index scanning only relevant arrays:
632
+ // forward === true => check current then next
633
+ // forward === false => check current then prev
634
+ const findNextInitializedTickIndex = (startTick, forward) => {
635
+ const lists = forward ? [currIdxs, nextIdxs] : [currIdxs, prevIdxs];
636
+ let best = null;
637
+ for (const list of lists) {
638
+ // for forward: find smallest tick > startTick
639
+ // for backward: find largest tick < startTick
640
+ if (forward) {
641
+ for (let i = 0; i < list.length; i++) {
642
+ const v = list[i];
643
+ if (v > startTick) {
644
+ best = best === null ? v : Math.min(best, v);
645
+ // since list ascending we can break early for this list
646
+ break;
647
+ }
648
+ }
649
+ if (best !== null)
650
+ break; // we already found closest in current list
651
+ }
652
+ else {
653
+ // backward: list ascending, scan from end for speed
654
+ for (let i = list.length - 1; i >= 0; i--) {
655
+ const v = list[i];
656
+ if (v < startTick) {
657
+ best = best === null ? v : Math.max(best, v);
658
+ break;
659
+ }
660
+ }
661
+ if (best !== null)
662
+ break;
663
+ }
664
+ }
665
+ return best;
666
+ };
667
+ const sFor = (tickIdx) => tickIdx === null ? null : this.x64ToDecimal(this.getSqrtPriceX64FromTick(tickIdx));
668
+ // simulate quote -> base (s increases). returns avg quote-per-base or null
669
+ const simulateIncrease = (amountQ) => {
670
+ let remaining = amountQ;
671
+ let s = sStart;
672
+ let L = Lstart;
673
+ let totalBaseOut = new decimal_js_1.default(0);
674
+ let currentTick = poolState.tickCurrent;
675
+ let boundary = findNextInitializedTickIndex(currentTick, true);
676
+ let steps = 0;
677
+ while (remaining.gt(0) && steps++ < 500) {
678
+ const sBoundary = sFor(boundary);
679
+ if (!sBoundary) {
680
+ // no known boundary -> consume all in one segment
681
+ const sEnd = s.add(remaining.div(L));
682
+ totalBaseOut = totalBaseOut.add(L.mul(new decimal_js_1.default(1).div(s).sub(new decimal_js_1.default(1).div(sEnd))));
683
+ remaining = new decimal_js_1.default(0);
684
+ break;
685
+ }
686
+ const deltaQToBoundary = L.mul(sBoundary.sub(s));
687
+ if (remaining.lte(deltaQToBoundary)) {
688
+ const sEnd = s.add(remaining.div(L));
689
+ totalBaseOut = totalBaseOut.add(L.mul(new decimal_js_1.default(1).div(s).sub(new decimal_js_1.default(1).div(sEnd))));
690
+ remaining = new decimal_js_1.default(0);
691
+ break;
692
+ }
693
+ // consume whole segment to boundary
694
+ totalBaseOut = totalBaseOut.add(L.mul(new decimal_js_1.default(1).div(s).sub(new decimal_js_1.default(1).div(sBoundary))));
695
+ remaining = remaining.sub(deltaQToBoundary);
696
+ // cross tick: safe to assert boundary non-null because sBoundary existed
697
+ const bt = boundary;
698
+ const ts = findTickState(bt);
699
+ if (ts)
700
+ L = L.add(new decimal_js_1.default(ts.liquidityNet.toString()));
701
+ s = sBoundary;
702
+ currentTick = bt + tickSpacing;
703
+ boundary = findNextInitializedTickIndex(currentTick, true);
704
+ }
705
+ if (totalBaseOut.lte(0))
706
+ return null;
707
+ return amountQ.div(totalBaseOut);
708
+ };
709
+ // simulate base -> quote (s decreases). amountQuote is converted to approximate amountBase via spot
710
+ const simulateDecrease = (amountQ) => {
711
+ const amountBase = amountQ.div(spotPrice);
712
+ let remainingBase = amountBase;
713
+ let s = sStart;
714
+ let L = Lstart;
715
+ let totalQuoteOut = new decimal_js_1.default(0);
716
+ let currentTick = poolState.tickCurrent;
717
+ let boundary = findNextInitializedTickIndex(currentTick, false);
718
+ let steps = 0;
719
+ while (remainingBase.gt(0) && steps++ < 500) {
720
+ const sBoundary = sFor(boundary);
721
+ if (!sBoundary) {
722
+ const denom = L.add(remainingBase.mul(s));
723
+ if (denom.eq(0))
724
+ return null;
725
+ const sEnd = L.mul(s).div(denom);
726
+ totalQuoteOut = totalQuoteOut.add(L.mul(s.sub(sEnd)));
727
+ remainingBase = new decimal_js_1.default(0);
728
+ break;
729
+ }
730
+ const deltaAtoBoundary = L.mul(new decimal_js_1.default(1).div(sBoundary).sub(new decimal_js_1.default(1).div(s)));
731
+ if (remainingBase.lte(deltaAtoBoundary)) {
732
+ const denom = L.add(remainingBase.mul(s));
733
+ const sEnd = L.mul(s).div(denom);
734
+ totalQuoteOut = totalQuoteOut.add(L.mul(s.sub(sEnd)));
735
+ remainingBase = new decimal_js_1.default(0);
736
+ break;
737
+ }
738
+ // consume to boundary
739
+ const denom = L.add(deltaAtoBoundary.mul(s));
740
+ const sEnd = L.mul(s).div(denom);
741
+ totalQuoteOut = totalQuoteOut.add(L.mul(s.sub(sEnd)));
742
+ remainingBase = remainingBase.sub(deltaAtoBoundary);
743
+ // cross tick going down
744
+ const bt = boundary;
745
+ const ts = findTickState(bt);
746
+ if (ts)
747
+ L = L.sub(new decimal_js_1.default(ts.liquidityNet.toString()));
748
+ s = sEnd;
749
+ currentTick = bt - tickSpacing;
750
+ boundary = findNextInitializedTickIndex(currentTick, false);
751
+ }
752
+ if (totalQuoteOut.lte(0))
753
+ return null;
754
+ if (amountBase.eq(0))
755
+ return null;
756
+ return totalQuoteOut.div(amountBase);
757
+ };
758
+ const up = simulateIncrease(amountQuote);
759
+ const down = simulateDecrease(amountQuote);
760
+ const toImpact = (avg) => !avg || avg.lte(0) ? new decimal_js_1.default(0) : avg.sub(spotPrice).abs().div(spotPrice).mul(new decimal_js_1.default(10000));
761
+ return decimal_js_1.default.max(toImpact(up), toImpact(down));
762
+ }
763
+ static fetch(oracleParams, accountInfos, solPrice, usdPrice) {
764
+ const poolState = accountInfos[0];
765
+ const observationState = accountInfos[1];
766
+ const prevTickArrayState = accountInfos[2];
767
+ const currentTickArrayState = accountInfos[3];
768
+ const nextTickArrayState = accountInfos[4];
769
+ const [decodedPoolState, num] = PoolState.decode(poolState.data, 8);
770
+ const [decodedObservationState, _] = ObservationState.decode(observationState.data, 8);
771
+ const [decodedPrevTickArrayState, __] = TickArrayState.decode(prevTickArrayState.data, 8);
772
+ const [decodedCurrentTickArrayState, ___] = TickArrayState.decode(currentTickArrayState.data, 8);
773
+ const [decodedNextTickArrayState, ____] = TickArrayState.decode(nextTickArrayState.data, 8);
774
+ const currentTime = new bn_js_1.default(Math.floor(Date.now() / 1000));
775
+ let primaryPrice = RaydiumCLMMOracle.getTwapPrimary(decodedObservationState.observations, decodedObservationState.observationIndex, oracleParams.twapSecondsAgo, oracleParams.twapSecondarySecondsAgo, decodedPoolState.mintDecimals0, decodedPoolState.mintDecimals1, currentTime);
776
+ let secondaryPrice = RaydiumCLMMOracle.getTwapSecondary(decodedObservationState.observations, decodedObservationState.observationIndex, oracleParams.twapSecondsAgo, oracleParams.twapSecondarySecondsAgo, decodedPoolState.mintDecimals0, decodedPoolState.mintDecimals1, currentTime);
777
+ let spotPrice = RaydiumCLMMOracle.getSpotPrice(decodedPoolState.tickCurrent, decodedPoolState.mintDecimals0, decodedPoolState.mintDecimals1);
778
+ // Validate primary TWAP is not zero
779
+ if (primaryPrice.eq(0)) {
780
+ return new oracle_2.OraclePrice(new decimal_js_1.default(0), new decimal_js_1.default(0), 0);
781
+ }
782
+ // === 1. Min liquidity and price impact validation (FIRST) ===
783
+ if (oracleParams.minLiquidity.gt(new bn_js_1.default(0))) {
784
+ const impactBps = RaydiumCLMMOracle.calculateMaxPriceImpactForMinLiquidity(oracleParams, decodedPoolState, decodedPrevTickArrayState, decodedCurrentTickArrayState, decodedNextTickArrayState, solPrice, usdPrice);
785
+ if (oracleParams.maxSlippageBps > 0 && impactBps.gt(new decimal_js_1.default(oracleParams.maxSlippageBps))) {
786
+ return new oracle_2.OraclePrice(new decimal_js_1.default(0), new decimal_js_1.default(0), 0);
787
+ }
788
+ }
789
+ // === 2. Confidence calculation ===
790
+ let maxPrice = decimal_js_1.default.max(primaryPrice, secondaryPrice, spotPrice);
791
+ let minPrice = decimal_js_1.default.min(primaryPrice, secondaryPrice, spotPrice);
792
+ let confidence = maxPrice.gt(minPrice) ? maxPrice.sub(minPrice) : new decimal_js_1.default(0);
793
+ const lastUpdateTimestamp = decodedObservationState.observations[decodedObservationState.observationIndex].blockTimestamp;
794
+ // === 3. Inflate confidence by staleness ===
795
+ const deltaSecondsBN = currentTime.sub(lastUpdateTimestamp);
796
+ const deltaSeconds = new decimal_js_1.default(deltaSecondsBN.toString());
797
+ if (oracleParams.stalenessConfRateBps > 0 && deltaSeconds.gt(0)) {
798
+ const stalenessRate = new decimal_js_1.default(oracleParams.stalenessConfRateBps).div(new decimal_js_1.default(10000));
799
+ const inflateFactor = new decimal_js_1.default(1).add(deltaSeconds.mul(stalenessRate));
800
+ confidence = confidence.mul(inflateFactor);
801
+ }
802
+ // === 4. Validate confidence threshold ===
803
+ const confRatioBps = confidence.div(primaryPrice).mul(new decimal_js_1.default(constants_1.HUNDRED_PERCENT_BPS));
804
+ if (confRatioBps.gt(new decimal_js_1.default(oracleParams.confThreshBps))) {
805
+ return new oracle_2.OraclePrice(new decimal_js_1.default(0), new decimal_js_1.default(0), 0);
806
+ }
807
+ // === 5. Validate staleness threshold ===
808
+ if (oracleParams.stalenessThresh.gt(new bn_js_1.default(0))) {
809
+ if (currentTime.sub(lastUpdateTimestamp).gt(oracleParams.stalenessThresh)) {
810
+ return new oracle_2.OraclePrice(new decimal_js_1.default(0), new decimal_js_1.default(0), 0);
811
+ }
812
+ }
813
+ // === 6. Validate volatility threshold ===
814
+ if (!minPrice.eq(0)) {
815
+ const volRatioBps = maxPrice.sub(minPrice).div(minPrice).mul(new decimal_js_1.default(constants_1.HUNDRED_PERCENT_BPS));
816
+ if (volRatioBps.gt(new decimal_js_1.default(oracleParams.volatilityThreshBps))) {
817
+ return new oracle_2.OraclePrice(new decimal_js_1.default(0), new decimal_js_1.default(0), 0);
818
+ }
819
+ }
820
+ else {
821
+ return new oracle_2.OraclePrice(new decimal_js_1.default(0), new decimal_js_1.default(0), 0);
822
+ }
823
+ // === 7. Convert to USD ===
824
+ if (oracleParams.quote === oracle_1.Quote.Usdc) {
825
+ primaryPrice = primaryPrice.mul(usdPrice.price);
826
+ confidence = confidence.mul(usdPrice.price);
827
+ }
828
+ else {
829
+ primaryPrice = primaryPrice.mul(solPrice.price);
830
+ confidence = confidence.mul(solPrice.price);
831
+ }
832
+ return new oracle_2.OraclePrice(primaryPrice, confidence, currentTime.toNumber());
833
+ }
834
+ }
835
+ exports.RaydiumCLMMOracle = RaydiumCLMMOracle;
836
+ function mulRightShift(val, mulBy) {
837
+ return signedRightShift(val.mul(mulBy), 64, 256);
838
+ }
839
+ function signedRightShift(n0, shiftBy, bitWidth) {
840
+ const twoN0 = n0.toTwos(bitWidth).shrn(shiftBy);
841
+ twoN0.imaskn(bitWidth - shiftBy + 1);
842
+ return twoN0.fromTwos(bitWidth - shiftBy);
843
+ }