Skip to content

Commit

Permalink
feat: actual pyth accounts data
Browse files Browse the repository at this point in the history
  • Loading branch information
beautyfree committed Feb 29, 2024
1 parent 319f848 commit ee9882e
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 81 deletions.
8 changes: 8 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"recommendations": [
"arcanis.vscode-zipfs",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"solana-playground.solpg"
]
}
9 changes: 9 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.formatOnSave": true,
"editor.codeActionsOnSave": [
"source.addMissingImports"
]
}
35 changes: 35 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Run localnet",
"type": "shell",
"command": "anchor localnet",
"group": "test",
"presentation": {
"group": "testGroup",
"reveal": "always",
"panel": "new",
"echo": false
}
},
{
"label": "Run tests (localnet)",
"type": "shell",
"command": "anchor test --skip-local-validator",
"group": "test",
"presentation": {
"group": "testGroup",
"reveal": "always",
"panel": "new",
"echo": false
}
},
{
"label": "Get program pubkey",
"type": "shell",
"command": "solana-keygen pubkey ./target/deploy/mock_oracles-keypair.json",
"group": "test"
}
]
}
6 changes: 5 additions & 1 deletion Anchor.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
[toolchain]
anchor_version = "0.29.0" # `anchor-cli` version to use
solana_version = "1.17.0" # Solana version to use

[programs.localnet]
mock_oracles = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
mock_oracles = "6PLWdUXJJRYeTsCHv72iwubm43E1Z1HChkyC3cQHCEtD"

[registry]
url = "https://anchor.projectserum.com"
Expand Down
27 changes: 13 additions & 14 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion programs/mock-oracles/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mock-oracles"
version = "0.1.3"
version = "0.2.0"
description = "Created with Anchor"
edition = "2018"

Expand Down
63 changes: 28 additions & 35 deletions programs/mock-oracles/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
use anchor_lang::prelude::*;
use bytemuck::{cast_slice_mut, from_bytes_mut, try_cast_slice_mut, Pod, PodCastError};
use bytemuck::Pod;
use pyth_sdk_solana::state::{
AccountType, PriceAccount, PriceStatus, ProductAccount, MAGIC, PROD_ACCT_SIZE, PROD_ATTR_SIZE,
VERSION_2,
AccountType, PriceAccount, PriceStatus, PriceType, ProductAccount, Rational, MAGIC,
PROD_ACCT_SIZE, PROD_ATTR_SIZE, VERSION_2,
};
use std::cell::RefMut;
use std::mem::size_of;
use switchboard_v2::{AggregatorAccountData, SwitchboardDecimal};

declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
declare_id!("6PLWdUXJJRYeTsCHv72iwubm43E1Z1HChkyC3cQHCEtD");

const QUOTE_CURRENCY: [u8; 32] = *b"USD\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";

