Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat() : fetch strk price from oracle #368

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/starknet-js-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
fail-on-cache-miss: true
- name: Setup dev chain and run tests
run: |
./target/release/madara --name madara --base-path ../madara_db --rpc-port 9944 --rpc-cors "*" --rpc-external --devnet --preset devnet --gas-price 0 --blob-gas-price 0 --no-l1-sync &
./target/release/madara --name madara --base-path ../madara_db --rpc-port 9944 --rpc-cors "*" --rpc-external --devnet --preset devnet --gas-price 0 --blob-gas-price 0 --strk-gas-price 0 --strk-blob-gas-price 0 --no-l1-sync &
MADARA_PID=$!
while ! echo exit | nc localhost 9944; do sleep 1; done
cd tests/js_tests
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Next release

- feat: fetch eth/strk price and sync strk gas price
- feat(confg): added chain config template and fgw example
- feat(v0.8.0-rc0): starknet_subscribeNewHeads
- fix(rocksdb): update max open files opt
Expand Down
29 changes: 29 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ mp-receipt = { path = "crates/primitives/receipt", default-features = false }
mp-state-update = { path = "crates/primitives/state_update", default-features = false }
mp-utils = { path = "crates/primitives/utils", default-features = false }
mp-chain-config = { path = "crates/primitives/chain_config", default-features = false }
mp-oracle = { path = "crates/primitives/oracle", default-features = false }

# Madara client
mc-analytics = { path = "crates/client/analytics" }
Expand Down
1 change: 1 addition & 0 deletions crates/client/eth/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ starknet_api = { workspace = true }
# Other
alloy = { workspace = true }
anyhow = "1.0.75"
bigdecimal = "0.4.5"
bitvec = { workspace = true }
blockifier = { workspace = true }
futures = { workspace = true, default-features = true }
Expand Down
16 changes: 16 additions & 0 deletions crates/client/eth/src/l1_gas_price.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::client::EthereumClient;
use alloy::eips::BlockNumberOrTag;
use alloy::providers::Provider;
use anyhow::Context;
use bigdecimal::BigDecimal;
use mc_mempool::{GasPriceProvider, L1DataProvider};
use std::time::{Duration, UNIX_EPOCH};

