v0.2.11-pre.006

This commit is contained in:
2026-08-25 23:06:26 +02:00
parent 4021d08541
commit 98093d859b
12 changed files with 1079 additions and 48 deletions

View File

@@ -0,0 +1,105 @@
// file: crates/ksp-offchain-transport-lib/unit_tests/market_price_dexscreener.rs
// version: 1
const TEST_PAIR: &str = "Czfq3xZZDmsdGdUyrNLtRhGc47cXcZtLG4crryfu44zE";
#[test]
fn dexscreener_fixture_validates_chain_pair_and_sol_identity_and_maps_exact_pair_usd() -> ksp_core_lib::Result<()> {
let provider_id = match crate::MarketPriceProviderId::new(super::DEXSCREENER_PROVIDER_ID) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let pair_address = match TEST_PAIR.parse::<ksp_core_lib::Pubkey>() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::result::Result::Err(super::provider_settings_error("test_pair")),
};
let fixture = concat!(
r#"{"schemaVersion":"1.0.0","pairs":[{"chainId":"solana","dexId":"orca","pairAddress":"Czfq3xZZDmsdGdUyrNLtRhGc47cXcZtLG4crryfu44zE","#,
r#""baseToken":{"address":"So11111111111111111111111111111111111111112","name":"Wrapped SOL","symbol":"SOL"},"#,
r#""quoteToken":{"address":"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v","name":"USD Coin","symbol":"USDC"},"#,
r#""priceNative":"151.987654321012345678","priceUsd":"151.987654321012345678"}]}"#
)
.as_bytes();
let started = crate::MarketPriceTimestamp::from_unix_millis(10);
let received = crate::MarketPriceTimestamp::from_unix_millis(20);
let observation = match super::parse_response(fixture, provider_id, &pair_address, started, received) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
assert_eq!(observation.price().to_canonical_string(), "151.987654321012345678");
assert_eq!(observation.semantics(), crate::MarketPriceSemantics::DexPairUsd);
assert_eq!(observation.provider_timestamp(), std::option::Option::None);
assert_eq!(observation.provenance().as_str(), "dexscreener:solana:Czfq3xZZDmsdGdUyrNLtRhGc47cXcZtLG4crryfu44zE:priceUsd");
return std::result::Result::Ok(());
}
#[test]
fn dexscreener_settings_and_descriptor_keep_pair_explicit_and_pair_endpoint_rate_limited() -> ksp_core_lib::Result<()> {
assert!(crate::MarketPriceDexScreenerSettings::new(true, "not-a-pubkey").is_err());
let settings = match crate::MarketPriceDexScreenerSettings::new(true, TEST_PAIR) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
assert_eq!(settings.pair_address().to_string(), TEST_PAIR);
let request = match super::build_request(settings.pair_address()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
assert_eq!(request.url_for_test().as_str(), "https://api.dexscreener.com/latest/dex/pairs/solana/Czfq3xZZDmsdGdUyrNLtRhGc47cXcZtLG4crryfu44zE");
let provider = match crate::MarketPriceDexScreenerProvider::new(settings) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
assert_eq!(provider.descriptor().auth_mode(), crate::MarketPriceProviderAuthMode::None);
assert_eq!(provider.descriptor().semantics(), crate::MarketPriceSemantics::DexPairUsd);
assert_eq!(provider.descriptor().rate_limit().requests(), std::option::Option::Some(300));
assert_eq!(provider.descriptor().rate_limit().window_seconds(), std::option::Option::Some(60));
assert_eq!(provider.descriptor().rate_limit().scope(), crate::MarketPriceProviderRateLimitScope::Unspecified);
return std::result::Result::Ok(());
}
#[test]
fn dexscreener_rejects_wrong_chain_pair_base_identity_multiple_pairs_and_missing_price() -> ksp_core_lib::Result<()> {
let provider_id = match crate::MarketPriceProviderId::new(super::DEXSCREENER_PROVIDER_ID) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let pair_address = match TEST_PAIR.parse::<ksp_core_lib::Pubkey>() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::result::Result::Err(super::provider_settings_error("test_pair")),
};
let timestamp = crate::MarketPriceTimestamp::from_unix_millis(10);
let wrong_chain = concat!(
r#"{"pairs":[{"chainId":"ethereum","pairAddress":"Czfq3xZZDmsdGdUyrNLtRhGc47cXcZtLG4crryfu44zE","#,
r#""baseToken":{"address":"So11111111111111111111111111111111111111112"},"priceUsd":"151.98"}]}"#
)
.as_bytes();
assert!(super::parse_response(wrong_chain, provider_id.clone(), &pair_address, timestamp, timestamp).is_err());
let wrong_pair = concat!(
r#"{"pairs":[{"chainId":"solana","pairAddress":"58oQChx4yWmvKdwLLZzBi4ChoCc2fqCUWBkwMihLYQo2","#,
r#""baseToken":{"address":"So11111111111111111111111111111111111111112"},"priceUsd":"151.98"}]}"#
)
.as_bytes();
assert!(super::parse_response(wrong_pair, provider_id.clone(), &pair_address, timestamp, timestamp).is_err());
let wrong_base = concat!(
r#"{"pairs":[{"chainId":"solana","pairAddress":"Czfq3xZZDmsdGdUyrNLtRhGc47cXcZtLG4crryfu44zE","#,
r#""baseToken":{"address":"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"},"priceUsd":"1.00"}]}"#
)
.as_bytes();
assert!(super::parse_response(wrong_base, provider_id.clone(), &pair_address, timestamp, timestamp).is_err());
let multiple = concat!(
r#"{"pairs":[{"chainId":"solana","pairAddress":"Czfq3xZZDmsdGdUyrNLtRhGc47cXcZtLG4crryfu44zE","#,
r#""baseToken":{"address":"So11111111111111111111111111111111111111112"},"priceUsd":"151.98"},{"#,
r#""chainId":"solana","pairAddress":"Czfq3xZZDmsdGdUyrNLtRhGc47cXcZtLG4crryfu44zE","#,
r#""baseToken":{"address":"So11111111111111111111111111111111111111112"},"priceUsd":"151.98"}]}"#
)
.as_bytes();
assert!(super::parse_response(multiple, provider_id.clone(), &pair_address, timestamp, timestamp).is_err());
let missing_price = concat!(
r#"{"pairs":[{"chainId":"solana","pairAddress":"Czfq3xZZDmsdGdUyrNLtRhGc47cXcZtLG4crryfu44zE","#,
r#""baseToken":{"address":"So11111111111111111111111111111111111111112"},"priceUsd":null}]}"#
)
.as_bytes();
assert!(super::parse_response(missing_price, provider_id, &pair_address, timestamp, timestamp).is_err());
return std::result::Result::Ok(());
}

