@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,609 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.PythOracle = exports.PythState = exports.PriceFeedMessage = exports.TIP_ACCOUNTS = void 0;
|
|
16
|
+
exports.parseVerificationLevel = parseVerificationLevel;
|
|
17
|
+
exports.getRandomTipAccount = getRandomTipAccount;
|
|
18
|
+
exports.buildJitoTipInstruction = buildJitoTipInstruction;
|
|
19
|
+
exports.getPythPriceFeedAccountAddress = getPythPriceFeedAccountAddress;
|
|
20
|
+
exports.parsePriceFeedMessage = parsePriceFeedMessage;
|
|
21
|
+
exports.parseAccumulatorUpdateData = parseAccumulatorUpdateData;
|
|
22
|
+
exports.getSizeOfTransaction = getSizeOfTransaction;
|
|
23
|
+
exports.getSizeOfCompressedU16 = getSizeOfCompressedU16;
|
|
24
|
+
exports.fetchFeedIdsFromAccounts = fetchFeedIdsFromAccounts;
|
|
25
|
+
exports.buildPythPriceFeedUpdateIxs = buildPythPriceFeedUpdateIxs;
|
|
26
|
+
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
27
|
+
const bn_js_1 = __importDefault(require("bn.js"));
|
|
28
|
+
const hermes_client_1 = require("@pythnetwork/hermes-client");
|
|
29
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
30
|
+
const constants_1 = require("../../constants");
|
|
31
|
+
const constants_2 = require("./constants");
|
|
32
|
+
const oracle_1 = require("./oracle");
|
|
33
|
+
function parseVerificationLevel(buf, offset) {
|
|
34
|
+
const discr = buf.readUInt8(offset);
|
|
35
|
+
if (discr === 0) { // Partial
|
|
36
|
+
const numSignatures = buf.readUInt8(offset + 1);
|
|
37
|
+
return { level: { kind: 'Partial', numSignatures }, size: 2 };
|
|
38
|
+
}
|
|
39
|
+
else if (discr === 1) { // Full
|
|
40
|
+
return { level: { kind: 'Full' }, size: 1 };
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
throw new Error(`Unknown verification level: ${discr}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Pyth program IDs (mainnet)
|
|
47
|
+
const DEFAULT_RECEIVER_PROGRAM_ID = new web3_js_1.PublicKey("rec5EKMGg6MxZYaMdyBfgwp4d5rB9T1VQH5pJv5LtFJ");
|
|
48
|
+
const DEFAULT_PUSH_ORACLE_PROGRAM_ID = new web3_js_1.PublicKey("pythWSnswVUd12oZpeFP8e9CVaEqJg25g1Vtc2biRsT");
|
|
49
|
+
const DEFAULT_WORMHOLE_PROGRAM_ID = new web3_js_1.PublicKey("HDwcJBJXjL9FpJ7UBsYBtaDjsBUhuLCUYoz3zr8SWWaQ");
|
|
50
|
+
// Constants from Pyth SDK vaa.js
|
|
51
|
+
const VAA_START = 46;
|
|
52
|
+
const VAA_SPLIT_INDEX = 721;
|
|
53
|
+
const VAA_SIGNATURE_SIZE = 66;
|
|
54
|
+
const DEFAULT_REDUCED_GUARDIAN_SET_SIZE = 5;
|
|
55
|
+
// Compute budget constants
|
|
56
|
+
const VERIFY_ENCODED_VAA_COMPUTE_BUDGET = 350000;
|
|
57
|
+
const UPDATE_PRICE_FEED_COMPUTE_BUDGET = 55000;
|
|
58
|
+
const INIT_ENCODED_VAA_COMPUTE_BUDGET = 3000;
|
|
59
|
+
const WRITE_ENCODED_VAA_COMPUTE_BUDGET = 3000;
|
|
60
|
+
const CLOSE_ENCODED_VAA_COMPUTE_BUDGET = 30000;
|
|
61
|
+
// Pre-computed discriminators
|
|
62
|
+
const DISCRIMINATORS = {
|
|
63
|
+
// Wormhole instructions
|
|
64
|
+
initEncodedVaa: Buffer.from([209, 193, 173, 25, 91, 202, 181, 218]),
|
|
65
|
+
writeEncodedVaa: Buffer.from([199, 208, 110, 177, 150, 76, 118, 42]),
|
|
66
|
+
verifyEncodedVaaV1: Buffer.from([103, 56, 177, 229, 240, 103, 68, 73]),
|
|
67
|
+
closeEncodedVaa: Buffer.from([48, 221, 174, 198, 231, 7, 152, 38]),
|
|
68
|
+
// Pyth Push Oracle instructions
|
|
69
|
+
updatePriceFeed: Buffer.from([28, 9, 93, 150, 86, 153, 188, 115]),
|
|
70
|
+
};
|
|
71
|
+
// Jito tip accounts (copied from pyth solana-utils)
|
|
72
|
+
exports.TIP_ACCOUNTS = [
|
|
73
|
+
"HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe",
|
|
74
|
+
"Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY",
|
|
75
|
+
"DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh",
|
|
76
|
+
"ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49",
|
|
77
|
+
"3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT",
|
|
78
|
+
"DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL",
|
|
79
|
+
"96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5",
|
|
80
|
+
"ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt",
|
|
81
|
+
];
|
|
82
|
+
function getRandomTipAccount() {
|
|
83
|
+
const idx = Math.floor(Math.random() * exports.TIP_ACCOUNTS.length);
|
|
84
|
+
return new web3_js_1.PublicKey(exports.TIP_ACCOUNTS[idx]);
|
|
85
|
+
}
|
|
86
|
+
function buildJitoTipInstruction(payer, lamports) {
|
|
87
|
+
return web3_js_1.SystemProgram.transfer({
|
|
88
|
+
fromPubkey: payer,
|
|
89
|
+
toPubkey: getRandomTipAccount(),
|
|
90
|
+
lamports,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Get a random treasury ID (0-255) - same as Pyth SDK's getRandomTreasuryId()
|
|
95
|
+
*/
|
|
96
|
+
function getRandomTreasuryId() {
|
|
97
|
+
return Math.floor(Math.random() * 256);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Derives the Pyth price feed account address
|
|
101
|
+
*/
|
|
102
|
+
function getPythPriceFeedAccountAddress(shardId, priceFeedId, pushOracleProgramId = DEFAULT_PUSH_ORACLE_PROGRAM_ID) {
|
|
103
|
+
let feedIdBuffer;
|
|
104
|
+
if (typeof priceFeedId === "string") {
|
|
105
|
+
const hexString = priceFeedId.startsWith("0x")
|
|
106
|
+
? priceFeedId.slice(2)
|
|
107
|
+
: priceFeedId;
|
|
108
|
+
feedIdBuffer = Buffer.from(hexString, "hex");
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
feedIdBuffer = priceFeedId;
|
|
112
|
+
}
|
|
113
|
+
if (feedIdBuffer.length !== 32) {
|
|
114
|
+
throw new Error("Feed ID should be 32 bytes long");
|
|
115
|
+
}
|
|
116
|
+
const shardBuffer = Buffer.alloc(2);
|
|
117
|
+
shardBuffer.writeUint16LE(shardId, 0);
|
|
118
|
+
return web3_js_1.PublicKey.findProgramAddressSync([shardBuffer, feedIdBuffer], pushOracleProgramId)[0];
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Get Treasury PDA for Pyth Receiver
|
|
122
|
+
*/
|
|
123
|
+
function getTreasuryPda(treasuryId, programId = DEFAULT_RECEIVER_PROGRAM_ID) {
|
|
124
|
+
return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("treasury"), Buffer.from([treasuryId])], programId)[0];
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Get Config PDA for Pyth Receiver
|
|
128
|
+
*/
|
|
129
|
+
function getConfigPda(programId = DEFAULT_RECEIVER_PROGRAM_ID) {
|
|
130
|
+
return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("config")], programId)[0];
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get Guardian Set PDA for Wormhole
|
|
134
|
+
*/
|
|
135
|
+
function getGuardianSetPda(guardianSetIndex, programId = DEFAULT_WORMHOLE_PROGRAM_ID) {
|
|
136
|
+
const indexBuffer = Buffer.alloc(4);
|
|
137
|
+
indexBuffer.writeUInt32BE(guardianSetIndex, 0);
|
|
138
|
+
return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("GuardianSet"), indexBuffer], programId)[0];
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get guardian set index from VAA (offset 1, big-endian u32)
|
|
142
|
+
*/
|
|
143
|
+
function getGuardianSetIndex(vaa) {
|
|
144
|
+
return vaa.readUInt32BE(1);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Serialize Borsh Vec<u8> (4-byte LE length prefix + data)
|
|
148
|
+
*/
|
|
149
|
+
function serializeBorshBytes(data) {
|
|
150
|
+
const lenBuffer = Buffer.alloc(4);
|
|
151
|
+
lenBuffer.writeUInt32LE(data.length, 0);
|
|
152
|
+
return Buffer.concat([lenBuffer, data]);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Serialize Borsh Vec<[u8; 20]> for merkle proof
|
|
156
|
+
*/
|
|
157
|
+
function serializeBorshProof(proof) {
|
|
158
|
+
const lenBuffer = Buffer.alloc(4);
|
|
159
|
+
lenBuffer.writeUInt32LE(proof.length, 0);
|
|
160
|
+
return Buffer.concat([lenBuffer, ...proof]);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Build initEncodedVaa instruction
|
|
164
|
+
*/
|
|
165
|
+
function buildInitEncodedVaaInstruction(writeAuthority, encodedVaa, wormholeProgramId = DEFAULT_WORMHOLE_PROGRAM_ID) {
|
|
166
|
+
return new web3_js_1.TransactionInstruction({
|
|
167
|
+
keys: [
|
|
168
|
+
{ pubkey: writeAuthority, isSigner: true, isWritable: false },
|
|
169
|
+
{ pubkey: encodedVaa, isSigner: false, isWritable: true },
|
|
170
|
+
],
|
|
171
|
+
programId: wormholeProgramId,
|
|
172
|
+
data: DISCRIMINATORS.initEncodedVaa,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Build writeEncodedVaa instruction
|
|
177
|
+
*/
|
|
178
|
+
function buildWriteEncodedVaaInstruction(writeAuthority, draftVaa, index, data, wormholeProgramId = DEFAULT_WORMHOLE_PROGRAM_ID) {
|
|
179
|
+
// Serialize args: index (u32 LE) + data (Borsh Vec<u8>)
|
|
180
|
+
const indexBuffer = Buffer.alloc(4);
|
|
181
|
+
indexBuffer.writeUInt32LE(index, 0);
|
|
182
|
+
const instructionData = Buffer.concat([
|
|
183
|
+
DISCRIMINATORS.writeEncodedVaa,
|
|
184
|
+
indexBuffer,
|
|
185
|
+
serializeBorshBytes(data),
|
|
186
|
+
]);
|
|
187
|
+
return new web3_js_1.TransactionInstruction({
|
|
188
|
+
keys: [
|
|
189
|
+
{ pubkey: writeAuthority, isSigner: true, isWritable: false },
|
|
190
|
+
{ pubkey: draftVaa, isSigner: false, isWritable: true },
|
|
191
|
+
],
|
|
192
|
+
programId: wormholeProgramId,
|
|
193
|
+
data: instructionData,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Build verifyEncodedVaaV1 instruction
|
|
198
|
+
*/
|
|
199
|
+
function buildVerifyEncodedVaaV1Instruction(writeAuthority, draftVaa, guardianSetIndex, wormholeProgramId = DEFAULT_WORMHOLE_PROGRAM_ID) {
|
|
200
|
+
return new web3_js_1.TransactionInstruction({
|
|
201
|
+
keys: [
|
|
202
|
+
{ pubkey: writeAuthority, isSigner: true, isWritable: false },
|
|
203
|
+
{ pubkey: draftVaa, isSigner: false, isWritable: true },
|
|
204
|
+
{ pubkey: getGuardianSetPda(guardianSetIndex, wormholeProgramId), isSigner: false, isWritable: false },
|
|
205
|
+
],
|
|
206
|
+
programId: wormholeProgramId,
|
|
207
|
+
data: DISCRIMINATORS.verifyEncodedVaaV1,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Build closeEncodedVaa instruction
|
|
212
|
+
*/
|
|
213
|
+
function buildCloseEncodedVaaInstruction(writeAuthority, encodedVaa, wormholeProgramId = DEFAULT_WORMHOLE_PROGRAM_ID) {
|
|
214
|
+
return new web3_js_1.TransactionInstruction({
|
|
215
|
+
keys: [
|
|
216
|
+
{ pubkey: writeAuthority, isSigner: true, isWritable: true },
|
|
217
|
+
{ pubkey: encodedVaa, isSigner: false, isWritable: true },
|
|
218
|
+
],
|
|
219
|
+
programId: wormholeProgramId,
|
|
220
|
+
data: DISCRIMINATORS.closeEncodedVaa,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Build updatePriceFeed instruction for Pyth Push Oracle
|
|
225
|
+
*/
|
|
226
|
+
function buildUpdatePriceFeedInstruction(params) {
|
|
227
|
+
const { payer, encodedVaa, priceFeedAccount, treasuryId, shardId, feedId, merklePriceUpdate, receiverProgramId = DEFAULT_RECEIVER_PROGRAM_ID, pushOracleProgramId = DEFAULT_PUSH_ORACLE_PROGRAM_ID, } = params;
|
|
228
|
+
const shardBuffer = Buffer.alloc(2);
|
|
229
|
+
shardBuffer.writeUint16LE(shardId, 0);
|
|
230
|
+
const data = Buffer.concat([
|
|
231
|
+
DISCRIMINATORS.updatePriceFeed,
|
|
232
|
+
// MerklePriceUpdate
|
|
233
|
+
serializeBorshBytes(merklePriceUpdate.message),
|
|
234
|
+
serializeBorshProof(merklePriceUpdate.proof),
|
|
235
|
+
// treasuryId (u8)
|
|
236
|
+
Buffer.from([treasuryId]),
|
|
237
|
+
// shardId (u16 LE)
|
|
238
|
+
shardBuffer,
|
|
239
|
+
// feedId ([u8; 32])
|
|
240
|
+
feedId,
|
|
241
|
+
]);
|
|
242
|
+
const keys = [
|
|
243
|
+
{ pubkey: payer, isSigner: true, isWritable: true },
|
|
244
|
+
{ pubkey: receiverProgramId, isSigner: false, isWritable: false },
|
|
245
|
+
{ pubkey: encodedVaa, isSigner: false, isWritable: false },
|
|
246
|
+
{ pubkey: getConfigPda(receiverProgramId), isSigner: false, isWritable: false },
|
|
247
|
+
{ pubkey: getTreasuryPda(treasuryId, receiverProgramId), isSigner: false, isWritable: true },
|
|
248
|
+
{ pubkey: priceFeedAccount, isSigner: false, isWritable: true },
|
|
249
|
+
{ pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
|
|
250
|
+
];
|
|
251
|
+
return new web3_js_1.TransactionInstruction({
|
|
252
|
+
keys,
|
|
253
|
+
programId: pushOracleProgramId,
|
|
254
|
+
data,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Convert proof from number[][] to Buffer[]
|
|
259
|
+
*/
|
|
260
|
+
function convertProof(proof) {
|
|
261
|
+
return proof.map(p => Buffer.from(p));
|
|
262
|
+
}
|
|
263
|
+
function parsePriceFeedMessage(message) {
|
|
264
|
+
let cursor = 0;
|
|
265
|
+
const variant = message.readUInt8(cursor);
|
|
266
|
+
cursor += 1;
|
|
267
|
+
if (variant !== 0)
|
|
268
|
+
throw new Error('Not a price feed message');
|
|
269
|
+
const feedId = message.subarray(cursor, cursor + 32);
|
|
270
|
+
cursor += 32;
|
|
271
|
+
const price = message.subarray(cursor, cursor + 8);
|
|
272
|
+
cursor += 8;
|
|
273
|
+
const confidence = message.subarray(cursor, cursor + 8);
|
|
274
|
+
cursor += 8;
|
|
275
|
+
const exponent = message.readInt32BE(cursor);
|
|
276
|
+
cursor += 4;
|
|
277
|
+
const publishTime = message.subarray(cursor, cursor + 8);
|
|
278
|
+
cursor += 8;
|
|
279
|
+
const prevPublishTime = message.subarray(cursor, cursor + 8);
|
|
280
|
+
cursor += 8;
|
|
281
|
+
const emaPrice = message.subarray(cursor, cursor + 8);
|
|
282
|
+
cursor += 8;
|
|
283
|
+
const emaConf = message.subarray(cursor, cursor + 8);
|
|
284
|
+
return { feedId, price, confidence, exponent, publishTime, prevPublishTime, emaPrice, emaConf };
|
|
285
|
+
}
|
|
286
|
+
/** parsing of accumulator update data (VAA + updates) */
|
|
287
|
+
function parseAccumulatorUpdateData(data) {
|
|
288
|
+
const ACC_MAGIC = '504e4155';
|
|
289
|
+
if (data.toString('hex').slice(0, 8) !== ACC_MAGIC || data[4] !== 1 || data[5] !== 0) {
|
|
290
|
+
throw new Error('Invalid accumulator message');
|
|
291
|
+
}
|
|
292
|
+
let cursor = 6;
|
|
293
|
+
const trailingPayloadSize = data.readUInt8(cursor);
|
|
294
|
+
cursor += 1 + trailingPayloadSize;
|
|
295
|
+
cursor += 1;
|
|
296
|
+
const vaaSize = data.readUInt16BE(cursor);
|
|
297
|
+
cursor += 2;
|
|
298
|
+
const vaa = data.subarray(cursor, cursor + vaaSize);
|
|
299
|
+
cursor += vaaSize;
|
|
300
|
+
const numUpdates = data.readUInt8(cursor);
|
|
301
|
+
cursor += 1;
|
|
302
|
+
const updates = [];
|
|
303
|
+
const HASH_SIZE = 20;
|
|
304
|
+
for (let i = 0; i < numUpdates; i++) {
|
|
305
|
+
const messageSize = data.readUInt16BE(cursor);
|
|
306
|
+
cursor += 2;
|
|
307
|
+
const message = data.subarray(cursor, cursor + messageSize);
|
|
308
|
+
cursor += messageSize;
|
|
309
|
+
const numProofs = data.readUInt8(cursor);
|
|
310
|
+
cursor += 1;
|
|
311
|
+
const proof = [];
|
|
312
|
+
for (let j = 0; j < numProofs; j++) {
|
|
313
|
+
proof.push(Array.from(data.subarray(cursor, cursor + HASH_SIZE)));
|
|
314
|
+
cursor += HASH_SIZE;
|
|
315
|
+
}
|
|
316
|
+
updates.push({ message, proof });
|
|
317
|
+
}
|
|
318
|
+
if (cursor !== data.length)
|
|
319
|
+
throw new Error("Didn't reach end of message");
|
|
320
|
+
return { vaa, updates };
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Get the size of a transaction that would contain the provided array of instructions
|
|
324
|
+
*/
|
|
325
|
+
function getSizeOfTransaction(instructions, versionedTransaction = true, addressLookupTable) {
|
|
326
|
+
const programs = new Set();
|
|
327
|
+
const signers = new Set();
|
|
328
|
+
let accounts = new Set();
|
|
329
|
+
instructions.map((ix) => {
|
|
330
|
+
programs.add(ix.programId.toBase58());
|
|
331
|
+
accounts.add(ix.programId.toBase58());
|
|
332
|
+
ix.keys.map((key) => {
|
|
333
|
+
if (key.isSigner) {
|
|
334
|
+
signers.add(key.pubkey.toBase58());
|
|
335
|
+
}
|
|
336
|
+
accounts.add(key.pubkey.toBase58());
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
const instruction_sizes = instructions
|
|
340
|
+
.map((ix) => 1 +
|
|
341
|
+
getSizeOfCompressedU16(ix.keys.length) +
|
|
342
|
+
ix.keys.length +
|
|
343
|
+
getSizeOfCompressedU16(ix.data.length) +
|
|
344
|
+
ix.data.length)
|
|
345
|
+
.reduce((a, b) => a + b, 0);
|
|
346
|
+
let numberOfAddressLookups = 0;
|
|
347
|
+
if (addressLookupTable) {
|
|
348
|
+
const lookupTableAddresses = addressLookupTable.state.addresses.map((address) => address.toBase58());
|
|
349
|
+
const totalNumberOfAccounts = accounts.size;
|
|
350
|
+
accounts = new Set([...accounts].filter((account) => !lookupTableAddresses.includes(account)));
|
|
351
|
+
accounts = new Set([...accounts, ...programs, ...signers]);
|
|
352
|
+
numberOfAddressLookups = totalNumberOfAccounts - accounts.size;
|
|
353
|
+
}
|
|
354
|
+
return (getSizeOfCompressedU16(signers.size) +
|
|
355
|
+
signers.size * 64 +
|
|
356
|
+
3 +
|
|
357
|
+
getSizeOfCompressedU16(accounts.size) +
|
|
358
|
+
32 * accounts.size +
|
|
359
|
+
32 +
|
|
360
|
+
getSizeOfCompressedU16(instructions.length) +
|
|
361
|
+
instruction_sizes +
|
|
362
|
+
(versionedTransaction ? 1 + getSizeOfCompressedU16(0) : 0) +
|
|
363
|
+
(versionedTransaction && addressLookupTable ? 32 : 0) +
|
|
364
|
+
(versionedTransaction && addressLookupTable ? 2 : 0) +
|
|
365
|
+
numberOfAddressLookups);
|
|
366
|
+
}
|
|
367
|
+
/** Get the size of n in bytes when serialized as a CompressedU16 */
|
|
368
|
+
function getSizeOfCompressedU16(n) {
|
|
369
|
+
return 1 + Number(n >= 128) + Number(n >= 16384);
|
|
370
|
+
}
|
|
371
|
+
const ENCODED_VAA_RENT_EXEMPTION = 7836960;
|
|
372
|
+
/**
|
|
373
|
+
* Fetch Pyth price accounts from RPC and extract feed IDs
|
|
374
|
+
*/
|
|
375
|
+
function fetchFeedIdsFromAccounts(connection, priceAccounts) {
|
|
376
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
377
|
+
const accountInfos = yield connection.getMultipleAccountsInfo(priceAccounts, "confirmed");
|
|
378
|
+
const feedIds = [];
|
|
379
|
+
const feedIdToAccount = new Map();
|
|
380
|
+
for (let i = 0; i < priceAccounts.length; i++) {
|
|
381
|
+
const ai = accountInfos[i];
|
|
382
|
+
if (!ai)
|
|
383
|
+
throw new Error(`Account ${priceAccounts[i].toBase58()} not found`);
|
|
384
|
+
const [state, _] = PythState.decode(ai.data, 8);
|
|
385
|
+
const feedIdHex = "0x" + state.priceMessage.feedId.toString("hex");
|
|
386
|
+
feedIds.push(feedIdHex);
|
|
387
|
+
feedIdToAccount.set(feedIdHex, priceAccounts[i]);
|
|
388
|
+
}
|
|
389
|
+
return { feedIds, feedIdToAccount };
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Build Pyth price feed update as TxBatchData compatible with SDK txUtils.
|
|
394
|
+
*
|
|
395
|
+
* TxBatchData.batches layout:
|
|
396
|
+
* batch 0 (sequential): [createAccount + initEncodedVaa + writeVaa_part1]
|
|
397
|
+
* batch 1 (sequential): [writeVaa_part2 + verifyEncodedVaa]
|
|
398
|
+
* batch 2 (parallel): [updateFeed_0], [updateFeed_1], ... [updateFeed_N-2]
|
|
399
|
+
* batch 3 (sequential): [updateFeed_last + closeEncodedVaa]
|
|
400
|
+
*
|
|
401
|
+
* Transactions within each batch are sent in parallel by sendVersionedTxs.
|
|
402
|
+
* Batches are sent sequentially.
|
|
403
|
+
*/
|
|
404
|
+
function buildPythPriceFeedUpdateIxs(payer, priceFeedIds, defaultShardId, hermesClient) {
|
|
405
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
406
|
+
var _a;
|
|
407
|
+
const client = hermesClient !== null && hermesClient !== void 0 ? hermesClient : new hermes_client_1.HermesClient(constants_2.HERMES_PUBLIC_ENDPOINT, {});
|
|
408
|
+
const shardId = defaultShardId !== null && defaultShardId !== void 0 ? defaultShardId : 0;
|
|
409
|
+
const priceUpdates = yield client.getLatestPriceUpdates(priceFeedIds, { encoding: "base64" });
|
|
410
|
+
if (!((_a = priceUpdates.binary) === null || _a === void 0 ? void 0 : _a.data) || priceUpdates.binary.data.length === 0) {
|
|
411
|
+
throw new Error("Failed to fetch price updates from Hermes");
|
|
412
|
+
}
|
|
413
|
+
const treasuryId = getRandomTreasuryId();
|
|
414
|
+
let allVaaCreateInitEncodeIxs = [];
|
|
415
|
+
let allVaaWriteVerifyIxs = [];
|
|
416
|
+
const allUpdateIxs = [];
|
|
417
|
+
const allCloseIxs = [];
|
|
418
|
+
for (const updateData of priceUpdates.binary.data) {
|
|
419
|
+
const accumulatorData = parseAccumulatorUpdateData(Buffer.from(updateData, "base64"));
|
|
420
|
+
const vaa = accumulatorData.vaa;
|
|
421
|
+
const encodedVaaKeypair = web3_js_1.Keypair.generate();
|
|
422
|
+
const encodedVaaSize = vaa.length + VAA_START;
|
|
423
|
+
const guardianSetIndex = getGuardianSetIndex(vaa);
|
|
424
|
+
let vaaCreateInitEncodeIxs = [];
|
|
425
|
+
let vaaWriteVerifyIxs = [];
|
|
426
|
+
// createAccount
|
|
427
|
+
vaaCreateInitEncodeIxs.push(web3_js_1.SystemProgram.createAccount({
|
|
428
|
+
fromPubkey: payer,
|
|
429
|
+
newAccountPubkey: encodedVaaKeypair.publicKey,
|
|
430
|
+
lamports: ENCODED_VAA_RENT_EXEMPTION,
|
|
431
|
+
space: encodedVaaSize,
|
|
432
|
+
programId: DEFAULT_WORMHOLE_PROGRAM_ID,
|
|
433
|
+
}));
|
|
434
|
+
// initEncodedVaa
|
|
435
|
+
vaaCreateInitEncodeIxs.push(buildInitEncodedVaaInstruction(payer, encodedVaaKeypair.publicKey));
|
|
436
|
+
// writeEncodedVaa part 1
|
|
437
|
+
const firstPartEnd = Math.min(VAA_SPLIT_INDEX, vaa.length);
|
|
438
|
+
vaaCreateInitEncodeIxs.push(buildWriteEncodedVaaInstruction(payer, encodedVaaKeypair.publicKey, 0, vaa.subarray(0, firstPartEnd)));
|
|
439
|
+
// writeEncodedVaa part 2 (if needed)
|
|
440
|
+
if (vaa.length > VAA_SPLIT_INDEX) {
|
|
441
|
+
vaaWriteVerifyIxs.push(buildWriteEncodedVaaInstruction(payer, encodedVaaKeypair.publicKey, VAA_SPLIT_INDEX, vaa.subarray(VAA_SPLIT_INDEX)));
|
|
442
|
+
}
|
|
443
|
+
// verifyEncodedVaaV1
|
|
444
|
+
vaaWriteVerifyIxs.push(buildVerifyEncodedVaaV1Instruction(payer, encodedVaaKeypair.publicKey, guardianSetIndex));
|
|
445
|
+
// updateFeed per price update
|
|
446
|
+
for (const update of accumulatorData.updates) {
|
|
447
|
+
const parsedMessage = parsePriceFeedMessage(update.message);
|
|
448
|
+
const feedId = parsedMessage.feedId;
|
|
449
|
+
allUpdateIxs.push(buildUpdatePriceFeedInstruction({
|
|
450
|
+
payer,
|
|
451
|
+
encodedVaa: encodedVaaKeypair.publicKey,
|
|
452
|
+
priceFeedAccount: getPythPriceFeedAccountAddress(shardId, feedId),
|
|
453
|
+
treasuryId,
|
|
454
|
+
shardId,
|
|
455
|
+
feedId,
|
|
456
|
+
merklePriceUpdate: {
|
|
457
|
+
message: update.message,
|
|
458
|
+
proof: convertProof(update.proof),
|
|
459
|
+
},
|
|
460
|
+
}));
|
|
461
|
+
}
|
|
462
|
+
allVaaCreateInitEncodeIxs.push({
|
|
463
|
+
ixs: vaaCreateInitEncodeIxs,
|
|
464
|
+
signer: encodedVaaKeypair,
|
|
465
|
+
});
|
|
466
|
+
allVaaWriteVerifyIxs.push(vaaWriteVerifyIxs);
|
|
467
|
+
// closeEncodedVaa
|
|
468
|
+
allCloseIxs.push(buildCloseEncodedVaaInstruction(payer, encodedVaaKeypair.publicKey));
|
|
469
|
+
}
|
|
470
|
+
return {
|
|
471
|
+
vaaCreateInitEncodeIxs: allVaaCreateInitEncodeIxs,
|
|
472
|
+
vaaWriteVerifyIxs: allVaaWriteVerifyIxs,
|
|
473
|
+
updateFeedIxs: allUpdateIxs,
|
|
474
|
+
closeVaaIxs: allCloseIxs,
|
|
475
|
+
};
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
class PriceFeedMessage {
|
|
479
|
+
constructor(params) {
|
|
480
|
+
this.feedId = params.feedId;
|
|
481
|
+
this.price = params.price;
|
|
482
|
+
this.conf = params.conf;
|
|
483
|
+
this.exponent = params.exponent;
|
|
484
|
+
this.publishTime = params.publishTime;
|
|
485
|
+
this.prevPublishTime = params.prevPublishTime;
|
|
486
|
+
this.emaPrice = params.emaPrice;
|
|
487
|
+
this.emaConf = params.emaConf;
|
|
488
|
+
}
|
|
489
|
+
static decode(buf, offset = 0) {
|
|
490
|
+
let cursor = offset;
|
|
491
|
+
// feed_id: [u8;32]
|
|
492
|
+
const feedId = buf.subarray(cursor, cursor + 32);
|
|
493
|
+
cursor += 32;
|
|
494
|
+
// price: i64 (signed)
|
|
495
|
+
const price = new bn_js_1.default(buf.subarray(cursor, cursor + 8), "le").fromTwos(64);
|
|
496
|
+
cursor += 8;
|
|
497
|
+
// conf: u64 (unsigned)
|
|
498
|
+
const conf = new bn_js_1.default(buf.subarray(cursor, cursor + 8), "le");
|
|
499
|
+
cursor += 8;
|
|
500
|
+
// exponent: i32 (signed)
|
|
501
|
+
const exponent = new bn_js_1.default(buf.subarray(cursor, cursor + 4), "le").fromTwos(32).toNumber();
|
|
502
|
+
cursor += 4;
|
|
503
|
+
// publish_time: i64 (signed)
|
|
504
|
+
const publishTime = new bn_js_1.default(buf.subarray(cursor, cursor + 8), "le").fromTwos(64);
|
|
505
|
+
cursor += 8;
|
|
506
|
+
// prev_publish_time: i64 (signed)
|
|
507
|
+
const prevPublishTime = new bn_js_1.default(buf.subarray(cursor, cursor + 8), "le").fromTwos(64);
|
|
508
|
+
cursor += 8;
|
|
509
|
+
// ema_price: i64 (signed)
|
|
510
|
+
const emaPrice = new bn_js_1.default(buf.subarray(cursor, cursor + 8), "le").fromTwos(64);
|
|
511
|
+
cursor += 8;
|
|
512
|
+
// ema_conf: u64 (unsigned)
|
|
513
|
+
const emaConf = new bn_js_1.default(buf.subarray(cursor, cursor + 8), "le");
|
|
514
|
+
cursor += 8;
|
|
515
|
+
return [new PriceFeedMessage({
|
|
516
|
+
feedId,
|
|
517
|
+
price,
|
|
518
|
+
conf,
|
|
519
|
+
exponent,
|
|
520
|
+
publishTime,
|
|
521
|
+
prevPublishTime,
|
|
522
|
+
emaPrice,
|
|
523
|
+
emaConf
|
|
524
|
+
}), cursor];
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
exports.PriceFeedMessage = PriceFeedMessage;
|
|
528
|
+
class PythState {
|
|
529
|
+
constructor(writeAuthority, verificationLevel, priceMessage, postedSlot) {
|
|
530
|
+
this.writeAuthority = writeAuthority;
|
|
531
|
+
this.verificationLevel = verificationLevel;
|
|
532
|
+
this.priceMessage = priceMessage;
|
|
533
|
+
this.postedSlot = postedSlot;
|
|
534
|
+
}
|
|
535
|
+
;
|
|
536
|
+
static decode(buf, offset = 0) {
|
|
537
|
+
let cursor = offset;
|
|
538
|
+
// write_authority: Pubkey (32)
|
|
539
|
+
const writeAuthority = new web3_js_1.PublicKey(buf.subarray(cursor, cursor + 32));
|
|
540
|
+
cursor += 32;
|
|
541
|
+
const { level, size } = parseVerificationLevel(buf, cursor);
|
|
542
|
+
cursor += size;
|
|
543
|
+
// price_message: PriceFeedMessage
|
|
544
|
+
const [priceMessage, next] = PriceFeedMessage.decode(buf, cursor);
|
|
545
|
+
cursor = next;
|
|
546
|
+
// posted_slot: u64
|
|
547
|
+
const postedSlot = new bn_js_1.default(buf.subarray(cursor, cursor + 8), "le");
|
|
548
|
+
cursor += 8;
|
|
549
|
+
return [new PythState(writeAuthority, level, priceMessage, postedSlot), cursor];
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
exports.PythState = PythState;
|
|
553
|
+
class PythOracle {
|
|
554
|
+
static fetch(oracleParams, accountInfos, solPrice, usdPrice) {
|
|
555
|
+
//@ts-ignore
|
|
556
|
+
let state = null;
|
|
557
|
+
try {
|
|
558
|
+
const stateAi = accountInfos[0];
|
|
559
|
+
const [parsedState, _] = PythState.decode(stateAi.data, 8);
|
|
560
|
+
state = parsedState;
|
|
561
|
+
}
|
|
562
|
+
catch (error) {
|
|
563
|
+
return new oracle_1.OraclePrice(new decimal_js_1.default(0), new decimal_js_1.default(0), 0);
|
|
564
|
+
}
|
|
565
|
+
let pr = new decimal_js_1.default(state.priceMessage.price.toString()).mul(decimal_js_1.default.pow(10, state.priceMessage.exponent - oracleParams.tokenDecimals));
|
|
566
|
+
let ema = new decimal_js_1.default(state.priceMessage.emaPrice.toString()).mul(decimal_js_1.default.pow(10, state.priceMessage.exponent - oracleParams.tokenDecimals));
|
|
567
|
+
let cf = new decimal_js_1.default(state.priceMessage.conf.toString()).mul(decimal_js_1.default.pow(10, state.priceMessage.exponent - oracleParams.tokenDecimals));
|
|
568
|
+
const lastUpdateTimestamp = state.priceMessage.publishTime;
|
|
569
|
+
// Validate primary price is not zero
|
|
570
|
+
if (pr.lte(0)) {
|
|
571
|
+
return new oracle_1.OraclePrice(new decimal_js_1.default(0), new decimal_js_1.default(0), 0);
|
|
572
|
+
}
|
|
573
|
+
// === 1. Inflate confidence by staleness ===
|
|
574
|
+
// confidence = confidence * (1 + delta_t * stalenessConfRateBps / 10_000)
|
|
575
|
+
const now = new bn_js_1.default(Math.floor(Date.now() / 1000));
|
|
576
|
+
const deltaSeconds = Math.max(now.toNumber() - lastUpdateTimestamp.toNumber(), 0);
|
|
577
|
+
const deltaT = new decimal_js_1.default(deltaSeconds);
|
|
578
|
+
const deltaTBN = new bn_js_1.default(deltaSeconds);
|
|
579
|
+
const stalenessRate = new decimal_js_1.default(oracleParams.stalenessConfRateBps).div(new decimal_js_1.default(constants_1.HUNDRED_PERCENT_BPS));
|
|
580
|
+
const inflateFactor = new decimal_js_1.default(1).add(deltaT.mul(stalenessRate));
|
|
581
|
+
cf = cf.mul(inflateFactor);
|
|
582
|
+
let validated = true;
|
|
583
|
+
// === 2. Validate confidence threshold ===
|
|
584
|
+
// confidence / price * 10_000 < confThreshBps
|
|
585
|
+
const confRatioBps = cf.div(pr).mul(new decimal_js_1.default(constants_1.HUNDRED_PERCENT_BPS));
|
|
586
|
+
if (confRatioBps.gt(new decimal_js_1.default(oracleParams.confThreshBps)))
|
|
587
|
+
validated = false;
|
|
588
|
+
// === 3. Validate staleness threshold ===
|
|
589
|
+
if (deltaTBN.gt(oracleParams.stalenessThresh))
|
|
590
|
+
validated = false;
|
|
591
|
+
// === 4. Validate volatility threshold using EMA and price ===
|
|
592
|
+
// max(ema, price) / min(ema, price) * 10_000 < volatilityThreshBps
|
|
593
|
+
const maxPrice = decimal_js_1.default.max(ema, pr);
|
|
594
|
+
const minPrice = decimal_js_1.default.min(ema, pr);
|
|
595
|
+
if (!minPrice.eq(0)) {
|
|
596
|
+
const volRatio = maxPrice.sub(minPrice).div(minPrice);
|
|
597
|
+
const volRatioBps = volRatio.mul(new decimal_js_1.default(constants_1.HUNDRED_PERCENT_BPS));
|
|
598
|
+
if (volRatioBps.gt(new decimal_js_1.default(oracleParams.volatilityThreshBps))) {
|
|
599
|
+
validated = false;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
else {
|
|
603
|
+
validated = false;
|
|
604
|
+
}
|
|
605
|
+
// we don't validate liquidity for Pyth
|
|
606
|
+
return new oracle_1.OraclePrice(pr, cf, parseInt(state.priceMessage.publishTime.toString()), validated);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
exports.PythOracle = PythOracle;
|