1use crate::SignedOrder;
2use alloy::primitives::{Address, U256};
3use serde::{Deserialize, Serialize};
4use signet_zenith::RollupOrders;
5use std::borrow::Cow;
6use std::collections::{HashMap, HashSet};
7
8#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)]
10pub struct AggregateOrders {
11 pub outputs: HashMap<(u64, Address), HashMap<Address, U256>>,
15 pub inputs: HashMap<Address, U256>,
18}
19
20impl AggregateOrders {
21 pub fn new() -> Self {
23 Default::default()
24 }
25
26 pub fn with_capacity(capacity: usize) -> Self {
29 Self { outputs: HashMap::with_capacity(capacity), inputs: HashMap::with_capacity(capacity) }
30 }
31
32 pub(crate) fn ingest_output(&mut self, output: &RollupOrders::Output) {
34 self.ingest_raw_output(
35 output.chainId as u64,
36 output.token,
37 output.recipient,
38 output.amount,
39 );
40 }
41
42 fn ingest_raw_output(
44 &mut self,
45 chain_id: u64,
46 token: Address,
47 recipient: Address,
48 amount: U256,
49 ) {
50 let entry =
51 self.outputs.entry((chain_id, token)).or_default().entry(recipient).or_default();
52 *entry = entry.saturating_add(amount);
53 }
54
55 pub(crate) fn ingest_input(&mut self, input: &RollupOrders::Input) {
57 self.ingest_raw_input(input.token, input.amount);
58 }
59
60 fn ingest_raw_input(&mut self, token: Address, amount: U256) {
62 let entry = self.inputs.entry(token).or_default();
63 *entry = entry.saturating_add(amount);
64 }
65
66 pub fn ingest(&mut self, order: &RollupOrders::Order) {
68 order.outputs.iter().for_each(|o| self.ingest_output(o));
69 order.inputs.iter().for_each(|i| self.ingest_input(i));
70 }
71
72 pub fn ingest_signed(&mut self, order: &SignedOrder) {
74 order
75 .outputs
76 .iter()
77 .for_each(|o| self.ingest_raw_output(o.chainId as u64, o.token, o.recipient, o.amount));
78 order
79 .permit
80 .permit
81 .permitted
82 .iter()
83 .for_each(|tp| self.ingest_raw_input(tp.token, tp.amount));
84 }
85
86 pub fn extend<'a>(&mut self, orders: impl IntoIterator<Item = &'a RollupOrders::Order>) {
88 for order in orders {
89 self.ingest(order);
90 }
91 }
92
93 pub fn extend_signed<'a>(&mut self, orders: impl IntoIterator<Item = &'a SignedOrder>) {
95 for order in orders {
96 self.ingest_signed(order);
97 }
98 }
99
100 pub fn target_chain_ids(&self) -> Vec<u64> {
102 HashSet::<u64>::from_iter(self.outputs.keys().map(|(chain_id, _)| *chain_id))
103 .into_iter()
104 .collect()
105 }
106
107 pub fn outputs_for(&self, target_chain_id: u64, ru_chain_id: u64) -> Vec<RollupOrders::Output> {
112 let mut o = Vec::new();
113 for ((chain_id, token), recipient_map) in &self.outputs {
114 if *chain_id == target_chain_id {
115 for (recipient, amount) in recipient_map {
116 o.push(RollupOrders::Output {
117 token: *token,
118 amount: U256::from(*amount),
119 recipient: *recipient,
120 chainId: ru_chain_id as u32,
121 });
122 }
123 }
124 }
125 o
126 }
127}
128
129impl<'a> FromIterator<&'a RollupOrders::Order> for AggregateOrders {
130 fn from_iter<T: IntoIterator<Item = &'a RollupOrders::Order>>(iter: T) -> Self {
131 let mut orders = AggregateOrders::new();
132 orders.extend(iter);
133 orders
134 }
135}
136
137impl<'a> FromIterator<&'a SignedOrder> for AggregateOrders {
138 fn from_iter<T: IntoIterator<Item = &'a SignedOrder>>(iter: T) -> Self {
139 let mut orders = AggregateOrders::new();
140 orders.extend_signed(iter);
141 orders
142 }
143}
144
145impl<'a> From<&'a RollupOrders::Order> for AggregateOrders {
146 fn from(order: &'a RollupOrders::Order) -> Self {
147 let mut orders = AggregateOrders::new();
148 orders.ingest(order);
149 orders
150 }
151}
152
153impl<'a> From<&'a SignedOrder> for AggregateOrders {
154 fn from(order: &'a SignedOrder) -> Self {
155 let mut orders = AggregateOrders::new();
156 orders.ingest_signed(order);
157 orders
158 }
159}
160
161impl<'a> From<&'a AggregateOrders> for Cow<'a, AggregateOrders> {
162 fn from(orders: &'a AggregateOrders) -> Self {
163 Cow::Borrowed(orders)
164 }
165}
166
167#[cfg(test)]
168mod test {
169 use super::*;
170 use alloy::primitives::{Address, U256};
171
172 const ASSET_A: Address = Address::repeat_byte(1);
173 const ASSET_B: Address = Address::repeat_byte(2);
174 const ASSET_C: Address = Address::repeat_byte(3);
175
176 const USER_A: Address = Address::repeat_byte(4);
177 const USER_B: Address = Address::repeat_byte(5);
178 const USER_C: Address = Address::repeat_byte(6);
179
180 fn input(asset: Address, amount: u64) -> RollupOrders::Input {
181 RollupOrders::Input { token: asset, amount: U256::from(amount) }
182 }
183
184 fn output(asset: Address, recipient: Address, amount: u64) -> RollupOrders::Output {
185 RollupOrders::Output { chainId: 1, token: asset, recipient, amount: U256::from(amount) }
186 }
187
188 #[test]
189 fn test_single_order() {
190 let order = RollupOrders::Order {
191 inputs: vec![input(ASSET_A, 100), input(ASSET_B, 200)],
192 outputs: vec![
193 output(ASSET_A, USER_A, 50),
194 output(ASSET_A, USER_B, 50),
195 output(ASSET_B, USER_B, 100),
196 output(ASSET_C, USER_C, 200),
197 output(ASSET_C, USER_C, 200),
198 ],
199 deadline: U256::ZERO,
200 };
201
202 let agg: AggregateOrders = [&order].into_iter().collect();
203 assert_eq!(agg.inputs.get(&ASSET_A), Some(&U256::from(100)), "ASSET_A input");
204 assert_eq!(agg.inputs.get(&ASSET_B), Some(&U256::from(200)), "ASSET_B input");
205
206 assert_eq!(
207 agg.outputs.get(&(1, ASSET_A)).map(|m| m.get(&USER_A)),
208 Some(Some(&U256::from(50))),
209 "ASSET_A USER_A output"
210 );
211 assert_eq!(
212 agg.outputs.get(&(1, ASSET_A)).map(|m| m.get(&USER_B)),
213 Some(Some(&U256::from(50))),
214 "ASSET_A USER_B output"
215 );
216 assert_eq!(
217 agg.outputs.get(&(1, ASSET_B)).map(|m| m.get(&USER_B)),
218 Some(Some(&U256::from(100))),
219 "ASSET_B USER_B output"
220 );
221 assert_eq!(
222 agg.outputs.get(&(1, ASSET_C)).map(|m| m.get(&USER_C)),
223 Some(Some(&U256::from(400))),
224 "ASSET_C USER_C output"
225 );
226 }
227
228 #[test]
229 fn test_two_orders() {
230 let order_1 = RollupOrders::Order {
231 inputs: vec![input(ASSET_A, 100), input(ASSET_B, 200)],
232 outputs: vec![
233 output(ASSET_A, USER_A, 50),
234 output(ASSET_A, USER_B, 50),
235 output(ASSET_B, USER_B, 100),
236 output(ASSET_C, USER_C, 200),
237 output(ASSET_C, USER_C, 200),
238 ],
239 deadline: U256::ZERO,
240 };
241 let order_2 = RollupOrders::Order {
242 inputs: vec![input(ASSET_A, 50), input(ASSET_C, 100)],
243 outputs: vec![
244 output(ASSET_A, USER_A, 50),
245 output(ASSET_B, USER_B, 100),
246 output(ASSET_C, USER_C, 100),
247 ],
248 deadline: U256::ZERO,
249 };
250
251 let agg: AggregateOrders = [&order_1, &order_2].into_iter().collect();
252
253 assert_eq!(agg.inputs.get(&ASSET_A), Some(&U256::from(150)), "ASSET_A input");
254 assert_eq!(agg.inputs.get(&ASSET_B), Some(&U256::from(200)), "ASSET_B input");
255 assert_eq!(agg.inputs.get(&ASSET_C), Some(&U256::from(100)), "ASSET_C input");
256
257 assert_eq!(
258 agg.outputs.get(&(1, ASSET_A)).map(|m| m.get(&USER_A)),
259 Some(Some(&U256::from(100))),
260 "ASSET_A USER_A output"
261 );
262 assert_eq!(
263 agg.outputs.get(&(1, ASSET_A)).map(|m| m.get(&USER_B)),
264 Some(Some(&U256::from(50))),
265 "ASSET_A USER_B output"
266 );
267 assert_eq!(
268 agg.outputs.get(&(1, ASSET_B)).map(|m| m.get(&USER_B)),
269 Some(Some(&U256::from(200))),
270 "ASSET_B USER_B output"
271 );
272 assert_eq!(
273 agg.outputs.get(&(1, ASSET_C)).map(|m| m.get(&USER_C)),
274 Some(Some(&U256::from(500))),
275 "ASSET_C USER_C output"
276 );
277 }
278}