diff --git a/contracts/price-oracle/src/callbacks.rs b/contracts/price-oracle/src/callbacks.rs index 7d27591..62abf38 100644 --- a/contracts/price-oracle/src/callbacks.rs +++ b/contracts/price-oracle/src/callbacks.rs @@ -1,3 +1,5 @@ +use soroban_sdk::{Address, Env, Symbol, Vec}; +use alloc::string::ToString; use soroban_sdk::{Address, Env, Symbol, Vec, IntoVal}; use crate::types::{DataKey, PriceUpdatePayload}; @@ -23,6 +25,7 @@ pub fn subscribe(env: &Env, callback_contract: Address) -> Result<(), crate::Err // Check if already subscribed if subscribers.iter().any(|sub| sub == callback_contract) { + return Err("Contract is already subscribed".to_string()); return Err(crate::Error::AlreadyInitialized); } @@ -109,6 +112,7 @@ fn try_invoke_callback(env: &Env, callback_contract: &Address, payload: &PriceUp env.invoke_contract::<()>( callback_contract, &Symbol::new(env, "on_price_update"), + soroban_sdk::vec![env, payload.clone()], soroban_sdk::vec![env, payload.into_val(env)], ); Ok(()) @@ -139,6 +143,7 @@ mod tests { // Verify both are in the list let subscribers = get_subscribers(&env); assert!(subscribers.iter().any(|s| s == contract1)); + assert!(subscribers.iter().any(|s| s == &contract2)); assert!(subscribers.iter().any(|s| s == contract2)); } diff --git a/contracts/price-oracle/src/lib.rs b/contracts/price-oracle/src/lib.rs index f36c9c7..5a07eb1 100644 --- a/contracts/price-oracle/src/lib.rs +++ b/contracts/price-oracle/src/lib.rs @@ -1332,6 +1332,11 @@ impl PriceOracle { }; callbacks::notify_subscribers(&env, &payload); + // Automatically extend the storage lifetime of the relayer's profile + // after successful price update submission + env.storage().persistent().extend_ttl(&crate::auth::DataKey::Provider(source.clone()), 10000); + env.storage().persistent().extend_ttl(&crate::auth::DataKey::ProviderWeight(source.clone()), 10000); + Ok(()) } diff --git a/contracts/price-oracle/test_ttl_extension.rs b/contracts/price-oracle/test_ttl_extension.rs new file mode 100644 index 0000000..f17c637 --- /dev/null +++ b/contracts/price-oracle/test_ttl_extension.rs @@ -0,0 +1,47 @@ +#[cfg(test)] +mod ttl_extension_tests { + use super::*; + use soroban_sdk::testutils::Address as _; + + #[test] + fn test_update_price_extends_relayer_ttl() { + let env = Env::default(); + let contract_id = env.register(PriceOracle, ()); + + let admin = Address::generate(&env); + let relayer = Address::generate(&env); + let asset = Symbol::new(&env, "BTC"); + + env.as_contract(&contract_id, || { + // Initialize contract + let assets = soroban_sdk::vec![&env, asset.clone()]; + PriceOracle::initialize(env.clone(), admin.clone(), assets); + + // Add relayer to whitelist + crate::auth::_add_provider(&env, &relayer); + + // Add asset + PriceOracle::add_asset(env.clone(), admin.clone(), asset.clone()).unwrap(); + + // Verify relayer is stored + assert!(crate::auth::_is_provider(&env, &relayer)); + + // Call update_price which should extend TTL + let result = PriceOracle::update_price( + env.clone(), + relayer.clone(), + asset.clone(), + 50000_i128, // price + 7, // decimals + 100, // confidence_score + 3600, // ttl + ); + + // The update should succeed + assert!(result.is_ok()); + + // The relayer should still be whitelisted (TTL extended) + assert!(crate::auth::_is_provider(&env, &relayer)); + }); + } +}