521 lines
22 KiB
Rust
521 lines
22 KiB
Rust
// file: crates/ksp-config-lib/src/offchain_transport.rs
|
|
// version: 2
|
|
|
|
//! Adapter from Config-owned Off-chain Transport documents to the provider-agnostic market-price runtime service.
|
|
|
|
/// Effective standard Off-chain Transport configuration resolved from Config.
|
|
pub struct ResolvedOffchainTransportConfig {
|
|
effective: crate::ResolvedConfigJson,
|
|
file_id: crate::ConfigFileId,
|
|
profile_id: String,
|
|
selection_source: crate::ConfigProfileSelectionSource,
|
|
service: ksp_offchain_transport_lib::MarketPriceService,
|
|
source_path: std::path::PathBuf,
|
|
}
|
|
|
|
impl crate::ResolvedOffchainTransportConfig {
|
|
/// Returns the detailed environment-resolved effective Config view with secret-safe diagnostics.
|
|
#[must_use]
|
|
pub const fn effective(&self) -> &crate::ResolvedConfigJson {
|
|
return &self.effective;
|
|
}
|
|
|
|
/// Returns the logical Config document identifier used by this runtime configuration.
|
|
#[must_use]
|
|
pub const fn file_id(&self) -> &crate::ConfigFileId {
|
|
return &self.file_id;
|
|
}
|
|
|
|
/// Returns the selected standard Off-chain Transport profile identifier.
|
|
#[must_use]
|
|
pub fn profile_id(&self) -> &str {
|
|
return self.profile_id.as_str();
|
|
}
|
|
|
|
/// Returns the source that selected the standard Off-chain Transport profile.
|
|
#[must_use]
|
|
pub const fn selection_source(&self) -> crate::ConfigProfileSelectionSource {
|
|
return self.selection_source;
|
|
}
|
|
|
|
/// Returns the provider-agnostic market-price runtime service.
|
|
#[must_use]
|
|
pub const fn service(&self) -> &ksp_offchain_transport_lib::MarketPriceService {
|
|
return &self.service;
|
|
}
|
|
|
|
/// Returns the physical source Config document path.
|
|
#[must_use]
|
|
pub fn source_path(&self) -> &std::path::Path {
|
|
return self.source_path.as_path();
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Debug for crate::ResolvedOffchainTransportConfig {
|
|
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
let registry = self.service.registry();
|
|
return formatter
|
|
.debug_struct("ResolvedOffchainTransportConfig")
|
|
.field("effective", &self.effective)
|
|
.field("file_id", &self.file_id)
|
|
.field("profile_id", &self.profile_id)
|
|
.field("provider_registry", ®istry)
|
|
.field("selection_source", &self.selection_source)
|
|
.field("source_path", &self.source_path)
|
|
.finish();
|
|
}
|
|
}
|
|
|
|
impl crate::ConfigDocumentEngine {
|
|
/// Loads the standard Off-chain Transport document and maps one profile to the generic market-price runtime service.
|
|
pub fn load_resolved_offchain_transport_config(
|
|
&self,
|
|
requested_profile: std::option::Option<&str>,
|
|
environment: &crate::ConfigEnvironment,
|
|
) -> ksp_core_lib::Result<crate::ResolvedOffchainTransportConfig> {
|
|
let file_id = crate::ConfigFileId::new(crate::FILE_ID_STD_OFFCHAIN_TRANSPORT);
|
|
let file_id = match file_id {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let profile = self.load_resolved_profile(&file_id, requested_profile);
|
|
let profile = match profile {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
return resolve_offchain_transport_profile(&profile, environment);
|
|
}
|
|
|
|
/// Maps an already resolved standard Off-chain Transport profile to the runtime service while preserving composite selection provenance.
|
|
pub fn resolve_offchain_transport_config_profile(
|
|
&self,
|
|
profile: &crate::ResolvedConfigProfile,
|
|
environment: &crate::ConfigEnvironment,
|
|
) -> ksp_core_lib::Result<crate::ResolvedOffchainTransportConfig> {
|
|
if profile.file_id().as_str() != crate::FILE_ID_STD_OFFCHAIN_TRANSPORT {
|
|
return std::result::Result::Err(effective_error(profile, "resolved Config profile does not reference the standard Off-chain Transport document"));
|
|
}
|
|
let descriptor = self.registry().descriptor(profile.file_id());
|
|
if let std::result::Result::Err(error) = descriptor {
|
|
return std::result::Result::Err(error);
|
|
}
|
|
return resolve_offchain_transport_profile(profile, environment);
|
|
}
|
|
}
|
|
|
|
#[derive(serde::Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
struct EffectiveOffchainTransportSource {
|
|
format_version: u32,
|
|
market_price: EffectiveMarketPriceSource,
|
|
profile_id: String,
|
|
}
|
|
|
|
#[derive(serde::Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
struct EffectiveMarketPriceSource {
|
|
birdeye: EffectiveBirdeyeSource,
|
|
coinbase_exchange: EffectiveEnabledSource,
|
|
coingecko: EffectiveCoinGeckoSource,
|
|
coinmarketcap: EffectiveCoinMarketCapSource,
|
|
coinpaprika: EffectiveEnabledSource,
|
|
dexscreener: EffectiveDexScreenerSource,
|
|
jupiter: EffectiveJupiterSource,
|
|
kraken: EffectiveEnabledSource,
|
|
}
|
|
|
|
#[derive(serde::Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
struct EffectiveEnabledSource {
|
|
enabled: bool,
|
|
}
|
|
|
|
#[derive(serde::Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
struct EffectiveBirdeyeSource {
|
|
api_key: std::option::Option<String>,
|
|
enabled: bool,
|
|
}
|
|
|
|
#[derive(serde::Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
struct EffectiveCoinGeckoSource {
|
|
access_mode: ksp_offchain_transport_lib::MarketPriceCoinGeckoAccessMode,
|
|
api_key: std::option::Option<String>,
|
|
enabled: bool,
|
|
}
|
|
|
|
#[derive(serde::Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
struct EffectiveCoinMarketCapSource {
|
|
access_mode: ksp_offchain_transport_lib::MarketPriceCoinMarketCapAccessMode,
|
|
api_key: std::option::Option<String>,
|
|
enabled: bool,
|
|
}
|
|
|
|
#[derive(serde::Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
struct EffectiveDexScreenerSource {
|
|
enabled: bool,
|
|
sol_usd_pair_address: std::option::Option<String>,
|
|
}
|
|
|
|
#[derive(serde::Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
struct EffectiveJupiterSource {
|
|
access_mode: ksp_offchain_transport_lib::MarketPriceJupiterAccessMode,
|
|
api_key: std::option::Option<String>,
|
|
enabled: bool,
|
|
}
|
|
|
|
fn resolve_offchain_transport_profile(
|
|
profile: &crate::ResolvedConfigProfile,
|
|
environment: &crate::ConfigEnvironment,
|
|
) -> ksp_core_lib::Result<crate::ResolvedOffchainTransportConfig> {
|
|
ksp_logging_lib::trace!(
|
|
target: crate::TRACING_TARGET,
|
|
profile_id = profile.profile_id(),
|
|
"mapping standard Off-chain Transport Config profile"
|
|
);
|
|
let effective = profile.resolve_effective_environment_detailed(environment);
|
|
let effective = match effective {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let source = serde_json::from_value::<EffectiveOffchainTransportSource>(effective.value().clone());
|
|
let source = match source {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => {
|
|
return std::result::Result::Err(
|
|
effective_error(profile, "effective Off-chain Transport Config cannot be decoded into the runtime adapter contract").with_source(error),
|
|
);
|
|
},
|
|
};
|
|
if source.format_version != 1 {
|
|
return std::result::Result::Err(effective_error(profile, "effective Off-chain Transport format_version is unsupported"));
|
|
}
|
|
if source.profile_id != profile.profile_id() {
|
|
return std::result::Result::Err(effective_error(profile, "effective Off-chain Transport profile_id does not match the selected profile"));
|
|
}
|
|
let setups = map_market_price_setups(source.market_price, &effective, profile);
|
|
let setups = match setups {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let service = ksp_offchain_transport_lib::MarketPriceService::new(setups);
|
|
let service = match service {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => {
|
|
return std::result::Result::Err(offchain_contract_error(
|
|
profile,
|
|
"effective market-price providers fail the Off-chain Transport runtime contract",
|
|
&error,
|
|
));
|
|
},
|
|
};
|
|
let provider_count = service.registry().entries().len();
|
|
ksp_logging_lib::debug!(
|
|
target: crate::TRACING_TARGET,
|
|
profile_id = profile.profile_id(),
|
|
format_version = source.format_version,
|
|
provider_count,
|
|
"mapped standard Off-chain Transport Config to market-price service"
|
|
);
|
|
return std::result::Result::Ok(crate::ResolvedOffchainTransportConfig {
|
|
effective,
|
|
file_id: profile.file_id().clone(),
|
|
profile_id: profile.profile_id().to_owned(),
|
|
selection_source: profile.selection_source(),
|
|
service,
|
|
source_path: profile.path().to_path_buf(),
|
|
});
|
|
}
|
|
|
|
fn map_market_price_setups(
|
|
source: EffectiveMarketPriceSource,
|
|
effective: &crate::ResolvedConfigJson,
|
|
profile: &crate::ResolvedConfigProfile,
|
|
) -> ksp_core_lib::Result<std::vec::Vec<ksp_offchain_transport_lib::MarketPriceProviderSetup>> {
|
|
let birdeye = map_birdeye(source.birdeye, effective, profile);
|
|
let birdeye = match birdeye {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let coinbase_exchange = ksp_offchain_transport_lib::MarketPriceCoinbaseExchangeSettings::new(source.coinbase_exchange.enabled);
|
|
let coinbase_exchange = match coinbase_exchange {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(provider_contract_error(profile, "coinbase_exchange", &error)),
|
|
};
|
|
let coingecko = map_coingecko(source.coingecko, effective, profile);
|
|
let coingecko = match coingecko {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let coinmarketcap = map_coinmarketcap(source.coinmarketcap, effective, profile);
|
|
let coinmarketcap = match coinmarketcap {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let coinpaprika = ksp_offchain_transport_lib::MarketPriceCoinPaprikaSettings::new(source.coinpaprika.enabled);
|
|
let coinpaprika = match coinpaprika {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(provider_contract_error(profile, "coinpaprika", &error)),
|
|
};
|
|
let dexscreener = map_dexscreener(source.dexscreener, effective, profile);
|
|
let dexscreener = match dexscreener {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let jupiter = map_jupiter(source.jupiter, effective, profile);
|
|
let jupiter = match jupiter {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let kraken = ksp_offchain_transport_lib::MarketPriceKrakenSettings::new(source.kraken.enabled);
|
|
let kraken = match kraken {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(provider_contract_error(profile, "kraken", &error)),
|
|
};
|
|
return std::result::Result::Ok(std::vec![
|
|
ksp_offchain_transport_lib::MarketPriceProviderSetup::Birdeye(birdeye),
|
|
ksp_offchain_transport_lib::MarketPriceProviderSetup::CoinbaseExchange(coinbase_exchange),
|
|
ksp_offchain_transport_lib::MarketPriceProviderSetup::CoinGecko(coingecko),
|
|
ksp_offchain_transport_lib::MarketPriceProviderSetup::CoinMarketCap(coinmarketcap),
|
|
ksp_offchain_transport_lib::MarketPriceProviderSetup::CoinPaprika(coinpaprika),
|
|
ksp_offchain_transport_lib::MarketPriceProviderSetup::DexScreener(dexscreener),
|
|
ksp_offchain_transport_lib::MarketPriceProviderSetup::Jupiter(jupiter),
|
|
ksp_offchain_transport_lib::MarketPriceProviderSetup::Kraken(kraken),
|
|
]);
|
|
}
|
|
|
|
fn map_birdeye(
|
|
source: EffectiveBirdeyeSource,
|
|
effective: &crate::ResolvedConfigJson,
|
|
profile: &crate::ResolvedConfigProfile,
|
|
) -> ksp_core_lib::Result<ksp_offchain_transport_lib::MarketPriceBirdeyeSettings> {
|
|
if source.api_key.is_some()
|
|
&& let std::result::Result::Err(error) = validate_secret_field_provenance(effective, "/market_price/birdeye/api_key", profile, "birdeye")
|
|
{
|
|
return std::result::Result::Err(error);
|
|
}
|
|
let settings = ksp_offchain_transport_lib::MarketPriceBirdeyeSettings::new(source.enabled, source.api_key);
|
|
return match settings {
|
|
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
|
std::result::Result::Err(error) => std::result::Result::Err(provider_contract_error(profile, "birdeye", &error)),
|
|
};
|
|
}
|
|
|
|
fn map_coingecko(
|
|
source: EffectiveCoinGeckoSource,
|
|
effective: &crate::ResolvedConfigJson,
|
|
profile: &crate::ResolvedConfigProfile,
|
|
) -> ksp_core_lib::Result<ksp_offchain_transport_lib::MarketPriceCoinGeckoSettings> {
|
|
if source.api_key.is_some()
|
|
&& let std::result::Result::Err(error) = validate_secret_field_provenance(effective, "/market_price/coingecko/api_key", profile, "coingecko")
|
|
{
|
|
return std::result::Result::Err(error);
|
|
}
|
|
let settings = match source.access_mode {
|
|
ksp_offchain_transport_lib::MarketPriceCoinGeckoAccessMode::Demo => {
|
|
ksp_offchain_transport_lib::MarketPriceCoinGeckoSettings::demo(source.enabled, source.api_key)
|
|
},
|
|
ksp_offchain_transport_lib::MarketPriceCoinGeckoAccessMode::Keyless => {
|
|
ksp_offchain_transport_lib::MarketPriceCoinGeckoSettings::keyless(source.enabled)
|
|
},
|
|
_ => {
|
|
return std::result::Result::Err(
|
|
effective_error(profile, "Off-chain Transport CoinGecko access mode is not supported by this Config adapter")
|
|
.with_context("provider", "coingecko")
|
|
.with_context("field", "access_mode"),
|
|
);
|
|
},
|
|
};
|
|
return match settings {
|
|
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
|
std::result::Result::Err(error) => std::result::Result::Err(provider_contract_error(profile, "coingecko", &error)),
|
|
};
|
|
}
|
|
|
|
fn map_coinmarketcap(
|
|
source: EffectiveCoinMarketCapSource,
|
|
effective: &crate::ResolvedConfigJson,
|
|
profile: &crate::ResolvedConfigProfile,
|
|
) -> ksp_core_lib::Result<ksp_offchain_transport_lib::MarketPriceCoinMarketCapSettings> {
|
|
if source.api_key.is_some()
|
|
&& let std::result::Result::Err(error) = validate_secret_field_provenance(effective, "/market_price/coinmarketcap/api_key", profile, "coinmarketcap")
|
|
{
|
|
return std::result::Result::Err(error);
|
|
}
|
|
let settings = match source.access_mode {
|
|
ksp_offchain_transport_lib::MarketPriceCoinMarketCapAccessMode::Basic => {
|
|
ksp_offchain_transport_lib::MarketPriceCoinMarketCapSettings::basic(source.enabled, source.api_key)
|
|
},
|
|
ksp_offchain_transport_lib::MarketPriceCoinMarketCapAccessMode::Keyless => {
|
|
ksp_offchain_transport_lib::MarketPriceCoinMarketCapSettings::keyless(source.enabled)
|
|
},
|
|
_ => {
|
|
return std::result::Result::Err(
|
|
effective_error(profile, "Off-chain Transport CoinMarketCap access mode is not supported by this Config adapter")
|
|
.with_context("provider", "coinmarketcap")
|
|
.with_context("field", "access_mode"),
|
|
);
|
|
},
|
|
};
|
|
return match settings {
|
|
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
|
std::result::Result::Err(error) => std::result::Result::Err(provider_contract_error(profile, "coinmarketcap", &error)),
|
|
};
|
|
}
|
|
|
|
fn map_dexscreener(
|
|
source: EffectiveDexScreenerSource,
|
|
effective: &crate::ResolvedConfigJson,
|
|
profile: &crate::ResolvedConfigProfile,
|
|
) -> ksp_core_lib::Result<ksp_offchain_transport_lib::MarketPriceDexScreenerSettings> {
|
|
if source.sol_usd_pair_address.is_some()
|
|
&& let std::result::Result::Err(error) =
|
|
validate_public_field_provenance(effective, "/market_price/dexscreener/sol_usd_pair_address", profile, "dexscreener")
|
|
{
|
|
return std::result::Result::Err(error);
|
|
}
|
|
let settings = ksp_offchain_transport_lib::MarketPriceDexScreenerSettings::new(source.enabled, source.sol_usd_pair_address);
|
|
return match settings {
|
|
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
|
std::result::Result::Err(error) => std::result::Result::Err(provider_contract_error(profile, "dexscreener", &error)),
|
|
};
|
|
}
|
|
|
|
fn map_jupiter(
|
|
source: EffectiveJupiterSource,
|
|
effective: &crate::ResolvedConfigJson,
|
|
profile: &crate::ResolvedConfigProfile,
|
|
) -> ksp_core_lib::Result<ksp_offchain_transport_lib::MarketPriceJupiterSettings> {
|
|
if source.api_key.is_some()
|
|
&& let std::result::Result::Err(error) = validate_secret_field_provenance(effective, "/market_price/jupiter/api_key", profile, "jupiter")
|
|
{
|
|
return std::result::Result::Err(error);
|
|
}
|
|
let settings = match source.access_mode {
|
|
ksp_offchain_transport_lib::MarketPriceJupiterAccessMode::Free => {
|
|
ksp_offchain_transport_lib::MarketPriceJupiterSettings::free(source.enabled, source.api_key)
|
|
},
|
|
ksp_offchain_transport_lib::MarketPriceJupiterAccessMode::Keyless => ksp_offchain_transport_lib::MarketPriceJupiterSettings::keyless(source.enabled),
|
|
_ => {
|
|
return std::result::Result::Err(
|
|
effective_error(profile, "Off-chain Transport Jupiter access mode is not supported by this Config adapter")
|
|
.with_context("provider", "jupiter")
|
|
.with_context("field", "access_mode"),
|
|
);
|
|
},
|
|
};
|
|
return match settings {
|
|
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
|
std::result::Result::Err(error) => std::result::Result::Err(provider_contract_error(profile, "jupiter", &error)),
|
|
};
|
|
}
|
|
|
|
fn validate_secret_field_provenance(
|
|
effective: &crate::ResolvedConfigJson,
|
|
pointer: &str,
|
|
profile: &crate::ResolvedConfigProfile,
|
|
provider: &'static str,
|
|
) -> ksp_core_lib::Result<()> {
|
|
let provenance = match effective.provenance_at(pointer) {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => {
|
|
return std::result::Result::Err(
|
|
effective_error(profile, "Off-chain Transport secret provenance is unavailable")
|
|
.with_context("provider", provider)
|
|
.with_context("field", pointer),
|
|
);
|
|
},
|
|
};
|
|
let mut has_secret_environment = false;
|
|
for item in provenance {
|
|
let variable_name = match item.variable_name() {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => continue,
|
|
};
|
|
let sensitivity = crate::ConfigSensitivity::from_variable_name(variable_name);
|
|
let sensitivity = match sensitivity {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
if !sensitivity.is_secret() {
|
|
return std::result::Result::Err(
|
|
effective_error(profile, "Off-chain Transport credentials may reference only secret environment variables")
|
|
.with_context("provider", provider)
|
|
.with_context("field", pointer),
|
|
);
|
|
}
|
|
has_secret_environment = true;
|
|
}
|
|
if !has_secret_environment {
|
|
return std::result::Result::Err(
|
|
effective_error(profile, "Off-chain Transport credentials require secret environment provenance")
|
|
.with_context("provider", provider)
|
|
.with_context("field", pointer),
|
|
);
|
|
}
|
|
return std::result::Result::Ok(());
|
|
}
|
|
|
|
fn validate_public_field_provenance(
|
|
effective: &crate::ResolvedConfigJson,
|
|
pointer: &str,
|
|
profile: &crate::ResolvedConfigProfile,
|
|
provider: &'static str,
|
|
) -> ksp_core_lib::Result<()> {
|
|
let provenance = match effective.provenance_at(pointer) {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => {
|
|
return std::result::Result::Err(
|
|
effective_error(profile, "Off-chain Transport public provenance is unavailable")
|
|
.with_context("provider", provider)
|
|
.with_context("field", pointer),
|
|
);
|
|
},
|
|
};
|
|
for item in provenance {
|
|
let variable_name = match item.variable_name() {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => continue,
|
|
};
|
|
let sensitivity = crate::ConfigSensitivity::from_variable_name(variable_name);
|
|
let sensitivity = match sensitivity {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
if sensitivity != crate::ConfigSensitivity::Public {
|
|
return std::result::Result::Err(
|
|
effective_error(profile, "public Off-chain Transport fields may reference only public environment variables")
|
|
.with_context("provider", provider)
|
|
.with_context("field", pointer),
|
|
);
|
|
}
|
|
}
|
|
return std::result::Result::Ok(());
|
|
}
|
|
|
|
fn provider_contract_error(profile: &crate::ResolvedConfigProfile, provider: &'static str, error: &ksp_core_lib::Error) -> ksp_core_lib::Error {
|
|
return offchain_contract_error(profile, "effective market-price provider settings fail the Off-chain Transport runtime contract", error)
|
|
.with_context("provider", provider);
|
|
}
|
|
|
|
fn offchain_contract_error(profile: &crate::ResolvedConfigProfile, reason: &'static str, error: &ksp_core_lib::Error) -> ksp_core_lib::Error {
|
|
return effective_error(profile, reason)
|
|
.with_context("offchain_transport_error_domain", error.code().domain())
|
|
.with_context("offchain_transport_error_code", error.code().code());
|
|
}
|
|
|
|
fn effective_error(profile: &crate::ResolvedConfigProfile, reason: &'static str) -> ksp_core_lib::Error {
|
|
return ksp_core_lib::Error::new(crate::ERROR_CODE_EFFECTIVE_CONFIG_INVALID, "effective Config cannot be mapped to the requested runtime contract")
|
|
.with_context("file_id", profile.file_id().as_str())
|
|
.with_context("profile_id", profile.profile_id())
|
|
.with_context("reason", reason);
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[path = "../unit_tests/offchain_transport.rs"]
|
|
mod tests;
|