Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ The Revenue Pool contract (`contracts/revenue_pool`) operates under the followin
- *Mitigation:* The deployment process must verify the official Stellar USDC (or appropriate wrapped USDC) contract address before initialization. The `init` function guards against re-initialization.

- **Operational Griefing (Balances):** Anyone can effectively transfer USDC to the revenue pool. If an attacker sends unsolicited funds, it increases the `balance()` but does not disrupt the `distribute` logic, as distribution is explicitly controlled by the admin.
- *Mitigation:* The pool does not rely on strict balance equality invariants for its core operations, mitigating balance-based operational griefing. Off-chain monitoring should track `receive_payment` events and native token transfers to reconcile expected vs. actual balances.
- *Mitigation:* The pool does not rely on strict balance equality invariants for its core operations, mitigating balance-based operational griefing. The `receive_payment` entrypoint is admin-only and event-only (no token movement), so indexers should reconcile `receive_payment` logs with actual token transfers.

### Input Validation

Expand Down
5 changes: 3 additions & 2 deletions contracts/revenue_pool/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,15 +144,16 @@ impl RevenuePool {
///
/// # Arguments
/// * `env` - The environment running the contract.
/// * `caller` - Must be admin (or could be extended to allow vault to call).
/// * `caller` - Must be the current admin.
/// * `amount` - Amount received (for event logging).
/// * `from_vault` - Optional; true if the source was the vault.
///
/// # Panics
/// * If the caller is not the current admin (`"unauthorized: caller is not admin"`).
///
/// # Events
/// Emits a `receive_payment` event with `caller` as a topic, and a tuple of `(amount, from_vault)` as data.
/// Emits a `receive_payment` event with `caller` as a topic, and a tuple of
/// `(amount, from_vault)` as data.
pub fn receive_payment(env: Env, caller: Address, amount: i128, from_vault: bool) {
caller.require_auth();
let admin = Self::get_admin(env.clone());
Expand Down
35 changes: 35 additions & 0 deletions contracts/revenue_pool/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,41 @@ fn receive_payment_emits_event() {
assert_eq!(amount_and_source, (250, true));
}

#[test]
#[should_panic(expected = "unauthorized: caller is not admin")]
fn receive_payment_non_admin_panics() {
let env = Env::default();
env.mock_all_auths();
let admin = Address::generate(&env);
let attacker = Address::generate(&env);
let (_, client) = create_pool(&env);
let (usdc, _, _) = create_usdc(&env, &admin);

client.init(&admin, &usdc);
client.receive_payment(&attacker, &250, &true);
}

#[test]
fn receive_payment_is_event_only_and_does_not_move_tokens() {
let env = Env::default();
env.mock_all_auths();
let admin = Address::generate(&env);
let developer = Address::generate(&env);
let (pool_addr, client) = create_pool(&env);
let (usdc_address, usdc_client, usdc_admin) = create_usdc(&env, &admin);

client.init(&admin, &usdc_address);
fund_pool(&usdc_admin, &pool_addr, 500);

let before_pool = usdc_client.balance(&pool_addr);
let before_developer = usdc_client.balance(&developer);

client.receive_payment(&admin, &250, &true);

assert_eq!(usdc_client.balance(&pool_addr), before_pool);
assert_eq!(usdc_client.balance(&developer), before_developer);
}

#[test]
fn batch_distribute_success() {
let env = Env::default();
Expand Down
Loading