pyth2wormhole: rustfmt --config-path solana/rustfmt.toml
commit-id:a4fae495
This commit is contained in:
parent
b13c565e59
commit
0642288697
|
@ -35,10 +35,10 @@ pub struct Cli {
|
|||
pub enum Action {
|
||||
#[clap(about = "Initialize a pyth2wormhole program freshly deployed under <p2w_addr>")]
|
||||
Init {
|
||||
/// The bridge program account
|
||||
#[clap(short = 'w', long = "wh-prog")]
|
||||
wh_prog: Pubkey,
|
||||
#[clap(short = 'o', long = "owner")]
|
||||
/// The bridge program account
|
||||
#[clap(short = 'w', long = "wh-prog")]
|
||||
wh_prog: Pubkey,
|
||||
#[clap(short = 'o', long = "owner")]
|
||||
owner_addr: Pubkey,
|
||||
#[clap(short = 'p', long = "pyth-owner")]
|
||||
pyth_owner_addr: Pubkey,
|
||||
|
@ -47,20 +47,22 @@ pub enum Action {
|
|||
about = "Use an existing pyth2wormhole program to attest product price information to another chain"
|
||||
)]
|
||||
Attest {
|
||||
#[clap(short = 'f', long = "--config", about = "Attestation YAML config")]
|
||||
attestation_cfg: PathBuf,
|
||||
#[clap(short = 'f', long = "--config", about = "Attestation YAML config")]
|
||||
attestation_cfg: PathBuf,
|
||||
},
|
||||
#[clap(about = "Update an existing pyth2wormhole program's settings (currently set owner only)")]
|
||||
#[clap(
|
||||
about = "Update an existing pyth2wormhole program's settings (currently set owner only)"
|
||||
)]
|
||||
SetConfig {
|
||||
/// Current owner keypair path
|
||||
/// Current owner keypair path
|
||||
#[clap(long = "owner", default_value = "~/.config/solana/id.json")]
|
||||
owner: String,
|
||||
/// New owner to set
|
||||
owner: String,
|
||||
/// New owner to set
|
||||
#[clap(long = "new-owner")]
|
||||
new_owner_addr: Pubkey,
|
||||
new_owner_addr: Pubkey,
|
||||
#[clap(long = "new-wh-prog")]
|
||||
new_wh_prog: Pubkey,
|
||||
new_wh_prog: Pubkey,
|
||||
#[clap(long = "new-pyth-owner")]
|
||||
new_pyth_owner_addr: Pubkey,
|
||||
new_pyth_owner_addr: Pubkey,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use crate::{
|
||||
config::P2WConfigAccount,
|
||||
types::{PriceAttestation, batch_serialize},
|
||||
types::{
|
||||
batch_serialize,
|
||||
PriceAttestation,
|
||||
},
|
||||
};
|
||||
use borsh::{
|
||||
BorshDeserialize,
|
||||
|
@ -97,7 +100,6 @@ pub struct Attest<'b> {
|
|||
|
||||
// pub pyth_product10: Option<Info<'b>>,
|
||||
// pub pyth_price10: Option<Info<'b>>,
|
||||
|
||||
pub clock: Sysvar<'b, Clock>,
|
||||
|
||||
/// Wormhole program address - must match the config value
|
||||
|
@ -108,7 +110,6 @@ pub struct Attest<'b> {
|
|||
// This contract makes no attempt to exhaustively validate
|
||||
// Wormhole's account inputs. Only the wormhole contract address
|
||||
// is validated (see above).
|
||||
|
||||
/// Bridge config needed for fee calculation
|
||||
pub wh_bridge: Mut<Info<'b>>,
|
||||
|
||||
|
@ -162,9 +163,8 @@ pub fn attest(ctx: &ExecutionContext, accs: &mut Attest, data: AttestData) -> So
|
|||
accs.pyth_price4.as_ref(),
|
||||
accs.pyth_product5.as_ref(),
|
||||
accs.pyth_price5.as_ref(),
|
||||
|
||||
// Did you read the comment near `pyth_product`?
|
||||
// accs.pyth_product6.as_ref(),
|
||||
// Did you read the comment near `pyth_product`?
|
||||
// accs.pyth_product6.as_ref(),
|
||||
// accs.pyth_price6.as_ref(),
|
||||
// accs.pyth_product7.as_ref(),
|
||||
// accs.pyth_price7.as_ref(),
|
||||
|
@ -192,45 +192,41 @@ pub fn attest(ctx: &ExecutionContext, accs: &mut Attest, data: AttestData) -> So
|
|||
let mut attestations = Vec::with_capacity(price_pairs.len() / 2);
|
||||
|
||||
for pair in price_pairs.as_slice().chunks_exact(2) {
|
||||
let product = pair[0];
|
||||
let price = pair[1];
|
||||
|
||||
let product = pair[0];
|
||||
let price = pair[1];
|
||||
|
||||
if accs.config.pyth_owner != *price.owner
|
||||
|| accs.config.pyth_owner != *product.owner
|
||||
{
|
||||
if accs.config.pyth_owner != *price.owner || accs.config.pyth_owner != *product.owner {
|
||||
trace!(&format!(
|
||||
"Pair {:?} - {:?}: pyth_owner pubkey mismatch (expected {:?}, got product owner {:?} and price owner {:?}",
|
||||
|
||||
product, price,
|
||||
product, price,
|
||||
accs.config.pyth_owner, product.owner, price.owner
|
||||
));
|
||||
return Err(SolitaireError::InvalidOwner(accs.pyth_price.owner.clone()).into());
|
||||
}
|
||||
|
||||
let attestation = PriceAttestation::from_pyth_price_bytes(
|
||||
price.key.clone(),
|
||||
accs.clock.unix_timestamp,
|
||||
&*price.try_borrow_data()?,
|
||||
)?;
|
||||
let attestation = PriceAttestation::from_pyth_price_bytes(
|
||||
price.key.clone(),
|
||||
accs.clock.unix_timestamp,
|
||||
&*price.try_borrow_data()?,
|
||||
)?;
|
||||
|
||||
// The following check is crucial against poorly ordered
|
||||
// account inputs, e.g. [Some(prod1), Some(price1),
|
||||
// Some(prod2), None, None, Some(price)], interpreted by
|
||||
// earlier logic as [(prod1, price1), (prod2, price3)].
|
||||
//
|
||||
// Failing to verify the product/price relationship could lead
|
||||
// to mismatched product/price metadata, which would result in
|
||||
// a false attestation.
|
||||
if &attestation.product_id != product.key {
|
||||
trace!(&format!(
|
||||
"Price's product_id does not match the pased account (points at {:?} instead)",
|
||||
attestation.product_id
|
||||
));
|
||||
return Err(ProgramError::InvalidAccountData.into());
|
||||
}
|
||||
// The following check is crucial against poorly ordered
|
||||
// account inputs, e.g. [Some(prod1), Some(price1),
|
||||
// Some(prod2), None, None, Some(price)], interpreted by
|
||||
// earlier logic as [(prod1, price1), (prod2, price3)].
|
||||
//
|
||||
// Failing to verify the product/price relationship could lead
|
||||
// to mismatched product/price metadata, which would result in
|
||||
// a false attestation.
|
||||
if &attestation.product_id != product.key {
|
||||
trace!(&format!(
|
||||
"Price's product_id does not match the pased account (points at {:?} instead)",
|
||||
attestation.product_id
|
||||
));
|
||||
return Err(ProgramError::InvalidAccountData.into());
|
||||
}
|
||||
|
||||
attestations.push(attestation);
|
||||
attestations.push(attestation);
|
||||
}
|
||||
|
||||
trace!("Attestations successfully created");
|
||||
|
@ -249,12 +245,12 @@ pub fn attest(ctx: &ExecutionContext, accs: &mut Attest, data: AttestData) -> So
|
|||
let post_message_data = (
|
||||
bridge::instruction::Instruction::PostMessage,
|
||||
PostMessageData {
|
||||
nonce: 0, // Superseded by the sequence number
|
||||
nonce: 0, // Superseded by the sequence number
|
||||
payload: batch_serialize(attestations.as_slice().iter()).map_err(|e| {
|
||||
trace!(e.to_string());
|
||||
ProgramError::InvalidAccountData
|
||||
})?,
|
||||
consistency_level: data.consistency_level,
|
||||
trace!(e.to_string());
|
||||
ProgramError::InvalidAccountData
|
||||
})?,
|
||||
consistency_level: data.consistency_level,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -5,9 +5,18 @@
|
|||
//! problems related to max batch size mismatches between config and
|
||||
//! contract logic. See attest.rs for details.
|
||||
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use borsh::{
|
||||
BorshDeserialize,
|
||||
BorshSerialize,
|
||||
};
|
||||
use solana_program::pubkey::Pubkey;
|
||||
use solitaire::{processors::seeded::AccountOwner, AccountState, Data, Derive, Owned};
|
||||
use solitaire::{
|
||||
processors::seeded::AccountOwner,
|
||||
AccountState,
|
||||
Data,
|
||||
Derive,
|
||||
Owned,
|
||||
};
|
||||
|
||||
#[derive(Default, BorshDeserialize, BorshSerialize)]
|
||||
pub struct Pyth2WormholeConfig {
|
||||
|
|
|
@ -1,11 +1,27 @@
|
|||
use solana_program::pubkey::Pubkey;
|
||||
use solitaire::{AccountState, CreationLamports, ExecutionContext, FromAccounts, Info, InstructionContext, Keyed, Mut, Peel, Result as SoliResult, Signer, ToInstruction};
|
||||
use solitaire::{
|
||||
AccountState,
|
||||
CreationLamports,
|
||||
ExecutionContext,
|
||||
FromAccounts,
|
||||
Info,
|
||||
InstructionContext,
|
||||
Keyed,
|
||||
Mut,
|
||||
Peel,
|
||||
Result as SoliResult,
|
||||
Signer,
|
||||
ToInstruction,
|
||||
};
|
||||
|
||||
use crate::config::{P2WConfigAccount, Pyth2WormholeConfig};
|
||||
use crate::config::{
|
||||
P2WConfigAccount,
|
||||
Pyth2WormholeConfig,
|
||||
};
|
||||
|
||||
#[derive(FromAccounts, ToInstruction)]
|
||||
pub struct Initialize<'b> {
|
||||
pub new_config: Mut<P2WConfigAccount<'b, {AccountState::Uninitialized}>>,
|
||||
pub new_config: Mut<P2WConfigAccount<'b, { AccountState::Uninitialized }>>,
|
||||
pub payer: Mut<Signer<Info<'b>>>,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
#![feature(adt_const_params)]
|
||||
pub mod attest;
|
||||
pub mod config;
|
||||
|
@ -29,7 +28,6 @@ solitaire! {
|
|||
SetConfig(Pyth2WormholeConfig) => set_config,
|
||||
}
|
||||
|
||||
|
||||
#[cfg(feature = "wasm")]
|
||||
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
|
||||
pub mod wasm;
|
||||
|
|
|
@ -47,7 +47,6 @@ use self::pyth_extensions::{
|
|||
P2WPriceType,
|
||||
};
|
||||
|
||||
|
||||
/// Precedes every message implementing the p2w serialization format
|
||||
pub const P2W_MAGIC: &'static [u8] = b"P2WH";
|
||||
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
//! This module contains 1:1 (or close) copies of selected Pyth types
|
||||
//! with quick and dirty enhancements.
|
||||
|
||||
use std::{convert::TryInto, io::Read, mem};
|
||||
use std::{
|
||||
convert::TryInto,
|
||||
io::Read,
|
||||
mem,
|
||||
};
|
||||
|
||||
use pyth_client::{
|
||||
CorpAction,
|
||||
|
@ -13,7 +17,10 @@ use solitaire::ErrBox;
|
|||
|
||||
/// 1:1 Copy of pyth_client::PriceType with derived additional traits.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "wasm", derive(serde_derive::Serialize, serde_derive::Deserialize))]
|
||||
#[cfg_attr(
|
||||
feature = "wasm",
|
||||
derive(serde_derive::Serialize, serde_derive::Deserialize)
|
||||
)]
|
||||
#[repr(u8)]
|
||||
pub enum P2WPriceType {
|
||||
Unknown,
|
||||
|
@ -37,7 +44,10 @@ impl Default for P2WPriceType {
|
|||
|
||||
/// 1:1 Copy of pyth_client::PriceStatus with derived additional traits.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "wasm", derive(serde_derive::Serialize, serde_derive::Deserialize))]
|
||||
#[cfg_attr(
|
||||
feature = "wasm",
|
||||
derive(serde_derive::Serialize, serde_derive::Deserialize)
|
||||
)]
|
||||
pub enum P2WPriceStatus {
|
||||
Unknown,
|
||||
Trading,
|
||||
|
@ -64,7 +74,10 @@ impl Default for P2WPriceStatus {
|
|||
|
||||
/// 1:1 Copy of pyth_client::CorpAction with derived additional traits.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "wasm", derive(serde_derive::Serialize, serde_derive::Deserialize))]
|
||||
#[cfg_attr(
|
||||
feature = "wasm",
|
||||
derive(serde_derive::Serialize, serde_derive::Deserialize)
|
||||
)]
|
||||
pub enum P2WCorpAction {
|
||||
NoCorpAct,
|
||||
}
|
||||
|
@ -85,7 +98,10 @@ impl From<&CorpAction> for P2WCorpAction {
|
|||
|
||||
/// 1:1 Copy of pyth_client::Ema with all-pub fields.
|
||||
#[derive(Clone, Default, Debug, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "wasm", derive(serde_derive::Serialize, serde_derive::Deserialize))]
|
||||
#[cfg_attr(
|
||||
feature = "wasm",
|
||||
derive(serde_derive::Serialize, serde_derive::Deserialize)
|
||||
)]
|
||||
#[repr(C)]
|
||||
pub struct P2WEma {
|
||||
pub val: i64,
|
||||
|
@ -146,7 +162,7 @@ impl P2WEma {
|
|||
let mut val_vec = vec![0u8; mem::size_of::<i64>()];
|
||||
bytes.read_exact(val_vec.as_mut_slice())?;
|
||||
let val = i64::from_be_bytes(val_vec.as_slice().try_into()?);
|
||||
|
||||
|
||||
let mut numer_vec = vec![0u8; mem::size_of::<i64>()];
|
||||
bytes.read_exact(numer_vec.as_mut_slice())?;
|
||||
let numer = i64::from_be_bytes(numer_vec.as_slice().try_into()?);
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
use solitaire::Seeded;
|
||||
use solana_program::pubkey::Pubkey;
|
||||
use solitaire::Seeded;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::{attest::P2WEmitter, types};
|
||||
use crate::{
|
||||
attest::P2WEmitter,
|
||||
types,
|
||||
};
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn get_emitter_address(program_id: String) -> Vec<u8> {
|
||||
|
@ -17,14 +20,13 @@ pub fn get_emitter_address(program_id: String) -> Vec<u8> {
|
|||
#[wasm_bindgen]
|
||||
pub fn parse_attestation(bytes: Vec<u8>) -> JsValue {
|
||||
let a = types::PriceAttestation::deserialize(bytes.as_slice()).unwrap();
|
||||
|
||||
|
||||
JsValue::from_serde(&a).unwrap()
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn parse_batch_attestation(bytes: Vec<u8>) -> JsValue {
|
||||
let a = types::batch_deserialize(bytes.as_slice()).unwrap();
|
||||
|
||||
|
||||
JsValue::from_serde(&a).unwrap()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue