diff --git a/Cargo.toml b/Cargo.toml
index 381f7743..973f004a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,15 @@
[workspace]
resolver = "2"
-members = ["contracts/vault", "contracts/settlement", "contracts/revenue_pool"]
+members = [
+ "contracts/vault",
+ "contracts/revenue_pool",
+ "contracts/settlement",
+]
+default-members = [
+ "contracts/vault",
+ "contracts/revenue_pool",
+ "contracts/settlement",
+]
[workspace.dependencies]
soroban-sdk = "22"
diff --git a/contracts/revenue_pool/src/lib.rs b/contracts/revenue_pool/src/lib.rs
index 3d62c703..463fea4e 100644
--- a/contracts/revenue_pool/src/lib.rs
+++ b/contracts/revenue_pool/src/lib.rs
@@ -315,7 +315,7 @@ impl RevenuePool {
let mut total_amount: i128 = 0;
for payment in payments.iter() {
let (_, amount) = payment;
-
+
// Validate each amount is strictly positive
if amount <= 0 {
panic!("{}", ERR_AMOUNT_NOT_POSITIVE);
@@ -346,7 +346,7 @@ impl RevenuePool {
let (to, amount) = payment;
Self::validate_recipient(&to, &contract_address);
usdc.transfer(&contract_address, &to, &amount);
-
+
// Emit event for this leg of the batch
env.events()
.publish((Symbol::new(&env, "batch_distribute"), to), amount);
diff --git a/contracts/vault/src/lib.rs b/contracts/vault/src/lib.rs
index bd2dd0c7..7c67e89e 100644
--- a/contracts/vault/src/lib.rs
+++ b/contracts/vault/src/lib.rs
@@ -82,7 +82,7 @@ impl CalloraVault {
) -> VaultMeta {
owner.require_auth();
let inst = env.storage().instance();
- if inst.has(&StorageKey::Meta) {
+ if inst.has(&StorageKey::MetaKey) {
panic!("vault already initialized");
}
assert!(
@@ -122,7 +122,7 @@ impl CalloraVault {
authorized_caller,
min_deposit: min_d,
};
- inst.set(&StorageKey::Meta, &meta);
+ inst.set(&StorageKey::MetaKey, &meta);
inst.set(&StorageKey::UsdcToken, &usdc_token);
inst.set(&StorageKey::Admin, &owner);
if let Some(p) = revenue_pool {
@@ -231,7 +231,7 @@ impl CalloraVault {
pub fn set_allowed_depositor(env: Env, caller: Address, depositor: Option
) {
caller.require_auth();
- Self::require_owner(env.clone(), caller);
+ Self::require_owner(env.clone(), caller.clone());
match depositor {
Some(d) => {
@@ -285,10 +285,14 @@ impl CalloraVault {
pub fn set_authorized_caller(env: Env, caller: Option) {
let mut meta = Self::get_meta(env.clone());
meta.owner.require_auth();
- meta.authorized_caller = Some(caller.clone());
- env.storage().instance().set(&StorageKey::Meta, &meta);
+ let old_authorized_caller = meta.authorized_caller.clone();
+ meta.authorized_caller = caller.clone();
+ env.storage().instance().set(&StorageKey::MetaKey, &meta);
env.events().publish(
- (Symbol::new(&env, "set_authorized_caller"), meta.owner.clone()),
+ (
+ Symbol::new(&env, "set_authorized_caller"),
+ meta.owner.clone(),
+ ),
(old_authorized_caller, caller),
);
}
@@ -374,7 +378,7 @@ impl CalloraVault {
caller.require_auth();
assert!(amount > 0, "amount must be positive");
assert!(
- Self::is_authorized_depositor(env.clone(), depositor.clone()),
+ Self::is_authorized_depositor(env.clone(), caller.clone()),
"unauthorized: only owner or allowed depositor can deposit"
);
let meta = Self::get_meta(env.clone());
@@ -390,13 +394,13 @@ impl CalloraVault {
.get(&StorageKey::UsdcToken)
.expect("vault not initialized");
let usdc = token::Client::new(&env, &usdc_addr);
- usdc.transfer(&depositor, &env.current_contract_address(), &amount);
+ usdc.transfer(&caller, &env.current_contract_address(), &amount);
let mut meta = Self::get_meta(env.clone());
meta.balance = meta
.balance
.checked_add(amount)
.unwrap_or_else(|| panic!("balance overflow"));
- env.storage().instance().set(&StorageKey::Meta, &meta);
+ env.storage().instance().set(&StorageKey::MetaKey, &meta);
env.events().publish(
(Symbol::new(&env, "deposit"), caller),
(amount, meta.balance),
@@ -425,23 +429,18 @@ impl CalloraVault {
Self::require_not_paused(env.clone());
caller.require_auth();
assert!(amount > 0, "amount must be positive");
- let max_d: i128 = env
- .storage()
- .instance()
- .get(&StorageKey::MaxDeduct)
- .unwrap_or(DEFAULT_MAX_DEDUCT);
+ Self::require_authorized_deduct_caller(env.clone(), &caller);
+ let max_d = Self::get_max_deduct(env.clone());
assert!(amount <= max_d, "deduct amount exceeds max_deduct");
let meta = Self::get_meta(env.clone());
- let auth = caller == meta.owner || meta.authorized_caller.as_ref() == Some(&caller);
- assert!(auth, "unauthorized caller");
assert!(meta.balance >= amount, "insufficient balance");
- Self::require_authorized_deduct_caller(env.clone(), &caller);
+ let settlement = Self::require_settlement(&env);
let mut meta = Self::get_meta(env.clone());
meta.balance = meta
.balance
.checked_sub(amount)
.unwrap_or_else(|| panic!("balance underflow"));
- env.storage().instance().set(&StorageKey::Meta, &meta);
+ env.storage().instance().set(&StorageKey::MetaKey, &meta);
let ut: Address = env
.storage()
.instance()
@@ -474,16 +473,12 @@ impl CalloraVault {
pub fn batch_deduct(env: Env, caller: Address, items: Vec) -> i128 {
Self::require_not_paused(env.clone());
caller.require_auth();
+ Self::require_authorized_deduct_caller(env.clone(), &caller);
let n = items.len();
assert!(n > 0, "batch_deduct requires at least one item");
assert!(n <= MAX_BATCH_SIZE, "batch too large");
- let max_d: i128 = env
- .storage()
- .instance()
- .get(&StorageKey::MaxDeduct)
- .unwrap_or(DEFAULT_MAX_DEDUCT);
+ let max_d = Self::get_max_deduct(env.clone());
let mut meta = Self::get_meta(env.clone());
- Self::require_authorized_deduct_caller(env.clone(), &caller);
let mut running = meta.balance;
let mut total: i128 = 0;
for item in items.iter() {
@@ -497,6 +492,7 @@ impl CalloraVault {
.checked_add(item.amount)
.unwrap_or_else(|| panic!("total overflow"));
}
+ let settlement = Self::require_settlement(&env);
meta.balance = running;
@@ -508,7 +504,14 @@ impl CalloraVault {
Self::transfer_funds(&env, &ut, &settlement, total);
meta.balance = running;
- env.storage().instance().set(&StorageKey::Meta, &meta);
+ env.storage().instance().set(&StorageKey::MetaKey, &meta);
+ for item in items.iter() {
+ let rid = item.request_id.unwrap_or(Symbol::new(&env, ""));
+ env.events().publish(
+ (Symbol::new(&env, "deduct"), caller.clone(), rid),
+ (item.amount, meta.balance),
+ );
+ }
meta.balance
}
@@ -546,7 +549,7 @@ impl CalloraVault {
let mut meta = Self::get_meta(env.clone());
let old = meta.owner.clone();
meta.owner = pending;
- env.storage().instance().set(&StorageKey::Meta, &meta);
+ env.storage().instance().set(&StorageKey::MetaKey, &meta);
env.storage().instance().remove(&StorageKey::PendingOwner);
env.events().publish(
(Symbol::new(&env, "ownership_accepted"), old, meta.owner),
@@ -570,13 +573,10 @@ impl CalloraVault {
.balance
.checked_sub(amount)
.unwrap_or_else(|| panic!("balance underflow"));
- env.storage().instance().set(&StorageKey::Meta, &meta);
+ env.storage().instance().set(&StorageKey::MetaKey, &meta);
env.events().publish(
(Symbol::new(&env, "withdraw"), meta.owner.clone()),
- WithdrawEventData {
- amount,
- new_balance: meta.balance,
- },
+ (amount, meta.balance),
);
meta.balance
}
@@ -597,13 +597,10 @@ impl CalloraVault {
.balance
.checked_sub(amount)
.unwrap_or_else(|| panic!("balance underflow"));
- env.storage().instance().set(&StorageKey::Meta, &meta);
+ env.storage().instance().set(&StorageKey::MetaKey, &meta);
env.events().publish(
(Symbol::new(&env, "withdraw_to"), meta.owner.clone(), to),
- WithdrawEventData {
- amount,
- new_balance: meta.balance,
- },
+ (amount, meta.balance),
);
meta.balance
}
@@ -784,13 +781,6 @@ impl CalloraVault {
.unwrap_or_else(|| panic!("settlement address not set"))
}
- fn get_max_deduct(env: Env) -> i128 {
- env.storage()
- .instance()
- .get(&StorageKey::MaxDeduct)
- .unwrap_or(DEFAULT_MAX_DEDUCT)
- }
-
fn require_not_paused(env: Env) {
assert!(!Self::is_paused(env), "vault is paused");
}
@@ -807,6 +797,22 @@ impl CalloraVault {
"unauthorized: caller is not admin or owner"
);
}
+
+ pub fn add_address(env: Env, caller: Address, depositor: Address) {
+ Self::set_allowed_depositor(env.clone(), caller.clone(), Some(depositor.clone()));
+ env.events()
+ .publish((Symbol::new(&env, "allowlist_add"), caller, depositor), ());
+ }
+
+ pub fn get_allowlist(env: Env) -> Vec {
+ Self::get_allowed_depositors(env)
+ }
+
+ pub fn clear_all(env: Env, caller: Address) {
+ Self::clear_allowed_depositors(env.clone(), caller.clone());
+ env.events()
+ .publish((Symbol::new(&env, "allowlist_clear"), caller), ());
+ }
}
#[cfg(test)]
diff --git a/contracts/vault/src/test.rs b/contracts/vault/src/test.rs
index 80c92d24..d28f839b 100644
--- a/contracts/vault/src/test.rs
+++ b/contracts/vault/src/test.rs
@@ -1118,7 +1118,17 @@ fn deduct_reduces_balance() {
env.mock_all_auths();
fund_vault(&usdc_admin, &vault_address, 300);
- client.init(&owner, &usdc, &Some(300), &Some(caller.clone()), &None, &None, &None);
+ client.init(
+ &owner,
+ &usdc,
+ &Some(300),
+ &Some(caller.clone()),
+ &None,
+ &None,
+ &None,
+ );
+ let settlement = Address::generate(&env);
+ client.set_settlement(&owner, &settlement);
let returned = client.deduct(&owner, &50, &None);
assert_eq!(returned, 250);
@@ -1134,7 +1144,17 @@ fn deduct_with_request_id() {
env.mock_all_auths();
fund_vault(&usdc_admin, &vault_address, 1000);
- client.init(&owner, &usdc, &Some(1000), &Some(caller.clone()), &None, &None, &None);
+ client.init(
+ &owner,
+ &usdc,
+ &Some(1000),
+ &Some(caller.clone()),
+ &None,
+ &None,
+ &None,
+ );
+ let settlement = Address::generate(&env);
+ client.set_settlement(&owner, &settlement);
let remaining = client.deduct(&owner, &100, &Some(Symbol::new(&env, "req123")));
assert_eq!(remaining, 900);
@@ -1149,7 +1169,15 @@ fn deduct_insufficient_balance_fails() {
env.mock_all_auths();
fund_vault(&usdc_admin, &vault_address, 10);
- client.init(&owner, &usdc, &Some(10), &Some(caller.clone()), &None, &None, &None);
+ client.init(
+ &owner,
+ &usdc,
+ &Some(10),
+ &Some(caller.clone()),
+ &None,
+ &None,
+ &None,
+ );
let result = client.try_deduct(&owner, &100, &None);
assert!(result.is_err(), "expected error for insufficient balance");
@@ -1164,7 +1192,17 @@ fn deduct_exact_balance_succeeds() {
env.mock_all_auths();
fund_vault(&usdc_admin, &vault_address, 75);
- client.init(&owner, &usdc, &Some(75), &Some(caller.clone()), &None, &None, &None);
+ client.init(
+ &owner,
+ &usdc,
+ &Some(75),
+ &Some(caller.clone()),
+ &None,
+ &None,
+ &None,
+ );
+ let settlement = Address::generate(&env);
+ client.set_settlement(&owner, &settlement);
let remaining = client.deduct(&owner, &75, &None);
assert_eq!(remaining, 0);
@@ -1180,7 +1218,17 @@ fn deduct_event_contains_request_id() {
env.mock_all_auths();
fund_vault(&usdc_admin, &vault_address, 500);
- client.init(&owner, &usdc, &Some(500), &Some(caller.clone()), &None, &None, &None);
+ client.init(
+ &owner,
+ &usdc,
+ &Some(500),
+ &Some(caller.clone()),
+ &None,
+ &None,
+ &None,
+ );
+ let settlement = Address::generate(&env);
+ client.set_settlement(&owner, &settlement);
let request_id = Symbol::new(&env, "api_call_42");
client.deduct(&owner, &150, &Some(request_id.clone()));
@@ -1274,7 +1322,17 @@ fn deduct_event_no_request_id_uses_empty_symbol() {
env.mock_all_auths();
fund_vault(&usdc_admin, &vault_address, 300);
- client.init(&owner, &usdc, &Some(300), &Some(caller.clone()), &None, &None, &None);
+ client.init(
+ &owner,
+ &usdc,
+ &Some(300),
+ &Some(caller.clone()),
+ &None,
+ &None,
+ &None,
+ );
+ let settlement = Address::generate(&env);
+ client.set_settlement(&owner, &settlement);
client.deduct(&caller, &100, &None);
let events = env.events().all();
@@ -1304,7 +1362,15 @@ fn deduct_zero_panics() {
env.mock_all_auths();
fund_vault(&usdc_admin, &vault_address, 500);
- client.init(&owner, &usdc, &Some(500), &Some(caller.clone()), &None, &None, &None);
+ client.init(
+ &owner,
+ &usdc,
+ &Some(500),
+ &Some(caller.clone()),
+ &None,
+ &None,
+ &None,
+ );
client.deduct(&caller, &0, &None);
}
@@ -1319,7 +1385,15 @@ fn deduct_negative_panics() {
env.mock_all_auths();
fund_vault(&usdc_admin, &vault_address, 100);
- client.init(&owner, &usdc, &Some(100), &Some(caller.clone()), &None, &None, &None);
+ client.init(
+ &owner,
+ &usdc,
+ &Some(100),
+ &Some(caller.clone()),
+ &None,
+ &None,
+ &None,
+ );
client.deduct(&caller, &-50, &None);
}
@@ -1333,7 +1407,15 @@ fn deduct_exceeds_balance_panics() {
env.mock_all_auths();
fund_vault(&usdc_admin, &vault_address, 50);
- client.init(&owner, &usdc, &Some(50), &Some(caller.clone()), &None, &None, &None);
+ client.init(
+ &owner,
+ &usdc,
+ &Some(50),
+ &Some(caller.clone()),
+ &None,
+ &None,
+ &None,
+ );
client.deduct(&caller, &100, &None);
}
@@ -1346,7 +1428,15 @@ fn balance_unchanged_after_failed_deduct() {
env.mock_all_auths();
fund_vault(&usdc_admin, &vault_address, 100);
- client.init(&owner, &usdc, &Some(100), &Some(caller.clone()), &None, &None, &None);
+ client.init(
+ &owner,
+ &usdc,
+ &Some(100),
+ &Some(caller.clone()),
+ &None,
+ &None,
+ &None,
+ );
let _ = client.try_deduct(&owner, &200, &None);
assert_eq!(client.balance(), 100);
@@ -1366,7 +1456,17 @@ fn batch_deduct_multiple_items() {
env.mock_all_auths();
fund_vault(&usdc_admin, &vault_address, 1000);
- client.init(&owner, &usdc, &Some(1000), &Some(caller.clone()), &None, &None, &None);
+ client.init(
+ &owner,
+ &usdc,
+ &Some(1000),
+ &Some(caller.clone()),
+ &None,
+ &None,
+ &None,
+ );
+ let settlement = Address::generate(&env);
+ client.set_settlement(&owner, &settlement);
let items = soroban_sdk::vec![
&env,
@@ -1399,7 +1499,17 @@ fn batch_deduct_events_contain_request_ids() {
env.mock_all_auths();
fund_vault(&usdc_admin, &vault_address, 1000);
- client.init(&owner, &usdc, &Some(1000), &Some(caller.clone()), &None, &None, &None);
+ client.init(
+ &owner,
+ &usdc,
+ &Some(1000),
+ &Some(caller.clone()),
+ &None,
+ &None,
+ &None,
+ );
+ let settlement = Address::generate(&env);
+ client.set_settlement(&owner, &settlement);
let rid_a = Symbol::new(&env, "batch_a");
let rid_b = Symbol::new(&env, "batch_b");
@@ -1455,7 +1565,15 @@ fn batch_deduct_insufficient_balance_fails() {
env.mock_all_auths();
fund_vault(&usdc_admin, &vault_address, 100);
- client.init(&owner, &usdc, &Some(100), &Some(caller.clone()), &None, &None, &None);
+ client.init(
+ &owner,
+ &usdc,
+ &Some(100),
+ &Some(caller.clone()),
+ &None,
+ &None,
+ &None,
+ );
let items = soroban_sdk::vec![
&env,
@@ -1485,7 +1603,15 @@ fn batch_deduct_empty_fails() {
env.mock_all_auths();
fund_vault(&usdc_admin, &vault_address, 100);
- client.init(&owner, &usdc, &Some(100), &Some(caller.clone()), &None, &None, &None);
+ client.init(
+ &owner,
+ &usdc,
+ &Some(100),
+ &Some(caller.clone()),
+ &None,
+ &None,
+ &None,
+ );
let items: soroban_sdk::Vec = soroban_sdk::vec![&env];
let result = client.try_batch_deduct(&caller, &items);
@@ -1502,7 +1628,15 @@ fn batch_deduct_zero_amount_fails() {
env.mock_all_auths();
fund_vault(&usdc_admin, &vault_address, 100);
- client.init(&owner, &usdc, &Some(100), &Some(caller.clone()), &None, &None, &None);
+ client.init(
+ &owner,
+ &usdc,
+ &Some(100),
+ &Some(caller.clone()),
+ &None,
+ &None,
+ &None,
+ );
let items = soroban_sdk::vec![
&env,
@@ -2188,7 +2322,15 @@ fn vault_full_lifecycle() {
// Init with 500 balance, min_deposit = 10
fund_vault(&usdc_admin, &vault_address, 500);
- let meta = client.init(&owner, &usdc, &Some(500), &None, &Some(10), &None, &None);
+ let meta = client.init(
+ &owner,
+ &usdc,
+ &Some(500),
+ &Some(caller.clone()),
+ &Some(10),
+ &None,
+ &None,
+ );
assert_eq!(meta.balance, 500);
assert_eq!(meta.owner, owner);
assert_eq!(client.balance(), 500);
@@ -2962,7 +3104,7 @@ fn test_set_authorized_caller() {
env.mock_all_auths();
client.init(&owner, &usdc, &None, &None, &None, &None, &None);
- client.set_authorized_caller(&auth_caller);
+ client.set_authorized_caller(&Some(auth_caller.clone()));
let meta = client.get_meta();
assert_eq!(meta.authorized_caller, Some(auth_caller));
}
@@ -3750,9 +3892,6 @@ fn withdraw_emits_event() {
}
})
.expect("expected withdraw event");
- let payload: WithdrawEventData = ev.2.into_val(&env);
- assert_eq!(payload.amount, 100);
- assert_eq!(payload.new_balance, 200);
let (amt, bal): (i128, i128) = ev.2.into_val(&env);
assert_eq!(amt, 100);
assert_eq!(bal, 200);
@@ -3779,9 +3918,6 @@ fn withdraw_to_emits_event() {
}
})
.expect("expected withdraw_to event");
- let payload: WithdrawEventData = ev.2.into_val(&env);
- assert_eq!(payload.amount, 150);
- assert_eq!(payload.new_balance, 150);
let (amt, bal): (i128, i128) = ev.2.into_val(&env);
assert_eq!(amt, 150);
assert_eq!(bal, 150);
@@ -3884,7 +4020,7 @@ mod fuzz {
let (usdc_addr, usdc_client, usdc_admin) = create_usdc(&env, &owner);
let (vault_addr, client) = create_vault(&env);
- let _settlement = Address::generate(&env);
+ let settlement = Address::generate(&env);
// Pre-fund vault so initial_balance is valid.
usdc_admin.mint(&vault_addr, &initial);
client.init(
@@ -3931,7 +4067,9 @@ mod fuzz {
if paused {
// deposit must fail while paused
assert!(client.try_deposit(&owner, &amount).is_err());
- } else if let (Some(new_sim), Some(new_token_sim)) = (sim.checked_add(amount), token_sim.checked_add(amount)) {
+ } else if let (Some(new_sim), Some(new_token_sim)) =
+ (sim.checked_add(amount), token_sim.checked_add(amount))
+ {
// mint amount to owner to avoid insufficient balance on large fuzz tests
usdc_admin.mint(&owner, &amount);
sim = new_sim;
@@ -3963,11 +4101,6 @@ mod fuzz {
let mut batch_total: i128 = 0;
let mut valid = true;
for _ in 0..n {
- let amt: i128 = rng.gen_range(1..=max_deduct_val);
- items.push_back(DeductItem {
- amount: amt,
- request_id: None,
- });
let amt: i128 = rng.gen_range(1..=op_cap);
batch_total = match batch_total.checked_add(amt) {
Some(v) => v,
@@ -4079,7 +4212,7 @@ mod fuzz {
&owner,
&usdc_addr,
&Some(300),
- &None,
+ &Some(caller.clone()),
&None,
&None,
&Some(200),
@@ -4128,7 +4261,7 @@ mod fuzz {
&owner,
&usdc_addr,
&Some(10_000),
- &None,
+ &Some(caller.clone()),
&None,
&None,
&Some(max_d),
diff --git a/coverage/cobertura.xml b/coverage/cobertura.xml
index ab114dad..c4ad4231 100644
--- a/coverage/cobertura.xml
+++ b/coverage/cobertura.xml
@@ -1 +1 @@
-/home/chukwuemekadr/Documents/Drips/Wave4/Callora-Contracts
\ No newline at end of file
+/home/merlinthewhiz/Documents/Callora-Contracts
\ No newline at end of file
diff --git a/coverage/tarpaulin-report.html b/coverage/tarpaulin-report.html
index 52411463..60e8cea5 100644
--- a/coverage/tarpaulin-report.html
+++ b/coverage/tarpaulin-report.html
@@ -193,8 +193,8 @@