Skip to content

Commit b8da46c

Browse files
committed
feat: add solution for ex10
1 parent 73709ca commit b8da46c

File tree

2 files changed

+47
-17
lines changed
  • exercises/ex10-offchain-worker/pallet-price-oracle/src

2 files changed

+47
-17
lines changed

exercises/ex10-offchain-worker/pallet-price-oracle/src/lib.rs

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,12 @@ pub mod pallet {
4747
#[pallet::hooks]
4848
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
4949
fn offchain_worker(_n: BlockNumberFor<T>) {
50-
// TODO: call `fetch_btc_price_and_send_unsigned_transaction` and log any error
50+
match Self::fetch_btc_price_and_send_unsigned_transaction() {
51+
Ok(_) => {},
52+
Err(e) => {
53+
log::error!("Failed to fetch and set BTC price: {e}");
54+
},
55+
};
5156
}
5257
}
5358

@@ -58,10 +63,11 @@ pub mod pallet {
5863
impl<T: Config> Pallet<T> {
5964
#[pallet::weight(0)]
6065
pub fn set_btc_price(origin: OriginFor<T>, btc_price: FixedI64) -> DispatchResult {
61-
// TODO:
62-
// - ensure origin is none
63-
// - set BTCPrice storage
64-
// - emit `BtcPriceSet` event
66+
ensure_none(origin)?;
67+
68+
BTCPrice::<T>::set(Some(btc_price));
69+
70+
Self::deposit_event(Event::<T>::BtcPriceSet(btc_price));
6571

6672
Ok(())
6773
}
@@ -77,10 +83,24 @@ pub mod pallet {
7783
/// here we make sure that some particular calls (the ones produced by offchain worker)
7884
/// are being whitelisted and marked as valid.
7985
fn validate_unsigned(source: TransactionSource, call: &Self::Call) -> TransactionValidity {
80-
// TODO: implemente some kind of validation
81-
// It should accept calls to `set_btc_price` and refuse any other
82-
83-
InvalidTransaction::Call.into()
86+
if source == TransactionSource::External {
87+
return InvalidTransaction::Call.into()
88+
}
89+
90+
const UNSIGNED_TXS_PRIORITY: TransactionPriority = u64::MAX;
91+
let valid_tx = |provide| {
92+
ValidTransaction::with_tag_prefix("price-oracle")
93+
.priority(UNSIGNED_TXS_PRIORITY)
94+
.and_provides([&provide])
95+
.longevity(1) // Only valid for this block
96+
.propagate(false) // Should not be gossiped
97+
.build()
98+
};
99+
100+
match call {
101+
Call::set_btc_price { .. } => valid_tx(b"set_btc_price".to_vec()),
102+
_ => InvalidTransaction::Call.into(),
103+
}
84104
}
85105
}
86106
}

exercises/ex10-offchain-worker/pallet-price-oracle/src/offchain_worker/mod.rs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,30 @@ struct CoinbaseResponseBody {
2222
}
2323

2424
pub(crate) fn fetch_btc_price() -> Result<FixedI64, OffchainWorkerError> {
25-
// TODO:
26-
// - do an http get request to `"https://api.coinbase.com/v2/prices/BTC-USD/buy`
27-
// - extract the price form the response body
28-
// - convert it to `FixedI64` before returning it
25+
let res = http::Request::get(&"https://api.coinbase.com/v2/prices/BTC-USD/buy")
26+
.send()?
27+
.wait()?;
28+
let body_bytes = res.body().collect::<Vec<u8>>();
29+
let body: CoinbaseResponseBody = serde_json::from_slice(&body_bytes)?;
2930

30-
Ok(Default::default())
31+
if &body.data.base != "BTC" || &body.data.currency != "USD" {
32+
return Err(OffchainWorkerError::WrongPair)
33+
}
34+
35+
let price: f64 = body.data.amount.parse().map_err(|e| OffchainWorkerError::ParsePrice(e))?;
36+
37+
Ok(f64_to_fixed_i64(price))
3138
}
3239

3340
impl<T: Config> Pallet<T> {
3441
pub(crate) fn fetch_btc_price_and_send_unsigned_transaction() -> Result<(), String> {
35-
// Todo: call `fetch_btc_price` and use the return to submit an unsigned transaction
36-
// containing a call to `set_btc_price`
42+
let btc_price = fetch_btc_price().map_err(|e| e.to_string())?;
3743

38-
Ok(())
44+
let call = Call::set_btc_price { btc_price };
45+
frame_system::offchain::SubmitTransaction::<T, Call<T>>::submit_unsigned_transaction(
46+
call.into(),
47+
)
48+
.map_err(|_| String::from("Failed to submit unsigned `set_btc_price` call"))
3949
}
4050
}
4151

0 commit comments

Comments
 (0)