@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.
- package/dist/src/constants.d.ts +23 -0
- package/dist/src/constants.js +38 -0
- package/dist/src/index.d.ts +804 -0
- package/dist/src/index.js +2097 -0
- package/dist/src/instructions/automation/auction.d.ts +6 -0
- package/dist/src/instructions/automation/auction.js +40 -0
- package/dist/src/instructions/automation/claimBounty.d.ts +12 -0
- package/dist/src/instructions/automation/claimBounty.js +44 -0
- package/dist/src/instructions/automation/flashSwap.d.ts +21 -0
- package/dist/src/instructions/automation/flashSwap.js +74 -0
- package/dist/src/instructions/automation/priceUpdate.d.ts +19 -0
- package/dist/src/instructions/automation/priceUpdate.js +89 -0
- package/dist/src/instructions/automation/rebalanceIntent.d.ts +32 -0
- package/dist/src/instructions/automation/rebalanceIntent.js +117 -0
- package/dist/src/instructions/automation/rebalanceSwap.d.ts +11 -0
- package/dist/src/instructions/automation/rebalanceSwap.js +42 -0
- package/dist/src/instructions/management/addBounty.d.ts +7 -0
- package/dist/src/instructions/management/addBounty.js +41 -0
- package/dist/src/instructions/management/admin.d.ts +9 -0
- package/dist/src/instructions/management/admin.js +53 -0
- package/dist/src/instructions/management/claimFees.d.ts +15 -0
- package/dist/src/instructions/management/claimFees.js +95 -0
- package/dist/src/instructions/management/createBasket.d.ts +21 -0
- package/dist/src/instructions/management/createBasket.js +98 -0
- package/dist/src/instructions/management/edit.d.ts +51 -0
- package/dist/src/instructions/management/edit.js +477 -0
- package/dist/src/instructions/management/luts.d.ts +30 -0
- package/dist/src/instructions/management/luts.js +99 -0
- package/dist/src/instructions/pda.d.ts +25 -0
- package/dist/src/instructions/pda.js +128 -0
- package/dist/src/instructions/user/deposit.d.ts +20 -0
- package/dist/src/instructions/user/deposit.js +100 -0
- package/dist/src/instructions/user/withdraw.d.ts +8 -0
- package/dist/src/instructions/user/withdraw.js +36 -0
- package/dist/src/jup.d.ts +49 -0
- package/dist/src/jup.js +80 -0
- package/dist/src/keeperMonitor.d.ts +52 -0
- package/dist/src/keeperMonitor.js +624 -0
- package/dist/src/layouts/basket.d.ts +191 -0
- package/dist/src/layouts/basket.js +51 -0
- package/dist/src/layouts/config.d.ts +281 -0
- package/dist/src/layouts/config.js +237 -0
- package/dist/src/layouts/fraction.d.ts +20 -0
- package/dist/src/layouts/fraction.js +164 -0
- package/dist/src/layouts/intents/bounty.d.ts +18 -0
- package/dist/src/layouts/intents/bounty.js +19 -0
- package/dist/src/layouts/intents/intent.d.ts +209 -0
- package/dist/src/layouts/intents/intent.js +97 -0
- package/dist/src/layouts/intents/rebalanceIntent.d.ts +212 -0
- package/dist/src/layouts/intents/rebalanceIntent.js +94 -0
- package/dist/src/layouts/lookupTable.d.ts +7 -0
- package/dist/src/layouts/lookupTable.js +10 -0
- package/dist/src/layouts/oracle.d.ts +63 -0
- package/dist/src/layouts/oracle.js +96 -0
- package/dist/src/states/basket.d.ts +14 -0
- package/dist/src/states/basket.js +479 -0
- package/dist/src/states/config.d.ts +3 -0
- package/dist/src/states/config.js +71 -0
- package/dist/src/states/intents/intent.d.ts +10 -0
- package/dist/src/states/intents/intent.js +316 -0
- package/dist/src/states/intents/rebalanceIntent.d.ts +42 -0
- package/dist/src/states/intents/rebalanceIntent.js +680 -0
- package/dist/src/states/oracles/constants.d.ts +9 -0
- package/dist/src/states/oracles/constants.js +15 -0
- package/dist/src/states/oracles/oracle.d.ts +24 -0
- package/dist/src/states/oracles/oracle.js +168 -0
- package/dist/src/states/oracles/pythOracle.d.ts +132 -0
- package/dist/src/states/oracles/pythOracle.js +609 -0
- package/dist/src/states/oracles/raydiumClmmOracle.d.ts +184 -0
- package/dist/src/states/oracles/raydiumClmmOracle.js +843 -0
- package/dist/src/states/oracles/raydiumCpmmOracle.d.ts +120 -0
- package/dist/src/states/oracles/raydiumCpmmOracle.js +540 -0
- package/dist/src/states/oracles/switchboardOracle.d.ts +0 -0
- package/dist/src/states/oracles/switchboardOracle.js +1 -0
- package/dist/src/states/withdrawBasketFees.d.ts +10 -0
- package/dist/src/states/withdrawBasketFees.js +154 -0
- package/dist/src/txUtils.d.ts +65 -0
- package/dist/src/txUtils.js +306 -0
- package/dist/test.d.ts +1 -0
- package/dist/test.js +561 -0
- package/package.json +31 -0
- package/src/constants.ts +40 -0
- package/src/index.ts +2431 -0
- package/src/instructions/automation/auction.ts +55 -0
- package/src/instructions/automation/claimBounty.ts +69 -0
- package/src/instructions/automation/flashSwap.ts +104 -0
- package/src/instructions/automation/priceUpdate.ts +117 -0
- package/src/instructions/automation/rebalanceIntent.ts +181 -0
- package/src/instructions/management/addBounty.ts +55 -0
- package/src/instructions/management/admin.ts +72 -0
- package/src/instructions/management/claimFees.ts +129 -0
- package/src/instructions/management/createBasket.ts +138 -0
- package/src/instructions/management/edit.ts +602 -0
- package/src/instructions/management/luts.ts +157 -0
- package/src/instructions/pda.ts +151 -0
- package/src/instructions/user/deposit.ts +143 -0
- package/src/instructions/user/withdraw.ts +53 -0
- package/src/jup.ts +113 -0
- package/src/keeperMonitor.ts +585 -0
- package/src/layouts/basket.ts +233 -0
- package/src/layouts/config.ts +576 -0
- package/src/layouts/fraction.ts +164 -0
- package/src/layouts/intents/bounty.ts +35 -0
- package/src/layouts/intents/intent.ts +324 -0
- package/src/layouts/intents/rebalanceIntent.ts +306 -0
- package/src/layouts/lookupTable.ts +14 -0
- package/src/layouts/oracle.ts +157 -0
- package/src/states/basket.ts +527 -0
- package/src/states/config.ts +62 -0
- package/src/states/intents/intent.ts +311 -0
- package/src/states/intents/rebalanceIntent.ts +751 -0
- package/src/states/oracles/constants.ts +13 -0
- package/src/states/oracles/oracle.ts +212 -0
- package/src/states/oracles/pythOracle.ts +874 -0
- package/src/states/oracles/raydiumClmmOracle.ts +1193 -0
- package/src/states/oracles/raydiumCpmmOracle.ts +784 -0
- package/src/states/oracles/switchboardOracle.ts +0 -0
- package/src/states/withdrawBasketFees.ts +160 -0
- package/src/txUtils.ts +424 -0
- package/test.ts +609 -0
- package/tsconfig.json +101 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import Decimal from 'decimal.js';
|
|
2
|
+
import BN from 'bn.js';
|
|
3
|
+
import { AccountInfo, PublicKey } from '@solana/web3.js';
|
|
4
|
+
import { OracleSettings, Side } from '../../layouts/oracle';
|
|
5
|
+
import { OraclePrice } from './oracle';
|
|
6
|
+
declare class Observation {
|
|
7
|
+
timestamp: BN;
|
|
8
|
+
cumT0Price: BN;
|
|
9
|
+
cumT1Price: BN;
|
|
10
|
+
constructor(params: {
|
|
11
|
+
timestamp: BN;
|
|
12
|
+
cumT0Price: BN;
|
|
13
|
+
cumT1Price: BN;
|
|
14
|
+
});
|
|
15
|
+
static decode(data: Buffer, offset?: number): [Observation, number];
|
|
16
|
+
sub(other: Observation): Observation;
|
|
17
|
+
add(other: Observation): Observation;
|
|
18
|
+
getWeightedObservation(time: BN): Observation;
|
|
19
|
+
adjustToTimestamp(targetTimestamp: BN, observationPrev: Observation): Observation;
|
|
20
|
+
getTwap(side: Side): BN;
|
|
21
|
+
}
|
|
22
|
+
declare class VaultState {
|
|
23
|
+
mint: PublicKey;
|
|
24
|
+
owner: PublicKey;
|
|
25
|
+
amount: string;
|
|
26
|
+
constructor(params: {
|
|
27
|
+
mint: PublicKey;
|
|
28
|
+
owner: PublicKey;
|
|
29
|
+
amount: string;
|
|
30
|
+
});
|
|
31
|
+
static decode(data: Buffer, offset?: number): [VaultState, number];
|
|
32
|
+
}
|
|
33
|
+
declare class PoolState {
|
|
34
|
+
ammConfig: PublicKey;
|
|
35
|
+
poolCreator: PublicKey;
|
|
36
|
+
token0Vault: PublicKey;
|
|
37
|
+
token1Vault: PublicKey;
|
|
38
|
+
lpMint: PublicKey;
|
|
39
|
+
token0Mint: PublicKey;
|
|
40
|
+
token1Mint: PublicKey;
|
|
41
|
+
token0Program: PublicKey;
|
|
42
|
+
token1Program: PublicKey;
|
|
43
|
+
observationKey: PublicKey;
|
|
44
|
+
authBump: number;
|
|
45
|
+
status: number;
|
|
46
|
+
lpMintDecimals: number;
|
|
47
|
+
mint0Decimals: number;
|
|
48
|
+
mint1Decimals: number;
|
|
49
|
+
lpSupply: BN;
|
|
50
|
+
protocolFeesToken0: BN;
|
|
51
|
+
protocolFeesToken1: BN;
|
|
52
|
+
fundFeesToken0: BN;
|
|
53
|
+
fundFeesToken1: BN;
|
|
54
|
+
openTime: BN;
|
|
55
|
+
recentEpoch: BN;
|
|
56
|
+
creatorFeeOn: number;
|
|
57
|
+
enableCreatorFee: boolean;
|
|
58
|
+
padding1: number[];
|
|
59
|
+
creatorFeesToken0: BN;
|
|
60
|
+
creatorFeesToken1: BN;
|
|
61
|
+
padding: BN[];
|
|
62
|
+
constructor(params: {
|
|
63
|
+
ammConfig: PublicKey;
|
|
64
|
+
poolCreator: PublicKey;
|
|
65
|
+
token0Vault: PublicKey;
|
|
66
|
+
token1Vault: PublicKey;
|
|
67
|
+
lpMint: PublicKey;
|
|
68
|
+
token0Mint: PublicKey;
|
|
69
|
+
token1Mint: PublicKey;
|
|
70
|
+
token0Program: PublicKey;
|
|
71
|
+
token1Program: PublicKey;
|
|
72
|
+
observationKey: PublicKey;
|
|
73
|
+
authBump: number;
|
|
74
|
+
status: number;
|
|
75
|
+
lpMintDecimals: number;
|
|
76
|
+
mint0Decimals: number;
|
|
77
|
+
mint1Decimals: number;
|
|
78
|
+
lpSupply: BN;
|
|
79
|
+
protocolFeesToken0: BN;
|
|
80
|
+
protocolFeesToken1: BN;
|
|
81
|
+
fundFeesToken0: BN;
|
|
82
|
+
fundFeesToken1: BN;
|
|
83
|
+
openTime: BN;
|
|
84
|
+
recentEpoch: BN;
|
|
85
|
+
creatorFeeOn: number;
|
|
86
|
+
enableCreatorFee: boolean;
|
|
87
|
+
padding1: number[];
|
|
88
|
+
creatorFeesToken0: BN;
|
|
89
|
+
creatorFeesToken1: BN;
|
|
90
|
+
padding: BN[];
|
|
91
|
+
});
|
|
92
|
+
static decode(data: Buffer, offset?: number): [PoolState, number];
|
|
93
|
+
}
|
|
94
|
+
export declare class RaydiumCPMMOracle {
|
|
95
|
+
static deriveObservationKey(poolId: PublicKey): [PublicKey, number];
|
|
96
|
+
static getObservationAtIndex(observations: Observation[], index: number): Observation;
|
|
97
|
+
static getObservationAtTimestamp(timestamp: BN, observations: Observation[], startIndex: number): Observation;
|
|
98
|
+
static getDeltaObservations(currentTime: BN, observations: Observation[], observationIndex: number, primarySeconds: BN, secondarySeconds: BN): Observation[];
|
|
99
|
+
static getDecimals(side: Side, mint0Decimals: number, mint1Decimals: number): number;
|
|
100
|
+
/**
|
|
101
|
+
* Calculate spot price from pool reserves after subtracting all fees.
|
|
102
|
+
* Returns: { netBase, netQuote, spotPrice } where spotPrice is in USD per base token.
|
|
103
|
+
*/
|
|
104
|
+
static getSpotPriceWithReserves(poolState: PoolState, vault0State: VaultState, vault1State: VaultState, side: Side, quotePrice: OraclePrice): {
|
|
105
|
+
netBase: Decimal;
|
|
106
|
+
netQuote: Decimal;
|
|
107
|
+
spotPrice: Decimal;
|
|
108
|
+
};
|
|
109
|
+
static getTwapPrimary(currentTime: BN, observations: Observation[], observationIndex: number, side: Side, primarySeconds: BN, secondarySeconds: BN, mint0Decimals: number, mint1Decimals: number): Decimal;
|
|
110
|
+
static getTwapSecondary(currentTime: BN, observations: Observation[], observationIndex: number, side: Side, primarySeconds: BN, secondarySeconds: BN, mint0Decimals: number, mint1Decimals: number): Decimal;
|
|
111
|
+
/**
|
|
112
|
+
* Calculate max price impact for minLiquidity trade in both directions (buy and sell).
|
|
113
|
+
* Uses constant-product AMM formulas:
|
|
114
|
+
* - BUY: delta_base_out = netBase * delta_q / (netQuote + delta_q)
|
|
115
|
+
* - SELL: delta_quote_out = netQuote * delta_base / (netBase + delta_base)
|
|
116
|
+
*/
|
|
117
|
+
static calculateMaxPriceImpactForMinLiquidity(oracleParams: OracleSettings, netBase: Decimal, netQuote: Decimal, spotPriceUsd: Decimal, quotePrice: OraclePrice): Decimal;
|
|
118
|
+
static fetch(oracleParams: OracleSettings, accountInfos: AccountInfo<Buffer>[], solPrice: OraclePrice, usdPrice: OraclePrice): OraclePrice;
|
|
119
|
+
}
|
|
120
|
+
export {};
|
|
@@ -0,0 +1,540 @@
|
|
|
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.RaydiumCPMMOracle = void 0;
|
|
7
|
+
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
8
|
+
const bn_js_1 = __importDefault(require("bn.js"));
|
|
9
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
10
|
+
const oracle_1 = require("../../layouts/oracle");
|
|
11
|
+
const oracle_2 = require("./oracle");
|
|
12
|
+
const CPMM_PROGRAM_ID = new web3_js_1.PublicKey("CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C");
|
|
13
|
+
const DEV_CPMM_PROGRAM_ID = new web3_js_1.PublicKey("DRaycpLY18LhpbydsBWbVJtxpNv9oXPgjRSfpF2bWpYb");
|
|
14
|
+
class Observation {
|
|
15
|
+
constructor(params) {
|
|
16
|
+
this.timestamp = params.timestamp;
|
|
17
|
+
this.cumT0Price = params.cumT0Price;
|
|
18
|
+
this.cumT1Price = params.cumT1Price;
|
|
19
|
+
}
|
|
20
|
+
static decode(data, offset = 0) {
|
|
21
|
+
const timestamp = new bn_js_1.default(data.subarray(offset, offset + 8), "le");
|
|
22
|
+
const cumT0Price = new bn_js_1.default(data.subarray(offset + 8, offset + 24), 'le');
|
|
23
|
+
const cumT1Price = new bn_js_1.default(data.subarray(offset + 24, offset + 40), 'le');
|
|
24
|
+
return [
|
|
25
|
+
new Observation({
|
|
26
|
+
timestamp,
|
|
27
|
+
cumT0Price,
|
|
28
|
+
cumT1Price
|
|
29
|
+
}),
|
|
30
|
+
offset + 40
|
|
31
|
+
];
|
|
32
|
+
}
|
|
33
|
+
sub(other) {
|
|
34
|
+
if (!(other instanceof Observation)) {
|
|
35
|
+
throw new TypeError("Subtraction is only supported between Observation instances");
|
|
36
|
+
}
|
|
37
|
+
return new Observation({
|
|
38
|
+
timestamp: this.timestamp.sub(other.timestamp),
|
|
39
|
+
cumT0Price: this.cumT0Price.sub(other.cumT0Price),
|
|
40
|
+
cumT1Price: this.cumT1Price.sub(other.cumT1Price),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
add(other) {
|
|
44
|
+
if (!(other instanceof Observation)) {
|
|
45
|
+
throw new TypeError("Addition is only supported between Observation instances");
|
|
46
|
+
}
|
|
47
|
+
return new Observation({
|
|
48
|
+
timestamp: this.timestamp.add(other.timestamp),
|
|
49
|
+
cumT0Price: this.cumT0Price.add(other.cumT0Price),
|
|
50
|
+
cumT1Price: this.cumT1Price.add(other.cumT1Price),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
getWeightedObservation(time) {
|
|
54
|
+
const cumT0Price = this.cumT0Price.mul(time).div(this.timestamp);
|
|
55
|
+
const cumT1Price = this.cumT1Price.mul(time).div(this.timestamp);
|
|
56
|
+
return new Observation({
|
|
57
|
+
timestamp: time,
|
|
58
|
+
cumT0Price,
|
|
59
|
+
cumT1Price
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
adjustToTimestamp(targetTimestamp, observationPrev) {
|
|
63
|
+
// delta = self.sub(prevPbservation)
|
|
64
|
+
const delta = this.sub(observationPrev);
|
|
65
|
+
// weightedDelta = delta.getWeightedObservation(targetTimestamp - self.blockTimestamp)
|
|
66
|
+
const timeForWeighted = targetTimestamp.sub(this.timestamp);
|
|
67
|
+
const weightedDelta = delta.getWeightedObservation(timeForWeighted);
|
|
68
|
+
// return self.add(weightedDelta)
|
|
69
|
+
return this.add(weightedDelta);
|
|
70
|
+
}
|
|
71
|
+
getTwap(side) {
|
|
72
|
+
let cumPrice = new bn_js_1.default(0);
|
|
73
|
+
if (side === oracle_1.Side.Base)
|
|
74
|
+
cumPrice = this.cumT0Price;
|
|
75
|
+
else
|
|
76
|
+
cumPrice = this.cumT1Price;
|
|
77
|
+
return cumPrice.div(this.timestamp);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
class VaultState {
|
|
81
|
+
constructor(params) {
|
|
82
|
+
this.mint = params.mint;
|
|
83
|
+
this.owner = params.owner;
|
|
84
|
+
this.amount = params.amount;
|
|
85
|
+
}
|
|
86
|
+
static decode(data, offset = 0) {
|
|
87
|
+
// mint (32)
|
|
88
|
+
const mint = new web3_js_1.PublicKey(data.slice(offset, offset + 32));
|
|
89
|
+
offset += 32;
|
|
90
|
+
// owner (32)
|
|
91
|
+
const owner = new web3_js_1.PublicKey(data.slice(offset, offset + 32));
|
|
92
|
+
offset += 32;
|
|
93
|
+
// amount u64 (8)
|
|
94
|
+
const amount = (new bn_js_1.default(data.subarray(offset, offset + 8), 'le')).toString();
|
|
95
|
+
offset += 8;
|
|
96
|
+
return [
|
|
97
|
+
new VaultState({ mint, owner, amount }),
|
|
98
|
+
offset
|
|
99
|
+
];
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
class ObservationState {
|
|
103
|
+
constructor(params) {
|
|
104
|
+
this.initialized = params.initialized;
|
|
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 observationIndex = data.readUInt16LE(cursor);
|
|
115
|
+
cursor += 2;
|
|
116
|
+
const poolId = new web3_js_1.PublicKey(data.subarray(cursor, cursor + 32));
|
|
117
|
+
cursor += 32;
|
|
118
|
+
const observations = [];
|
|
119
|
+
for (let i = 0; i < 100; i++) {
|
|
120
|
+
let obs;
|
|
121
|
+
[obs, cursor] = Observation.decode(data, cursor);
|
|
122
|
+
observations.push(obs);
|
|
123
|
+
}
|
|
124
|
+
const padding = [];
|
|
125
|
+
for (let i = 0; i < 4; i++) {
|
|
126
|
+
padding.push(new bn_js_1.default(data.subarray(cursor, cursor + 8), "le"));
|
|
127
|
+
cursor += 8;
|
|
128
|
+
}
|
|
129
|
+
return [
|
|
130
|
+
new ObservationState({
|
|
131
|
+
initialized,
|
|
132
|
+
observationIndex,
|
|
133
|
+
poolId,
|
|
134
|
+
observations,
|
|
135
|
+
padding
|
|
136
|
+
}),
|
|
137
|
+
cursor
|
|
138
|
+
];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
class PoolState {
|
|
142
|
+
constructor(params) {
|
|
143
|
+
this.ammConfig = params.ammConfig;
|
|
144
|
+
this.poolCreator = params.poolCreator;
|
|
145
|
+
this.token0Vault = params.token0Vault;
|
|
146
|
+
this.token1Vault = params.token1Vault;
|
|
147
|
+
this.lpMint = params.lpMint;
|
|
148
|
+
this.token0Mint = params.token0Mint;
|
|
149
|
+
this.token1Mint = params.token1Mint;
|
|
150
|
+
this.token0Program = params.token0Program;
|
|
151
|
+
this.token1Program = params.token1Program;
|
|
152
|
+
this.observationKey = params.observationKey;
|
|
153
|
+
this.authBump = params.authBump; // u8
|
|
154
|
+
this.status = params.status; // u8
|
|
155
|
+
this.lpMintDecimals = params.lpMintDecimals; // u8
|
|
156
|
+
this.mint0Decimals = params.mint0Decimals; // u8
|
|
157
|
+
this.mint1Decimals = params.mint1Decimals; // u8
|
|
158
|
+
this.lpSupply = params.lpSupply; // u64
|
|
159
|
+
this.protocolFeesToken0 = params.protocolFeesToken0; // u64
|
|
160
|
+
this.protocolFeesToken1 = params.protocolFeesToken1; // u64
|
|
161
|
+
this.fundFeesToken0 = params.fundFeesToken0; // u64
|
|
162
|
+
this.fundFeesToken1 = params.fundFeesToken1; // u64
|
|
163
|
+
this.openTime = params.openTime; // u64
|
|
164
|
+
this.recentEpoch = params.recentEpoch; // u64
|
|
165
|
+
this.creatorFeeOn = params.creatorFeeOn; //u8
|
|
166
|
+
this.enableCreatorFee = params.enableCreatorFee;
|
|
167
|
+
this.padding1 = params.padding1; // [u8; 6]
|
|
168
|
+
this.creatorFeesToken0 = params.creatorFeesToken0; // u64
|
|
169
|
+
this.creatorFeesToken1 = params.creatorFeesToken1; // u64
|
|
170
|
+
this.padding = params.padding; // [u64; 28]
|
|
171
|
+
}
|
|
172
|
+
static decode(data, offset = 8) {
|
|
173
|
+
let cursor = offset;
|
|
174
|
+
const ammConfig = new web3_js_1.PublicKey(data.subarray(cursor, cursor + 32));
|
|
175
|
+
cursor += 32;
|
|
176
|
+
const poolCreator = new web3_js_1.PublicKey(data.subarray(cursor, cursor + 32));
|
|
177
|
+
cursor += 32;
|
|
178
|
+
const token0Vault = new web3_js_1.PublicKey(data.subarray(cursor, cursor + 32));
|
|
179
|
+
cursor += 32;
|
|
180
|
+
const token1Vault = new web3_js_1.PublicKey(data.subarray(cursor, cursor + 32));
|
|
181
|
+
cursor += 32;
|
|
182
|
+
const lpMint = new web3_js_1.PublicKey(data.subarray(cursor, cursor + 32));
|
|
183
|
+
cursor += 32;
|
|
184
|
+
const token0Mint = new web3_js_1.PublicKey(data.subarray(cursor, cursor + 32));
|
|
185
|
+
cursor += 32;
|
|
186
|
+
const token1Mint = new web3_js_1.PublicKey(data.subarray(cursor, cursor + 32));
|
|
187
|
+
cursor += 32;
|
|
188
|
+
const token0Program = new web3_js_1.PublicKey(data.subarray(cursor, cursor + 32));
|
|
189
|
+
cursor += 32;
|
|
190
|
+
const token1Program = new web3_js_1.PublicKey(data.subarray(cursor, cursor + 32));
|
|
191
|
+
cursor += 32;
|
|
192
|
+
const observationKey = new web3_js_1.PublicKey(data.subarray(cursor, cursor + 32));
|
|
193
|
+
cursor += 32;
|
|
194
|
+
const authBump = data.readUInt8(cursor);
|
|
195
|
+
cursor += 1;
|
|
196
|
+
const status = data.readUInt8(cursor);
|
|
197
|
+
cursor += 1;
|
|
198
|
+
const lpMintDecimals = data.readUInt8(cursor);
|
|
199
|
+
cursor += 1;
|
|
200
|
+
const mint0Decimals = data.readUInt8(cursor);
|
|
201
|
+
cursor += 1;
|
|
202
|
+
const mint1Decimals = data.readUInt8(cursor);
|
|
203
|
+
cursor += 1;
|
|
204
|
+
const lpSupply = new bn_js_1.default(data.subarray(cursor, cursor + 8), "le");
|
|
205
|
+
cursor += 8;
|
|
206
|
+
const protocolFeesToken0 = new bn_js_1.default(data.subarray(cursor, cursor + 8), "le");
|
|
207
|
+
cursor += 8;
|
|
208
|
+
const protocolFeesToken1 = new bn_js_1.default(data.subarray(cursor, cursor + 8), "le");
|
|
209
|
+
cursor += 8;
|
|
210
|
+
const fundFeesToken0 = new bn_js_1.default(data.subarray(cursor, cursor + 8), "le");
|
|
211
|
+
cursor += 8;
|
|
212
|
+
const fundFeesToken1 = new bn_js_1.default(data.subarray(cursor, cursor + 8), "le");
|
|
213
|
+
cursor += 8;
|
|
214
|
+
const openTime = new bn_js_1.default(data.subarray(cursor, cursor + 8), "le");
|
|
215
|
+
cursor += 8;
|
|
216
|
+
const recentEpoch = new bn_js_1.default(data.subarray(cursor, cursor + 8), "le");
|
|
217
|
+
cursor += 8;
|
|
218
|
+
const creatorFeeOn = data.readUInt8(cursor);
|
|
219
|
+
cursor += 1;
|
|
220
|
+
const enableCreatorFee = !!data.readUInt8(cursor);
|
|
221
|
+
cursor += 1;
|
|
222
|
+
const padding1 = [];
|
|
223
|
+
for (let i = 0; i < 6; i++) {
|
|
224
|
+
padding1.push(data.readUInt8(cursor));
|
|
225
|
+
cursor += 1;
|
|
226
|
+
}
|
|
227
|
+
const creatorFeesToken0 = new bn_js_1.default(data.subarray(cursor, cursor + 8), "le");
|
|
228
|
+
cursor += 8;
|
|
229
|
+
const creatorFeesToken1 = new bn_js_1.default(data.subarray(cursor, cursor + 8), "le");
|
|
230
|
+
cursor += 8;
|
|
231
|
+
const padding = [];
|
|
232
|
+
for (let i = 0; i < 28; i++) {
|
|
233
|
+
padding.push(new bn_js_1.default(data.subarray(cursor, cursor + 8), "le"));
|
|
234
|
+
cursor += 8;
|
|
235
|
+
}
|
|
236
|
+
return [
|
|
237
|
+
new PoolState({
|
|
238
|
+
ammConfig,
|
|
239
|
+
poolCreator,
|
|
240
|
+
token0Vault,
|
|
241
|
+
token1Vault,
|
|
242
|
+
lpMint,
|
|
243
|
+
token0Mint,
|
|
244
|
+
token1Mint,
|
|
245
|
+
token0Program,
|
|
246
|
+
token1Program,
|
|
247
|
+
observationKey,
|
|
248
|
+
authBump,
|
|
249
|
+
status,
|
|
250
|
+
lpMintDecimals,
|
|
251
|
+
mint0Decimals,
|
|
252
|
+
mint1Decimals,
|
|
253
|
+
lpSupply,
|
|
254
|
+
protocolFeesToken0,
|
|
255
|
+
protocolFeesToken1,
|
|
256
|
+
fundFeesToken0,
|
|
257
|
+
fundFeesToken1,
|
|
258
|
+
openTime,
|
|
259
|
+
recentEpoch,
|
|
260
|
+
creatorFeeOn,
|
|
261
|
+
enableCreatorFee,
|
|
262
|
+
padding1,
|
|
263
|
+
creatorFeesToken0,
|
|
264
|
+
creatorFeesToken1,
|
|
265
|
+
padding,
|
|
266
|
+
}),
|
|
267
|
+
cursor,
|
|
268
|
+
];
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
class RaydiumCPMMOracle {
|
|
272
|
+
// poolId: PublicKey;
|
|
273
|
+
// poolState: PoolState;
|
|
274
|
+
// observationState: ObservationState;
|
|
275
|
+
// vault0: VaultState;
|
|
276
|
+
// vault1: VaultState;
|
|
277
|
+
// constructor(
|
|
278
|
+
// oracleParams: OracleSettings,
|
|
279
|
+
// cpmmParams: {
|
|
280
|
+
// poolId: PublicKey;
|
|
281
|
+
// poolState: PoolState;
|
|
282
|
+
// observationState: ObservationState,
|
|
283
|
+
// vault0: VaultState,
|
|
284
|
+
// vault1: VaultState,
|
|
285
|
+
// }){
|
|
286
|
+
// super(
|
|
287
|
+
// oracleParams
|
|
288
|
+
// );
|
|
289
|
+
// this.poolId = cpmmParams.poolId;
|
|
290
|
+
// this.poolState = cpmmParams.poolState;
|
|
291
|
+
// this.observationState = cpmmParams.observationState;
|
|
292
|
+
// this.vault0 = cpmmParams.vault0;
|
|
293
|
+
// this.vault1 = cpmmParams.vault1;
|
|
294
|
+
// }
|
|
295
|
+
static deriveObservationKey(poolId) {
|
|
296
|
+
const seeds = [
|
|
297
|
+
Buffer.from("observation"),
|
|
298
|
+
poolId.toBuffer(),
|
|
299
|
+
];
|
|
300
|
+
return web3_js_1.PublicKey.findProgramAddressSync(seeds, CPMM_PROGRAM_ID);
|
|
301
|
+
}
|
|
302
|
+
;
|
|
303
|
+
static getObservationAtIndex(observations, index) {
|
|
304
|
+
return observations[index];
|
|
305
|
+
}
|
|
306
|
+
static getObservationAtTimestamp(timestamp, observations, startIndex) {
|
|
307
|
+
let observationCurrent = this.getObservationAtIndex(observations, startIndex);
|
|
308
|
+
let index = startIndex;
|
|
309
|
+
// Loop backwards until we find the observation <= timestamp
|
|
310
|
+
while (observationCurrent.timestamp.gt(timestamp)) {
|
|
311
|
+
index = (index - 1 + 100) % 100;
|
|
312
|
+
observationCurrent = this.getObservationAtIndex(observations, index);
|
|
313
|
+
if (index === startIndex) {
|
|
314
|
+
throw Error("Observations do not go back far enough in time");
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// Previous index for interpolation
|
|
318
|
+
const prevIndex = (index - 1 + 100) % 100;
|
|
319
|
+
const observationPrev = this.getObservationAtIndex(observations, prevIndex);
|
|
320
|
+
// Adjust current observation to the target timestamp
|
|
321
|
+
const result = observationCurrent.adjustToTimestamp(timestamp, observationPrev);
|
|
322
|
+
return result;
|
|
323
|
+
}
|
|
324
|
+
static getDeltaObservations(currentTime, observations, observationIndex, primarySeconds, secondarySeconds) {
|
|
325
|
+
const obsIndex = observationIndex;
|
|
326
|
+
const observationCurrent = this.getObservationAtTimestamp(currentTime, observations, obsIndex);
|
|
327
|
+
const observationPrimary = this.getObservationAtTimestamp(currentTime.sub(primarySeconds), observations, obsIndex);
|
|
328
|
+
const deltaPrimary = observationCurrent.sub(observationPrimary);
|
|
329
|
+
const observationSecondary = this.getObservationAtTimestamp(currentTime.sub(secondarySeconds), observations, obsIndex);
|
|
330
|
+
const deltaSecondary = observationCurrent.sub(observationSecondary);
|
|
331
|
+
return [deltaPrimary, deltaSecondary];
|
|
332
|
+
}
|
|
333
|
+
;
|
|
334
|
+
static getDecimals(side, mint0Decimals, mint1Decimals) {
|
|
335
|
+
let dec;
|
|
336
|
+
if (side === oracle_1.Side.Base)
|
|
337
|
+
dec = mint0Decimals - mint1Decimals;
|
|
338
|
+
else
|
|
339
|
+
dec = mint1Decimals - mint0Decimals;
|
|
340
|
+
return dec;
|
|
341
|
+
}
|
|
342
|
+
// static getSpotPrice(
|
|
343
|
+
// baseAmount: BN, quoteAmount: BN, protocolFeesToken0: BN, protocolFeesToken1: BN,
|
|
344
|
+
// fundFeesToken0: BN, fundFeesToken1: BN, mint0Decimals: number, mint1Decimals: number, side: Side
|
|
345
|
+
// ): Decimal {
|
|
346
|
+
// let decimals = this.getDecimals(side, mint0Decimals, mint1Decimals);
|
|
347
|
+
// let fees0 = protocolFeesToken0.add(fundFeesToken0);
|
|
348
|
+
// let fees1 = protocolFeesToken1.add(fundFeesToken1);
|
|
349
|
+
// if(side === Side.Base) {
|
|
350
|
+
// baseAmount = baseAmount.sub(fees0);
|
|
351
|
+
// quoteAmount = quoteAmount.sub(fees1);
|
|
352
|
+
// }
|
|
353
|
+
// else {
|
|
354
|
+
// baseAmount = baseAmount.sub(fees1);
|
|
355
|
+
// quoteAmount = quoteAmount.sub(fees0);
|
|
356
|
+
// }
|
|
357
|
+
// let price_scaled = new Decimal(baseAmount.toString()).div(quoteAmount.toString());
|
|
358
|
+
// return price_scaled.div(10 ** decimals);
|
|
359
|
+
// }
|
|
360
|
+
/**
|
|
361
|
+
* Calculate spot price from pool reserves after subtracting all fees.
|
|
362
|
+
* Returns: { netBase, netQuote, spotPrice } where spotPrice is in USD per base token.
|
|
363
|
+
*/
|
|
364
|
+
static getSpotPriceWithReserves(poolState, vault0State, vault1State, side, quotePrice) {
|
|
365
|
+
// Total fees per token (protocol + fund + creator)
|
|
366
|
+
let fees0 = new decimal_js_1.default(poolState.protocolFeesToken0.toString())
|
|
367
|
+
.add(new decimal_js_1.default(poolState.fundFeesToken0.toString()))
|
|
368
|
+
.add(new decimal_js_1.default(poolState.creatorFeesToken0.toString()));
|
|
369
|
+
let fees1 = new decimal_js_1.default(poolState.protocolFeesToken1.toString())
|
|
370
|
+
.add(new decimal_js_1.default(poolState.fundFeesToken1.toString()))
|
|
371
|
+
.add(new decimal_js_1.default(poolState.creatorFeesToken1.toString()));
|
|
372
|
+
// Swap fees if side is Quote
|
|
373
|
+
if (side === oracle_1.Side.Quote) {
|
|
374
|
+
[fees0, fees1] = [fees1, fees0];
|
|
375
|
+
}
|
|
376
|
+
// Get vault balances
|
|
377
|
+
const baseBalance = new decimal_js_1.default(vault0State.amount);
|
|
378
|
+
const quoteBalance = new decimal_js_1.default(vault1State.amount);
|
|
379
|
+
// Net reserves after subtracting fees
|
|
380
|
+
const netBase = baseBalance.sub(fees0);
|
|
381
|
+
const netQuote = quoteBalance.sub(fees1);
|
|
382
|
+
// Guard against zero or negative reserves
|
|
383
|
+
if (netBase.lte(0) || netQuote.lte(0)) {
|
|
384
|
+
return { netBase: new decimal_js_1.default(0), netQuote: new decimal_js_1.default(0), spotPrice: new decimal_js_1.default(0) };
|
|
385
|
+
}
|
|
386
|
+
// Relative price = netQuote / netBase (quote tokens per 1 base token)
|
|
387
|
+
// Example: 10,000 USDC / 100 SOL = 100 USDC per SOL
|
|
388
|
+
const relativePrice = netQuote.div(netBase);
|
|
389
|
+
// Convert to USD: spotPrice = relativePrice * quotePrice.price
|
|
390
|
+
// If quote is USDC and quotePrice.price ≈ 1, spotPrice ≈ relativePrice
|
|
391
|
+
// If quote is WSOL and quotePrice.price = $150, spotPrice = relativePrice * 150
|
|
392
|
+
const spotPrice = relativePrice.mul(quotePrice.price);
|
|
393
|
+
return { netBase, netQuote, spotPrice };
|
|
394
|
+
}
|
|
395
|
+
static getTwapPrimary(currentTime, observations, observationIndex, side, primarySeconds, secondarySeconds, mint0Decimals, mint1Decimals) {
|
|
396
|
+
let observation = this.getDeltaObservations(currentTime, observations, observationIndex, primarySeconds, secondarySeconds)[0];
|
|
397
|
+
// TODO: use constant for 2**32 number is not safe
|
|
398
|
+
let twap_scaled = new decimal_js_1.default(observation.getTwap(side).toString()).div(2 ** 32);
|
|
399
|
+
let twap = new decimal_js_1.default(twap_scaled.toString()).div(10 ** this.getDecimals(side, mint0Decimals, mint1Decimals));
|
|
400
|
+
return twap;
|
|
401
|
+
}
|
|
402
|
+
static getTwapSecondary(currentTime, observations, observationIndex, side, primarySeconds, secondarySeconds, mint0Decimals, mint1Decimals) {
|
|
403
|
+
let observation = this.getDeltaObservations(currentTime, observations, observationIndex, primarySeconds, secondarySeconds)[1];
|
|
404
|
+
// TODO: use constant for 2**32 number is not safe
|
|
405
|
+
let twap_scaled = new decimal_js_1.default(observation.getTwap(side).toString()).div(2 ** 32);
|
|
406
|
+
let twap = new decimal_js_1.default(twap_scaled.toString()).div(10 ** this.getDecimals(side, mint0Decimals, mint1Decimals));
|
|
407
|
+
return twap;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Calculate max price impact for minLiquidity trade in both directions (buy and sell).
|
|
411
|
+
* Uses constant-product AMM formulas:
|
|
412
|
+
* - BUY: delta_base_out = netBase * delta_q / (netQuote + delta_q)
|
|
413
|
+
* - SELL: delta_quote_out = netQuote * delta_base / (netBase + delta_base)
|
|
414
|
+
*/
|
|
415
|
+
static calculateMaxPriceImpactForMinLiquidity(oracleParams, netBase, netQuote, spotPriceUsd, quotePrice) {
|
|
416
|
+
const minLiquidityUsd = new decimal_js_1.default(oracleParams.minLiquidity.toString());
|
|
417
|
+
if (spotPriceUsd.lte(0)) {
|
|
418
|
+
return new decimal_js_1.default(10000); // 100% impact - invalid
|
|
419
|
+
}
|
|
420
|
+
// Convert minLiquidity USD to quote tokens
|
|
421
|
+
// delta_q_tokens = minLiquidityUsd / quotePrice.price
|
|
422
|
+
const deltaQTokens = minLiquidityUsd.div(quotePrice.price);
|
|
423
|
+
// Require pool has at least that many quote tokens
|
|
424
|
+
if (netQuote.lt(deltaQTokens)) {
|
|
425
|
+
// Not enough liquidity - return max impact (will fail validation)
|
|
426
|
+
return new decimal_js_1.default(10000); // 100% impact
|
|
427
|
+
}
|
|
428
|
+
// === BUY simulation (user spends delta_q quote to receive base) ===
|
|
429
|
+
// delta_base_buy = netBase * delta_q / (netQuote + delta_q)
|
|
430
|
+
const denomBuy = netQuote.add(deltaQTokens);
|
|
431
|
+
const deltaBaseBuy = netBase.mul(deltaQTokens).div(denomBuy);
|
|
432
|
+
let impactBuy = new decimal_js_1.default(0);
|
|
433
|
+
if (deltaBaseBuy.gt(0)) {
|
|
434
|
+
// quote spent in USD = delta_q * quotePrice
|
|
435
|
+
const quoteUsd = deltaQTokens.mul(quotePrice.price);
|
|
436
|
+
// execution price in USD per base = quoteUsd / deltaBaseBuy
|
|
437
|
+
const execPriceBuy = quoteUsd.div(deltaBaseBuy);
|
|
438
|
+
// impact = |execPrice - spotPrice| / spotPrice
|
|
439
|
+
const diffBuy = execPriceBuy.sub(spotPriceUsd).abs();
|
|
440
|
+
impactBuy = diffBuy.div(spotPriceUsd);
|
|
441
|
+
}
|
|
442
|
+
// === SELL simulation (user sells base roughly equivalent to minLiquidity USD) ===
|
|
443
|
+
// Approximate base amount to sell: delta_base_sell = delta_q * netBase / netQuote
|
|
444
|
+
let deltaBaseSell = new decimal_js_1.default(0);
|
|
445
|
+
if (netQuote.gt(0)) {
|
|
446
|
+
deltaBaseSell = deltaQTokens.mul(netBase).div(netQuote);
|
|
447
|
+
}
|
|
448
|
+
let impactSell = new decimal_js_1.default(0);
|
|
449
|
+
if (deltaBaseSell.gt(0)) {
|
|
450
|
+
// delta_quote_out = netQuote * delta_base_sell / (netBase + delta_base_sell)
|
|
451
|
+
const denomSell = netBase.add(deltaBaseSell);
|
|
452
|
+
const deltaQuoteOut = netQuote.mul(deltaBaseSell).div(denomSell);
|
|
453
|
+
// quote out in USD
|
|
454
|
+
const quoteOutUsd = deltaQuoteOut.mul(quotePrice.price);
|
|
455
|
+
// execution price in USD per base = quoteOutUsd / deltaBaseSell
|
|
456
|
+
const execPriceSell = quoteOutUsd.div(deltaBaseSell);
|
|
457
|
+
// impact = |execPrice - spotPrice| / spotPrice
|
|
458
|
+
const diffSell = execPriceSell.sub(spotPriceUsd).abs();
|
|
459
|
+
impactSell = diffSell.div(spotPriceUsd);
|
|
460
|
+
}
|
|
461
|
+
// Take max impact of both sides and convert to bps
|
|
462
|
+
const maxImpact = decimal_js_1.default.max(impactBuy, impactSell);
|
|
463
|
+
return maxImpact.mul(new decimal_js_1.default(10000)); // Convert to bps
|
|
464
|
+
}
|
|
465
|
+
static fetch(oracleParams, accountInfos, solPrice, usdPrice) {
|
|
466
|
+
const poolStateInfo = accountInfos[0];
|
|
467
|
+
const vault0Info = accountInfos[1];
|
|
468
|
+
const vault1Info = accountInfos[2];
|
|
469
|
+
const observationStateInfo = accountInfos[3];
|
|
470
|
+
const [decodedPoolState, _] = PoolState.decode(poolStateInfo.data, 8);
|
|
471
|
+
const [decodedVault0State, __] = VaultState.decode(vault0Info.data, 0);
|
|
472
|
+
const [decodedVault1State, ___] = VaultState.decode(vault1Info.data, 0);
|
|
473
|
+
const [decodedObservationState, ____] = ObservationState.decode(observationStateInfo.data, 8);
|
|
474
|
+
const currentTime = new bn_js_1.default(Math.floor(Date.now() / 1000));
|
|
475
|
+
// Determine quote oracle
|
|
476
|
+
const quotePrice = oracleParams.quote === oracle_1.Quote.Usdc ? usdPrice : solPrice;
|
|
477
|
+
// Get spot price with reserves
|
|
478
|
+
const { netBase, netQuote, spotPrice } = this.getSpotPriceWithReserves(decodedPoolState, decodedVault0State, decodedVault1State, oracleParams.side, quotePrice);
|
|
479
|
+
// Get TWAP prices
|
|
480
|
+
let primaryPrice = RaydiumCPMMOracle.getTwapPrimary(currentTime, decodedObservationState.observations, decodedObservationState.observationIndex, oracleParams.side, oracleParams.twapSecondsAgo, oracleParams.twapSecondarySecondsAgo, decodedPoolState.mint0Decimals, decodedPoolState.mint1Decimals);
|
|
481
|
+
let secondaryPrice = RaydiumCPMMOracle.getTwapSecondary(currentTime, decodedObservationState.observations, decodedObservationState.observationIndex, oracleParams.side, oracleParams.twapSecondsAgo, oracleParams.twapSecondarySecondsAgo, decodedPoolState.mint0Decimals, decodedPoolState.mint1Decimals);
|
|
482
|
+
// Convert TWAP prices to USD
|
|
483
|
+
const primaryPriceUsd = primaryPrice.mul(quotePrice.price);
|
|
484
|
+
const secondaryPriceUsd = secondaryPrice.mul(quotePrice.price);
|
|
485
|
+
// Validate primary TWAP is not zero
|
|
486
|
+
if (primaryPriceUsd.eq(0)) {
|
|
487
|
+
return new oracle_2.OraclePrice(new decimal_js_1.default(0), new decimal_js_1.default(0), 0);
|
|
488
|
+
}
|
|
489
|
+
// === Min liquidity and price impact validation ===
|
|
490
|
+
if (oracleParams.minLiquidity.gt(new bn_js_1.default(0))) {
|
|
491
|
+
const impactBps = this.calculateMaxPriceImpactForMinLiquidity(oracleParams, netBase, netQuote, spotPrice, quotePrice);
|
|
492
|
+
if (oracleParams.maxSlippageBps > 0) {
|
|
493
|
+
if (impactBps.gt(new decimal_js_1.default(oracleParams.maxSlippageBps))) {
|
|
494
|
+
return new oracle_2.OraclePrice(new decimal_js_1.default(0), new decimal_js_1.default(0), 0);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
// === Confidence calculation ===
|
|
499
|
+
const maxPrice = decimal_js_1.default.max(primaryPriceUsd, secondaryPriceUsd, spotPrice);
|
|
500
|
+
const minPrice = decimal_js_1.default.min(primaryPriceUsd, secondaryPriceUsd, spotPrice);
|
|
501
|
+
let confidence = maxPrice.gt(minPrice) ? maxPrice.sub(minPrice) : new decimal_js_1.default(0);
|
|
502
|
+
// Get last update timestamp from latest observation
|
|
503
|
+
const lastUpdateTimestamp = decodedObservationState.observations[decodedObservationState.observationIndex].timestamp;
|
|
504
|
+
// === Inflate confidence by staleness ===
|
|
505
|
+
// confidence = confidence * (1 + delta_t * stalenessConfRateBps / 10_000)
|
|
506
|
+
const deltaSecondsBN = currentTime.sub(lastUpdateTimestamp);
|
|
507
|
+
const deltaSeconds = new decimal_js_1.default(deltaSecondsBN.toString());
|
|
508
|
+
if (oracleParams.stalenessConfRateBps > 0 && deltaSeconds.gt(0)) {
|
|
509
|
+
const stalenessRate = new decimal_js_1.default(oracleParams.stalenessConfRateBps).div(new decimal_js_1.default(10000));
|
|
510
|
+
const inflateFactor = new decimal_js_1.default(1).add(deltaSeconds.mul(stalenessRate));
|
|
511
|
+
confidence = confidence.mul(inflateFactor);
|
|
512
|
+
}
|
|
513
|
+
// === Validate confidence threshold ===
|
|
514
|
+
// confidence / primaryPrice * 10_000 < confThreshBps
|
|
515
|
+
const confRatioBps = confidence.div(primaryPriceUsd).mul(new decimal_js_1.default(10000));
|
|
516
|
+
if (confRatioBps.gt(new decimal_js_1.default(oracleParams.confThreshBps))) {
|
|
517
|
+
return new oracle_2.OraclePrice(new decimal_js_1.default(0), new decimal_js_1.default(0), 0);
|
|
518
|
+
}
|
|
519
|
+
// === Validate staleness threshold ===
|
|
520
|
+
if (oracleParams.stalenessThresh.gt(new bn_js_1.default(0))) {
|
|
521
|
+
if (currentTime.sub(lastUpdateTimestamp).gt(oracleParams.stalenessThresh)) {
|
|
522
|
+
return new oracle_2.OraclePrice(new decimal_js_1.default(0), new decimal_js_1.default(0), 0);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
// === Validate volatility threshold ===
|
|
526
|
+
// (maxPrice - minPrice) / minPrice * 10_000 <= volatilityThreshBps
|
|
527
|
+
if (!minPrice.eq(0)) {
|
|
528
|
+
const volRatioBps = maxPrice.sub(minPrice).div(minPrice).mul(new decimal_js_1.default(10000));
|
|
529
|
+
if (volRatioBps.gt(new decimal_js_1.default(oracleParams.volatilityThreshBps))) {
|
|
530
|
+
return new oracle_2.OraclePrice(new decimal_js_1.default(0), new decimal_js_1.default(0), 0);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
else {
|
|
534
|
+
// minPrice == 0 is invalid
|
|
535
|
+
return new oracle_2.OraclePrice(new decimal_js_1.default(0), new decimal_js_1.default(0), 0);
|
|
536
|
+
}
|
|
537
|
+
return new oracle_2.OraclePrice(primaryPriceUsd, confidence, currentTime.toNumber());
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
exports.RaydiumCPMMOracle = RaydiumCPMMOracle;
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Connection, PublicKey } from '@solana/web3.js';
|
|
2
|
+
import { WithdrawBasketFees } from '../layouts/basket';
|
|
3
|
+
export declare function addFieldsToWithdrawBasketFees(withdrawBasketFees: WithdrawBasketFees): WithdrawBasketFees;
|
|
4
|
+
export declare function fetchWithdrawBasketFees(connection: Connection, withdrawBasketFeesAddress: PublicKey): Promise<WithdrawBasketFees>;
|
|
5
|
+
export declare function fetchWithdrawBasketFeesMultiple(connection: Connection, withdrawBasketFeesAddresses: PublicKey[]): Promise<Map<string, WithdrawBasketFees>>;
|
|
6
|
+
export interface WithdrawBasketFeesFilter {
|
|
7
|
+
type: "basket" | "manager" | "creator" | "host" | "symmetry";
|
|
8
|
+
pubkey: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function fetchWithdrawBasketFeesList(connection: Connection, filter?: WithdrawBasketFeesFilter): Promise<WithdrawBasketFees[]>;
|