use std::env;
use alloy::primitives::Address;
use crate::global::shared_data::SharedData;
lazy_static::lazy_static! {
pub(crate) static ref DB_VERSION_KEY: String = "DB_VERSION".to_string();
pub(crate) static ref DB_VERSION: u32 = 4;
pub(crate) static ref PROTOCOL_VERSION_KEY: String = "PROTOCOL_VERSION".to_string();
pub(crate) static ref PROTOCOL_VERSION: u32 = 1;
static ref DB_PATH_KEY: String = "BRC20_PROG_DB_PATH".to_string();
static ref DB_PATH_DEFAULT: String = "target/db".to_string();
static ref BRC20_PROG_RPC_SERVER_ENABLE_AUTH_KEY: String = "BRC20_PROG_RPC_SERVER_ENABLE_AUTH".to_string();
static ref BRC20_PROG_RPC_SERVER_ENABLE_AUTH_DEFAULT: bool = false;
static ref BRC20_PROG_RPC_SERVER_USER_KEY: String = "BRC20_PROG_RPC_SERVER_USER".to_string();
static ref BRC20_PROG_RPC_SERVER_PASSWORD_KEY: String = "BRC20_PROG_RPC_SERVER_PASSWORD".to_string();
static ref BRC20_PROG_RPC_SERVER_URL_KEY: String = "BRC20_PROG_RPC_SERVER_URL".to_string();
static ref BRC20_PROG_RPC_SERVER_URL_DEFAULT: String = "127.0.0.1:18545".to_string();
static ref BRC20_PROG_BALANCE_SERVER_URL_KEY: String = "BRC20_PROG_BALANCE_SERVER_URL".to_string();
static ref BRC20_PROG_BALANCE_SERVER_URL_DEFAULT: String = "http://localhost:18546".to_string();
static ref FAIL_ON_BRC20_BALANCE_SERVER_ERROR_KEY: String = "FAIL_ON_BRC20_BALANCE_SERVER_ERROR".to_string();
static ref FAIL_ON_BRC20_BALANCE_SERVER_ERROR_DEFAULT: bool = true;
static ref FAIL_ON_BITCOIN_RPC_ERROR_KEY: String = "FAIL_ON_BITCOIN_RPC_ERROR".to_string();
static ref FAIL_ON_BITCOIN_RPC_ERROR_DEFAULT: bool = true;
pub(crate) static ref EVM_RECORD_TRACES_KEY: String = "EVM_RECORD_TRACES".to_string();
static ref EVM_RECORD_TRACES_DEFAULT: bool = false;
static ref EVM_CALL_GAS_LIMIT_KEY: String = "EVM_CALL_GAS_LIMIT".to_string();
static ref EVM_CALL_GAS_LIMIT: u64 = 1_000_000_000;
static ref BITCOIN_RPC_URL_KEY: String = "BITCOIN_RPC_URL".to_string();
static ref BITCOIN_RPC_URL_DEFAULT_SIGNET: String = "http://localhost:38332".to_string();
static ref BITCOIN_RPC_USER_KEY: String = "BITCOIN_RPC_USER".to_string();
static ref BITCOIN_RPC_PASSWORD_KEY: String = "BITCOIN_RPC_PASSWORD".to_string();
pub(crate) static ref BITCOIN_RPC_NETWORK_KEY: String = "BITCOIN_RPC_NETWORK".to_string();
static ref BITCOIN_RPC_NETWORK_DEFAULT_SIGNET: String = "signet".to_string();
pub(crate) static ref BRC20_TRANSACT_ENDPOINT_ENABLED_KEY: String = "BRC20_TRANSACT_ENDPOINT_ENABLED".to_string();
static ref BRC20_TRANSACT_ENDPOINT_ENABLED_DEFAULT: bool = false;
pub static ref CARGO_PKG_VERSION: String = {
let version = env!("CARGO_PKG_VERSION");
if version.is_empty() {
"0.0.0".to_string()
} else {
version.to_string()
}
};
pub static ref INDEXER_ADDRESS: Address = "0x0000000000000000000000000000000000003Ca6".parse().expect("Failed to parse indexer address");
pub static ref INDEXER_ADDRESS_STRING: String = INDEXER_ADDRESS.to_string();
pub static ref INVALID_ADDRESS: Address = "0x000000000000000000000000000000000000dead".parse().expect("Failed to parse invalid address");
}
lazy_static::lazy_static! {
pub static ref CONFIG: SharedData<Brc20ProgConfig> = SharedData::new(Brc20ProgConfig::from_env());
}
pub const MAX_REORG_HISTORY_SIZE: u64 = 10; pub const MAX_BLOCK_SIZE: u64 = 4 * 1024 * 1024; pub const GAS_PER_BYTE: u64 = 12000;
pub const GAS_PER_BITCOIN_RPC_CALL: u64 = 400000; pub const GAS_PER_BRC20_BALANCE_CALL: u64 = 200000; pub const GAS_PER_BIP_322_VERIFY: u64 = 20000; pub const GAS_PER_LOCKED_PKSCRIPT: u64 = 20000;
pub const CHAIN_ID: u64 = 0x4252433230;
pub const CHAIN_ID_STRING: &str = "0x4252433230";
pub const CALLDATA_LIMIT: usize = 1024 * 1024;
#[derive(Clone, Debug)]
pub struct Brc20ProgConfig {
pub brc20_prog_rpc_server_url: String,
pub brc20_prog_rpc_server_enable_auth: bool,
pub brc20_prog_rpc_server_user: Option<String>,
pub brc20_prog_rpc_server_password: Option<String>,
pub brc20_balance_server_url: String,
pub evm_record_traces: bool,
pub evm_call_gas_limit: u64,
pub bitcoin_rpc_url: String,
pub bitcoin_rpc_user: String,
pub bitcoin_rpc_password: String,
pub bitcoin_rpc_network: String,
pub fail_on_brc20_balance_server_error: bool,
pub fail_on_bitcoin_rpc_error: bool,
pub db_path: String,
pub brc20_transact_endpoint_enabled: bool,
}
impl Default for Brc20ProgConfig {
fn default() -> Self {
Self::from_env()
}
}
impl Brc20ProgConfig {
pub fn new(
brc20_prog_rpc_server_url: String,
brc20_prog_rpc_server_enable_auth: bool,
brc20_prog_rpc_server_user: Option<String>,
brc20_prog_rpc_server_password: Option<String>,
brc20_balance_server_url: String,
evm_record_traces: bool,
evm_call_gas_limit: u64,
bitcoin_rpc_url: String,
bitcoin_rpc_user: String,
bitcoin_rpc_password: String,
bitcoin_rpc_network: String,
fail_on_brc20_balance_server_error: bool,
fail_on_bitcoin_rpc_error: bool,
db_path: String,
brc20_transact_endpoint_enabled: bool,
) -> Self {
Self {
brc20_prog_rpc_server_url,
brc20_prog_rpc_server_enable_auth,
brc20_prog_rpc_server_user,
brc20_prog_rpc_server_password,
brc20_balance_server_url,
evm_record_traces,
evm_call_gas_limit,
bitcoin_rpc_url,
bitcoin_rpc_user,
bitcoin_rpc_password,
bitcoin_rpc_network,
fail_on_brc20_balance_server_error,
fail_on_bitcoin_rpc_error,
db_path,
brc20_transact_endpoint_enabled,
}
}
pub fn from_env() -> Self {
Self {
brc20_prog_rpc_server_url: env::var(&*BRC20_PROG_RPC_SERVER_URL_KEY)
.unwrap_or(BRC20_PROG_RPC_SERVER_URL_DEFAULT.clone()),
brc20_prog_rpc_server_enable_auth: env::var(&*BRC20_PROG_RPC_SERVER_ENABLE_AUTH_KEY)
.map(|x| x == "true")
.unwrap_or(*BRC20_PROG_RPC_SERVER_ENABLE_AUTH_DEFAULT),
brc20_prog_rpc_server_user: env::var(&*BRC20_PROG_RPC_SERVER_USER_KEY).ok(),
brc20_prog_rpc_server_password: env::var(&*BRC20_PROG_RPC_SERVER_PASSWORD_KEY).ok(),
brc20_balance_server_url: env::var(&*BRC20_PROG_BALANCE_SERVER_URL_KEY)
.unwrap_or(BRC20_PROG_BALANCE_SERVER_URL_DEFAULT.clone()),
evm_record_traces: env::var(&*EVM_RECORD_TRACES_KEY)
.map(|x| x == "true")
.unwrap_or(*EVM_RECORD_TRACES_DEFAULT),
evm_call_gas_limit: env::var(&*EVM_CALL_GAS_LIMIT_KEY)
.map(|x| x.parse::<u64>().unwrap_or(*EVM_CALL_GAS_LIMIT))
.unwrap_or(*EVM_CALL_GAS_LIMIT),
bitcoin_rpc_url: env::var(&*BITCOIN_RPC_URL_KEY)
.unwrap_or(BITCOIN_RPC_URL_DEFAULT_SIGNET.clone()),
bitcoin_rpc_user: env::var(&*BITCOIN_RPC_USER_KEY).unwrap_or(Default::default()),
bitcoin_rpc_password: env::var(&*BITCOIN_RPC_PASSWORD_KEY)
.unwrap_or(Default::default()),
bitcoin_rpc_network: env::var(&*BITCOIN_RPC_NETWORK_KEY)
.unwrap_or("signet".to_string()),
fail_on_brc20_balance_server_error: env::var(&*FAIL_ON_BRC20_BALANCE_SERVER_ERROR_KEY)
.map(|x| x == "true")
.unwrap_or(*FAIL_ON_BRC20_BALANCE_SERVER_ERROR_DEFAULT),
fail_on_bitcoin_rpc_error: env::var(&*FAIL_ON_BITCOIN_RPC_ERROR_KEY)
.map(|x| x == "true")
.unwrap_or(*FAIL_ON_BITCOIN_RPC_ERROR_DEFAULT),
db_path: env::var(&*DB_PATH_KEY).unwrap_or(DB_PATH_DEFAULT.clone()),
brc20_transact_endpoint_enabled: env::var(&*BRC20_TRANSACT_ENDPOINT_ENABLED_KEY)
.map(|x| x == "true")
.unwrap_or(*BRC20_TRANSACT_ENDPOINT_ENABLED_DEFAULT),
}
}
}
#[cfg(feature = "server")]
pub fn validate_config(config: &Brc20ProgConfig) -> Result<(), Box<dyn std::error::Error>> {
if config.brc20_prog_rpc_server_enable_auth
&& (config.brc20_prog_rpc_server_user.is_none()
|| config.brc20_prog_rpc_server_password.is_none())
{
return Err("Authentication is enabled but no username or password is set".into());
}
if config.brc20_prog_rpc_server_url.is_empty() {
return Err("RPC server URL is empty".into());
}
if config.brc20_balance_server_url.is_empty() && config.fail_on_brc20_balance_server_error {
return Err("BRC20 balance server URL is empty".into());
}
if !config.brc20_balance_server_url.starts_with("http://")
&& !config.brc20_balance_server_url.starts_with("https://")
&& config.fail_on_brc20_balance_server_error
{
return Err("BRC20 balance server URL must start with http:// or https://".into());
}
if config.bitcoin_rpc_url.is_empty() && config.fail_on_bitcoin_rpc_error {
return Err("Bitcoin RPC URL is empty".into());
}
if config.bitcoin_rpc_user.is_empty() && config.fail_on_bitcoin_rpc_error {
return Err("Bitcoin RPC user is empty".into());
}
if config.bitcoin_rpc_password.is_empty() && config.fail_on_bitcoin_rpc_error {
return Err("Bitcoin RPC password is empty".into());
}
if config.bitcoin_rpc_network.is_empty() && config.fail_on_bitcoin_rpc_error {
return Err("Bitcoin RPC network is empty".into());
}
Ok(())
}