#[program]
pub mod mock_oracles {
use pyth_sdk_solana::state::Rational;

use super::*;

pub fn init_pyth(ctx: Context<InitPyth>) -> Result<()> {
Expand All @@ -25,19 +23,15 @@ pub mod mock_oracles {
let product_account_info = &ctx.accounts.product_account;

// write PriceAccount
let price_account = PriceAccount {
magic: MAGIC,
ver: VERSION_2,
atype: AccountType::Price as u32,
size: 240, // PC_PRICE_T_COMP_OFFSET from pyth_client repo
..PriceAccount::default()
};

let mut data = price_account_info.try_borrow_mut_data()?;
data.copy_from_slice(bytemuck::bytes_of(&price_account));
let mut price_account = load_account_as_mut::<PriceAccount>(price_account_info)?;
price_account.magic = MAGIC;
price_account.ver = VERSION_2;
price_account.atype = AccountType::Price as u32;
price_account.size = size_of::<PriceAccount>() as u32;
price_account.ptype = PriceType::Price;

// write ProductAccount
let attr = {
let attr: [u8; 464] = {
let mut attr: Vec<u8> = Vec::new();
let quote_currency = b"quote_currency";
attr.push(quote_currency.len() as u8);
Expand All @@ -51,17 +45,13 @@ pub mod mock_oracles {
buf
};

let product_account = ProductAccount {
magic: MAGIC,
ver: VERSION_2,
atype: AccountType::Product as u32,
size: PROD_ACCT_SIZE as u32,
px_acc: *price_account_info.key,
attr,
};

let mut data = product_account_info.try_borrow_mut_data()?;
data.copy_from_slice(bytemuck::bytes_of(&product_account));
let mut product_account = load_account_as_mut::<ProductAccount>(product_account_info)?;
product_account.magic = MAGIC;
product_account.ver = VERSION_2;
product_account.atype = AccountType::Product as u32;
product_account.size = PROD_ACCT_SIZE as u32;
product_account.px_acc = *price_account_info.key;
product_account.attr = attr;

Ok(())
}
Expand All @@ -75,8 +65,7 @@ pub mod mock_oracles {
ema_conf: u64,
) -> Result<()> {
msg!("Mock Pyth: Set price");
let data = &mut ctx.accounts.target.try_borrow_mut_data()?;
let mut price_account: &mut PriceAccount = load_mut(data).unwrap();
let mut price_account = load_account_as_mut::<PriceAccount>(&ctx.accounts.target)?;

price_account.agg.price = price;
price_account.agg.conf = conf;
Expand All @@ -103,6 +92,7 @@ pub mod mock_oracles {
}

pub fn init_switchboard(ctx: Context<Write>) -> Result<()> {
msg!("Mock Switchboard: Init Switchboard");
let mut data = ctx.accounts.target.try_borrow_mut_data()?;

let discriminator = [217, 230, 65, 101, 201, 162, 27, 125];
Expand Down Expand Up @@ -132,11 +122,14 @@ pub mod mock_oracles {
}
}

pub fn load_mut<T: Pod>(data: &mut [u8]) -> std::result::Result<&mut T, PodCastError> {
let size = size_of::<T>();
Ok(from_bytes_mut(cast_slice_mut::<u8, u8>(
try_cast_slice_mut(&mut data[0..size])?,
)))
pub fn load_account_as_mut<'a, T: Pod>(
account: &'a AccountInfo,
) -> std::result::Result<RefMut<'a, T>, ProgramError> {
let data = account.try_borrow_mut_data()?;

Ok(RefMut::map(data, |data| {
bytemuck::from_bytes_mut(&mut data[0..size_of::<T>()])
}))
}

#[derive(Accounts)]
Expand Down
2 changes: 1 addition & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PublicKey } from "@saberhq/solana-contrib";

export const MOCK_ORACLES_ADDRESS = new PublicKey(
"Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
"6PLWdUXJJRYeTsCHv72iwubm43E1Z1HChkyC3cQHCEtD"
);
34 changes: 5 additions & 29 deletions tests/mock-oracles.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import * as anchor from "@coral-xyz/anchor";
import { AggregatorAccountData } from "@switchboard-xyz/solana.js/generated";
import { Connection, PublicKey } from "@solana/web3.js";
import { parsePriceData } from "@pythnetwork/client";
import { assert } from "chai";

import { makeSDK } from "./workspace";
import { AggregatorAccountData } from "@switchboard-xyz/solana.js/generated";

async function loadZeroCopyAggregator(
con: Connection,
Expand All @@ -15,52 +16,37 @@ async function loadZeroCopyAggregator(
}

describe("Test Mock Oracles", () => {
const _mockOracles = makeSDK();
const provider = _mockOracles.provider;
const mockOracles = _mockOracles.withSigner(
(provider.wallet as anchor.Wallet).payer
);
const mockOracles = makeSDK();
const provider = mockOracles.provider;

it("Write Pyth Data", async () => {
const { priceKeypair } = await mockOracles.createPyth();
const price = 10;
const slot = 10;

await mockOracles.setPythPrice(priceKeypair, {
price: new anchor.BN(price),
slot: new anchor.BN(slot),
});
let pythData = await provider.connection.getAccountInfo(
priceKeypair.publicKey
);
let pythPriceRecord = parsePriceData(pythData.data);
assert(pythPriceRecord.price === price);
assert(pythPriceRecord.exponent === 0);
assert(pythPriceRecord.validSlot.toString() === slot.toString());

await mockOracles.setPythPrice(priceKeypair, {
price: new anchor.BN(price * 2),
});
pythData = await provider.connection.getAccountInfo(priceKeypair.publicKey);
pythPriceRecord = parsePriceData(pythData.data);
assert(pythPriceRecord.price === price * 2);

await mockOracles.setPythPrice(priceKeypair, {
slot: new anchor.BN(slot * 2),
});
pythData = await provider.connection.getAccountInfo(priceKeypair.publicKey);
pythPriceRecord = parsePriceData(pythData.data);
assert(pythPriceRecord.validSlot.toString() === (slot * 2).toString());
});

it("Write Switchboard Data", async () => {
const { switchboardKeypair } = await mockOracles.createSwitchboard();
const price = 10;
const slot = 10;

await mockOracles.setSwitchboardPrice(switchboardKeypair, {
price: new anchor.BN(price),
slot: new anchor.BN(slot),
});
let switchboardPrice = await loadZeroCopyAggregator(
provider.connection,
Expand All @@ -70,10 +56,6 @@ describe("Test Mock Oracles", () => {
switchboardPrice.latestConfirmedRound.result.toString() ===
price.toString()
);
assert(
switchboardPrice.latestConfirmedRound.roundOpenSlot.toString() ===
slot.toString()
);

await mockOracles.setSwitchboardPrice(switchboardKeypair, {
price: new anchor.BN(price * 2),
Expand All @@ -87,16 +69,10 @@ describe("Test Mock Oracles", () => {
(price * 2).toString()
);

await mockOracles.setSwitchboardPrice(switchboardKeypair, {
slot: new anchor.BN(slot * 2),
});
await mockOracles.setSwitchboardPrice(switchboardKeypair, {});
switchboardPrice = await loadZeroCopyAggregator(
provider.connection,
switchboardKeypair.publicKey
);
assert(
switchboardPrice.latestConfirmedRound.roundOpenSlot.toString() ===
(slot * 2).toString()
);
});
});

0 comments on commit ee9882e

Please sign in to comment.