feat: pyth pull-based push oracle (#1370)
* feat: implement oracle instance * Go * Remove key * Go * Add instance id, fix conditional deser * Go * Rename * Revert changes to cli * Checkpoint * Cleanup deps * Refactor tests * Cleanup deps * Write test * Fix comment * Shard id * ADd tests * Extract common test utils * Fix test * Better name * Cleanup * Instance -> shard * Update test * Make shard id a u16
This commit is contained in:
parent
a888ba318c
commit
866b6a5b4b
|
@ -6,6 +6,7 @@ use {
|
|||
},
|
||||
hashers::keccak256_160::Keccak160,
|
||||
messages::{
|
||||
FeedId,
|
||||
Message,
|
||||
PriceFeedMessage,
|
||||
TwapMessage,
|
||||
|
@ -95,22 +96,30 @@ pub fn dummy_guardians() -> Vec<SecretKey> {
|
|||
result
|
||||
}
|
||||
|
||||
pub fn create_dummy_price_feed_message(value: i64) -> Message {
|
||||
pub fn create_dummy_feed_id(value: i64) -> FeedId {
|
||||
let mut dummy_id = [0; 32];
|
||||
dummy_id[0] = value as u8;
|
||||
dummy_id
|
||||
}
|
||||
|
||||
pub fn create_dummy_price_feed_message_with_feed_id(value: i64, feed_id: FeedId) -> Message {
|
||||
let msg = PriceFeedMessage {
|
||||
feed_id: dummy_id,
|
||||
price: value,
|
||||
conf: value as u64,
|
||||
exponent: value as i32,
|
||||
publish_time: value,
|
||||
feed_id,
|
||||
price: value,
|
||||
conf: value as u64,
|
||||
exponent: value as i32,
|
||||
publish_time: value,
|
||||
prev_publish_time: value,
|
||||
ema_price: value,
|
||||
ema_conf: value as u64,
|
||||
ema_price: value,
|
||||
ema_conf: value as u64,
|
||||
};
|
||||
Message::PriceFeedMessage(msg)
|
||||
}
|
||||
|
||||
pub fn create_dummy_price_feed_message(value: i64) -> Message {
|
||||
create_dummy_price_feed_message_with_feed_id(value, create_dummy_feed_id(value))
|
||||
}
|
||||
|
||||
pub fn create_dummy_twap_message() -> Message {
|
||||
let msg = TwapMessage {
|
||||
feed_id: [0; 32],
|
||||
|
|
|
@ -1018,6 +1018,30 @@ dependencies = [
|
|||
"unreachable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "common-test-utils"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anchor-lang",
|
||||
"bincode",
|
||||
"lazy_static",
|
||||
"libsecp256k1 0.7.1",
|
||||
"program-simulator",
|
||||
"pyth-sdk",
|
||||
"pyth-sdk-solana",
|
||||
"pyth-solana-receiver",
|
||||
"pyth-solana-receiver-sdk",
|
||||
"pythnet-sdk",
|
||||
"rand 0.8.5",
|
||||
"serde_wormhole",
|
||||
"solana-program",
|
||||
"solana-program-test",
|
||||
"solana-sdk",
|
||||
"tokio",
|
||||
"wormhole-core-bridge-solana",
|
||||
"wormhole-vaas-serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "2.3.0"
|
||||
|
@ -2990,6 +3014,23 @@ dependencies = [
|
|||
"solana-sdk",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyth-push-oracle"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anchor-lang",
|
||||
"common-test-utils",
|
||||
"program-simulator",
|
||||
"pyth-solana-receiver",
|
||||
"pyth-solana-receiver-sdk",
|
||||
"pythnet-sdk",
|
||||
"serde_wormhole",
|
||||
"solana-program",
|
||||
"solana-sdk",
|
||||
"tokio",
|
||||
"wormhole-vaas-serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyth-sdk"
|
||||
version = "0.8.0"
|
||||
|
@ -3026,19 +3067,13 @@ name = "pyth-solana-receiver"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anchor-lang",
|
||||
"bincode",
|
||||
"byteorder",
|
||||
"lazy_static",
|
||||
"libsecp256k1 0.7.1",
|
||||
"common-test-utils",
|
||||
"program-simulator",
|
||||
"pyth-sdk",
|
||||
"pyth-sdk-solana",
|
||||
"pyth-solana-receiver-sdk",
|
||||
"pythnet-sdk",
|
||||
"rand 0.8.5",
|
||||
"serde_wormhole",
|
||||
"solana-program",
|
||||
"solana-program-test",
|
||||
"solana-sdk",
|
||||
"tokio",
|
||||
"wormhole-core-bridge-solana",
|
||||
|
|
|
@ -3,7 +3,8 @@ members = [
|
|||
"programs/*",
|
||||
"cli/",
|
||||
"program_simulator/",
|
||||
"pyth_solana_receiver_sdk/"
|
||||
"pyth_solana_receiver_sdk/",
|
||||
"common_test_utils"
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
[package]
|
||||
name = "common-test-utils"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib"]
|
||||
name = "common_test_utils"
|
||||
|
||||
[dependencies]
|
||||
pyth-sdk = "0.8.0"
|
||||
pyth-sdk-solana = "0.8.0"
|
||||
solana-program-test = { workspace = true }
|
||||
solana-sdk = { workspace = true }
|
||||
tokio = "1.14.1"
|
||||
bincode = "1.3.3"
|
||||
libsecp256k1 = "0.7.1"
|
||||
rand = "0.8.5"
|
||||
lazy_static = "1.4.0"
|
||||
program-simulator = { path = "../program_simulator" }
|
||||
wormhole-vaas-serde = { workspace = true }
|
||||
serde_wormhole = { workspace = true }
|
||||
pythnet-sdk = { path = "../../../pythnet/pythnet_sdk", features = ["test-utils"] }
|
||||
anchor-lang = { workspace = true }
|
||||
solana-program = { workspace = true }
|
||||
pyth-solana-receiver = { path = "../programs/pyth-solana-receiver" }
|
||||
wormhole-core-bridge-solana = {workspace = true}
|
||||
pyth-solana-receiver-sdk = { path = "../pyth_solana_receiver_sdk"}
|
|
@ -11,9 +11,12 @@ use {
|
|||
},
|
||||
ID,
|
||||
},
|
||||
pyth_solana_receiver_sdk::config::{
|
||||
Config,
|
||||
DataSource,
|
||||
pyth_solana_receiver_sdk::{
|
||||
config::{
|
||||
Config,
|
||||
DataSource,
|
||||
},
|
||||
PYTH_PUSH_ORACLE_ID,
|
||||
},
|
||||
pythnet_sdk::test_utils::{
|
||||
dummy_guardians,
|
||||
|
@ -163,6 +166,7 @@ pub async fn setup_pyth_receiver(
|
|||
) -> ProgramTestFixtures {
|
||||
let mut program_test = ProgramTest::default();
|
||||
program_test.add_program("pyth_solana_receiver", ID, None);
|
||||
program_test.add_program("pyth_push_oracle", PYTH_PUSH_ORACLE_ID, None);
|
||||
|
||||
let mut encoded_vaa_addresses: Vec<Pubkey> = vec![];
|
||||
for vaa in vaas {
|
|
@ -0,0 +1,31 @@
|
|||
[package]
|
||||
name = "pyth-push-oracle"
|
||||
version = "0.1.0"
|
||||
description = "Created with Anchor"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
name = "pyth_push_oracle"
|
||||
|
||||
[features]
|
||||
no-entrypoint = []
|
||||
no-idl = []
|
||||
no-log-ix-name = []
|
||||
cpi = ["no-entrypoint"]
|
||||
test-bpf = []
|
||||
|
||||
[dependencies]
|
||||
anchor-lang = { workspace = true }
|
||||
pythnet-sdk = { path = "../../../../pythnet/pythnet_sdk" }
|
||||
solana-program = { workspace = true }
|
||||
pyth-solana-receiver-sdk = { path = "../../pyth_solana_receiver_sdk"}
|
||||
pyth-solana-receiver = { path = "../pyth-solana-receiver", features = ["cpi"]}
|
||||
|
||||
[dev-dependencies]
|
||||
solana-sdk = { workspace = true }
|
||||
tokio = "1.14.1"
|
||||
program-simulator = { path = "../../program_simulator" }
|
||||
wormhole-vaas-serde = { workspace = true }
|
||||
serde_wormhole = { workspace = true }
|
||||
common-test-utils = { path = "../../common_test_utils" }
|
|
@ -0,0 +1,99 @@
|
|||
use {
|
||||
anchor_lang::prelude::*,
|
||||
pyth_solana_receiver::{
|
||||
cpi::accounts::PostUpdate,
|
||||
program::PythSolanaReceiver,
|
||||
PostUpdateParams,
|
||||
},
|
||||
pyth_solana_receiver_sdk::{
|
||||
price_update::PriceUpdateV2,
|
||||
PYTH_PUSH_ORACLE_ID,
|
||||
},
|
||||
pythnet_sdk::messages::FeedId,
|
||||
};
|
||||
|
||||
pub mod sdk;
|
||||
|
||||
pub const ID: Pubkey = PYTH_PUSH_ORACLE_ID;
|
||||
|
||||
#[error_code]
|
||||
pub enum PushOracleError {
|
||||
#[msg("Updates must be monotonically increasing")]
|
||||
UpdatesNotMonotonic,
|
||||
#[msg("Trying to update price feed with the wrong feed id")]
|
||||
PriceFeedMessageMismatch,
|
||||
}
|
||||
#[program]
|
||||
pub mod pyth_push_oracle {
|
||||
|
||||
use super::*;
|
||||
|
||||
pub fn update_price_feed(
|
||||
ctx: Context<UpdatePriceFeed>,
|
||||
params: PostUpdateParams,
|
||||
shard_id: u16,
|
||||
feed_id: FeedId,
|
||||
) -> Result<()> {
|
||||
let cpi_program = ctx.accounts.pyth_solana_receiver.to_account_info().clone();
|
||||
let cpi_accounts = PostUpdate {
|
||||
payer: ctx.accounts.payer.to_account_info().clone(),
|
||||
encoded_vaa: ctx.accounts.encoded_vaa.to_account_info().clone(),
|
||||
config: ctx.accounts.config.to_account_info().clone(),
|
||||
treasury: ctx.accounts.treasury.to_account_info().clone(),
|
||||
price_update_account: ctx.accounts.price_feed_account.to_account_info().clone(),
|
||||
system_program: ctx.accounts.system_program.to_account_info().clone(),
|
||||
write_authority: ctx.accounts.price_feed_account.to_account_info().clone(),
|
||||
};
|
||||
|
||||
let seeds = &[
|
||||
&shard_id.to_le_bytes(),
|
||||
feed_id.as_ref(),
|
||||
&[*ctx.bumps.get("price_feed_account").unwrap()],
|
||||
];
|
||||
let signer_seeds = &[&seeds[..]];
|
||||
let cpi_context = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer_seeds);
|
||||
|
||||
|
||||
let current_timestamp = {
|
||||
if ctx.accounts.price_feed_account.data_is_empty() {
|
||||
0
|
||||
} else {
|
||||
let price_feed_account_data = ctx.accounts.price_feed_account.try_borrow_data()?;
|
||||
let price_feed_account =
|
||||
PriceUpdateV2::try_deserialize(&mut &price_feed_account_data[..])?;
|
||||
price_feed_account.price_message.publish_time
|
||||
}
|
||||
};
|
||||
pyth_solana_receiver::cpi::post_update(cpi_context, params)?;
|
||||
{
|
||||
let price_feed_account_data = ctx.accounts.price_feed_account.try_borrow_data()?;
|
||||
let price_feed_account =
|
||||
PriceUpdateV2::try_deserialize(&mut &price_feed_account_data[..])?;
|
||||
|
||||
require!(
|
||||
price_feed_account.price_message.publish_time > current_timestamp,
|
||||
PushOracleError::UpdatesNotMonotonic
|
||||
);
|
||||
require!(
|
||||
price_feed_account.price_message.feed_id == feed_id,
|
||||
PushOracleError::PriceFeedMessageMismatch
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
#[instruction(params : PostUpdateParams, shard_id : u16, feed_id : FeedId)]
|
||||
pub struct UpdatePriceFeed<'info> {
|
||||
#[account(mut)]
|
||||
pub payer: Signer<'info>,
|
||||
pub pyth_solana_receiver: Program<'info, PythSolanaReceiver>,
|
||||
pub encoded_vaa: AccountInfo<'info>,
|
||||
pub config: AccountInfo<'info>,
|
||||
#[account(mut)]
|
||||
pub treasury: AccountInfo<'info>,
|
||||
#[account(mut, seeds = [&shard_id.to_le_bytes(), &feed_id], bump)]
|
||||
pub price_feed_account: AccountInfo<'info>,
|
||||
pub system_program: Program<'info, System>,
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
use {
|
||||
crate::{
|
||||
accounts,
|
||||
instruction,
|
||||
PostUpdateParams,
|
||||
ID,
|
||||
},
|
||||
anchor_lang::{
|
||||
prelude::*,
|
||||
system_program,
|
||||
InstructionData,
|
||||
},
|
||||
pyth_solana_receiver::sdk::{
|
||||
get_config_address,
|
||||
get_treasury_address,
|
||||
},
|
||||
pythnet_sdk::{
|
||||
messages::FeedId,
|
||||
wire::v1::MerklePriceUpdate,
|
||||
},
|
||||
solana_program::instruction::Instruction,
|
||||
};
|
||||
|
||||
pub fn get_price_feed_address(shard_id: u16, feed_id: FeedId) -> Pubkey {
|
||||
Pubkey::find_program_address(&[&shard_id.to_le_bytes(), feed_id.as_ref()], &ID).0
|
||||
}
|
||||
|
||||
impl accounts::UpdatePriceFeed {
|
||||
pub fn populate(
|
||||
payer: Pubkey,
|
||||
encoded_vaa: Pubkey,
|
||||
shard_id: u16,
|
||||
feed_id: FeedId,
|
||||
treasury_id: u8,
|
||||
) -> Self {
|
||||
accounts::UpdatePriceFeed {
|
||||
payer,
|
||||
encoded_vaa,
|
||||
config: get_config_address(),
|
||||
treasury: get_treasury_address(treasury_id),
|
||||
price_feed_account: get_price_feed_address(shard_id, feed_id),
|
||||
pyth_solana_receiver: pyth_solana_receiver::ID,
|
||||
system_program: system_program::ID,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl instruction::UpdatePriceFeed {
|
||||
pub fn populate(
|
||||
payer: Pubkey,
|
||||
encoded_vaa: Pubkey,
|
||||
shard_id: u16,
|
||||
feed_id: FeedId,
|
||||
treasury_id: u8,
|
||||
merkle_price_update: MerklePriceUpdate,
|
||||
) -> Instruction {
|
||||
let update_price_feed_accounts =
|
||||
accounts::UpdatePriceFeed::populate(payer, encoded_vaa, shard_id, feed_id, treasury_id)
|
||||
.to_account_metas(None);
|
||||
Instruction {
|
||||
program_id: ID,
|
||||
accounts: update_price_feed_accounts,
|
||||
data: instruction::UpdatePriceFeed {
|
||||
params: PostUpdateParams {
|
||||
merkle_price_update,
|
||||
treasury_id,
|
||||
},
|
||||
shard_id,
|
||||
feed_id,
|
||||
}
|
||||
.data(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,324 @@
|
|||
use {
|
||||
common_test_utils::{
|
||||
assert_treasury_balance,
|
||||
setup_pyth_receiver,
|
||||
ProgramTestFixtures,
|
||||
WrongSetupOption,
|
||||
},
|
||||
program_simulator::into_transaction_error,
|
||||
pyth_push_oracle::{
|
||||
instruction::UpdatePriceFeed,
|
||||
sdk::get_price_feed_address,
|
||||
PushOracleError,
|
||||
},
|
||||
pyth_solana_receiver::sdk::{
|
||||
deserialize_accumulator_update_data,
|
||||
DEFAULT_TREASURY_ID,
|
||||
},
|
||||
pyth_solana_receiver_sdk::price_update::{
|
||||
PriceUpdateV2,
|
||||
VerificationLevel,
|
||||
},
|
||||
pythnet_sdk::{
|
||||
messages::Message,
|
||||
test_utils::{
|
||||
create_accumulator_message,
|
||||
create_dummy_feed_id,
|
||||
create_dummy_price_feed_message_with_feed_id,
|
||||
},
|
||||
},
|
||||
solana_sdk::{
|
||||
rent::Rent,
|
||||
signer::Signer,
|
||||
},
|
||||
};
|
||||
|
||||
const DEFAULT_SHARD: u16 = 0;
|
||||
const SECOND_SHARD: u16 = 1;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_update_price_feed() {
|
||||
let feed_id: [u8; 32] = create_dummy_feed_id(100);
|
||||
let feed_id_2: [u8; 32] = create_dummy_feed_id(200);
|
||||
|
||||
let feed_1_old = create_dummy_price_feed_message_with_feed_id(100, feed_id);
|
||||
let feed_1_recent = create_dummy_price_feed_message_with_feed_id(200, feed_id);
|
||||
|
||||
let feed_2 = create_dummy_price_feed_message_with_feed_id(300, feed_id_2);
|
||||
|
||||
let message = create_accumulator_message(
|
||||
&[feed_1_old, feed_1_recent, feed_2],
|
||||
&[feed_1_old, feed_1_recent, feed_2],
|
||||
false,
|
||||
false,
|
||||
);
|
||||
let (vaa, merkle_price_updates) = deserialize_accumulator_update_data(message).unwrap();
|
||||
|
||||
|
||||
let ProgramTestFixtures {
|
||||
mut program_simulator,
|
||||
encoded_vaa_addresses,
|
||||
governance_authority: _,
|
||||
} = setup_pyth_receiver(
|
||||
vec![serde_wormhole::from_slice(&vaa).unwrap()],
|
||||
WrongSetupOption::None,
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_treasury_balance(&mut program_simulator, 0, DEFAULT_TREASURY_ID).await;
|
||||
|
||||
let poster = program_simulator.get_funded_keypair().await.unwrap();
|
||||
|
||||
// post one update
|
||||
program_simulator
|
||||
.process_ix_with_default_compute_limit(
|
||||
UpdatePriceFeed::populate(
|
||||
poster.pubkey(),
|
||||
encoded_vaa_addresses[0],
|
||||
DEFAULT_SHARD,
|
||||
feed_id,
|
||||
DEFAULT_TREASURY_ID,
|
||||
merkle_price_updates[0].clone(),
|
||||
),
|
||||
&vec![&poster],
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_treasury_balance(
|
||||
&mut program_simulator,
|
||||
Rent::default().minimum_balance(0),
|
||||
DEFAULT_TREASURY_ID,
|
||||
)
|
||||
.await;
|
||||
|
||||
let price_feed_account = program_simulator
|
||||
.get_anchor_account_data::<PriceUpdateV2>(get_price_feed_address(DEFAULT_SHARD, feed_id))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
price_feed_account.write_authority,
|
||||
get_price_feed_address(DEFAULT_SHARD, feed_id)
|
||||
);
|
||||
assert_eq!(
|
||||
price_feed_account.verification_level,
|
||||
VerificationLevel::Full
|
||||
);
|
||||
assert_eq!(
|
||||
Message::PriceFeedMessage(price_feed_account.price_message),
|
||||
feed_1_old
|
||||
);
|
||||
assert_eq!(
|
||||
price_feed_account.posted_slot,
|
||||
program_simulator.get_clock().await.unwrap().slot
|
||||
);
|
||||
|
||||
// post another update, same price feed
|
||||
program_simulator
|
||||
.process_ix_with_default_compute_limit(
|
||||
UpdatePriceFeed::populate(
|
||||
poster.pubkey(),
|
||||
encoded_vaa_addresses[0],
|
||||
DEFAULT_SHARD,
|
||||
feed_id,
|
||||
DEFAULT_TREASURY_ID,
|
||||
merkle_price_updates[1].clone(),
|
||||
),
|
||||
&vec![&poster],
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_treasury_balance(
|
||||
&mut program_simulator,
|
||||
Rent::default().minimum_balance(0) + 1,
|
||||
DEFAULT_TREASURY_ID,
|
||||
)
|
||||
.await;
|
||||
|
||||
let price_feed_account = program_simulator
|
||||
.get_anchor_account_data::<PriceUpdateV2>(get_price_feed_address(DEFAULT_SHARD, feed_id))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
price_feed_account.write_authority,
|
||||
get_price_feed_address(DEFAULT_SHARD, feed_id)
|
||||
);
|
||||
assert_eq!(
|
||||
price_feed_account.verification_level,
|
||||
VerificationLevel::Full
|
||||
);
|
||||
assert_eq!(
|
||||
Message::PriceFeedMessage(price_feed_account.price_message),
|
||||
feed_1_recent
|
||||
);
|
||||
assert_eq!(
|
||||
price_feed_account.posted_slot,
|
||||
program_simulator.get_clock().await.unwrap().slot
|
||||
);
|
||||
|
||||
// post another update, outdated
|
||||
assert_eq!(
|
||||
program_simulator
|
||||
.process_ix_with_default_compute_limit(
|
||||
UpdatePriceFeed::populate(
|
||||
poster.pubkey(),
|
||||
encoded_vaa_addresses[0],
|
||||
DEFAULT_SHARD,
|
||||
feed_id,
|
||||
DEFAULT_TREASURY_ID,
|
||||
merkle_price_updates[1].clone(),
|
||||
),
|
||||
&vec![&poster],
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap_err()
|
||||
.unwrap(),
|
||||
into_transaction_error(PushOracleError::UpdatesNotMonotonic)
|
||||
);
|
||||
|
||||
assert_treasury_balance(
|
||||
&mut program_simulator,
|
||||
Rent::default().minimum_balance(0) + 1,
|
||||
DEFAULT_TREASURY_ID,
|
||||
)
|
||||
.await;
|
||||
|
||||
let price_feed_account = program_simulator
|
||||
.get_anchor_account_data::<PriceUpdateV2>(get_price_feed_address(DEFAULT_SHARD, feed_id))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
price_feed_account.write_authority,
|
||||
get_price_feed_address(DEFAULT_SHARD, feed_id)
|
||||
);
|
||||
assert_eq!(
|
||||
price_feed_account.verification_level,
|
||||
VerificationLevel::Full
|
||||
);
|
||||
assert_eq!(
|
||||
Message::PriceFeedMessage(price_feed_account.price_message),
|
||||
feed_1_recent
|
||||
);
|
||||
assert_eq!(
|
||||
price_feed_account.posted_slot,
|
||||
program_simulator.get_clock().await.unwrap().slot
|
||||
);
|
||||
|
||||
// works if you change the shard
|
||||
program_simulator
|
||||
.process_ix_with_default_compute_limit(
|
||||
UpdatePriceFeed::populate(
|
||||
poster.pubkey(),
|
||||
encoded_vaa_addresses[0],
|
||||
SECOND_SHARD,
|
||||
feed_id,
|
||||
DEFAULT_TREASURY_ID,
|
||||
merkle_price_updates[0].clone(),
|
||||
),
|
||||
&vec![&poster],
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_treasury_balance(
|
||||
&mut program_simulator,
|
||||
Rent::default().minimum_balance(0) + 2,
|
||||
DEFAULT_TREASURY_ID,
|
||||
)
|
||||
.await;
|
||||
|
||||
let price_feed_account = program_simulator
|
||||
.get_anchor_account_data::<PriceUpdateV2>(get_price_feed_address(DEFAULT_SHARD, feed_id))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
price_feed_account.write_authority,
|
||||
get_price_feed_address(DEFAULT_SHARD, feed_id)
|
||||
);
|
||||
assert_eq!(
|
||||
price_feed_account.verification_level,
|
||||
VerificationLevel::Full
|
||||
);
|
||||
assert_eq!(
|
||||
Message::PriceFeedMessage(price_feed_account.price_message),
|
||||
feed_1_recent
|
||||
);
|
||||
assert_eq!(
|
||||
price_feed_account.posted_slot,
|
||||
program_simulator.get_clock().await.unwrap().slot
|
||||
);
|
||||
|
||||
let price_feed_account = program_simulator
|
||||
.get_anchor_account_data::<PriceUpdateV2>(get_price_feed_address(SECOND_SHARD, feed_id))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
price_feed_account.write_authority,
|
||||
get_price_feed_address(SECOND_SHARD, feed_id)
|
||||
);
|
||||
assert_eq!(
|
||||
price_feed_account.verification_level,
|
||||
VerificationLevel::Full
|
||||
);
|
||||
assert_eq!(
|
||||
Message::PriceFeedMessage(price_feed_account.price_message),
|
||||
feed_1_old
|
||||
);
|
||||
assert_eq!(
|
||||
price_feed_account.posted_slot,
|
||||
program_simulator.get_clock().await.unwrap().slot
|
||||
);
|
||||
|
||||
// try to post the wrong price feed id
|
||||
assert_eq!(
|
||||
program_simulator
|
||||
.process_ix_with_default_compute_limit(
|
||||
UpdatePriceFeed::populate(
|
||||
poster.pubkey(),
|
||||
encoded_vaa_addresses[0],
|
||||
DEFAULT_SHARD,
|
||||
feed_id,
|
||||
DEFAULT_TREASURY_ID,
|
||||
merkle_price_updates[2].clone(),
|
||||
),
|
||||
&vec![&poster],
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap_err()
|
||||
.unwrap(),
|
||||
into_transaction_error(PushOracleError::PriceFeedMessageMismatch)
|
||||
);
|
||||
|
||||
let price_feed_account = program_simulator
|
||||
.get_anchor_account_data::<PriceUpdateV2>(get_price_feed_address(DEFAULT_SHARD, feed_id))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
price_feed_account.write_authority,
|
||||
get_price_feed_address(DEFAULT_SHARD, feed_id)
|
||||
);
|
||||
assert_eq!(
|
||||
price_feed_account.verification_level,
|
||||
VerificationLevel::Full
|
||||
);
|
||||
assert_eq!(
|
||||
Message::PriceFeedMessage(price_feed_account.price_message),
|
||||
feed_1_recent
|
||||
);
|
||||
assert_eq!(
|
||||
price_feed_account.posted_slot,
|
||||
program_simulator.get_clock().await.unwrap().slot
|
||||
);
|
||||
}
|
|
@ -25,16 +25,9 @@ wormhole-raw-vaas = {version = "0.1.3", features = ["ruint", "on-chain"], defaul
|
|||
pyth-solana-receiver-sdk = { path = "../../pyth_solana_receiver_sdk"}
|
||||
|
||||
[dev-dependencies]
|
||||
pyth-sdk = "0.8.0"
|
||||
pyth-sdk-solana = "0.8.0"
|
||||
solana-program-test = { workspace = true }
|
||||
solana-sdk = { workspace = true }
|
||||
tokio = "1.14.1"
|
||||
bincode = "1.3.3"
|
||||
libsecp256k1 = "0.7.1"
|
||||
rand = "0.8.5"
|
||||
lazy_static = "1.4.0"
|
||||
program-simulator = { path = "../../program_simulator" }
|
||||
wormhole-vaas-serde = { workspace = true }
|
||||
serde_wormhole = { workspace = true }
|
||||
pythnet-sdk = { path = "../../../../pythnet/pythnet_sdk", features = ["test-utils"] }
|
||||
common-test-utils = { path = "../../common_test_utils" }
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use {
|
||||
crate::common::WrongSetupOption,
|
||||
common::{
|
||||
common_test_utils::{
|
||||
setup_pyth_receiver,
|
||||
ProgramTestFixtures,
|
||||
WrongSetupOption,
|
||||
},
|
||||
program_simulator::into_transaction_error,
|
||||
pyth_solana_receiver::{
|
||||
|
@ -30,8 +30,6 @@ use {
|
|||
solana_sdk::signer::Signer,
|
||||
};
|
||||
|
||||
mod common;
|
||||
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_governance() {
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
use {
|
||||
crate::common::{
|
||||
common_test_utils::{
|
||||
assert_treasury_balance,
|
||||
WrongSetupOption,
|
||||
DEFAULT_GUARDIAN_SET_INDEX,
|
||||
},
|
||||
common::{
|
||||
setup_pyth_receiver,
|
||||
ProgramTestFixtures,
|
||||
WrongSetupOption,
|
||||
DEFAULT_GUARDIAN_SET_INDEX,
|
||||
},
|
||||
program_simulator::into_transaction_error,
|
||||
pyth_solana_receiver::{
|
||||
|
@ -51,9 +49,6 @@ use {
|
|||
wormhole_core_bridge_solana::ID as BRIDGE_ID,
|
||||
};
|
||||
|
||||
mod common;
|
||||
|
||||
|
||||
// This file is meant to test the errors that can be thrown by post_price_update_from_vaa
|
||||
#[tokio::test]
|
||||
async fn test_invalid_wormhole_message() {
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
use {
|
||||
crate::common::{
|
||||
common_test_utils::{
|
||||
assert_treasury_balance,
|
||||
WrongSetupOption,
|
||||
},
|
||||
common::{
|
||||
setup_pyth_receiver,
|
||||
ProgramTestFixtures,
|
||||
WrongSetupOption,
|
||||
},
|
||||
program_simulator::into_transaction_error,
|
||||
pyth_solana_receiver::{
|
||||
|
@ -38,8 +36,6 @@ use {
|
|||
},
|
||||
};
|
||||
|
||||
mod common;
|
||||
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_post_update() {
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
use {
|
||||
crate::common::{
|
||||
common_test_utils::{
|
||||
assert_treasury_balance,
|
||||
WrongSetupOption,
|
||||
DEFAULT_GUARDIAN_SET_INDEX,
|
||||
},
|
||||
common::{
|
||||
setup_pyth_receiver,
|
||||
ProgramTestFixtures,
|
||||
WrongSetupOption,
|
||||
DEFAULT_GUARDIAN_SET_INDEX,
|
||||
},
|
||||
program_simulator::into_transaction_error,
|
||||
pyth_solana_receiver::{
|
||||
|
@ -41,8 +39,6 @@ use {
|
|||
wormhole_sdk::Vaa,
|
||||
};
|
||||
|
||||
mod common;
|
||||
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_post_update_atomic() {
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
use anchor_lang::declare_id;
|
||||
use {
|
||||
anchor_lang::declare_id,
|
||||
solana_program::{
|
||||
pubkey,
|
||||
pubkey::Pubkey,
|
||||
},
|
||||
};
|
||||
|
||||
pub mod config;
|
||||
pub mod error;
|
||||
pub mod price_update;
|
||||
|
||||
declare_id!("rec5EKMGg6MxZYaMdyBfgwp4d5rB9T1VQH5pJv5LtFJ");
|
||||
|
||||
pub const PYTH_PUSH_ORACLE_ID: Pubkey = pubkey!("F9SP6tBXw9Af7BYauo7Y2R5Es2mpv8FP5aNCXMihp6Za");
|
||||
|
|
Loading…
Reference in New Issue