Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>
This commit is contained in:
microwavedcola1 2024-07-03 10:34:49 +02:00
parent 6540ef31c3
commit eeeb336bbc
2 changed files with 41 additions and 77 deletions

View File

@ -40,6 +40,16 @@ const SLEEP_MS = Number(process.env.SLEEP_MS) || 5_000;
// TODO use mangolana to send txs // TODO use mangolana to send txs
interface OracleInterface {
oracle: {
oraclePk: PublicKey;
oracleConfig: OracleConfig;
name: string;
};
decodedPullFeed: any;
ai: AccountInfo<Buffer> | null;
}
(async function main(): Promise<never> { (async function main(): Promise<never> {
const { group, client, connection, user } = await setupMango(); const { group, client, connection, user } = await setupMango();
@ -134,15 +144,9 @@ async function preparePullIx(
new PublicKey(oracle.oracle.oraclePk), new PublicKey(oracle.oracle.oraclePk),
); );
const decodedPullFeed = sbOnDemandProgram.coder.accounts.decode(
'pullFeedAccountData',
oracle.ai.data,
);
const conf = { const conf = {
queue: queue,
numSignatures: 3, numSignatures: 3,
feedHash: decodedPullFeed.feedHash, feed: oracle.oraclePk,
}; };
// TODO use fetchUpdateMany // TODO use fetchUpdateMany
const [pullIx, responses, success] = await pullFeed.fetchUpdateIx(conf); const [pullIx, responses, success] = await pullFeed.fetchUpdateIx(conf);
@ -167,30 +171,11 @@ async function preparePullIx(
} }
async function filterForVarianceThresholdOracles( async function filterForVarianceThresholdOracles(
filteredOracles: { filteredOracles: OracleInterface[],
oracle: { oraclePk: PublicKey; oracleConfig: OracleConfig; name: string };
decodedPullFeed: any;
ai: AccountInfo<Buffer> | null;
}[],
client: MangoClient, client: MangoClient,
crossbarClient: CrossbarClient, crossbarClient: CrossbarClient,
): Promise< ): Promise<OracleInterface[]> {
{ const varianceThresholdCrossedOracles = new Array<OracleInterface>();
oracle: {
oraclePk: PublicKey;
oracleConfig: OracleConfig;
};
ai: AccountInfo<Buffer> | null;
}[]
> {
const varianceThresholdCrossedOracles = new Array<{
oracle: {
oraclePk: PublicKey;
oracleConfig: OracleConfig;
};
decodedPullFeed: any;
ai: AccountInfo<Buffer> | null;
}>();
for (const item of filteredOracles) { for (const item of filteredOracles) {
const res = await parseSwitchboardOracle( const res = await parseSwitchboardOracle(
item.oracle.oraclePk, item.oracle.oraclePk,
@ -206,24 +191,16 @@ async function filterForVarianceThresholdOracles(
crossBarSim[0].results.reduce((a, b) => a + b, 0) / crossBarSim[0].results.reduce((a, b) => a + b, 0) /
crossBarSim[0].results.length; crossBarSim[0].results.length;
if (Math.abs(res.price - simPrice) / res.price > 0.01) { const changePct = (Math.abs(res.price - simPrice) * 100) / res.price;
const changeBps = changePct * 100;
if (changePct > item.decodedPullFeed.maxVariance) {
console.log( console.log(
`- Variance threshold crossed oracle, candidate ${ `- ${item.oracle.name}, variance threshold, candidate ${simPrice} ${res.price} ${changeBps} bps`,
item.oracle.name
} ${simPrice} ${res.price} ${(
(Math.abs(res.price - simPrice) * 10000) /
res.price
).toFixed()} bps`,
); );
varianceThresholdCrossedOracles.push(item); varianceThresholdCrossedOracles.push(item);
} else { } else {
console.log( console.log(
`- Variance threshold crossed oracle, non candidate ${ `- ${item.oracle.name}, variance threshold, non-candidate ${simPrice} ${res.price} ${changeBps} bps`,
item.oracle.name
} ${simPrice} ${res.price} ${(
(Math.abs(res.price - simPrice) * 10000) /
res.price
).toFixed()} bps`,
); );
} }
} }
@ -231,11 +208,7 @@ async function filterForVarianceThresholdOracles(
} }
async function filterForStaleOracles( async function filterForStaleOracles(
filteredOracles: { filteredOracles: OracleInterface[],
oracle: { oraclePk: PublicKey; oracleConfig: OracleConfig; name: string };
decodedPullFeed: any;
ai: AccountInfo<Buffer> | null;
}[],
client: MangoClient, client: MangoClient,
slot: number, slot: number,
): Promise< ): Promise<
@ -247,13 +220,7 @@ async function filterForStaleOracles(
ai: AccountInfo<Buffer> | null; ai: AccountInfo<Buffer> | null;
}[] }[]
> { > {
const staleOracles = new Array<{ const staleOracles = new Array<OracleInterface>();
oracle: {
oraclePk: PublicKey;
oracleConfig: OracleConfig;
};
ai: AccountInfo<Buffer> | null;
}>();
for (const item of filteredOracles) { for (const item of filteredOracles) {
const res = await parseSwitchboardOracle( const res = await parseSwitchboardOracle(
item.oracle.oraclePk, item.oracle.oraclePk,
@ -261,21 +228,18 @@ async function filterForStaleOracles(
client.connection, client.connection,
); );
const diff = slot - res.lastUpdatedSlot;
if ( if (
slot > res.lastUpdatedSlot && slot > res.lastUpdatedSlot &&
slot - res.lastUpdatedSlot > item.decodedPullFeed.maxStaleness slot - res.lastUpdatedSlot > item.decodedPullFeed.maxStaleness
) { ) {
console.log( console.log(
`- Stale oracle, candidate ${item.oracle.name} ${slot} ${ `- ${item.oracle.name}, stale oracle, candidate ${item.decodedPullFeed.maxStaleness} ${slot} ${res.lastUpdatedSlot} ${diff}`,
item.decodedPullFeed.maxStaleness
} ${res.lastUpdatedSlot} ${slot - res.lastUpdatedSlot}`,
); );
staleOracles.push(item); staleOracles.push(item);
} else { } else {
console.log( console.log(
`- Stale oracle, non candidate ${item.oracle.name} ${slot} ${ `- ${item.oracle.name}, stale oracle, non-candidate ${item.decodedPullFeed.maxStaleness} ${slot} ${res.lastUpdatedSlot} ${diff}`,
res.lastUpdatedSlot
} ${res.lastUpdatedSlot} ${slot - res.lastUpdatedSlot}`,
); );
} }
} }
@ -285,13 +249,7 @@ async function filterForStaleOracles(
async function prepareCandidateOracles( async function prepareCandidateOracles(
group: Group, group: Group,
client: MangoClient, client: MangoClient,
): Promise< ): Promise<OracleInterface[]> {
{
oracle: { oraclePk: PublicKey; oracleConfig: OracleConfig; name: string };
decodedPullFeed: any;
ai: AccountInfo<Buffer> | null;
}[]
> {
const oracles = getOraclesForMangoGroup(group); const oracles = getOraclesForMangoGroup(group);
oracles.push(...extendOraclesManually(CLUSTER)); oracles.push(...extendOraclesManually(CLUSTER));
@ -325,14 +283,18 @@ async function prepareCandidateOracles(
const filteredOracles = oracles const filteredOracles = oracles
.map((o, i) => { .map((o, i) => {
return { oracle: o, ai: ais[i] }; return { oracle: o, ai: ais[i], decodedPullFeed: undefined };
}) })
.filter((item) => item.ai?.owner.equals(SB_ON_DEMAND_PID)); .filter((item) => item.ai?.owner.equals(SB_ON_DEMAND_PID));
return filteredOracles; return filteredOracles;
} }
function extendOraclesManually(cluster: Cluster) { function extendOraclesManually(cluster: Cluster): {
oraclePk: PublicKey;
oracleConfig: { confFilter: I80F48; maxStalenessSlots: BN };
name: string;
}[] {
if (cluster == 'devnet') { if (cluster == 'devnet') {
return [ return [
{ {
@ -389,7 +351,9 @@ async function setupMango(): Promise<{
return { group, client, connection, user }; return { group, client, connection, user };
} }
function getOraclesForMangoGroup(group: Group) { function getOraclesForMangoGroup(
group: Group,
): { oraclePk: PublicKey; oracleConfig: OracleConfig; name: string }[] {
// oracles for tokens // oracles for tokens
const oracles1 = Array.from(group.banksMapByName.values()) const oracles1 = Array.from(group.banksMapByName.values())
.filter( .filter(
@ -439,7 +403,11 @@ function getOraclesForMangoGroup(group: Group) {
return oracles; return oracles;
} }
async function setupSwitchboard(client: MangoClient) { async function setupSwitchboard(client: MangoClient): Promise<{
sbOnDemandProgram: Anchor30Program<Idl>;
crossbarClient: CrossbarClient;
queue: PublicKey;
}> {
const idl = await Anchor30Program.fetchIdl( const idl = await Anchor30Program.fetchIdl(
SB_ON_DEMAND_PID, SB_ON_DEMAND_PID,
client.program.provider, client.program.provider,
@ -459,11 +427,7 @@ async function setupSwitchboard(client: MangoClient) {
async function updateFilteredOraclesAis( async function updateFilteredOraclesAis(
connection: Connection, connection: Connection,
sbOnDemandProgram: Anchor30Program<Idl>, sbOnDemandProgram: Anchor30Program<Idl>,
filteredOracles: { filteredOracles: OracleInterface[],
decodedPullFeed: any;
oracle: { oraclePk: PublicKey; oracleConfig: OracleConfig; name: string };
ai: AccountInfo<Buffer> | null;
}[],
): Promise<void> { ): Promise<void> {
const ais = ( const ais = (
await Promise.all( await Promise.all(

View File

@ -108,7 +108,7 @@ async function setupSwitchboard(userProvider: AnchorProvider) {
numSignatures: 3, // number of signatures to fetch per update numSignatures: 3, // number of signatures to fetch per update
minSampleSize: 2, // minimum number of responses to sample minSampleSize: 2, // minimum number of responses to sample
maxStaleness: maxStaleness:
tier!.maxStalenessSlots == -1 ? 10000 : tier!.maxStalenessSlots, // maximum staleness of responses in seconds to sample tier!.maxStalenessSlots == -1 ? 10000 : tier!.maxStalenessSlots!, // maximum staleness of responses in seconds to sample
}; };
console.log('Initializing new data feed'); console.log('Initializing new data feed');