diff --git a/betting/src/lib.rs b/betting/src/lib.rs
index 020f660..9531727 100644
--- a/betting/src/lib.rs
+++ b/betting/src/lib.rs
@@ -27,10 +27,8 @@
//! - `refund_bet` : user pulls a refund after the deadline if no settlement
//! - `claim_payout` : user pulls parimutuel winnings once the match is settled
-use soroban_sdk::{
- contract, contractimpl, contracttype, token, Address, BytesN, Env, Symbol, Vec,
-};
use renaissance_core::PlatformError;
+use soroban_sdk::{contract, contractimpl, contracttype, token, Address, BytesN, Env, Symbol, Vec};
// ── Outcomes ──────────────────────────────────────────────────────────────────
@@ -139,7 +137,12 @@ impl RenaissanceBettingContract {
}
fn ensure_not_paused(env: &Env) -> Result<(), PlatformError> {
- if env.storage().instance().get(&DataKey::Paused).unwrap_or(false) {
+ if env
+ .storage()
+ .instance()
+ .get(&DataKey::Paused)
+ .unwrap_or(false)
+ {
return Err(PlatformError::Paused);
}
Ok(())
@@ -155,16 +158,26 @@ impl RenaissanceBettingContract {
return Err(PlatformError::Unauthorized);
}
env.storage().instance().set(&DataKey::Admin, &admin);
- env.storage().instance().set(&DataKey::PlatformAdmin, &admin);
- env.storage().instance().set(&DataKey::SecurityAdmin, &admin);
- env.storage().instance().set(&DataKey::TreasuryAdmin, &admin);
+ env.storage()
+ .instance()
+ .set(&DataKey::PlatformAdmin, &admin);
+ env.storage()
+ .instance()
+ .set(&DataKey::SecurityAdmin, &admin);
+ env.storage()
+ .instance()
+ .set(&DataKey::TreasuryAdmin, &admin);
env.storage().instance().set(&DataKey::Paused, &false);
env.events()
.publish((Symbol::new(&env, "ContractInitialized"),), admin);
Ok(())
}
- pub fn upgrade(env: Env, caller: Address, new_wasm_hash: BytesN<32>) -> Result<(), PlatformError> {
+ pub fn upgrade(
+ env: Env,
+ caller: Address,
+ new_wasm_hash: BytesN<32>,
+ ) -> Result<(), PlatformError> {
let platform: Address = env
.storage()
.instance()
@@ -196,13 +209,20 @@ impl RenaissanceBettingContract {
approvals.push_back(caller.clone());
if approvals.len() >= 2 {
- env.storage().instance().set(&DataKey::WasmHash, &new_wasm_hash);
- env.deployer().update_current_contract_wasm(new_wasm_hash.clone());
- env.storage().instance().set(&DataKey::UpgradeApprovals, &Vec::
::new(&env));
+ env.storage()
+ .instance()
+ .set(&DataKey::WasmHash, &new_wasm_hash);
+ env.deployer()
+ .update_current_contract_wasm(new_wasm_hash.clone());
+ env.storage()
+ .instance()
+ .set(&DataKey::UpgradeApprovals, &Vec::::new(&env));
env.events()
.publish((Symbol::new(&env, "Upgraded"),), new_wasm_hash);
} else {
- env.storage().instance().set(&DataKey::UpgradeApprovals, &approvals);
+ env.storage()
+ .instance()
+ .set(&DataKey::UpgradeApprovals, &approvals);
}
Ok(())
}
@@ -210,7 +230,12 @@ impl RenaissanceBettingContract {
pub fn pause(env: Env) -> Result<(), PlatformError> {
let admin = Self::require_admin(&env)?;
admin.require_auth();
- if env.storage().instance().get(&DataKey::Paused).unwrap_or(false) {
+ if env
+ .storage()
+ .instance()
+ .get(&DataKey::Paused)
+ .unwrap_or(false)
+ {
return Ok(());
}
env.storage().instance().set(&DataKey::Paused, &true);
@@ -222,12 +247,16 @@ impl RenaissanceBettingContract {
let admin = Self::require_admin(&env)?;
admin.require_auth();
env.storage().instance().set(&DataKey::Paused, &false);
- env.events().publish((Symbol::new(&env, "Unpaused"),), admin);
+ env.events()
+ .publish((Symbol::new(&env, "Unpaused"),), admin);
Ok(())
}
pub fn is_paused(env: Env) -> bool {
- env.storage().instance().get(&DataKey::Paused).unwrap_or(false)
+ env.storage()
+ .instance()
+ .get(&DataKey::Paused)
+ .unwrap_or(false)
}
/// Define a new match id with its authorized oracle, settlement token,
@@ -273,17 +302,15 @@ impl RenaissanceBettingContract {
.instance()
.set(&DataKey::Stats(match_id), &stats);
- env.events()
- .publish((Symbol::new(&env, "MatchRegistered"), admin), (match_id, oracle, token, deadline));
+ env.events().publish(
+ (Symbol::new(&env, "MatchRegistered"), admin),
+ (match_id, oracle, token, deadline),
+ );
Ok(())
}
/// Replace the oracle of an unsettled match. Admin only.
- pub fn set_oracle(
- env: Env,
- match_id: u64,
- new_oracle: Address,
- ) -> Result<(), PlatformError> {
+ pub fn set_oracle(env: Env, match_id: u64, new_oracle: Address) -> Result<(), PlatformError> {
Self::ensure_not_paused(&env)?;
let admin: Address = env
.storage()
@@ -370,9 +397,10 @@ impl RenaissanceBettingContract {
.ok_or(PlatformError::InternalError)?;
let idx = outcome.index();
let prev = stats.pools.get(idx).unwrap_or(0);
- stats
- .pools
- .set(idx, prev.checked_add(amount).ok_or(PlatformError::Overflow)?);
+ stats.pools.set(
+ idx,
+ prev.checked_add(amount).ok_or(PlatformError::Overflow)?,
+ );
stats.bettors.push_back(user.clone());
env.storage()
.instance()
@@ -432,11 +460,7 @@ impl RenaissanceBettingContract {
/// ```
///
/// Emits `BetClaimed` with the paid amount.
- pub fn claim_payout(
- env: Env,
- user: Address,
- match_id: u64,
- ) -> Result {
+ pub fn claim_payout(env: Env, user: Address, match_id: u64) -> Result {
Self::ensure_not_paused(&env)?;
user.require_auth();
let key = DataKey::Bet(match_id, user.clone());
@@ -504,21 +528,15 @@ impl RenaissanceBettingContract {
let token_client = token::Client::new(&env, &m.token);
token_client.transfer(&env.current_contract_address(), &user, &payout);
- env.events().publish(
- (Symbol::new(&env, "BetClaimed"), user),
- (match_id, payout),
- );
+ env.events()
+ .publish((Symbol::new(&env, "BetClaimed"), user), (match_id, payout));
Ok(payout)
}
/// Refund the user's full bet amount if the match has passed its deadline
/// without being settled. Idempotent per `(user, match_id)`.
/// Emits `BetRefunded`.
- pub fn refund_bet(
- env: Env,
- user: Address,
- match_id: u64,
- ) -> Result<(), PlatformError> {
+ pub fn refund_bet(env: Env, user: Address, match_id: u64) -> Result<(), PlatformError> {
Self::ensure_not_paused(&env)?;
user.require_auth();
let key = DataKey::Bet(match_id, user.clone());
@@ -557,7 +575,12 @@ impl RenaissanceBettingContract {
/// Look up a user's bet on a match. Returns `None` if the bet does
/// not exist.
pub fn get_bet(env: Env, user: Address, match_id: u64) -> Option {
- if env.storage().instance().get(&DataKey::Paused).unwrap_or(false) {
+ if env
+ .storage()
+ .instance()
+ .get(&DataKey::Paused)
+ .unwrap_or(false)
+ {
return None;
}
env.storage()
@@ -568,7 +591,12 @@ impl RenaissanceBettingContract {
/// Look up a match descriptor. Returns `None` if the match id is
/// not registered.
pub fn get_match(env: Env, match_id: u64) -> Option {
- if env.storage().instance().get(&DataKey::Paused).unwrap_or(false) {
+ if env
+ .storage()
+ .instance()
+ .get(&DataKey::Paused)
+ .unwrap_or(false)
+ {
return None;
}
env.storage().instance().get(&DataKey::Match(match_id))
@@ -618,9 +646,15 @@ mod test {
let approver_a = Address::generate(&env);
let approver_b = Address::generate(&env);
env.as_contract(&contract_id, || {
- env.storage().instance().set(&DataKey::PlatformAdmin, &approver_a);
- env.storage().instance().set(&DataKey::SecurityAdmin, &approver_b);
- env.storage().instance().set(&DataKey::TreasuryAdmin, &admin);
+ env.storage()
+ .instance()
+ .set(&DataKey::PlatformAdmin, &approver_a);
+ env.storage()
+ .instance()
+ .set(&DataKey::SecurityAdmin, &approver_b);
+ env.storage()
+ .instance()
+ .set(&DataKey::TreasuryAdmin, &admin);
});
let new_hash = BytesN::from_array(&env, &[9; 32]);
@@ -763,8 +797,7 @@ mod test {
client.register_match(&8u64, &oracle, &token, &deadline_in(&env, 3_600));
let user = Address::generate(&env);
mint(&env, &token, &user, 100);
- let res =
- client.try_place_bet(&user, &8u64, &Outcome::HomeWin, &500i128);
+ let res = client.try_place_bet(&user, &8u64, &Outcome::HomeWin, &500i128);
match res {
Err(Ok(e)) => assert_eq!(e, PlatformError::InsufficientBalance),
_ => panic!("expected InsufficientBalance contract error"),
@@ -780,8 +813,7 @@ mod test {
mint(&env, &token, &user, 1_000);
env.ledger().set_timestamp(deadline_in(&env, 3_600) + 1);
- let res =
- client.try_place_bet(&user, &9u64, &Outcome::HomeWin, &100i128);
+ let res = client.try_place_bet(&user, &9u64, &Outcome::HomeWin, &100i128);
match res {
Err(Ok(e)) => assert_eq!(e, PlatformError::ExpiredDeadline),
_ => panic!("expected ExpiredDeadline contract error"),
diff --git a/oracle/src/lib.rs b/oracle/src/lib.rs
index 420de80..8c45304 100644
--- a/oracle/src/lib.rs
+++ b/oracle/src/lib.rs
@@ -90,7 +90,7 @@ impl FootballOracleContract {
.instance()
.get(&DataKey::Oracles)
.ok_or(PlatformError::Unauthorized)?;
-
+
let caller = env.current_contract_address();
if !oracles.contains(&caller) {
return Err(PlatformError::Unauthorized);
@@ -100,7 +100,12 @@ impl FootballOracleContract {
/// Helper to check that contract is not paused
fn ensure_not_paused(env: &Env) -> Result<(), PlatformError> {
- if env.storage().instance().get(&DataKey::Paused).unwrap_or(false) {
+ if env
+ .storage()
+ .instance()
+ .get(&DataKey::Paused)
+ .unwrap_or(false)
+ {
return Err(PlatformError::Paused);
}
Ok(())
@@ -108,7 +113,11 @@ impl FootballOracleContract {
/// One-time initialization of the oracle contract.
/// Sets up the admin and initial list of authorized oracles.
- pub fn initialize(env: Env, admin: Address, initial_oracles: Vec) -> Result<(), PlatformError> {
+ pub fn initialize(
+ env: Env,
+ admin: Address,
+ initial_oracles: Vec,
+ ) -> Result<(), PlatformError> {
admin.require_auth();
if env.storage().instance().has(&DataKey::Admin) {
@@ -121,14 +130,14 @@ impl FootballOracleContract {
}
env.storage().instance().set(&DataKey::Admin, &admin);
- env.storage().instance().set(&DataKey::Oracles, &initial_oracles);
+ env.storage()
+ .instance()
+ .set(&DataKey::Oracles, &initial_oracles);
env.storage().instance().set(&DataKey::Paused, &false);
// Extend TTL for instance storage
const MAX_TTL: u32 = 518400; // 30 days in ledgers (~5s per ledger)
- env.storage()
- .instance()
- .extend_ttl(MAX_TTL, MAX_TTL);
+ env.storage().instance().extend_ttl(MAX_TTL, MAX_TTL);
Ok(())
}
@@ -168,23 +177,31 @@ impl FootballOracleContract {
finished_at: u64,
) -> Result<(), OracleError> {
Self::ensure_not_paused(&env).map_err(|_| OracleError::OracleUnauthorized)?;
-
+
let oracles = Self::ensure_oracle(&env).map_err(|_| OracleError::OracleUnauthorized)?;
oracle.require_auth();
let caller = oracle;
-
+
// Validate caller is in the oracle list
if !oracles.contains(&caller) {
return Err(OracleError::OracleUnauthorized);
}
// Check if match is already finalized
- if env.storage().persistent().has(&DataKey::FinalizedResult(match_id)) {
+ if env
+ .storage()
+ .persistent()
+ .has(&DataKey::FinalizedResult(match_id))
+ {
return Err(OracleError::MatchAlreadyFinalized);
}
// Check if result is already pending
- if env.storage().persistent().has(&DataKey::PendingResult(match_id)) {
+ if env
+ .storage()
+ .persistent()
+ .has(&DataKey::PendingResult(match_id))
+ {
return Err(OracleError::ResultAlreadyExists);
}
@@ -210,8 +227,10 @@ impl FootballOracleContract {
.set(&DataKey::PendingResult(match_id), &pending);
// Emit ResultSubmitted event
- env.events()
- .publish((get_event_topic_by_string(&env, "ResultSubmitted"),), (match_id, caller));
+ env.events().publish(
+ (get_event_topic_by_string(&env, "ResultSubmitted"),),
+ (match_id, caller),
+ );
Ok(())
}
@@ -220,18 +239,22 @@ impl FootballOracleContract {
/// When the second confirmation is received, the result is finalized.
pub fn confirm_result(env: Env, oracle: Address, match_id: u64) -> Result<(), OracleError> {
Self::ensure_not_paused(&env).map_err(|_| OracleError::OracleUnauthorized)?;
-
+
let oracles = Self::ensure_oracle(&env).map_err(|_| OracleError::OracleUnauthorized)?;
oracle.require_auth();
let caller = oracle;
-
+
// Validate caller is in the oracle list
if !oracles.contains(&caller) {
return Err(OracleError::OracleUnauthorized);
}
// Check if match is already finalized
- if env.storage().persistent().has(&DataKey::FinalizedResult(match_id)) {
+ if env
+ .storage()
+ .persistent()
+ .has(&DataKey::FinalizedResult(match_id))
+ {
return Err(OracleError::MatchAlreadyFinalized);
}
@@ -259,8 +282,10 @@ impl FootballOracleContract {
.set(&DataKey::PendingResult(match_id), &pending);
// Emit ResultConfirmed event
- env.events()
- .publish((get_event_topic_by_string(&env, "ResultConfirmed"),), (match_id, caller));
+ env.events().publish(
+ (get_event_topic_by_string(&env, "ResultConfirmed"),),
+ (match_id, caller),
+ );
// Check if we have enough confirmations (2-of-N, so at least 1 confirmation since submitter is one)
if pending.confirmations.len() >= 1 {
@@ -284,8 +309,10 @@ impl FootballOracleContract {
.remove(&DataKey::PendingResult(match_id));
// Emit ResultFinalized event
- env.events()
- .publish((get_event_topic_by_string(&env, "ResultFinalized"),), match_id);
+ env.events().publish(
+ (get_event_topic_by_string(&env, "ResultFinalized"),),
+ match_id,
+ );
}
Ok(())
@@ -298,13 +325,15 @@ impl FootballOracleContract {
.persistent()
.get(&DataKey::FinalizedResult(match_id))
.ok_or(OracleError::ResultNotFound)?;
-
+
Ok(result)
}
/// Check if a match result is finalized
pub fn is_finalized(env: Env, match_id: u64) -> bool {
- env.storage().persistent().has(&DataKey::FinalizedResult(match_id))
+ env.storage()
+ .persistent()
+ .has(&DataKey::FinalizedResult(match_id))
}
/// Get the list of all authorized oracles
@@ -317,4 +346,4 @@ impl FootballOracleContract {
}
#[cfg(test)]
-mod test;
\ No newline at end of file
+mod test;
diff --git a/oracle/src/test.rs b/oracle/src/test.rs
index b08c534..70802c5 100644
--- a/oracle/src/test.rs
+++ b/oracle/src/test.rs
@@ -1,8 +1,8 @@
#![cfg(test)]
use super::*;
-use soroban_sdk::{testutils::AddressEnvTestUtils, Address, Env, Vec};
use soroban_sdk::testutils::Ledger;
+use soroban_sdk::{testutils::AddressEnvTestUtils, Address, Env, Vec};
#[test]
fn test_initialize() {
@@ -10,18 +10,18 @@ fn test_initialize() {
let admin = Address::generate(&env);
let oracle1 = Address::generate(&env);
let oracle2 = Address::generate(&env);
-
+
let mut oracles = Vec::new(&env);
oracles.push_back(oracle1);
oracles.push_back(oracle2);
-
+
// Register the contract
let contract_id = env.register_contract(None, FootballOracleContract);
let client = FootballOracleContractClient::new(&env, &contract_id);
-
+
// Initialize
client.initialize(&admin, &oracles);
-
+
// Check that oracles are correctly set
let stored_oracles = client.get_oracles();
assert_eq!(stored_oracles.len(), 2);
@@ -35,13 +35,13 @@ fn test_initialize_insufficient_oracles() {
let env = Env::default();
let admin = Address::generate(&env);
let oracle1 = Address::generate(&env);
-
+
let mut oracles = Vec::new(&env);
oracles.push_back(oracle1); // Only one oracle - should fail
-
+
let contract_id = env.register_contract(None, FootballOracleContract);
let client = FootballOracleContractClient::new(&env, &contract_id);
-
+
client.initialize(&admin, &oracles);
}
@@ -51,28 +51,28 @@ fn test_submit_result_success() {
let admin = Address::generate(&env);
let oracle1 = Address::generate(&env);
let oracle2 = Address::generate(&env);
-
+
let mut oracles = Vec::new(&env);
oracles.push_back(oracle1.clone());
oracles.push_back(oracle2.clone());
-
+
let contract_id = env.register_contract(None, FootballOracleContract);
let client = FootballOracleContractClient::new(&env, &contract_id);
-
+
client.initialize(&admin, &oracles);
-
+
// Set ledger timestamp
let now = 1620000000;
env.ledger().set_timestamp(now);
-
+
// Submit a valid result
let match_id = 123;
let started_at = now - 3600; // 1 hour ago
- let finished_at = now - 600; // 10 minutes ago
-
+ let finished_at = now - 600; // 10 minutes ago
+
oracle1.require_auth();
client.submit_result(&match_id, &2, &1, &started_at, &finished_at);
-
+
// Result shouldn't be finalized yet (needs second confirmation)
assert!(!client.is_finalized(&match_id));
}
@@ -85,20 +85,20 @@ fn test_submit_result_unauthorized() {
let oracle1 = Address::generate(&env);
let oracle2 = Address::generate(&env);
let unauthorized = Address::generate(&env);
-
+
let mut oracles = Vec::new(&env);
oracles.push_back(oracle1);
oracles.push_back(oracle2);
-
+
let contract_id = env.register_contract(None, FootballOracleContract);
let client = FootballOracleContractClient::new(&env, &contract_id);
-
+
client.initialize(&admin, &oracles);
-
+
// Set ledger timestamp
let now = 1620000000;
env.ledger().set_timestamp(now);
-
+
// Unauthorized address tries to submit
let match_id = 123;
client.submit_result(&match_id, &2, &1, &(now - 3600), &(now - 600));
@@ -111,25 +111,25 @@ fn test_submit_result_late_submission() {
let admin = Address::generate(&env);
let oracle1 = Address::generate(&env);
let oracle2 = Address::generate(&env);
-
+
let mut oracles = Vec::new(&env);
oracles.push_back(oracle1.clone());
oracles.push_back(oracle2.clone());
-
+
let contract_id = env.register_contract(None, FootballOracleContract);
let client = FootballOracleContractClient::new(&env, &contract_id);
-
+
client.initialize(&admin, &oracles);
-
+
// Set ledger timestamp
let now = 1620000000;
env.ledger().set_timestamp(now);
-
+
// Try to submit with finished_at in the future
let match_id = 123;
- let started_at = now + 3600; // Starts in 1 hour
+ let started_at = now + 3600; // Starts in 1 hour
let finished_at = now + 7200; // Finishes in 2 hours
-
+
oracle1.require_auth();
client.submit_result(&match_id, &2, &1, &started_at, &finished_at);
}
@@ -141,28 +141,28 @@ fn test_double_submit() {
let admin = Address::generate(&env);
let oracle1 = Address::generate(&env);
let oracle2 = Address::generate(&env);
-
+
let mut oracles = Vec::new(&env);
oracles.push_back(oracle1.clone());
oracles.push_back(oracle2.clone());
-
+
let contract_id = env.register_contract(None, FootballOracleContract);
let client = FootballOracleContractClient::new(&env, &contract_id);
-
+
client.initialize(&admin, &oracles);
-
+
// Set ledger timestamp
let now = 1620000000;
env.ledger().set_timestamp(now);
-
+
let match_id = 123;
let started_at = now - 3600;
let finished_at = now - 600;
-
+
// First submission from oracle1
oracle1.require_auth();
client.submit_result(&match_id, &2, &1, &started_at, &finished_at);
-
+
// Second submission from oracle2 for the same match - should fail
oracle2.require_auth();
client.submit_result(&match_id, &3, &1, &started_at, &finished_at);
@@ -174,35 +174,35 @@ fn test_confirm_and_finalize() {
let admin = Address::generate(&env);
let oracle1 = Address::generate(&env);
let oracle2 = Address::generate(&env);
-
+
let mut oracles = Vec::new(&env);
oracles.push_back(oracle1.clone());
oracles.push_back(oracle2.clone());
-
+
let contract_id = env.register_contract(None, FootballOracleContract);
let client = FootballOracleContractClient::new(&env, &contract_id);
-
+
client.initialize(&admin, &oracles);
-
+
// Set ledger timestamp
let now = 1620000000;
env.ledger().set_timestamp(now);
-
+
let match_id = 123;
let started_at = now - 3600;
let finished_at = now - 600;
-
+
// Submit from oracle1
oracle1.require_auth();
client.submit_result(&match_id, &2, &1, &started_at, &finished_at);
-
+
// Confirm from oracle2 - this should finalize
oracle2.require_auth();
client.confirm_result(&match_id);
-
+
// Check if finalized
assert!(client.is_finalized(&match_id));
-
+
// Get the result and verify
let result = client.get_result(&match_id);
assert_eq!(result.home_score, 2);
@@ -218,28 +218,28 @@ fn test_cannot_confirm_own_submission() {
let admin = Address::generate(&env);
let oracle1 = Address::generate(&env);
let oracle2 = Address::generate(&env);
-
+
let mut oracles = Vec::new(&env);
oracles.push_back(oracle1.clone());
oracles.push_back(oracle2.clone());
-
+
let contract_id = env.register_contract(None, FootballOracleContract);
let client = FootballOracleContractClient::new(&env, &contract_id);
-
+
client.initialize(&admin, &oracles);
-
+
// Set ledger timestamp
let now = 1620000000;
env.ledger().set_timestamp(now);
-
+
let match_id = 123;
let started_at = now - 3600;
let finished_at = now - 600;
-
+
// Submit from oracle1
oracle1.require_auth();
client.submit_result(&match_id, &2, &1, &started_at, &finished_at);
-
+
// Try to confirm own submission - should fail
oracle1.require_auth();
client.confirm_result(&match_id);
@@ -253,34 +253,34 @@ fn test_double_confirm() {
let oracle1 = Address::generate(&env);
let oracle2 = Address::generate(&env);
let oracle3 = Address::generate(&env);
-
+
let mut oracles = Vec::new(&env);
oracles.push_back(oracle1.clone());
oracles.push_back(oracle2.clone());
oracles.push_back(oracle3.clone());
-
+
let contract_id = env.register_contract(None, FootballOracleContract);
let client = FootballOracleContractClient::new(&env, &contract_id);
-
+
client.initialize(&admin, &oracles);
-
+
// Set ledger timestamp
let now = 1620000000;
env.ledger().set_timestamp(now);
-
+
let match_id = 123;
let started_at = now - 3600;
let finished_at = now - 600;
-
+
// Submit from oracle1
oracle1.require_auth();
client.submit_result(&match_id, &2, &1, &started_at, &finished_at);
-
+
// Confirm from oracle2
oracle2.require_auth();
client.confirm_result(&match_id);
-
+
// Try to confirm again from oracle2 - should fail
oracle2.require_auth();
client.confirm_result(&match_id);
-}
\ No newline at end of file
+}
diff --git a/rewards/src/lib.rs b/rewards/src/lib.rs
index 9ec613d..44eb9ba 100644
--- a/rewards/src/lib.rs
+++ b/rewards/src/lib.rs
@@ -55,7 +55,12 @@ impl FanRewardsContract {
}
fn ensure_not_paused(env: &Env) -> Result<(), PlatformError> {
- if env.storage().instance().get(&DataKey::Paused).unwrap_or(false) {
+ if env
+ .storage()
+ .instance()
+ .get(&DataKey::Paused)
+ .unwrap_or(false)
+ {
return Err(PlatformError::Paused);
}
Ok(())
@@ -75,9 +80,15 @@ impl FanRewardsContract {
}
env.storage().instance().set(&DataKey::Admin, &admin);
- env.storage().instance().set(&DataKey::PlatformAdmin, &admin);
- env.storage().instance().set(&DataKey::SecurityAdmin, &admin);
- env.storage().instance().set(&DataKey::TreasuryAdmin, &admin);
+ env.storage()
+ .instance()
+ .set(&DataKey::PlatformAdmin, &admin);
+ env.storage()
+ .instance()
+ .set(&DataKey::SecurityAdmin, &admin);
+ env.storage()
+ .instance()
+ .set(&DataKey::TreasuryAdmin, &admin);
env.storage().instance().set(&DataKey::Paused, &false);
// Re-bump instance storage TTL so config survives long inactivity.
env.storage()
@@ -87,7 +98,11 @@ impl FanRewardsContract {
Ok(())
}
- pub fn upgrade(env: Env, caller: Address, new_wasm_hash: BytesN<32>) -> Result<(), PlatformError> {
+ pub fn upgrade(
+ env: Env,
+ caller: Address,
+ new_wasm_hash: BytesN<32>,
+ ) -> Result<(), PlatformError> {
let platform: Address = env
.storage()
.instance()
@@ -119,13 +134,20 @@ impl FanRewardsContract {
approvals.push_back(caller.clone());
if approvals.len() >= 2 {
- env.storage().instance().set(&DataKey::WasmHash, &new_wasm_hash);
- env.deployer().update_current_contract_wasm(new_wasm_hash.clone());
- env.storage().instance().set(&DataKey::UpgradeApprovals, &Vec::::new(&env));
+ env.storage()
+ .instance()
+ .set(&DataKey::WasmHash, &new_wasm_hash);
+ env.deployer()
+ .update_current_contract_wasm(new_wasm_hash.clone());
+ env.storage()
+ .instance()
+ .set(&DataKey::UpgradeApprovals, &Vec::::new(&env));
env.events()
.publish((Symbol::new(&env, "Upgraded"),), new_wasm_hash);
} else {
- env.storage().instance().set(&DataKey::UpgradeApprovals, &approvals);
+ env.storage()
+ .instance()
+ .set(&DataKey::UpgradeApprovals, &approvals);
}
Ok(())
}
@@ -133,7 +155,12 @@ impl FanRewardsContract {
pub fn pause(env: Env) -> Result<(), PlatformError> {
let admin = Self::require_admin(&env)?;
admin.require_auth();
- if env.storage().instance().get(&DataKey::Paused).unwrap_or(false) {
+ if env
+ .storage()
+ .instance()
+ .get(&DataKey::Paused)
+ .unwrap_or(false)
+ {
return Ok(());
}
env.storage().instance().set(&DataKey::Paused, &true);
@@ -145,12 +172,16 @@ impl FanRewardsContract {
let admin = Self::require_admin(&env)?;
admin.require_auth();
env.storage().instance().set(&DataKey::Paused, &false);
- env.events().publish((Symbol::new(&env, "Unpaused"),), admin);
+ env.events()
+ .publish((Symbol::new(&env, "Unpaused"),), admin);
Ok(())
}
pub fn is_paused(env: Env) -> bool {
- env.storage().instance().get(&DataKey::Paused).unwrap_or(false)
+ env.storage()
+ .instance()
+ .get(&DataKey::Paused)
+ .unwrap_or(false)
}
/// Return the currently configured platform admin, if any.
@@ -187,9 +218,7 @@ impl FanRewardsContract {
let key = DataKey::Balance(user.clone());
let current: i128 = env.storage().persistent().get(&key).unwrap_or(0_i128);
- let new_balance = current
- .checked_add(amount)
- .ok_or(PlatformError::Overflow)?;
+ let new_balance = current.checked_add(amount).ok_or(PlatformError::Overflow)?;
env.storage().persistent().set(&key, &new_balance);
env.storage()
@@ -275,11 +304,7 @@ impl FanRewardsContract {
// Debit source first; refuse if it would underflow into a negative balance.
let from_key = DataKey::Balance(from.clone());
- let from_balance: i128 = env
- .storage()
- .persistent()
- .get(&from_key)
- .unwrap_or(0_i128);
+ let from_balance: i128 = env.storage().persistent().get(&from_key).unwrap_or(0_i128);
let new_from_balance = from_balance
.checked_sub(amount)
.ok_or(PlatformError::InvalidAmount)?;
@@ -349,9 +374,15 @@ mod test {
let approver_a = Address::generate(&env);
let approver_b = Address::generate(&env);
env.as_contract(&contract_id, || {
- env.storage().instance().set(&DataKey::PlatformAdmin, &approver_a);
- env.storage().instance().set(&DataKey::SecurityAdmin, &approver_b);
- env.storage().instance().set(&DataKey::TreasuryAdmin, &admin);
+ env.storage()
+ .instance()
+ .set(&DataKey::PlatformAdmin, &approver_a);
+ env.storage()
+ .instance()
+ .set(&DataKey::SecurityAdmin, &approver_b);
+ env.storage()
+ .instance()
+ .set(&DataKey::TreasuryAdmin, &admin);
});
let new_hash = BytesN::from_array(&env, &[7; 32]);
@@ -394,12 +425,8 @@ mod test {
let c = client(&env, &contract_id);
let user = Address::generate(&env);
- c.award_points(
- &user,
- &100_i128,
- &symbol_short!("watched"),
- )
- .unwrap();
+ c.award_points(&user, &100_i128, &symbol_short!("watched"))
+ .unwrap();
assert_eq!(c.get_balance(&user), 100_i128);
}
@@ -410,9 +437,12 @@ mod test {
let c = client(&env, &contract_id);
let user = Address::generate(&env);
- c.award_points(&user, &100_i128, &symbol_short!("watched")).unwrap();
- c.award_points(&user, &250_i128, &symbol_short!("shared")).unwrap();
- c.award_points(&user, &50_i128, &symbol_short!("referral")).unwrap();
+ c.award_points(&user, &100_i128, &symbol_short!("watched"))
+ .unwrap();
+ c.award_points(&user, &250_i128, &symbol_short!("shared"))
+ .unwrap();
+ c.award_points(&user, &50_i128, &symbol_short!("referral"))
+ .unwrap();
assert_eq!(c.get_balance(&user), 400_i128);
}
@@ -478,7 +508,8 @@ mod test {
let c = client(&env, &contract_id);
let user = Address::generate(&env);
- c.award_points(&user, &1_000_i128, &symbol_short!("watched")).unwrap();
+ c.award_points(&user, &1_000_i128, &symbol_short!("watched"))
+ .unwrap();
c.redeem_points(&user, &400_i128, &symbol_short!("nft_drop"))
.unwrap();
@@ -491,7 +522,8 @@ mod test {
let c = client(&env, &contract_id);
let user = Address::generate(&env);
- c.award_points(&user, &500_i128, &symbol_short!("watched")).unwrap();
+ c.award_points(&user, &500_i128, &symbol_short!("watched"))
+ .unwrap();
c.redeem_points(&user, &500_i128, &symbol_short!("nft_drop"))
.unwrap();
@@ -510,7 +542,8 @@ mod test {
assert!(res.is_err()); // InvalidAmount (underflow)
// Award a small balance, then try to redeem more than they hold.
- c.award_points(&user, &10_i128, &symbol_short!("watched")).unwrap();
+ c.award_points(&user, &10_i128, &symbol_short!("watched"))
+ .unwrap();
let res = c.try_redeem_points(&user, &11_i128, &symbol_short!("nft_drop"));
assert!(res.is_err()); // InvalidAmount (underflow)
@@ -524,7 +557,8 @@ mod test {
let c = client(&env, &contract_id);
let user = Address::generate(&env);
- c.award_points(&user, &100_i128, &symbol_short!("watched")).unwrap();
+ c.award_points(&user, &100_i128, &symbol_short!("watched"))
+ .unwrap();
let res = c.try_redeem_points(&user, &0_i128, &symbol_short!("nft_drop"));
assert!(res.is_err()); // InvalidAmount
}
@@ -535,7 +569,8 @@ mod test {
let c = client(&env, &contract_id);
let user = Address::generate(&env);
- c.award_points(&user, &100_i128, &symbol_short!("watched")).unwrap();
+ c.award_points(&user, &100_i128, &symbol_short!("watched"))
+ .unwrap();
let res = c.try_redeem_points(&user, &-10_i128, &symbol_short!("nft_drop"));
assert!(res.is_err()); // InvalidAmount
}
@@ -568,9 +603,7 @@ mod test {
.unwrap();
assert!(c.try_transfer_points(&alice, &bob, &0_i128).is_err());
- assert!(c
- .try_transfer_points(&alice, &bob, &-1_i128)
- .is_err());
+ assert!(c.try_transfer_points(&alice, &bob, &-1_i128).is_err());
// Balances unchanged after rejection.
assert_eq!(c.get_balance(&alice), 100_i128);
@@ -597,7 +630,8 @@ mod test {
let alice = Address::generate(&env);
let bob = Address::generate(&env);
- c.award_points(&alice, &5_i128, &symbol_short!("watched")).unwrap();
+ c.award_points(&alice, &5_i128, &symbol_short!("watched"))
+ .unwrap();
let res = c.try_transfer_points(&alice, &bob, &6_i128);
assert!(res.is_err()); // InvalidAmount (underflow)
diff --git a/vault/src/lib.rs b/vault/src/lib.rs
index 1a18c79..ff54351 100644
--- a/vault/src/lib.rs
+++ b/vault/src/lib.rs
@@ -7,7 +7,9 @@
//! and distribute payouts to winners. Implements reentrancy protection and strict access controls.
use renaissance_core::{get_event_topic_by_string, PlatformError};
-use soroban_sdk::{contract, contracterror, contractimpl, contracttype, token, Address, Env, Symbol};
+use soroban_sdk::{
+ contract, contracterror, contractimpl, contracttype, token, Address, Env, Symbol,
+};
// ── Balance structures ─────────────────────────────────────────────────────────
@@ -101,7 +103,9 @@ impl RenaissanceVaultContract {
if env.storage().instance().has(&DataKey::ReentrancyGuard) {
return Err(VaultError::ReentrancyDetected);
}
- env.storage().instance().set(&DataKey::ReentrancyGuard, &true);
+ env.storage()
+ .instance()
+ .set(&DataKey::ReentrancyGuard, &true);
Ok(())
}
@@ -116,14 +120,19 @@ impl RenaissanceVaultContract {
.instance()
.get(&DataKey::BettingContract)
.ok_or(PlatformError::Unauthorized)?;
-
+
betting_contract.require_auth();
Ok(betting_contract)
}
/// Helper to ensure contract is not paused
fn ensure_not_paused(env: &Env) -> Result<(), PlatformError> {
- if env.storage().instance().get(&DataKey::Paused).unwrap_or(false) {
+ if env
+ .storage()
+ .instance()
+ .get(&DataKey::Paused)
+ .unwrap_or(false)
+ {
return Err(PlatformError::Paused);
}
Ok(())
@@ -131,7 +140,11 @@ impl RenaissanceVaultContract {
/// One-time initialization of the vault contract
/// Sets up admin and authorizes the initial betting contract
- pub fn initialize(env: Env, admin: Address, betting_contract: Address) -> Result<(), PlatformError> {
+ pub fn initialize(
+ env: Env,
+ admin: Address,
+ betting_contract: Address,
+ ) -> Result<(), PlatformError> {
admin.require_auth();
if env.storage().instance().has(&DataKey::Admin) {
@@ -139,20 +152,23 @@ impl RenaissanceVaultContract {
}
env.storage().instance().set(&DataKey::Admin, &admin);
- env.storage().instance().set(&DataKey::BettingContract, &betting_contract);
+ env.storage()
+ .instance()
+ .set(&DataKey::BettingContract, &betting_contract);
env.storage().instance().set(&DataKey::Paused, &false);
// Extend TTL for instance storage (30 days)
const MAX_TTL: u32 = 518400;
- env.storage()
- .instance()
- .extend_ttl(MAX_TTL, MAX_TTL);
+ env.storage().instance().extend_ttl(MAX_TTL, MAX_TTL);
Ok(())
}
/// Update the authorized betting contract (admin only)
- pub fn set_betting_contract(env: Env, new_betting_contract: Address) -> Result<(), PlatformError> {
+ pub fn set_betting_contract(
+ env: Env,
+ new_betting_contract: Address,
+ ) -> Result<(), PlatformError> {
let admin: Address = env
.storage()
.instance()
@@ -169,7 +185,12 @@ impl RenaissanceVaultContract {
/// Deposit tokens into the vault - user stakes their tokens
/// Works with both native XLM (via SAC) and any custom asset
- pub fn deposit(env: Env, user: Address, amount: i128, asset: Address) -> Result<(), VaultError> {
+ pub fn deposit(
+ env: Env,
+ user: Address,
+ amount: i128,
+ asset: Address,
+ ) -> Result<(), VaultError> {
if amount <= 0 {
return Err(VaultError::InvalidAmount);
}
@@ -195,7 +216,9 @@ impl RenaissanceVaultContract {
locked: 0,
});
user_balance.available += amount;
- env.storage().persistent().set(&user_balance_key, &user_balance);
+ env.storage()
+ .persistent()
+ .set(&user_balance_key, &user_balance);
// Update vault's total balance
let vault_balance_key = DataKey::VaultBalance(asset.clone());
@@ -208,21 +231,30 @@ impl RenaissanceVaultContract {
total_locked: 0,
});
vault_balance.total_deposited += amount;
- env.storage().persistent().set(&vault_balance_key, &vault_balance);
+ env.storage()
+ .persistent()
+ .set(&vault_balance_key, &vault_balance);
// Exit reentrancy guard before external interactions are complete
Self::exit_reentrancy_guard(&env);
// Emit Deposited event
- env.events()
- .publish((get_event_topic_by_string(&env, "Deposited"),), (user, amount, asset));
+ env.events().publish(
+ (get_event_topic_by_string(&env, "Deposited"),),
+ (user, amount, asset),
+ );
Ok(())
}
/// Withdraw unused tokens from the vault
/// Only available funds (not locked in active bets) can be withdrawn
- pub fn withdraw(env: Env, user: Address, amount: i128, asset: Address) -> Result<(), VaultError> {
+ pub fn withdraw(
+ env: Env,
+ user: Address,
+ amount: i128,
+ asset: Address,
+ ) -> Result<(), VaultError> {
if amount <= 0 {
return Err(VaultError::InvalidAmount);
}
@@ -246,7 +278,9 @@ impl RenaissanceVaultContract {
// Update balances
user_balance.available -= amount;
- env.storage().persistent().set(&user_balance_key, &user_balance);
+ env.storage()
+ .persistent()
+ .set(&user_balance_key, &user_balance);
let vault_balance_key = DataKey::VaultBalance(asset.clone());
let mut vault_balance: VaultBalance = env
@@ -255,7 +289,9 @@ impl RenaissanceVaultContract {
.get(&vault_balance_key)
.ok_or(VaultError::InsufficientBalance)?;
vault_balance.total_deposited -= amount;
- env.storage().persistent().set(&vault_balance_key, &vault_balance);
+ env.storage()
+ .persistent()
+ .set(&vault_balance_key, &vault_balance);
// Transfer tokens back to user (after all state changes - checks-effects-interactions)
let client = token::Client::new(&env, &asset);
@@ -264,15 +300,23 @@ impl RenaissanceVaultContract {
Self::exit_reentrancy_guard(&env);
// Emit Withdrawn event
- env.events()
- .publish((get_event_topic_by_string(&env, "Withdrawn"),), (user, amount, asset));
+ env.events().publish(
+ (get_event_topic_by_string(&env, "Withdrawn"),),
+ (user, amount, asset),
+ );
Ok(())
}
/// Lock funds for an active bet - only callable by the betting contract
/// Moves funds from available to locked state
- pub fn lock_for_bet(env: Env, user: Address, amount: i128, asset: Address, match_id: u64) -> Result<(), VaultError> {
+ pub fn lock_for_bet(
+ env: Env,
+ user: Address,
+ amount: i128,
+ asset: Address,
+ match_id: u64,
+ ) -> Result<(), VaultError> {
if amount <= 0 {
return Err(VaultError::InvalidAmount);
}
@@ -305,7 +349,9 @@ impl RenaissanceVaultContract {
// Update user's balances
user_balance.available -= amount;
user_balance.locked += amount;
- env.storage().persistent().set(&user_balance_key, &user_balance);
+ env.storage()
+ .persistent()
+ .set(&user_balance_key, &user_balance);
// Update vault's totals
let vault_balance_key = DataKey::VaultBalance(asset.clone());
@@ -315,20 +361,30 @@ impl RenaissanceVaultContract {
.get(&vault_balance_key)
.ok_or(VaultError::InsufficientBalance)?;
vault_balance.total_locked += amount;
- env.storage().persistent().set(&vault_balance_key, &vault_balance);
+ env.storage()
+ .persistent()
+ .set(&vault_balance_key, &vault_balance);
Self::exit_reentrancy_guard(&env);
// Emit Locked event
- env.events()
- .publish((get_event_topic_by_string(&env, "Locked"),), (user, amount, asset, match_id));
+ env.events().publish(
+ (get_event_topic_by_string(&env, "Locked"),),
+ (user, amount, asset, match_id),
+ );
Ok(())
}
/// Payout winnings to a winner - only callable by the betting contract
/// Releases locked funds and transfers them to the winner
- pub fn payout(env: Env, winner: Address, amount: i128, asset: Address, match_id: u64) -> Result<(), VaultError> {
+ pub fn payout(
+ env: Env,
+ winner: Address,
+ amount: i128,
+ asset: Address,
+ match_id: u64,
+ ) -> Result<(), VaultError> {
if amount <= 0 {
return Err(VaultError::InvalidAmount);
}
@@ -356,7 +412,9 @@ impl RenaissanceVaultContract {
.ok_or(VaultError::InsufficientBalance)?;
user_balance.locked -= locked_amount;
user_balance.available += amount; // Add winnings to available balance
- env.storage().persistent().set(&user_balance_key, &user_balance);
+ env.storage()
+ .persistent()
+ .set(&user_balance_key, &user_balance);
// Update vault's totals
let vault_balance_key = DataKey::VaultBalance(asset.clone());
@@ -366,7 +424,9 @@ impl RenaissanceVaultContract {
.get(&vault_balance_key)
.ok_or(VaultError::InsufficientBalance)?;
vault_balance.total_locked -= locked_amount;
- env.storage().persistent().set(&vault_balance_key, &vault_balance);
+ env.storage()
+ .persistent()
+ .set(&vault_balance_key, &vault_balance);
// Transfer the payout (after all state updates)
let client = token::Client::new(&env, &asset);
@@ -375,8 +435,10 @@ impl RenaissanceVaultContract {
Self::exit_reentrancy_guard(&env);
// Emit PaidOut event
- env.events()
- .publish((get_event_topic_by_string(&env, "PaidOut"),), (winner, amount, asset, match_id));
+ env.events().publish(
+ (get_event_topic_by_string(&env, "PaidOut"),),
+ (winner, amount, asset, match_id),
+ );
Ok(())
}
@@ -396,8 +458,7 @@ impl RenaissanceVaultContract {
/// Get a specific user's balance for an asset
pub fn get_user_balance(env: Env, user: Address, asset: Address) -> UserBalance {
- env
- .storage()
+ env.storage()
.persistent()
.get(&DataKey::UserBalance(user, asset))
.unwrap_or_else(|| UserBalance {
@@ -408,11 +469,18 @@ impl RenaissanceVaultContract {
/// Check if funds are locked for a specific bet
pub fn is_locked_for_bet(env: Env, match_id: u64, user: Address, asset: Address) -> bool {
- env.storage().persistent().has(&DataKey::LockedBet(match_id, user, asset))
+ env.storage()
+ .persistent()
+ .has(&DataKey::LockedBet(match_id, user, asset))
}
/// Emergency withdraw of an asset (timelocked)
- pub fn emergency_withdraw(env: Env, asset: Address, to: Address, amount: i128) -> Result<(), VaultError> {
+ pub fn emergency_withdraw(
+ env: Env,
+ asset: Address,
+ to: Address,
+ amount: i128,
+ ) -> Result<(), VaultError> {
let admin: Address = env.storage().instance().get(&DataKey::Admin).unwrap();
admin.require_auth();
@@ -432,15 +500,24 @@ impl RenaissanceVaultContract {
let reason_hash = soroban_sdk::BytesN::from_array(&env, &[0; 32]);
env.events().publish(
(get_event_topic_by_string(&env, "EmergencyAction"),),
- (Symbol::new(&env, "emergency_withdraw"), asset, to, amount, reason_hash),
+ (
+ Symbol::new(&env, "emergency_withdraw"),
+ asset,
+ to,
+ amount,
+ reason_hash,
+ ),
);
Ok(())
} else {
- env.storage().instance().set(&key, &PendingWithdrawal {
- to: to.clone(),
- amount,
- pending_until: env.ledger().timestamp() + 24 * 60 * 60, // 24 hours
- });
+ env.storage().instance().set(
+ &key,
+ &PendingWithdrawal {
+ to: to.clone(),
+ amount,
+ pending_until: env.ledger().timestamp() + 24 * 60 * 60, // 24 hours
+ },
+ );
Ok(())
}
}
@@ -449,17 +526,28 @@ impl RenaissanceVaultContract {
pub fn cancel_emergency_withdraw(env: Env, asset: Address) -> Result<(), VaultError> {
let admin: Address = env.storage().instance().get(&DataKey::Admin).unwrap();
admin.require_auth();
- env.storage().instance().remove(&DataKey::PendingWithdrawal(asset));
+ env.storage()
+ .instance()
+ .remove(&DataKey::PendingWithdrawal(asset));
Ok(())
}
/// Recover accidentally sent tokens (only for non-tracked tokens)
- pub fn recover_token(env: Env, asset: Address, to: Address, amount: i128) -> Result<(), VaultError> {
+ pub fn recover_token(
+ env: Env,
+ asset: Address,
+ to: Address,
+ amount: i128,
+ ) -> Result<(), VaultError> {
let admin: Address = env.storage().instance().get(&DataKey::Admin).unwrap();
admin.require_auth();
let vault_balance_key = DataKey::VaultBalance(asset.clone());
- if let Some(vault_balance) = env.storage().persistent().get::<_, VaultBalance>(&vault_balance_key) {
+ if let Some(vault_balance) = env
+ .storage()
+ .persistent()
+ .get::<_, VaultBalance>(&vault_balance_key)
+ {
if vault_balance.total_deposited > 0 || vault_balance.total_locked > 0 {
return Err(VaultError::TokenIsTracked);
}
@@ -471,7 +559,13 @@ impl RenaissanceVaultContract {
let reason_hash = soroban_sdk::BytesN::from_array(&env, &[0; 32]);
env.events().publish(
(get_event_topic_by_string(&env, "EmergencyAction"),),
- (Symbol::new(&env, "recover_token"), asset, to, amount, reason_hash),
+ (
+ Symbol::new(&env, "recover_token"),
+ asset,
+ to,
+ amount,
+ reason_hash,
+ ),
);
Ok(())
}
@@ -500,10 +594,13 @@ impl RenaissanceVaultContract {
);
Ok(())
} else {
- env.storage().instance().set(&key, &PendingAdminData {
- new_admin: new_admin.clone(),
- pending_until: env.ledger().timestamp() + 48 * 60 * 60, // 48 hours
- });
+ env.storage().instance().set(
+ &key,
+ &PendingAdminData {
+ new_admin: new_admin.clone(),
+ pending_until: env.ledger().timestamp() + 48 * 60 * 60, // 48 hours
+ },
+ );
Ok(())
}
}
@@ -518,4 +615,4 @@ impl RenaissanceVaultContract {
}
#[cfg(test)]
-mod test;
\ No newline at end of file
+mod test;
diff --git a/vault/src/test.rs b/vault/src/test.rs
index b1d01bd..6c98f96 100644
--- a/vault/src/test.rs
+++ b/vault/src/test.rs
@@ -1,21 +1,21 @@
#![cfg(test)]
use super::*;
-use soroban_sdk::{testutils::AddressEnvTestUtils, Address, Env};
use soroban_sdk::testutils::Ledger;
use soroban_sdk::token::Client;
+use soroban_sdk::{testutils::AddressEnvTestUtils, Address, Env};
#[test]
fn test_initialize() {
let env = Env::default();
let admin = Address::generate(&env);
let betting_contract = Address::generate(&env);
-
+
let contract_id = env.register_contract(None, RenaissanceVaultContract);
let client = RenaissanceVaultContractClient::new(&env, &contract_id);
-
+
client.initialize(&admin, &betting_contract);
-
+
// Verify we can get the vault balance (starts at 0)
let asset = Address::generate(&env);
assert_eq!(client.get_vault_balance(&asset), 0);
@@ -28,12 +28,12 @@ fn test_lock_for_bet_unauthorized() {
let admin = Address::generate(&env);
let betting_contract = Address::generate(&env);
let unauthorized = Address::generate(&env);
-
+
let contract_id = env.register_contract(None, RenaissanceVaultContract);
let client = RenaissanceVaultContractClient::new(&env, &contract_id);
-
+
client.initialize(&admin, &betting_contract);
-
+
// Unauthorized address tries to lock funds
let user = Address::generate(&env);
let asset = Address::generate(&env);
@@ -45,39 +45,45 @@ fn test_deposit_withdraw() {
let env = Env::default();
let admin = Address::generate(&env);
let betting_contract = Address::generate(&env);
-
+
// Deploy and initialize token contract (mock SAC)
let token_contract = env.register_stellar_asset_contract(admin.clone());
let token_client = Client::new(&env, &token_contract.get_address());
-
+
// Deploy vault
let vault_id = env.register_contract(None, RenaissanceVaultContract);
let vault_client = RenaissanceVaultContractClient::new(&env, &vault_id);
-
+
vault_client.initialize(&admin, &betting_contract);
-
+
// Mint tokens to a user
let user = Address::generate(&env);
token_client.mint(&user, &1000);
-
+
// Deposit into vault
user.require_auth();
vault_client.deposit(&user, &500, &token_contract.get_address());
-
+
// Check balances
let user_balance = vault_client.get_user_balance(&user, &token_contract.get_address());
assert_eq!(user_balance.available, 500);
assert_eq!(user_balance.locked, 0);
- assert_eq!(vault_client.get_vault_balance(&token_contract.get_address()), 500);
-
+ assert_eq!(
+ vault_client.get_vault_balance(&token_contract.get_address()),
+ 500
+ );
+
// Withdraw some
user.require_auth();
vault_client.withdraw(&user, &200, &token_contract.get_address());
-
+
// Check updated balances
let user_balance = vault_client.get_user_balance(&user, &token_contract.get_address());
assert_eq!(user_balance.available, 300);
- assert_eq!(vault_client.get_vault_balance(&token_contract.get_address()), 300);
+ assert_eq!(
+ vault_client.get_vault_balance(&token_contract.get_address()),
+ 300
+ );
}
#[test]
@@ -86,21 +92,21 @@ fn test_withdraw_insufficient_balance() {
let env = Env::default();
let admin = Address::generate(&env);
let betting_contract = Address::generate(&env);
-
+
let token_contract = env.register_stellar_asset_contract(admin.clone());
let vault_id = env.register_contract(None, RenaissanceVaultContract);
let vault_client = RenaissanceVaultContractClient::new(&env, &vault_id);
-
+
vault_client.initialize(&admin, &betting_contract);
-
+
let user = Address::generate(&env);
let token_client = Client::new(&env, &token_contract.get_address());
token_client.mint(&user, &1000);
-
+
// Deposit 300
user.require_auth();
vault_client.deposit(&user, &300, &token_contract.get_address());
-
+
// Try to withdraw 400 - should fail
user.require_auth();
vault_client.withdraw(&user, &400, &token_contract.get_address());
@@ -111,25 +117,25 @@ fn test_lock_for_bet_success() {
let env = Env::default();
let admin = Address::generate(&env);
let betting_contract = Address::generate(&env);
-
+
let token_contract = env.register_stellar_asset_contract(admin.clone());
let vault_id = env.register_contract(None, RenaissanceVaultContract);
let vault_client = RenaissanceVaultContractClient::new(&env, &vault_id);
-
+
vault_client.initialize(&admin, &betting_contract);
-
+
let user = Address::generate(&env);
let token_client = Client::new(&env, &token_contract.get_address());
token_client.mint(&user, &1000);
-
+
// Deposit funds
user.require_auth();
vault_client.deposit(&user, &500, &token_contract.get_address());
-
+
// Betting contract locks funds for a bet
betting_contract.require_auth();
vault_client.lock_for_bet(&user, &200, &token_contract.get_address(), &123);
-
+
// Check balances after lock
let user_balance = vault_client.get_user_balance(&user, &token_contract.get_address());
assert_eq!(user_balance.available, 300);
@@ -143,21 +149,21 @@ fn test_lock_for_bet_insufficient_balance() {
let env = Env::default();
let admin = Address::generate(&env);
let betting_contract = Address::generate(&env);
-
+
let token_contract = env.register_stellar_asset_contract(admin.clone());
let vault_id = env.register_contract(None, RenaissanceVaultContract);
let vault_client = RenaissanceVaultContractClient::new(&env, &vault_id);
-
+
vault_client.initialize(&admin, &betting_contract);
-
+
let user = Address::generate(&env);
let token_client = Client::new(&env, &token_contract.get_address());
token_client.mint(&user, &1000);
-
+
// Deposit only 100
user.require_auth();
vault_client.deposit(&user, &100, &token_contract.get_address());
-
+
// Try to lock 200 - should fail
betting_contract.require_auth();
vault_client.lock_for_bet(&user, &200, &token_contract.get_address(), &123);
@@ -171,12 +177,12 @@ fn test_reentrancy_prevention() {
let env = Env::default();
let admin = Address::generate(&env);
let betting_contract = Address::generate(&env);
-
+
let vault_id = env.register_contract(None, RenaissanceVaultContract);
let vault_client = RenaissanceVaultContractClient::new(&env, &vault_id);
-
+
vault_client.initialize(&admin, &betting_contract);
-
+
// We'd need a malicious contract to test actual reentrancy, but the guard is in place
// The implementation follows checks-effects-interactions pattern which prevents reentrancy
}
@@ -186,28 +192,28 @@ fn test_payout_success() {
let env = Env::default();
let admin = Address::generate(&env);
let betting_contract = Address::generate(&env);
-
+
let token_contract = env.register_stellar_asset_contract(admin.clone());
let vault_id = env.register_contract(None, RenaissanceVaultContract);
let vault_client = RenaissanceVaultContractClient::new(&env, &vault_id);
-
+
vault_client.initialize(&admin, &betting_contract);
-
+
let winner = Address::generate(&env);
let token_client = Client::new(&env, &token_contract.get_address());
token_client.mint(&winner, &1000);
-
+
// Deposit and lock funds
winner.require_auth();
vault_client.deposit(&winner, &500, &token_contract.get_address());
-
+
betting_contract.require_auth();
vault_client.lock_for_bet(&winner, &200, &token_contract.get_address(), &123);
-
+
// Payout winnings
betting_contract.require_auth();
vault_client.payout(&winner, &300, &token_contract.get_address(), &123);
-
+
// Check final balances
let user_balance = vault_client.get_user_balance(&winner, &token_contract.get_address());
assert_eq!(user_balance.available, 600); // 300 left + 300 winnings
@@ -220,30 +226,30 @@ fn test_emergency_withdraw_schedule_and_execute() {
let env = Env::default();
let admin = Address::generate(&env);
let betting_contract = Address::generate(&env);
-
+
let token_contract = env.register_stellar_asset_contract(admin.clone());
let vault_id = env.register_contract(None, RenaissanceVaultContract);
let vault_client = RenaissanceVaultContractClient::new(&env, &vault_id);
-
+
vault_client.initialize(&admin, &betting_contract);
-
+
let to = Address::generate(&env);
let token_client = Client::new(&env, &token_contract.get_address());
-
+
// Simulate some tokens stuck in the contract
token_client.mint(&vault_id, &1000);
-
+
// Schedule emergency withdraw
admin.require_auth();
vault_client.emergency_withdraw(&token_contract.get_address(), &to, &500);
-
+
// Fast forward past 24 hours
env.ledger().set_timestamp(24 * 60 * 60 + 1);
-
+
// Execute
admin.require_auth();
vault_client.emergency_withdraw(&token_contract.get_address(), &to, &500);
-
+
assert_eq!(token_client.balance(&to), 500);
}
@@ -252,24 +258,24 @@ fn test_emergency_withdraw_cancel() {
let env = Env::default();
let admin = Address::generate(&env);
let betting_contract = Address::generate(&env);
-
+
let token_contract = env.register_stellar_asset_contract(admin.clone());
let vault_id = env.register_contract(None, RenaissanceVaultContract);
let vault_client = RenaissanceVaultContractClient::new(&env, &vault_id);
-
+
vault_client.initialize(&admin, &betting_contract);
let to = Address::generate(&env);
-
+
admin.require_auth();
vault_client.emergency_withdraw(&token_contract.get_address(), &to, &500);
-
+
// Cancel
admin.require_auth();
vault_client.cancel_emergency_withdraw(&token_contract.get_address());
-
+
// Fast forward
env.ledger().set_timestamp(24 * 60 * 60 + 1);
-
+
// Execute should fail because it was cancelled
// It will try to schedule again, so let's just make sure it doesn't transfer
let token_client = Client::new(&env, &token_contract.get_address());
@@ -283,24 +289,24 @@ fn test_set_admin_schedule_and_execute() {
let env = Env::default();
let admin = Address::generate(&env);
let betting_contract = Address::generate(&env);
-
+
let vault_id = env.register_contract(None, RenaissanceVaultContract);
let vault_client = RenaissanceVaultContractClient::new(&env, &vault_id);
vault_client.initialize(&admin, &betting_contract);
-
+
let new_admin = Address::generate(&env);
-
+
// Schedule
admin.require_auth();
vault_client.set_admin(&new_admin);
-
+
// Fast forward past 48 hours
env.ledger().set_timestamp(48 * 60 * 60 + 1);
-
+
// Execute
admin.require_auth();
vault_client.set_admin(&new_admin);
-
+
// To verify admin changed, we could call an admin function
new_admin.require_auth();
vault_client.cancel_set_admin(); // Should succeed with new admin
@@ -311,17 +317,17 @@ fn test_set_admin_cancel() {
let env = Env::default();
let admin = Address::generate(&env);
let betting_contract = Address::generate(&env);
-
+
let vault_id = env.register_contract(None, RenaissanceVaultContract);
let vault_client = RenaissanceVaultContractClient::new(&env, &vault_id);
vault_client.initialize(&admin, &betting_contract);
-
+
let new_admin = Address::generate(&env);
-
+
// Schedule
admin.require_auth();
vault_client.set_admin(&new_admin);
-
+
// Cancel
admin.require_auth();
vault_client.cancel_set_admin();
@@ -332,33 +338,33 @@ fn test_recover_token() {
let env = Env::default();
let admin = Address::generate(&env);
let betting_contract = Address::generate(&env);
-
+
let vault_id = env.register_contract(None, RenaissanceVaultContract);
let vault_client = RenaissanceVaultContractClient::new(&env, &vault_id);
vault_client.initialize(&admin, &betting_contract);
-
+
// Tracked token
let tracked_token = env.register_stellar_asset_contract(admin.clone());
let user = Address::generate(&env);
let token_client = Client::new(&env, &tracked_token.get_address());
token_client.mint(&user, &1000);
-
+
user.require_auth();
vault_client.deposit(&user, &500, &tracked_token.get_address());
-
+
// Recover should fail for tracked token
let to = Address::generate(&env);
admin.require_auth();
let result = vault_client.try_recover_token(&tracked_token.get_address(), &to, &100);
assert!(result.is_err());
-
+
// Untracked token
let untracked_token = env.register_stellar_asset_contract(admin.clone());
let untracked_client = Client::new(&env, &untracked_token.get_address());
untracked_client.mint(&vault_id, &500); // Send directly to contract
-
+
// Recover should succeed
admin.require_auth();
vault_client.recover_token(&untracked_token.get_address(), &to, &500);
assert_eq!(untracked_client.balance(&to), 500);
-}
\ No newline at end of file
+}