This commit is contained in:
Conner Gallagher 2023-06-26 17:25:23 -06:00
parent d7d2571750
commit 48e99d04a8
7 changed files with 95 additions and 59 deletions

View File

@ -13,6 +13,6 @@ tokio = "^1"
futures = "0.3"
serde = "^1"
serde_json = "^1"
switchboard-utils = "0.5.0"
switchboard-solana = "0.5.3"
# switchboard-solana = { path = "../../../../rust/switchboard-solana" }
# switchboard-utils = "0.5.0"
# switchboard-solana = "0.5.3"
switchboard-solana = { path = "../../../../rust/switchboard-solana" }

View File

@ -1,13 +1,12 @@
// Note: Binance API requires a non-US IP address
use crate::*;
use bytemuck;
pub use switchboard_utils::reqwest;
// pub use switchboard_utils::reqwest;
use serde::Deserialize;
const ONE: i128 = 1e9;
const ONE: i128 = 1000000000;
#[allow(non_snake_case)]
#[derive(Deserialize, Default, Clone, Debug)]
@ -62,15 +61,16 @@ impl Into<OracleData> for IndexData {
pub struct Binance {
btc_usdt: IndexData,
usdc_usdt: IndexData,
eth_usdt: IndexData,
sol_usdt: IndexData,
usdc_usdt: IndexData,
doge_usdt: IndexData,
}
impl Binance {
// Fetch data from the Binance API
pub async fn fetch() -> std::result::Result<Binance, SwitchboardClientError> {
let symbols = ["BTCUSDT", "ETHUSDT", "SOLUSDT", "USDCUSDT"];
let symbols = ["BTCUSDT", "USDCUSDT", "ETHUSDT", "SOLUSDT", "DOGEUSDT"];
let tickers_1hr = reqwest::get(format!(
"https://api.binance.com/api/v3/ticker?symbols=[{}]&windowSize=1h",
@ -121,34 +121,38 @@ impl Binance {
Ok(Binance {
btc_usdt: data.get(0).unwrap().clone(),
eth_usdt: data.get(1).unwrap().clone(),
sol_usdt: data.get(2).unwrap().clone(),
usdc_usdt: data.get(3).unwrap().clone(),
usdc_usdt: data.get(1).unwrap().clone(),
eth_usdt: data.get(2).unwrap().clone(),
sol_usdt: data.get(3).unwrap().clone(),
doge_usdt: data.get(4).unwrap().clone(),
})
}
pub fn to_ixns(&self, runner: &FunctionRunner) -> Vec<Instruction> {
let btc_usdt: OracleData = self.btc_usdt.clone().into();
// let btc_usdc: OracleData = self.btc_usdc.clone().into();
let eth_usdt: OracleData = self.eth_usdt.clone().into();
let sol_usdt: OracleData = self.sol_usdt.clone().into();
let usdc_usdt: OracleData = self.usdc_usdt.clone().into();
let usdt_usdc: OracleData = OracleData {
oracle_timestamp: usdc_usdt.oracle_timestamp,
price: ONE.checked_div(usdc_usdt.price).unwrap(),
volume_1hr: usdc_usdt.volume_1hr,
volume_24hr: usdc_usdt.volume_24hr,
twap_1hr: ONE.checked_div(usdc_usdt.twap_1hr).unwrap(),
twap_24hr: ONE.checked_div(usdc_usdt.twap_24hr).unwrap(),
};
let rows: Vec<OracleDataWithTradingSymbol> = vec![
OracleDataWithTradingSymbol {
symbol: TradingSymbol::BTC,
data: self.btc_usdt.clone().into(),
},
OracleDataWithTradingSymbol {
symbol: TradingSymbol::USDC,
data: self.usdc_usdt.clone().into(),
},
OracleDataWithTradingSymbol {
symbol: TradingSymbol::ETH,
data: self.eth_usdt.clone().into(),
},
OracleDataWithTradingSymbol {
symbol: TradingSymbol::SOL,
data: self.sol_usdt.clone().into(),
},
OracleDataWithTradingSymbol {
symbol: TradingSymbol::DOGE,
data: self.doge_usdt.clone().into(),
},
];
let params = RefreshPricesParams {
btc: Some(btc_usdt),
eth: Some(eth_usdt),
sol: Some(sol_usdt),
usdt: Some(usdt_usdc),
usdc: Some(usdc_usdt),
};
let params = RefreshPricesParams { rows };
let (program_state_pubkey, _state_bump) =
Pubkey::find_program_address(&[b"BASICORACLE"], &PROGRAM_ID);

View File

@ -4,7 +4,8 @@ pub mod binance;
pub use binance::*;
pub use basic_oracle::{
self, OracleData, RefreshPrices, RefreshPricesParams, SwitchboardDecimal, ID as PROGRAM_ID,
self, OracleData, OracleDataWithTradingSymbol, RefreshPrices, RefreshPricesParams,
SwitchboardDecimal, TradingSymbol, ID as PROGRAM_ID,
};
#[tokio::main(worker_threads = 12)]

View File

@ -4,10 +4,10 @@ use crate::*;
pub struct RefreshPrices<'info> {
#[account(
seeds = [PROGRAM_SEED],
bump = program.load()?.bump,
bump = program_state.load()?.bump,
// constraint = program.load()?.is_valid_enclave(&quote.load()?.mr_enclave) @ BasicOracleError::InvalidMrEnclave
)]
pub program: AccountLoader<'info, MyProgramState>,
pub program_state: AccountLoader<'info, MyProgramState>,
#[account(
mut,
@ -34,7 +34,7 @@ pub struct RefreshPrices<'info> {
#[derive(Clone, AnchorSerialize, AnchorDeserialize)]
pub struct RefreshPricesParams {
pub symbols: Vec<OracleDataWithTradingSymbol>,
pub rows: Vec<OracleDataWithTradingSymbol>,
}
impl RefreshPrices<'_> {
@ -48,6 +48,7 @@ impl RefreshPrices<'_> {
pub fn actuate(ctx: &Context<Self>, params: &RefreshPricesParams) -> anchor_lang::Result<()> {
let oracle = &mut ctx.accounts.oracle.load_mut()?;
oracle.save_rows(&params.rows)?;
// for data in params.data

View File

@ -15,4 +15,6 @@ pub enum BasicOracleError {
InvalidMrEnclave,
#[msg("Switchboard QuoteAccount has an empty MrEnclave (invalid)")]
EmptySwitchboardQuote,
#[msg("Failed to find a valid trading symbol for this price")]
InvalidSymbol,
}

View File

@ -1,43 +1,41 @@
use crate::*;
use bytemuck::{Pod, Zeroable};
#[repr(u8)]
#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, AnchorSerialize, AnchorDeserialize)]
pub enum TradingSymbol {
#[default]
Unknown = 0,
BTC = 1,
USDT,
USDC,
ETH,
SOL,
DOGE,
USDC = 2,
ETH = 3,
SOL = 4,
DOGE = 5,
}
unsafe impl Pod for TradingSymbol {}
unsafe impl Zeroable for TradingSymbol {}
impl From<TradingSymbol> for u32 {
impl From<TradingSymbol> for u8 {
fn from(value: TradingSymbol) -> Self {
match value {
TradingSymbol::BTC => 1,
TradingSymbol::USDT => 2,
TradingSymbol::USDC => 3,
TradingSymbol::ETH => 4,
TradingSymbol::SOL => 5,
TradingSymbol::DOGE => 6,
TradingSymbol::USDC => 2,
TradingSymbol::ETH => 3,
TradingSymbol::SOL => 4,
TradingSymbol::DOGE => 5,
_ => 0,
}
}
}
impl From<u32> for TradingSymbol {
fn from(value: u32) -> Self {
impl From<u8> for TradingSymbol {
fn from(value: u8) -> Self {
match value {
1 => TradingSymbol::BTC,
2 => TradingSymbol::USDT,
3 => TradingSymbol::USDC,
4 => TradingSymbol::ETH,
5 => TradingSymbol::SOL,
6 => TradingSymbol::DOGE,
2 => TradingSymbol::USDC,
3 => TradingSymbol::ETH,
4 => TradingSymbol::SOL,
5 => TradingSymbol::DOGE,
_ => TradingSymbol::default(),
}
}
@ -59,7 +57,6 @@ pub struct OracleData {
pub volume_24hr: i128,
pub twap_1hr: i128,
pub twap_24hr: i128,
// pub reserved: [u8; 12], // makes 100 bytes exactly
}
#[derive(Copy, Clone, Default, AnchorSerialize, AnchorDeserialize)]
@ -89,10 +86,9 @@ impl OracleData {
pub struct MyOracleState {
pub bump: u8,
pub btc: OracleData,
pub usdc: OracleData,
pub eth: OracleData,
pub sol: OracleData,
pub usdt: OracleData,
pub usdc: OracleData,
pub doge: OracleData,
// can always re-allocate to add more
// pub reserved: [u8; 2400],
@ -102,3 +98,35 @@ impl Default for MyOracleState {
unsafe { std::mem::zeroed() }
}
}
impl MyOracleState {
pub fn save_rows(
&mut self,
rows: &Vec<OracleDataWithTradingSymbol>,
) -> anchor_lang::Result<()> {
for row in rows.iter() {
match row.symbol {
TradingSymbol::BTC => {
self.btc = row.data;
}
TradingSymbol::USDC => {
self.usdc = row.data;
}
TradingSymbol::ETH => {
self.eth = row.data;
}
TradingSymbol::SOL => {
self.sol = row.data;
}
TradingSymbol::DOGE => {
self.doge = row.data;
}
_ => {
msg!("no trading symbol found for {:?}", row.symbol);
// TODO: emit an event so we can detect and fix
}
}
}
Ok(())
}
}

View File

@ -21,7 +21,7 @@ no-entrypoint = []
cpi = ["no-entrypoint"]
[dependencies]
anchor-spl = "=0.28.0"
anchor-spl = "0.28.0"
solana-program = ">= 1.16, < 1.17"
solana-address-lookup-table-program = ">= 1.16, < 1.17"
rust_decimal = "^1"
@ -30,11 +30,11 @@ superslice = "1"
[target.'cfg(target_os = "solana")'.dependencies]
switchboard-common = { version = "0.5.3" }
anchor-lang = { version = "=0.28.0" }
anchor-lang = { version = "0.28.0" }
[target.'cfg(not(target_os = "solana"))'.dependencies]
switchboard-common = { version = "0.5.3", features = ["sgx"] }
anchor-client = { version = "=0.28.0" }
anchor-client = { version = "0.28.0" }
bincode = { version = "^1" }
sgx-quote = { version = "0.1.0" }
cron = { version = "0.12.0" }