Skip to content

Commit 7ff0601

Browse files
committed
Implement a minimal BTC/USD price feed feature
1 parent 90b9501 commit 7ff0601

File tree

3 files changed

+69
-0
lines changed

3 files changed

+69
-0
lines changed

crates/cli-client/src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,7 @@ pub enum Error {
5555

5656
#[error("Taproot pubkey generation error: {0}")]
5757
TaprootPubkeyGen(#[from] contracts::error::TaprootPubkeyGenError),
58+
59+
#[error("Price feed error: {0}")]
60+
PriceFeed(#[from] crate::price_fetcher::PriceFetcherError),
5861
}

crates/cli-client/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ mod explorer;
77
mod fee;
88
mod logging;
99
mod metadata;
10+
mod price_fetcher;
1011
mod signing;
1112
mod sync;
1213
mod wallet;
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
use crate::error::Error;
2+
use serde::Deserialize;
3+
use thiserror::Error;
4+
5+
#[derive(Error, Debug)]
6+
pub enum PriceFetcherError {
7+
#[error("Request error: {0}")]
8+
Request(#[from] minreq::Error),
9+
#[error("Rate limit exceeded (429)")]
10+
RateLimit,
11+
#[error("Response status error: {0}")]
12+
Status(i32),
13+
#[error("Parse error: {0}")]
14+
Parse(String),
15+
}
16+
17+
#[derive(Deserialize)]
18+
struct BitcoinResponse {
19+
bitcoin: BitcoinPrice,
20+
}
21+
22+
#[derive(Deserialize)]
23+
struct BitcoinPrice {
24+
usd: f64,
25+
}
26+
27+
pub trait PriceFetcher {
28+
fn fetch_price(&self) -> Result<f64, Error>;
29+
}
30+
31+
pub struct CoingeckoPriceFetcher;
32+
33+
impl CoingeckoPriceFetcher {
34+
const URL: &'static str = "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd&precision=8";
35+
const TIMEOUT_SECS: u64 = 5;
36+
37+
pub fn new() -> Self {
38+
Self {}
39+
}
40+
}
41+
42+
impl PriceFetcher for CoingeckoPriceFetcher {
43+
fn fetch_price(&self) -> Result<f64, Error> {
44+
let resp = minreq::get(Self::URL)
45+
.with_header("User-Agent", "simplicity-dex/1.0")
46+
.with_timeout(Self::TIMEOUT_SECS)
47+
.send()
48+
.map_err(PriceFetcherError::from)?;
49+
50+
let price = match resp.status_code {
51+
200 => resp
52+
.json::<BitcoinResponse>()
53+
.map(|data| data.bitcoin.usd)
54+
.map_err(|e| PriceFetcherError::Parse(e.to_string()))?,
55+
429 => return Err(PriceFetcherError::RateLimit.into()),
56+
status => return Err(PriceFetcherError::Status(status).into()),
57+
};
58+
59+
Ok(price)
60+
}
61+
}
62+
63+
pub fn fetch_btc_usd_price<T: PriceFetcher>(fetcher: &T) -> Result<f64, Error> {
64+
fetcher.fetch_price()
65+
}

0 commit comments

Comments
 (0)