add support for pyth oracle

This commit is contained in:
Maximilian Schneider 2022-07-26 22:11:20 +02:00
parent 1c7749bdfc
commit 5a0cfa413f
5 changed files with 62 additions and 8 deletions

View File

@ -59,6 +59,7 @@
"@project-serum/common": "^0.0.1-beta.3",
"@project-serum/serum": "^0.13.20",
"@project-serum/sol-wallet-adapter": "^0.1.4",
"@pythnetwork/client": "^2.7.2",
"@solana/spl-token": "0.0.13",
"@solana/web3.js": "^0.95.0",
"bn.js": "^5.1.2",

View File

@ -28,6 +28,7 @@ import {
awaitTransactionSignatureConfirmation,
createAccountInstruction,
getFilteredProgramAccounts,
getOraclePrice,
getUnixTs,
nativeToUi,
parseTokenAccountData,
@ -105,15 +106,10 @@ export class MangoGroup {
}
async getPrices(connection: Connection): Promise<number[]> {
const aggs = await Promise.all(
this.oracles.map((pk) => Aggregator.loadWithConnection(pk, connection)),
const oraclePrices = await Promise.all(
this.oracles.map((pk) => getOraclePrice(connection, pk)),
);
return aggs
.map(
(agg) =>
agg.answer.median.toNumber() / Math.pow(10, agg.config.decimals),
)
.concat(1.0);
return oraclePrices.concat([1.0]);
}
getMarketIndex(spotMarket: Market): number {

View File

@ -14,6 +14,11 @@ import {
} from '@solana/web3.js';
import BN from 'bn.js';
import { WRAPPED_SOL_MINT } from '@project-serum/serum/lib/token-instructions';
import {
parseBaseData,
parsePriceData,
AccountType,
} from '@pythnetwork/client';
import { bits, blob, struct, u8, u32, nu64 } from 'buffer-layout';
import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
import { AccountLayout } from './layout';
@ -25,6 +30,7 @@ import {
u64,
zeros,
} from '@project-serum/serum/lib/layout';
import { Aggregator } from './schema';
export const zeroKey = new PublicKey(new Uint8Array(32));
@ -418,3 +424,24 @@ export function decodeRecentEvents(buffer: Buffer, lastSeenSeqNum?: number) {
return { header, nodes };
}
const PYTH_MAGIC = Buffer.from([0xa1, 0xb2, 0xc3, 0xd4]);
export async function getOraclePrice(
connection: Connection,
oracle: PublicKey,
): Promise<number> {
const info = await connection.getAccountInfo(oracle);
if (!info || !info.data.length) {
throw new Error('account does not exist');
}
const pythBase = parseBaseData(info.data);
if (pythBase?.type == AccountType.Price) {
const price = parsePriceData(info.data);
return price.aggregate.price;
} else {
const agg = Aggregator.deserialize(info.data);
return agg.answer.median.toNumber() / Math.pow(10, agg.config.decimals);
}
}

23
tests/oracle.test.ts Normal file
View File

@ -0,0 +1,23 @@
import { Connection, PublicKey } from '@solana/web3.js';
import { expect } from 'chai';
import { getOraclePrice } from '../src/utils';
const conn = new Connection('https://api.mainnet-beta.solana.com/');
describe('getOraclePrice', async () => {
it('should parse flux aggregator', async () => {
const p = await getOraclePrice(
conn,
new PublicKey('HxrRDnjj2Ltj9LMmtcN6PDuFqnDe3FqXDHPvs2pwmtYF'),
);
expect(p).to.be.within(5000, 80000);
});
it('should parse pyth', async () => {
const p = await getOraclePrice(
conn,
new PublicKey('GVXRSBjFk6e6J3NbVPXohDJetcTjaeeuykUpbQF8UoMU'),
);
expect(p).to.be.within(5000, 80000);
});
});

View File

@ -583,6 +583,13 @@
bs58 "^4.0.1"
eventemitter3 "^4.0.4"
"@pythnetwork/client@^2.7.2":
version "2.7.2"
resolved "https://registry.yarnpkg.com/@pythnetwork/client/-/client-2.7.2.tgz#eca3a59e8f222aa1b67c8e4653e2484079f5fb9a"
integrity sha512-Hx/GLaZH0evm0tT0gsO1S7r3liiDzUeqDfMaV1HH7a5yuQGH9zgUrmdEMEtkgRceLa2nGhEGnRtISqY/X96XtA==
dependencies:
buffer "^6.0.1"
"@sinonjs/commons@^1.7.0":
version "1.8.3"
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d"