ts: update to on demand code
Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>
This commit is contained in:
parent
95ba57627e
commit
737977f45e
|
@ -80,7 +80,7 @@
|
|||
"fast-copy": "^3.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"node-kraken-api": "^2.2.2",
|
||||
"switchboard-anchor": "npm:@coral-xyz/anchor@0.30.0"
|
||||
"switchboard-anchor": "npm:@coral-xyz/anchor@0.30.1"
|
||||
},
|
||||
"license": "MIT",
|
||||
"packageManager": "yarn@4.3.1"
|
||||
|
|
|
@ -18,7 +18,7 @@ import {
|
|||
} from '@switchboard-xyz/on-demand';
|
||||
import fs from 'fs';
|
||||
import chunk from 'lodash/chunk';
|
||||
import uniq from 'lodash/uniq';
|
||||
import uniqWith from 'lodash/uniqWith';
|
||||
import { Program as Anchor30Program } from 'switchboard-anchor';
|
||||
|
||||
import { OracleConfig } from '../src/accounts/bank';
|
||||
|
@ -43,62 +43,72 @@ const GROUP = process.env.GROUP_OVERRIDE || MANGO_V4_MAIN_GROUP.toBase58();
|
|||
client,
|
||||
);
|
||||
|
||||
// TODO reload group once in a while
|
||||
const filteredOracles = await prepareCandidateOracles(group, client);
|
||||
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
const slot = await client.connection.getSlot();
|
||||
try {
|
||||
const filteredOracles = await prepareCandidateOracles(group, client);
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const slot = await client.connection.getSlot();
|
||||
|
||||
const staleOracles = await filterForStaleOracles(
|
||||
filteredOracles,
|
||||
client,
|
||||
slot,
|
||||
);
|
||||
const staleOracles = await filterForStaleOracles(
|
||||
filteredOracles,
|
||||
client,
|
||||
slot,
|
||||
);
|
||||
|
||||
const varianceThresholdCrossedOracles =
|
||||
await filterForVarianceThresholdOracles(
|
||||
filteredOracles,
|
||||
client,
|
||||
sbOnDemandProgram,
|
||||
crossbarClient,
|
||||
);
|
||||
const varianceThresholdCrossedOracles =
|
||||
await filterForVarianceThresholdOracles(
|
||||
filteredOracles,
|
||||
client,
|
||||
sbOnDemandProgram,
|
||||
crossbarClient,
|
||||
);
|
||||
|
||||
const oraclesToCrank = uniq(
|
||||
[...staleOracles, ...varianceThresholdCrossedOracles],
|
||||
function (item) {
|
||||
return item.oracle.oraclePk.toString();
|
||||
},
|
||||
);
|
||||
const oraclesToCrank = uniqWith(
|
||||
[...staleOracles, ...varianceThresholdCrossedOracles],
|
||||
function (item) {
|
||||
return item.oracle.oraclePk.toString();
|
||||
},
|
||||
);
|
||||
|
||||
const pullIxs: TransactionInstruction[] = [];
|
||||
const lutOwners: (PublicKey | Oracle)[] = [];
|
||||
for (const oracle of oraclesToCrank) {
|
||||
await preparePullIx(sbOnDemandProgram, oracle, queue, lutOwners, pullIxs);
|
||||
const pullIxs: TransactionInstruction[] = [];
|
||||
const lutOwners: (PublicKey | Oracle)[] = [];
|
||||
for (const oracle of oraclesToCrank) {
|
||||
await preparePullIx(
|
||||
sbOnDemandProgram,
|
||||
oracle,
|
||||
queue,
|
||||
lutOwners,
|
||||
pullIxs,
|
||||
);
|
||||
}
|
||||
|
||||
for (const c of chunk(pullIxs, 5, false)) {
|
||||
const tx = await asV0Tx({
|
||||
connection,
|
||||
ixs: [...c],
|
||||
signers: [user],
|
||||
computeUnitPrice: 200_000,
|
||||
computeUnitLimitMultiple: 1.3,
|
||||
lookupTables: await loadLookupTables(lutOwners),
|
||||
});
|
||||
|
||||
const txOpts = {
|
||||
commitment: 'processed' as Commitment,
|
||||
skipPreflight: true,
|
||||
maxRetries: 0,
|
||||
};
|
||||
|
||||
const sim = await client.connection.simulateTransaction(tx, txOpts);
|
||||
const sig = await client.connection.sendTransaction(tx, txOpts);
|
||||
console.log(`updated in ${sig}`); // TODO add token names
|
||||
}
|
||||
|
||||
await new Promise((r) => setTimeout(r, 5000));
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
for (const c of chunk(pullIxs, 5)) {
|
||||
const tx = await asV0Tx({
|
||||
connection,
|
||||
ixs: [...c],
|
||||
signers: [user],
|
||||
computeUnitPrice: 200_000,
|
||||
computeUnitLimitMultiple: 1.3,
|
||||
lookupTables: await loadLookupTables(lutOwners),
|
||||
});
|
||||
|
||||
const txOpts = {
|
||||
commitment: 'processed' as Commitment,
|
||||
skipPreflight: true,
|
||||
maxRetries: 0,
|
||||
};
|
||||
|
||||
const sim = await client.connection.simulateTransaction(tx, txOpts);
|
||||
const sig = await client.connection.sendTransaction(tx, txOpts);
|
||||
console.log(`updated in ${sig}`); // TODO add token names
|
||||
}
|
||||
|
||||
await new Promise((r) => setTimeout(r, 5000));
|
||||
}
|
||||
})();
|
||||
|
||||
|
@ -123,12 +133,17 @@ async function preparePullIx(
|
|||
queue: queue,
|
||||
maxVariance: decodedPullFeed.maxVariance.toNumber(),
|
||||
minResponses: decodedPullFeed.minResponses,
|
||||
numSignatures: 3, // TODO hardcoded
|
||||
minSampleSize: decodedPullFeed.minSampleSize,
|
||||
maxStaleness: decodedPullFeed.maxStaleness,
|
||||
numSignatures: 1,
|
||||
// TODO: I think these are not required
|
||||
// minSampleSize: decodedPullFeed.minSampleSize,
|
||||
// maxStaleness: decodedPullFeed.maxStaleness,
|
||||
};
|
||||
const [pullIx, responses, success] = await pullFeed.fetchUpdateIx(conf);
|
||||
|
||||
if (pullIx === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const lutOwners_ = [...responses.map((x) => x.oracle), pullFeed.pubkey];
|
||||
lutOwners.push(...lutOwners_);
|
||||
pullIxs.push(pullIx!);
|
||||
|
@ -171,7 +186,14 @@ async function filterForVarianceThresholdOracles(
|
|||
crossBarSim[0].results.length;
|
||||
|
||||
if ((res.price - simPrice) / res.price > 0.01) {
|
||||
console.log(
|
||||
`- Variance threshold crossed oracles candidate ${item.oracle.name}`,
|
||||
);
|
||||
varianceThresholdCrossedOracles.push(item);
|
||||
} else {
|
||||
console.log(
|
||||
`- Variance threshold crossed oracles non candidate ${item.oracle.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
return varianceThresholdCrossedOracles;
|
||||
|
@ -200,12 +222,15 @@ async function filterForStaleOracles(
|
|||
);
|
||||
|
||||
if (slot > res.lastUpdatedSlot) {
|
||||
console.log(`- Stale oracle candidate ${item.oracle.name}`);
|
||||
if (
|
||||
slot - res.lastUpdatedSlot >
|
||||
item.oracle.oracleConfig.maxStalenessSlots.toNumber()
|
||||
) {
|
||||
staleOracles.push(item);
|
||||
}
|
||||
} else {
|
||||
console.log(`- Stale oracle non candidate ${item.oracle.name}`);
|
||||
}
|
||||
}
|
||||
return staleOracles;
|
||||
|
@ -213,11 +238,23 @@ async function filterForStaleOracles(
|
|||
|
||||
async function prepareCandidateOracles(group: Group, client: MangoClient) {
|
||||
const oracles = getOraclesForMangoGroup(group);
|
||||
oracles.push(...extendOraclesManually());
|
||||
oracles.push(...extendOraclesManually(CLUSTER));
|
||||
|
||||
const ais = (
|
||||
await Promise.all(
|
||||
chunk(
|
||||
oracles.map((item) => item.oraclePk),
|
||||
50,
|
||||
false,
|
||||
).map(
|
||||
async (chunk) =>
|
||||
await client.program.provider.connection.getMultipleAccountsInfo(
|
||||
chunk,
|
||||
),
|
||||
),
|
||||
)
|
||||
).flat();
|
||||
|
||||
const ais = await client.program.provider.connection.getMultipleAccountsInfo(
|
||||
oracles.map((item) => item.oraclePk),
|
||||
);
|
||||
for (const [idx, ai] of ais.entries()) {
|
||||
if (ai == null || ai.data == null) {
|
||||
throw new Error(
|
||||
|
@ -239,15 +276,28 @@ async function prepareCandidateOracles(group: Group, client: MangoClient) {
|
|||
return filteredOracles;
|
||||
}
|
||||
|
||||
function extendOraclesManually() {
|
||||
function extendOraclesManually(cluster: Cluster) {
|
||||
if (cluster == 'devnet') {
|
||||
return [
|
||||
{
|
||||
oraclePk: new PublicKey('EtbG8PSDCyCSmDH8RE4Nf2qTV9d6P6zShzHY2XWvjFJf'),
|
||||
oracleConfig: {
|
||||
confFilter: I80F48.fromString('0.1'),
|
||||
maxStalenessSlots: new BN(5),
|
||||
},
|
||||
name: 'BTC/USD',
|
||||
},
|
||||
];
|
||||
}
|
||||
return [
|
||||
{
|
||||
oraclePk: new PublicKey('EtbG8PSDCyCSmDH8RE4Nf2qTV9d6P6zShzHY2XWvjFJf'),
|
||||
// https://ondemand.switchboard.xyz/solana/mainnet/user/8SSLjXBEVk9nesbhi9UMCA32uijbVBUqWoKPPQPTekzt/
|
||||
oraclePk: new PublicKey('EFSBitmy2LZexy6CqYVDAqzWN6wYHZmHkFZxmUjNgwpb'),
|
||||
oracleConfig: {
|
||||
confFilter: I80F48.fromString('0.1'),
|
||||
maxStalenessSlots: new BN(5),
|
||||
confFilter: I80F48.fromString('1000'),
|
||||
maxStalenessSlots: new BN(-1),
|
||||
},
|
||||
name: 'BTC/USD',
|
||||
name: 'MNGO/USD',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
@ -342,7 +392,7 @@ async function setupSwitchboard(client: MangoClient) {
|
|||
}
|
||||
const crossbarClient = new CrossbarClient(
|
||||
'https://crossbar.switchboard.xyz',
|
||||
true,
|
||||
false,
|
||||
);
|
||||
return { sbOnDemandProgram, crossbarClient, queue };
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { LISTING_PRESETS } from '@blockworks-foundation/mango-v4-settings/lib/helpers/listingTools';
|
||||
import {
|
||||
Cluster,
|
||||
Commitment,
|
||||
|
@ -21,15 +22,15 @@ import {
|
|||
AnchorProvider,
|
||||
Wallet,
|
||||
} from 'switchboard-anchor';
|
||||
//basic configuration
|
||||
// basic configuration
|
||||
const USDC_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';
|
||||
const SWAP_VALUE = '100';
|
||||
const TOKEN_MINT = 'SLCLww7nc1PD2gQPQdGayHviVVcpMthnqUz2iWKhNQV';
|
||||
const TOKEN_MINT = 'MangoCzJ36AjZyKwVj3VnYU4GTonjfVEnJmvvWaxLac';
|
||||
const FALLBACK_POOL_NAME: 'orcaPoolAddress' | 'raydiumPoolAddress' =
|
||||
'raydiumPoolAddress';
|
||||
const FALLBACK_POOL = '5hbCYGfGGenjeYzfWGFZ8zoXTo3fcHw5KkdsnXpXdbeX';
|
||||
const TOKEN_SYMBOL = 'SLCL';
|
||||
//basic configuration
|
||||
const FALLBACK_POOL = '34tFULRrRwh4bMcBLPtJaNqqe5pVgGZACi5sR8Xz95KC';
|
||||
const TOKEN_SYMBOL = 'MNGO';
|
||||
// basic configuration
|
||||
|
||||
const pythUsdOracle = 'Gnt27xtC473ZT2Mw5u8wZ68Z3gULkSTb5DuxJy7eJotD';
|
||||
const switchboardUsdDaoOracle = 'FwYfsmj5x8YZXtQBNo2Cz8TE7WRCMFqA6UTffK4xQKMH';
|
||||
|
@ -73,6 +74,10 @@ async function setupSwitchboard(userProvider: AnchorProvider) {
|
|||
}
|
||||
|
||||
(async function main(): Promise<void> {
|
||||
const tier = Object.values(LISTING_PRESETS).find(
|
||||
(x) => x.preset_name === 'C',
|
||||
);
|
||||
|
||||
const { userProvider, connection, user } = await setupAnchor();
|
||||
|
||||
const { sbOnDemandProgram, crossbarClient, queue } = await setupSwitchboard(
|
||||
|
@ -93,15 +98,14 @@ async function setupSwitchboard(userProvider: AnchorProvider) {
|
|||
maxRetries: 0,
|
||||
};
|
||||
|
||||
// TODO @Adrian
|
||||
const conf = {
|
||||
name: `${TOKEN_SYMBOL}/USD`, // the feed name (max 32 bytes)
|
||||
queue, // the queue of oracles to bind to
|
||||
maxVariance: 1.0, // allow 1% variance between submissions and jobs
|
||||
minResponses: 2, // minimum number of responses of jobs to allow
|
||||
numSignatures: 3, // number of signatures to fetch per update
|
||||
maxVariance: 10, // allow 1% variance between submissions and jobs
|
||||
minResponses: 1, // minimum number of responses of jobs to allow
|
||||
numSignatures: 1, // number of signatures to fetch per update
|
||||
minSampleSize: 1, // minimum number of responses to sample
|
||||
maxStaleness: 60, // maximum staleness of responses in seconds to sample
|
||||
maxStaleness: tier!.maxStalenessSlots!, // maximum staleness of responses in seconds to sample
|
||||
};
|
||||
|
||||
console.log('Initializing new data feed');
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { AnchorProvider, Wallet } from '@coral-xyz/anchor';
|
||||
import { Magic as PythMagic } from '@pythnetwork/client';
|
||||
import { AccountInfo, Connection, Keypair, PublicKey } from '@solana/web3.js';
|
||||
import { SB_ON_DEMAND_PID } from '@switchboard-xyz/on-demand';
|
||||
import { SB_ON_DEMAND_PID, toFeedValue } from '@switchboard-xyz/on-demand';
|
||||
import SwitchboardProgram from '@switchboard-xyz/sbv2-lite';
|
||||
import Big from 'big.js';
|
||||
import BN from 'bn.js';
|
||||
|
@ -130,16 +130,19 @@ export function parseSwitchboardOnDemandOracle(
|
|||
accountInfo.data,
|
||||
);
|
||||
|
||||
// This code was used when decodedPullFeed.result used to be empty
|
||||
// const feedValue = toFeedValue(decodedPullFeed.submissions, new BN(0));
|
||||
// const price = new Big(feedValue?.value.toString()).div(1e18);
|
||||
// const lastUpdatedSlot = feedValue!.slot!.toNumber(); // TODO the !
|
||||
// const stdDeviation = 0; // TODO the 0
|
||||
if (decodedPullFeed.result == undefined) {
|
||||
const feedValue = toFeedValue(decodedPullFeed.submissions, new BN(0));
|
||||
console.log(decodedPullFeed.submissions);
|
||||
console.log(feedValue);
|
||||
const price = new Big(feedValue?.value.toString()).div(1e18);
|
||||
const lastUpdatedSlot = feedValue!.slot!.toNumber(); // TODO the !
|
||||
const stdDeviation = 0; // TODO the 0
|
||||
return { price, lastUpdatedSlot, uiDeviation: stdDeviation };
|
||||
}
|
||||
|
||||
const price = new Big(decodedPullFeed.result.value.toString()).div(1e18);
|
||||
const lastUpdatedSlot = decodedPullFeed.result.slot.toNumber();
|
||||
const stdDeviation = decodedPullFeed.result.stdDev.toNumber();
|
||||
|
||||
return { price, lastUpdatedSlot, uiDeviation: stdDeviation };
|
||||
} catch (e) {
|
||||
console.log(
|
||||
|
|
24
yarn.lock
24
yarn.lock
|
@ -108,7 +108,7 @@
|
|||
superstruct "^0.15.4"
|
||||
toml "^3.0.0"
|
||||
|
||||
"@coral-xyz/anchor@^0.30.0":
|
||||
"@coral-xyz/anchor@^0.30.0", "switchboard-anchor@npm:@coral-xyz/anchor@0.30.1":
|
||||
version "0.30.1"
|
||||
resolved "https://registry.yarnpkg.com/@coral-xyz/anchor/-/anchor-0.30.1.tgz#17f3e9134c28cd0ea83574c6bab4e410bcecec5d"
|
||||
integrity sha512-gDXFoF5oHgpriXAaLpxyWBHdCs8Awgf/gLHIo6crv7Aqm937CNdY+x+6hoj7QR5vaJV7MxWSQ0NGFzL3kPbWEQ==
|
||||
|
@ -145,7 +145,7 @@
|
|||
bn.js "^5.1.2"
|
||||
buffer-layout "^1.2.0"
|
||||
|
||||
"@coral-xyz/borsh@^0.30.0", "@coral-xyz/borsh@^0.30.1":
|
||||
"@coral-xyz/borsh@^0.30.1":
|
||||
version "0.30.1"
|
||||
resolved "https://registry.yarnpkg.com/@coral-xyz/borsh/-/borsh-0.30.1.tgz#869d8833abe65685c72e9199b8688477a4f6b0e3"
|
||||
integrity sha512-aaxswpPrCFKl8vZTbxLssA2RvwX2zmKLlRCIktJOwW+VpVwYtXRtlWiIP+c2pPRKneiTiWCN2GEMSH9j1zTlWQ==
|
||||
|
@ -3007,26 +3007,6 @@ supports-preserve-symlinks-flag@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
||||
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||
|
||||
"switchboard-anchor@npm:@coral-xyz/anchor@0.30.0":
|
||||
version "0.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@coral-xyz/anchor/-/anchor-0.30.0.tgz#52acdba504b0008f1026d3a4bbbcb2d4feb5c69e"
|
||||
integrity sha512-qreDh5ztiRHVnCbJ+RS70NJ6aSTPBYDAgFeQ7Z5QvaT5DcDIhNyt4onOciVz2ieIE1XWePOJDDu9SbNvPGBkvQ==
|
||||
dependencies:
|
||||
"@coral-xyz/borsh" "^0.30.0"
|
||||
"@noble/hashes" "^1.3.1"
|
||||
"@solana/web3.js" "^1.68.0"
|
||||
bn.js "^5.1.2"
|
||||
bs58 "^4.0.1"
|
||||
buffer-layout "^1.2.2"
|
||||
camelcase "^6.3.0"
|
||||
cross-fetch "^3.1.5"
|
||||
crypto-hash "^1.3.0"
|
||||
eventemitter3 "^4.0.7"
|
||||
pako "^2.0.3"
|
||||
snake-case "^3.0.4"
|
||||
superstruct "^0.15.4"
|
||||
toml "^3.0.0"
|
||||
|
||||
table@^6.0.9:
|
||||
version "6.8.0"
|
||||
resolved "https://registry.npmjs.org/table/-/table-6.8.0.tgz"
|
||||
|
|
Loading…
Reference in New Issue