forked from SO4-Markets/contracts
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlib.rs
More file actions
132 lines (115 loc) · 3.83 KB
/
Copy pathlib.rs
File metadata and controls
132 lines (115 loc) · 3.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//! Batch fee sweeper — claims protocol fees across many market/token pairs in one call.
//!
//! This contract is intentionally small and delegates each individual claim to the
//! canonical `fee_handler::claim_fees` entry point so existing accounting,
//! zero-balance skipping, pool-balance caps, and FEE_KEEPER role checks remain the
//! single source of truth.
#![no_std]
use fee_handler::FeeHandlerClient;
use soroban_sdk::{
contract, contracterror, contractevent, contractimpl, contracttype, panic_with_error, Address,
Env, Vec,
};
pub const MAX_BATCH_CLAIM_SIZE: u32 = 20;
#[contracterror]
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
#[repr(u32)]
pub enum Error {
TooManyEntries = 1,
}
#[contractevent(topics = ["fee_batch"])]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct BatchFeesClaimed {
pub keeper: Address,
pub receiver: Address,
pub market_count: u32,
pub token_count: u32,
pub total_claimed: u128,
}
#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct BatchClaimResult {
pub markets: u32,
pub tokens: u32,
pub claims_attempted: u32,
pub total_claimed: u128,
}
#[contract]
pub struct FeeBatchSweeper;
#[contractimpl]
impl FeeBatchSweeper {
/// Claim protocol fees across all market/token combinations in one call.
///
/// `fee_handler` remains responsible for the actual transfer and the
/// FEE_KEEPER authorization check. Zero balances are skipped because
/// `fee_handler::claim_fees` returns `0` for them.
pub fn claim_all_fees(
env: Env,
fee_handler: Address,
keeper: Address,
receiver: Address,
markets: Vec<Address>,
tokens: Vec<Address>,
) -> BatchClaimResult {
keeper.require_auth();
let market_count = markets.len();
let token_count = tokens.len();
let combinations = market_count.saturating_mul(token_count);
if market_count > MAX_BATCH_CLAIM_SIZE
|| token_count > MAX_BATCH_CLAIM_SIZE
|| combinations > MAX_BATCH_CLAIM_SIZE
{
panic_with_error!(&env, Error::TooManyEntries);
}
let fee_handler_client = FeeHandlerClient::new(&env, &fee_handler);
let mut total_claimed: u128 = 0;
let mut claims_attempted: u32 = 0;
for i in 0..market_count {
let market = markets.get_unchecked(i);
for j in 0..token_count {
let token = tokens.get_unchecked(j);
let claimed = fee_handler_client.claim_fees(&keeper, &market, &token, &receiver);
total_claimed = total_claimed.saturating_add(claimed);
claims_attempted = claims_attempted.saturating_add(1);
}
}
env.events().publish_event(&BatchFeesClaimed {
keeper,
receiver,
market_count,
token_count,
total_claimed,
});
BatchClaimResult {
markets: market_count,
tokens: token_count,
claims_attempted,
total_claimed,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use soroban_sdk::{testutils::Address as _, Env};
#[test]
fn max_batch_constant_matches_issue_bound() {
assert_eq!(MAX_BATCH_CLAIM_SIZE, 20);
}
#[test]
fn empty_batches_do_not_attempt_claims() {
let env = Env::default();
env.mock_all_auths();
let contract_id = env.register(FeeBatchSweeper, ());
let client = FeeBatchSweeperClient::new(&env, &contract_id);
let result = client.claim_all_fees(
&Address::generate(&env),
&Address::generate(&env),
&Address::generate(&env),
&Vec::new(&env),
&Vec::new(&env),
);
assert_eq!(result.claims_attempted, 0);
assert_eq!(result.total_claimed, 0);
}
}