Expand Down Expand Up @@ -67,6 +68,21 @@ async fn update_gas_price(eth_client: &EthereumClient, l1_gas_provider: GasPrice
l1_gas_provider.update_eth_l1_gas_price(*eth_gas_price);
l1_gas_provider.update_eth_l1_data_gas_price(avg_blob_base_fee);

// fetch eth/strk price and update
if let Some(oracle_provider) = &l1_gas_provider.oracle_provider {
let (eth_strk_price, decimals) =
oracle_provider.fetch_eth_strk_price().await.context("failed to retrieve ETH/STRK price")?;
let strk_gas_price = (BigDecimal::new((*eth_gas_price).into(), decimals.into())
/ BigDecimal::new(eth_strk_price.into(), decimals.into()))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if eth_strk_price price is 0 here there is a possible 0 division?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah you are right, added a check

.as_bigint_and_exponent();
let strk_data_gas_price = (BigDecimal::new(avg_blob_base_fee.into(), decimals.into())
/ BigDecimal::new(eth_strk_price.into(), decimals.into()))
.as_bigint_and_exponent();

l1_gas_provider.update_strk_l1_gas_price(strk_gas_price.0.to_str_radix(10).parse::<u128>()?);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would expect this not to overflow but would be great to ensure clean code

l1_gas_provider.update_strk_l1_data_gas_price(strk_data_gas_price.0.to_str_radix(10).parse::<u128>()?);
}

l1_gas_provider.update_last_update_timestamp();

// Update block number separately to avoid holding the lock for too long
Expand Down
3 changes: 3 additions & 0 deletions crates/client/mempool/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ mp-block.workspace = true
mp-chain-config.workspace = true
mp-class.workspace = true
mp-convert.workspace = true
mp-oracle.workspace = true
mp-receipt.workspace = true
mp-state-update.workspace = true
mp-transactions.workspace = true
Expand All @@ -58,6 +59,8 @@ starknet_api.workspace = true
anyhow.workspace = true
log.workspace = true
mockall = { workspace = true, optional = true }
reqwest.workspace = true
serde.workspace = true
thiserror.workspace = true
tokio.workspace = true

Expand Down
16 changes: 14 additions & 2 deletions crates/client/mempool/src/l1.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use mp_block::header::{GasPrices, L1DataAvailabilityMode};
use mp_oracle::Oracle;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::time::SystemTime;
Expand All @@ -11,6 +12,7 @@ pub struct GasPriceProvider {
data_gas_price_sync_enabled: Arc<AtomicBool>,
strk_gas_price_sync_enabled: Arc<AtomicBool>,
strk_data_gas_price_sync_enabled: Arc<AtomicBool>,
pub oracle_provider: Option<Arc<Oracle>>,
}

impl GasPriceProvider {
Expand All @@ -22,9 +24,19 @@ impl GasPriceProvider {
data_gas_price_sync_enabled: Arc::new(AtomicBool::new(true)),
strk_gas_price_sync_enabled: Arc::new(AtomicBool::new(true)),
strk_data_gas_price_sync_enabled: Arc::new(AtomicBool::new(true)),
oracle_provider: None,
}
}

pub fn is_oracle_needed(&self) -> bool {
self.strk_gas_price_sync_enabled.load(Ordering::Relaxed)
|| self.strk_data_gas_price_sync_enabled.load(Ordering::Relaxed)
}

pub fn set_oracle_provider(&mut self, oracle_provider: Oracle) {
self.oracle_provider = Some(Arc::new(oracle_provider));
}

pub fn set_gas_prices(&self, new_prices: GasPrices) {
self.update_eth_l1_gas_price(new_prices.eth_l1_gas_price);
self.update_strk_l1_gas_price(new_prices.strk_l1_gas_price);
Expand All @@ -41,11 +53,11 @@ impl GasPriceProvider {
}

pub fn set_strk_gas_price_sync_enabled(&self, enabled: bool) {
self.gas_price_sync_enabled.store(enabled, Ordering::Relaxed);
self.strk_gas_price_sync_enabled.store(enabled, Ordering::Relaxed);
}

pub fn set_strk_data_gas_price_sync_enabled(&self, enabled: bool) {
self.data_gas_price_sync_enabled.store(enabled, Ordering::Relaxed);
self.strk_data_gas_price_sync_enabled.store(enabled, Ordering::Relaxed);
}

pub fn update_last_update_timestamp(&self) {
Expand Down
1 change: 1 addition & 0 deletions crates/node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ mc-telemetry = { workspace = true }
mp-block = { workspace = true }
mp-chain-config = { workspace = true }
mp-convert = { workspace = true }
mp-oracle = { workspace = true }
mp-utils = { workspace = true }

# Starknet
Expand Down
8 changes: 8 additions & 0 deletions crates/node/src/cli/l1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ pub struct L1SyncParams {
#[clap(env = "MADARA_STRK_DATA_GAS_PRICE", long, alias = "strk-blob-gas-price")]
pub strk_blob_gas_price: Option<u64>,

/// Oracle API url.
#[clap(env = "ORACLE_URL", long, alias = "oracle-url")]
pub oracle_url: Option<Url>,

/// Oracle API key.
#[clap(env = "ORACLE_API_KEY", long, alias = "oracle-api-key")]
pub oracle_api_key: Option<String>,

/// Time in which the gas price worker will fetch the gas price.
#[clap(
env = "MADARA_GAS_PRICE_POLL",
Expand Down
15 changes: 14 additions & 1 deletion crates/node/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use mc_mempool::{GasPriceProvider, L1DataProvider, Mempool};
use mc_rpc::providers::{AddTransactionProvider, ForwardToProvider, MempoolAddTxProvider};
use mc_telemetry::{SysInfo, TelemetryService};
use mp_convert::ToFelt;
use mp_oracle::Oracle;
use mp_utils::service::{Service, ServiceGroup};
use service::{BlockProductionService, GatewayService, L1SyncService, RpcService, SyncService};
use starknet_providers::SequencerGatewayProvider;
Expand Down Expand Up @@ -90,7 +91,7 @@ async fn main() -> anyhow::Result<()> {
.context("Initializing importer service")?,
);

let l1_gas_setter = GasPriceProvider::new();
let mut l1_gas_setter = GasPriceProvider::new();

if let Some(fix_gas) = run_cmd.l1_sync_params.gas_price {
l1_gas_setter.update_eth_l1_gas_price(fix_gas as u128);
Expand All @@ -108,6 +109,18 @@ async fn main() -> anyhow::Result<()> {
l1_gas_setter.update_strk_l1_data_gas_price(strk_fix_blob_gas as u128);
l1_gas_setter.set_strk_data_gas_price_sync_enabled(false);
}
if let Some(ref oracle_url) = run_cmd.l1_sync_params.oracle_url {
if let Some(ref oracle_api_key) = run_cmd.l1_sync_params.oracle_api_key {
let oracle = Oracle::new("Pragma", oracle_url.to_string(), oracle_api_key.clone())?;
l1_gas_setter.set_oracle_provider(oracle);
}
}

if l1_gas_setter.is_oracle_needed() && l1_gas_setter.oracle_provider.is_none() {
log::error!("STRK gas is not fixed and oracle is not provided");
panic!();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would personally handle the case where we dont want the strk price by defaulting it

}

let l1_data_provider: Arc<dyn L1DataProvider> = Arc::new(l1_gas_setter.clone());

// declare mempool here so that it can be used to process l1->l2 messages in the l1 service
Expand Down
24 changes: 24 additions & 0 deletions crates/primitives/oracle/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
description = "Madara primitive for Oracles"
name = "mp-oracle"
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
version.workspace = true
homepage.workspace = true

[lints]
workspace = true

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

[dependencies]

# Other
anyhow = { workspace = true }
num-bigint = { workspace = true }
reqwest = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
63 changes: 63 additions & 0 deletions crates/primitives/oracle/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use anyhow::bail;
use serde::{Deserialize, Serialize};

mod pragma;

use pragma::*;

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "oracle_name", content = "config")]
pub enum Oracle {
Pragma(PragmaOracle),
}

impl Oracle {
pub fn new(oracle_name: &str, url: String, key: String) -> anyhow::Result<Self> {
match oracle_name {
"Pragma" => Ok(Oracle::Pragma(PragmaOracle::new(url, key))),
_ => bail!("Unknown Oracle name"),
}
}

pub fn set_base_url(&mut self, url: String) {
match self {
Oracle::Pragma(pragma_oracle) => pragma_oracle.api_url = url,
}
}

pub async fn fetch_eth_strk_price(&self) -> anyhow::Result<(u128, u32)> {
match self {
Oracle::Pragma(pragma_oracle) => pragma_oracle.fetch_eth_strk_price().await,
}
}

pub fn set_api_key(&mut self, key: String) {
match self {
Oracle::Pragma(pragma_oracle) => pragma_oracle.api_key = key,
}
}

pub fn get_fetch_url(&self, base: String, quote: String) -> String {
match self {
Oracle::Pragma(pragma_oracle) => pragma_oracle.get_fetch_url(base, quote),
}
}

pub fn get_api_key(&self) -> &String {
match self {
Oracle::Pragma(oracle) => &oracle.api_key,
}
}

pub fn is_in_bounds(&self, price: u128) -> bool {
match self {
Oracle::Pragma(oracle) => oracle.price_bounds.low <= price && price <= oracle.price_bounds.high,
}
}
}

impl Default for Oracle {
fn default() -> Self {
Self::Pragma(PragmaOracle::default())
}
}
Loading
Loading