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 pub fn absorb(&mut self, other: &Self) {
130 for (address, amount) in other.inputs.iter() {
131 self.ingest_raw_input(*address, *amount);
132 }
133
134 for ((chain_id, output_asset), recipients) in other.outputs.iter() {
135 for (recipient, value) in recipients {
136 self.ingest_raw_output(*chain_id, *output_asset, *recipient, *value);
137 }
138 }
139 }
140}
141
142impl<'a> FromIterator<&'a RollupOrders::Order> for AggregateOrders {
143 fn from_iter<T: IntoIterator<Item = &'a RollupOrders::Order>>(iter: T) -> Self {
144 let mut orders = AggregateOrders::new();
145 orders.extend(iter);
146 orders
147 }
148}
149
150impl<'a> FromIterator<&'a SignedOrder> for AggregateOrders {
151 fn from_iter<T: IntoIterator<Item = &'a SignedOrder>>(iter: T) -> Self {
152 let mut orders = AggregateOrders::new();
153 orders.extend_signed(iter);
154 orders
155 }
156}
157
158impl<'a> From<&'a RollupOrders::Order> for AggregateOrders {
159 fn from(order: &'a RollupOrders::Order) -> Self {
160 let mut orders = AggregateOrders::new();
161 orders.ingest(order);
162 orders
163 }
164}
165
166impl<'a> From<&'a SignedOrder> for AggregateOrders {
167 fn from(order: &'a SignedOrder) -> Self {
168 let mut orders = AggregateOrders::new();
169 orders.ingest_signed(order);
170 orders
171 }
172}
173
174impl<'a> From<&'a AggregateOrders> for Cow<'a, AggregateOrders> {
175 fn from(orders: &'a AggregateOrders) -> Self {
176 Cow::Borrowed(orders)
177 }
178}
179
180#[cfg(test)]
181mod test {
182 use super::*;
183 use alloy::primitives::{Address, U256};
184
185 const ASSET_A: Address = Address::repeat_byte(1);
186 const ASSET_B: Address = Address::repeat_byte(2);
187 const ASSET_C: Address = Address::repeat_byte(3);
188
189 const USER_A: Address = Address::repeat_byte(4);
190 const USER_B: Address = Address::repeat_byte(5);
191 const USER_C: Address = Address::repeat_byte(6);
192
193 fn input(asset: Address, amount: u64) -> RollupOrders::Input {
194 RollupOrders::Input { token: asset, amount: U256::from(amount) }
195 }
196
197 fn output(asset: Address, recipient: Address, amount: u64) -> RollupOrders::Output {
198 RollupOrders::Output { chainId: 1, token: asset, recipient, amount: U256::from(amount) }
199 }
200
201 #[test]
202 fn test_single_order() {
203 let order = RollupOrders::Order {
204 inputs: vec![input(ASSET_A, 100), input(ASSET_B, 200)],
205 outputs: vec![
206 output(ASSET_A, USER_A, 50),
207 output(ASSET_A, USER_B, 50),
208 output(ASSET_B, USER_B, 100),
209 output(ASSET_C, USER_C, 200),
210 output(ASSET_C, USER_C, 200),
211 ],
212 deadline: U256::ZERO,
213 };
214
215 let agg: AggregateOrders = [&order].into_iter().collect();
216 assert_eq!(agg.inputs.get(&ASSET_A), Some(&U256::from(100)), "ASSET_A input");
217 assert_eq!(agg.inputs.get(&ASSET_B), Some(&U256::from(200)), "ASSET_B input");
218
219 assert_eq!(
220 agg.outputs.get(&(1, ASSET_A)).map(|m| m.get(&USER_A)),
221 Some(Some(&U256::from(50))),
222 "ASSET_A USER_A output"
223 );
224 assert_eq!(
225 agg.outputs.get(&(1, ASSET_A)).map(|m| m.get(&USER_B)),
226 Some(Some(&U256::from(50))),
227 "ASSET_A USER_B output"
228 );
229 assert_eq!(
230 agg.outputs.get(&(1, ASSET_B)).map(|m| m.get(&USER_B)),
231 Some(Some(&U256::from(100))),
232 "ASSET_B USER_B output"
233 );
234 assert_eq!(
235 agg.outputs.get(&(1, ASSET_C)).map(|m| m.get(&USER_C)),
236 Some(Some(&U256::from(400))),
237 "ASSET_C USER_C output"
238 );
239 }
240
241 #[test]
242 fn test_two_orders() {
243 let order_1 = RollupOrders::Order {
244 inputs: vec![input(ASSET_A, 100), input(ASSET_B, 200)],
245 outputs: vec![
246 output(ASSET_A, USER_A, 50),
247 output(ASSET_A, USER_B, 50),
248 output(ASSET_B, USER_B, 100),
249 output(ASSET_C, USER_C, 200),
250 output(ASSET_C, USER_C, 200),
251 ],
252 deadline: U256::ZERO,
253 };
254 let order_2 = RollupOrders::Order {
255 inputs: vec![input(ASSET_A, 50), input(ASSET_C, 100)],
256 outputs: vec![
257 output(ASSET_A, USER_A, 50),
258 output(ASSET_B, USER_B, 100),
259 output(ASSET_C, USER_C, 100),
260 ],
261 deadline: U256::ZERO,
262 };
263
264 let agg: AggregateOrders = [&order_1, &order_2].into_iter().collect();
265
266 assert_eq!(agg.inputs.get(&ASSET_A), Some(&U256::from(150)), "ASSET_A input");
267 assert_eq!(agg.inputs.get(&ASSET_B), Some(&U256::from(200)), "ASSET_B input");
268 assert_eq!(agg.inputs.get(&ASSET_C), Some(&U256::from(100)), "ASSET_C input");
269
270 assert_eq!(
271 agg.outputs.get(&(1, ASSET_A)).map(|m| m.get(&USER_A)),
272 Some(Some(&U256::from(100))),
273 "ASSET_A USER_A output"
274 );
275 assert_eq!(
276 agg.outputs.get(&(1, ASSET_A)).map(|m| m.get(&USER_B)),
277 Some(Some(&U256::from(50))),
278 "ASSET_A USER_B output"
279 );
280 assert_eq!(
281 agg.outputs.get(&(1, ASSET_B)).map(|m| m.get(&USER_B)),
282 Some(Some(&U256::from(200))),
283 "ASSET_B USER_B output"
284 );
285 assert_eq!(
286 agg.outputs.get(&(1, ASSET_C)).map(|m| m.get(&USER_C)),
287 Some(Some(&U256::from(500))),
288 "ASSET_C USER_C output"
289 );
290 }
291}