From f44d984676b198169dfe1cb9d45d165942f78fd7 Mon Sep 17 00:00:00 2001 From: Marc Nijdam Date: Wed, 19 Jun 2024 11:46:43 -0500 Subject: [PATCH] Add price command to current feed price --- Cargo.lock | 4 ++-- helium-lib/src/token.rs | 28 ++++++++++++++++++++++------ helium-wallet/src/cmd/mod.rs | 1 + helium-wallet/src/cmd/price.rs | 19 +++++++++++++++++++ helium-wallet/src/main.rs | 6 +++++- 5 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 helium-wallet/src/cmd/price.rs diff --git a/Cargo.lock b/Cargo.lock index 1cfd6065..9bd175f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3964,7 +3964,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91e6559643f0b377b6f293269251f6a804ae7332c37f7310371f50c833453cd0" dependencies = [ - "anchor-lang", + "anchor-lang 0.30.0", "hex", "pythnet-sdk", "solana-program", @@ -3976,7 +3976,7 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bbbc0456f9f27c9ad16b6c3bf1b2a7fea61eebf900f4d024a0468b9a84fe0c1" dependencies = [ - "anchor-lang", + "anchor-lang 0.30.0", "bincode", "borsh 0.10.3", "bytemuck", diff --git a/helium-lib/src/token.rs b/helium-lib/src/token.rs index 2b1e2d4d..94d6d50c 100644 --- a/helium-lib/src/token.rs +++ b/helium-lib/src/token.rs @@ -20,7 +20,11 @@ lazy_static::lazy_static! { static ref HNT_PRICE_KEY: Pubkey = Pubkey::from_str("4DdmDswskDxXGpwHrXUfn2CNUm9rt21ac79GHNTN3J33").unwrap(); static ref MOBILE_MINT: Pubkey = Pubkey::from_str("mb1eu7TzEc71KxDpsmsKoucSSuuoGLv1drys1oP2jh6").unwrap(); + static ref MOBILE_PRICE_KEY: Pubkey = Pubkey::from_str("DQ4C1tzvu28cwo1roN1Wm6TW35sfJEjLh517k3ZeWevx").unwrap(); + static ref IOT_MINT: Pubkey = Pubkey::from_str("iotEVVZLEywoTn1QdwNPddxPWszn3zFhEot3MfL9fns").unwrap(); + static ref IOT_PRICE_KEY: Pubkey = Pubkey::from_str("8UYEn5Weq7toHwgcmctvcAxaNJo3SJxXEayM57rpoXr9").unwrap(); + static ref DC_MINT: Pubkey = Pubkey::from_str("dcuc8Amr83Wz27ZkQ2K9NS6r8zRpf1J6cvArEBDZDmm").unwrap(); static ref SOL_MINT: Pubkey = solana_sdk::system_program::ID; } @@ -125,6 +129,8 @@ pub mod price { pub enum PriceError { #[error("price too old")] PriceTooOld, + #[error("invalid price account: {0}")] + InvalidPriceAccount(std::io::Error), #[error("price below 0")] PriceBelowZero, #[error("invalid price timestamp: {0}")] @@ -141,13 +147,14 @@ pub mod price { } pub async fn get(settings: &Settings, token: Token) -> Result { + use helium_anchor_gen::anchor_lang::AccountDeserialize; let price_key = token .price_key() .ok_or_else(|| DecodeError::other(format!("No pyth price key for {token}")))?; - let anchor_client = settings.mk_anchor_client(crate::keypair::Keypair::void())?; - let program = anchor_client.program(pyth_solana_receiver_sdk::ID)?; + let solana_client = settings.mk_solana_client()?; + let account = solana_client.get_account(price_key).await?; let PriceUpdateV2 { price_message, .. } = - program.account::(*price_key).await?; + PriceUpdateV2::try_deserialize(&mut account.data.as_slice())?; if (price_message.publish_time.saturating_add(10 * 60)) < Utc::now().timestamp() { return Err(PriceError::PriceTooOld.into()); @@ -222,15 +229,22 @@ impl Token { vec![Self::Hnt, Self::Iot, Self::Mobile, Self::Dc, Self::Sol] } - pub fn transferrable_value_parser(s: &str) -> StdResult { - let transferrable = [Self::Iot, Self::Mobile, Self::Hnt, Self::Sol]; + fn from_allowed(s: &str, allowed: &[Self]) -> StdResult { let result = Self::from_str(s)?; - if !transferrable.contains(&result) { + if !allowed.contains(&result) { return Err(TokenError::InvalidToken(s.to_string())); } Ok(result) } + pub fn transferrable_value_parser(s: &str) -> StdResult { + Self::from_allowed(s, &[Self::Iot, Self::Mobile, Self::Hnt, Self::Sol]) + } + + pub fn pricekey_value_parser(s: &str) -> StdResult { + Self::from_allowed(s, &[Self::Iot, Self::Mobile, Self::Hnt]) + } + pub fn associated_token_adress(&self, address: &Pubkey) -> Pubkey { match self { Self::Sol => *address, @@ -362,6 +376,8 @@ impl Token { pub fn price_key(&self) -> Option<&Pubkey> { match self { Self::Hnt => Some(&HNT_PRICE_KEY), + Self::Iot => Some(&IOT_PRICE_KEY), + Self::Mobile => Some(&MOBILE_PRICE_KEY), _ => None, } } diff --git a/helium-wallet/src/cmd/mod.rs b/helium-wallet/src/cmd/mod.rs index 964fb579..81148a27 100644 --- a/helium-wallet/src/cmd/mod.rs +++ b/helium-wallet/src/cmd/mod.rs @@ -24,6 +24,7 @@ pub mod dc; pub mod export; pub mod hotspots; pub mod info; +pub mod price; pub mod router; pub mod sign; pub mod transfer; diff --git a/helium-wallet/src/cmd/price.rs b/helium-wallet/src/cmd/price.rs new file mode 100644 index 00000000..436a1027 --- /dev/null +++ b/helium-wallet/src/cmd/price.rs @@ -0,0 +1,19 @@ +use crate::cmd::*; +use helium_lib::token; + +#[derive(Clone, Debug, clap::Args)] +/// Get the current price from the pyth price feed for the given token +pub struct Cmd { + /// Token to look up + #[arg(value_parser = token::Token::pricekey_value_parser)] + token: token::Token, +} + +impl Cmd { + pub async fn run(&self, opts: Opts) -> Result { + let settings = opts.try_into()?; + let price = token::price::get(&settings, self.token).await?; + + print_json(&price) + } +} diff --git a/helium-wallet/src/main.rs b/helium-wallet/src/main.rs index cd56b2b1..7b2b7a22 100644 --- a/helium-wallet/src/main.rs +++ b/helium-wallet/src/main.rs @@ -1,6 +1,8 @@ use clap::Parser; use helium_wallet::{ - cmd::{balance, create, dc, export, hotspots, info, router, sign, transfer, upgrade, Opts}, + cmd::{ + balance, create, dc, export, hotspots, info, price, router, sign, transfer, upgrade, Opts, + }, result::Result, }; @@ -30,6 +32,7 @@ pub enum Cmd { Create(create::Cmd), Hotspots(Box), Dc(dc::Cmd), + Price(price::Cmd), Transfer(transfer::Cmd), Export(export::Cmd), Sign(sign::Cmd), @@ -53,6 +56,7 @@ async fn run(cli: Cli) -> Result { Cmd::Create(cmd) => cmd.run(cli.opts).await, Cmd::Hotspots(cmd) => cmd.run(cli.opts).await, Cmd::Dc(cmd) => cmd.run(cli.opts).await, + Cmd::Price(cmd) => cmd.run(cli.opts).await, Cmd::Transfer(cmd) => cmd.run(cli.opts).await, Cmd::Export(cmd) => cmd.run(cli.opts).await, Cmd::Sign(cmd) => cmd.run(cli.opts).await,