View File

@@ -0,0 +1,81 @@
// file: crates/ksp-offchain-transport-lib/unit_tests/market_price_jupiter.rs
// version: 1
#[test]
fn jupiter_fixture_maps_exact_heuristic_price_and_block_provenance_without_fake_timestamp() -> ksp_core_lib::Result<()> {
let provider_id = match crate::MarketPriceProviderId::new(super::JUPITER_PROVIDER_ID) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let fixture = concat!(
r#"{"So11111111111111111111111111111111111111112":{"createdAt":"2024-06-05T08:55:25.527Z","liquidity":621679197.67,"#,
r#""usdPrice":151.987654321012345678,"blockId":348004023,"decimals":9,"priceChange24h":1.29}}"#
)
.as_bytes();
let started = crate::MarketPriceTimestamp::from_unix_millis(10);
let received = crate::MarketPriceTimestamp::from_unix_millis(20);
let observation = match super::parse_response(fixture, provider_id, started, received) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
assert_eq!(observation.price().to_canonical_string(), "151.987654321012345678");
assert_eq!(observation.semantics(), crate::MarketPriceSemantics::SolanaHeuristic);
assert_eq!(observation.provider_timestamp(), std::option::Option::None);
assert_eq!(observation.provenance().as_str(), "jupiter:price_v3:So11111111111111111111111111111111111111112:block:348004023");
return std::result::Result::Ok(());
}
#[test]
fn jupiter_modes_match_current_keyless_and_free_rate_limits_and_redact_key() -> ksp_core_lib::Result<()> {
let keyless = match crate::MarketPriceJupiterSettings::keyless(true) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let keyless_provider = match crate::MarketPriceJupiterProvider::new(keyless) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
assert_eq!(keyless_provider.descriptor().auth_mode(), crate::MarketPriceProviderAuthMode::None);
assert_eq!(keyless_provider.descriptor().semantics(), crate::MarketPriceSemantics::SolanaHeuristic);
assert_eq!(keyless_provider.descriptor().rate_limit().requests(), std::option::Option::Some(1));
assert_eq!(keyless_provider.descriptor().rate_limit().window_seconds(), std::option::Option::Some(2));
assert_eq!(keyless_provider.descriptor().rate_limit().scope(), crate::MarketPriceProviderRateLimitScope::Unspecified);
let free = match crate::MarketPriceJupiterSettings::free(true, std::option::Option::Some("jup-free-canary-secret".to_owned())) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
assert!(!std::format!("{free:?}").contains("jup-free-canary-secret"));
let request = match super::build_request(&free) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
assert!(request.has_header_for_test(super::JUPITER_API_KEY_HEADER));
assert_eq!(request.url_for_test().query(), std::option::Option::Some("ids=So11111111111111111111111111111111111111112"));
let free_provider = match crate::MarketPriceJupiterProvider::new(free) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
assert_eq!(free_provider.descriptor().auth_mode(), crate::MarketPriceProviderAuthMode::RequiredApiKey);
assert_eq!(free_provider.descriptor().rate_limit().requests(), std::option::Option::Some(1));
assert_eq!(free_provider.descriptor().rate_limit().window_seconds(), std::option::Option::Some(1));
assert_eq!(free_provider.descriptor().rate_limit().scope(), crate::MarketPriceProviderRateLimitScope::Account);
return std::result::Result::Ok(());
}
#[test]
fn jupiter_rejects_keyless_credential_missing_price_wrong_identity_and_wrong_decimals() -> ksp_core_lib::Result<()> {
assert!(crate::MarketPriceJupiterSettings::free(true, std::option::Option::None).is_err());
assert!(crate::MarketPriceJupiterSettings::keyless(true).is_ok());
let provider_id = match crate::MarketPriceProviderId::new(super::JUPITER_PROVIDER_ID) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let timestamp = crate::MarketPriceTimestamp::from_unix_millis(10);
let missing_price = br#"{"So11111111111111111111111111111111111111112":{"usdPrice":null,"blockId":348004023,"decimals":9}}"#;
assert!(super::parse_response(missing_price, provider_id.clone(), timestamp, timestamp).is_err());
let wrong_identity = br#"{"JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN":{"usdPrice":1.0,"blockId":348004023,"decimals":6}}"#;
assert!(super::parse_response(wrong_identity, provider_id.clone(), timestamp, timestamp).is_err());
let wrong_decimals = br#"{"So11111111111111111111111111111111111111112":{"usdPrice":151.98,"blockId":348004023,"decimals":8}}"#;
assert!(super::parse_response(wrong_decimals, provider_id, timestamp, timestamp).is_err());
return std::result::Result::Ok(());
}