add support for pyth oracle
This commit is contained in:
parent
1c7749bdfc
commit
5a0cfa413f
|
@ -59,6 +59,7 @@
|
||||||
"@project-serum/common": "^0.0.1-beta.3",
|
"@project-serum/common": "^0.0.1-beta.3",
|
||||||
"@project-serum/serum": "^0.13.20",
|
"@project-serum/serum": "^0.13.20",
|
||||||
"@project-serum/sol-wallet-adapter": "^0.1.4",
|
"@project-serum/sol-wallet-adapter": "^0.1.4",
|
||||||
|
"@pythnetwork/client": "^2.7.2",
|
||||||
"@solana/spl-token": "0.0.13",
|
"@solana/spl-token": "0.0.13",
|
||||||
"@solana/web3.js": "^0.95.0",
|
"@solana/web3.js": "^0.95.0",
|
||||||
"bn.js": "^5.1.2",
|
"bn.js": "^5.1.2",
|
||||||
|
|
|
@ -28,6 +28,7 @@ import {
|
||||||
awaitTransactionSignatureConfirmation,
|
awaitTransactionSignatureConfirmation,
|
||||||
createAccountInstruction,
|
createAccountInstruction,
|
||||||
getFilteredProgramAccounts,
|
getFilteredProgramAccounts,
|
||||||
|
getOraclePrice,
|
||||||
getUnixTs,
|
getUnixTs,
|
||||||
nativeToUi,
|
nativeToUi,
|
||||||
parseTokenAccountData,
|
parseTokenAccountData,
|
||||||
|
@ -105,15 +106,10 @@ export class MangoGroup {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPrices(connection: Connection): Promise<number[]> {
|
async getPrices(connection: Connection): Promise<number[]> {
|
||||||
const aggs = await Promise.all(
|
const oraclePrices = await Promise.all(
|
||||||
this.oracles.map((pk) => Aggregator.loadWithConnection(pk, connection)),
|
this.oracles.map((pk) => getOraclePrice(connection, pk)),
|
||||||
);
|
);
|
||||||
return aggs
|
return oraclePrices.concat([1.0]);
|
||||||
.map(
|
|
||||||
(agg) =>
|
|
||||||
agg.answer.median.toNumber() / Math.pow(10, agg.config.decimals),
|
|
||||||
)
|
|
||||||
.concat(1.0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getMarketIndex(spotMarket: Market): number {
|
getMarketIndex(spotMarket: Market): number {
|
||||||
|
|
27
src/utils.ts
27
src/utils.ts
|
@ -14,6 +14,11 @@ import {
|
||||||
} from '@solana/web3.js';
|
} from '@solana/web3.js';
|
||||||
import BN from 'bn.js';
|
import BN from 'bn.js';
|
||||||
import { WRAPPED_SOL_MINT } from '@project-serum/serum/lib/token-instructions';
|
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 { bits, blob, struct, u8, u32, nu64 } from 'buffer-layout';
|
||||||
import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
|
import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
|
||||||
import { AccountLayout } from './layout';
|
import { AccountLayout } from './layout';
|
||||||
|
@ -25,6 +30,7 @@ import {
|
||||||
u64,
|
u64,
|
||||||
zeros,
|
zeros,
|
||||||
} from '@project-serum/serum/lib/layout';
|
} from '@project-serum/serum/lib/layout';
|
||||||
|
import { Aggregator } from './schema';
|
||||||
|
|
||||||
export const zeroKey = new PublicKey(new Uint8Array(32));
|
export const zeroKey = new PublicKey(new Uint8Array(32));
|
||||||
|
|
||||||
|
@ -418,3 +424,24 @@ export function decodeRecentEvents(buffer: Buffer, lastSeenSeqNum?: number) {
|
||||||
|
|
||||||
return { header, nodes };
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
|
@ -583,6 +583,13 @@
|
||||||
bs58 "^4.0.1"
|
bs58 "^4.0.1"
|
||||||
eventemitter3 "^4.0.4"
|
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":
|
"@sinonjs/commons@^1.7.0":
|
||||||
version "1.8.3"
|
version "1.8.3"
|
||||||
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d"
|
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d"
|
||||||
|
|
Loading…
Reference in New Issue