Skip to main content

cow_rs/settlement/
encoder.rs

1//! Settlement encoder for building `GPv2Settlement.settle()` calldata.
2//!
3//! The [`SettlementEncoder`] orchestrates assembling tokens, clearing prices,
4//! trades, and pre/intra/post interactions into the ABI-encoded calldata
5//! expected by the on-chain settlement contract.
6
7use std::fmt;
8
9use alloy_primitives::{Address, U256, keccak256};
10
11use crate::order_signing::{
12    trade::{EncodedTrade, SettlementTokenRegistry, SignatureData, encode_trade},
13    types::UnsignedOrder,
14};
15
16/// The three interaction stages in a `CoW` Protocol settlement.
17///
18/// Interactions execute at different points during the settlement transaction:
19/// - **Pre**: before any trades are executed (e.g., token approvals).
20/// - **Intra**: between trade execution and balance verification.
21/// - **Post**: after all balances are verified (e.g., surplus withdrawals).
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
23pub enum InteractionStage {
24    /// Interactions executed before any trades.
25    Pre = 0,
26    /// Interactions executed between trades and balance checks.
27    Intra = 1,
28    /// Interactions executed after balance verification.
29    Post = 2,
30}
31
32impl fmt::Display for InteractionStage {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        match self {
35            Self::Pre => write!(f, "Pre"),
36            Self::Intra => write!(f, "Intra"),
37            Self::Post => write!(f, "Post"),
38        }
39    }
40}
41
42/// An encoded interaction ready for inclusion in a settlement.
43///
44/// Contains the target contract address, ETH value to send, and the
45/// ABI-encoded calldata.
46#[derive(Debug, Clone, PartialEq, Eq)]
47pub struct EncodedInteraction {
48    /// Target contract address.
49    pub target: Address,
50    /// ETH value to send with the call (in wei).
51    pub value: U256,
52    /// ABI-encoded calldata for the interaction.
53    pub calldata: Vec<u8>,
54}
55
56/// Orchestrates building a complete `GPv2Settlement.settle()` calldata payload.
57///
58/// Collects tokens, clearing prices, trades, and interactions, then
59/// ABI-encodes everything into the format expected by the settlement
60/// contract's `settle` function.
61///
62/// # Example
63///
64/// ```
65/// use alloy_primitives::{Address, B256, Bytes, U256, address};
66/// use cow_rs::{
67///     OrderKind, SigningScheme, TokenBalance,
68///     order_signing::{trade::SignatureData, types::UnsignedOrder},
69///     settlement::encoder::{InteractionStage, SettlementEncoder},
70/// };
71///
72/// let mut encoder = SettlementEncoder::new();
73///
74/// let sell = address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
75/// let buy = address!("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
76///
77/// let sell_idx = encoder.add_token(sell);
78/// let buy_idx = encoder.add_token(buy);
79///
80/// encoder.set_clearing_price(sell_idx, U256::from(1000));
81/// encoder.set_clearing_price(buy_idx, U256::from(900));
82///
83/// assert_eq!(encoder.token_count(), 2);
84/// // Encoder with only tokens/prices but no trades is still "empty".
85/// assert!(encoder.is_empty());
86/// ```
87#[derive(Debug, Clone)]
88pub struct SettlementEncoder {
89    /// Registry mapping token addresses to indices.
90    tokens: SettlementTokenRegistry,
91    /// Clearing prices indexed by token position.
92    clearing_prices: Vec<U256>,
93    /// Encoded trades to include in the settlement.
94    trades: Vec<EncodedTrade>,
95    /// Interactions for each stage: [Pre, Intra, Post].
96    interactions: [Vec<EncodedInteraction>; 3],
97}
98
99impl Default for SettlementEncoder {
100    fn default() -> Self {
101        Self::new()
102    }
103}
104
105impl SettlementEncoder {
106    /// Create a new empty settlement encoder.
107    ///
108    /// # Returns
109    ///
110    /// An empty [`SettlementEncoder`] with no tokens, trades, or interactions.
111    #[must_use]
112    pub fn new() -> Self {
113        Self {
114            tokens: SettlementTokenRegistry::new(),
115            clearing_prices: Vec::new(),
116            trades: Vec::new(),
117            interactions: [Vec::new(), Vec::new(), Vec::new()],
118        }
119    }
120
121    /// Register a token in the settlement and return its index.
122    ///
123    /// If the token is already registered, its existing index is returned.
124    /// New tokens also get a default clearing price of zero.
125    ///
126    /// # Arguments
127    ///
128    /// * `token` - The token [`Address`] to register.
129    ///
130    /// # Returns
131    ///
132    /// The zero-based index of the token in the settlement.
133    pub fn add_token(&mut self, token: Address) -> usize {
134        let idx = self.tokens.index(token) as usize;
135        // Extend clearing prices if a new token was added.
136        if self.clearing_prices.len() <= idx {
137            self.clearing_prices.resize(idx + 1, U256::ZERO);
138        }
139        idx
140    }
141
142    /// Set the clearing price for a token at the given index.
143    ///
144    /// The clearing price array is extended with zeros if needed to
145    /// accommodate the index.
146    ///
147    /// # Arguments
148    ///
149    /// * `token_index` - The zero-based index of the token.
150    /// * `price` - The clearing price as [`U256`].
151    pub fn set_clearing_price(&mut self, token_index: usize, price: U256) {
152        if self.clearing_prices.len() <= token_index {
153            self.clearing_prices.resize(token_index + 1, U256::ZERO);
154        }
155        self.clearing_prices[token_index] = price;
156    }
157
158    /// Encode and append a trade to the settlement.
159    ///
160    /// The order's sell and buy tokens are automatically registered in the
161    /// token registry if not already present.
162    ///
163    /// # Arguments
164    ///
165    /// * `order` - The unsigned order to encode.
166    /// * `signature` - The signature data (scheme + bytes).
167    /// * `executed_amount` - The amount already executed for this trade.
168    pub fn add_trade(
169        &mut self,
170        order: &UnsignedOrder,
171        signature: &SignatureData,
172        executed_amount: U256,
173    ) {
174        let trade = encode_trade(&mut self.tokens, order, signature, executed_amount);
175        // Ensure clearing prices vector covers newly registered tokens.
176        let token_count = self.tokens.addresses().len();
177        if self.clearing_prices.len() < token_count {
178            self.clearing_prices.resize(token_count, U256::ZERO);
179        }
180        self.trades.push(trade);
181    }
182
183    /// Add an interaction to the specified stage.
184    ///
185    /// # Arguments
186    ///
187    /// * `stage` - When the interaction should execute ([`InteractionStage`]).
188    /// * `target` - The contract [`Address`] to call.
189    /// * `value` - ETH value to send with the call (in wei).
190    /// * `calldata` - ABI-encoded calldata for the interaction.
191    pub fn add_interaction(
192        &mut self,
193        stage: InteractionStage,
194        target: Address,
195        value: U256,
196        calldata: Vec<u8>,
197    ) {
198        self.interactions[stage as usize].push(EncodedInteraction { target, value, calldata });
199    }
200
201    /// ABI-encode the full `settle(tokens[], clearingPrices[], trades[], interactions[][])`
202    /// calldata.
203    ///
204    /// Produces the calldata for the `GPv2Settlement.settle()` function,
205    /// including the 4-byte function selector.
206    ///
207    /// # Returns
208    ///
209    /// The complete ABI-encoded calldata as a `Vec<u8>`.
210    #[must_use]
211    pub fn encode_settlement(&self) -> Vec<u8> {
212        let selector = &keccak256(
213            b"settle(address[],uint256[],(uint256,uint256,address,uint256,uint256,uint32,bytes32,uint256,uint256,uint256,bytes)[],(address,uint256,bytes)[][3])",
214        )[..4];
215
216        let tokens = self.tokens.addresses();
217        let mut buf = Vec::with_capacity(4 + 256);
218        buf.extend_from_slice(selector);
219
220        // Dynamic ABI encoding: four parameters, all dynamic.
221        // Head: 4 offsets (each 32 bytes) = 128 bytes from start of params.
222        let head_size: usize = 4 * 32;
223
224        // Encode each dynamic section to calculate offsets.
225        let tokens_enc = abi_encode_address_array(tokens);
226        let prices_enc = abi_encode_u256_array(&self.clearing_prices);
227        let trades_enc = self.abi_encode_trades();
228        let interactions_enc = self.abi_encode_interactions();
229
230        let offset_tokens = head_size;
231        let offset_prices = offset_tokens + tokens_enc.len();
232        let offset_trades = offset_prices + prices_enc.len();
233        let offset_interactions = offset_trades + trades_enc.len();
234
235        // Write head (offsets).
236        buf.extend_from_slice(&abi_u256(U256::from(offset_tokens)));
237        buf.extend_from_slice(&abi_u256(U256::from(offset_prices)));
238        buf.extend_from_slice(&abi_u256(U256::from(offset_trades)));
239        buf.extend_from_slice(&abi_u256(U256::from(offset_interactions)));
240
241        // Write tail (data).
242        buf.extend_from_slice(&tokens_enc);
243        buf.extend_from_slice(&prices_enc);
244        buf.extend_from_slice(&trades_enc);
245        buf.extend_from_slice(&interactions_enc);
246
247        buf
248    }
249
250    /// Return the number of registered tokens.
251    ///
252    /// # Returns
253    ///
254    /// The count of unique tokens in the settlement.
255    #[must_use]
256    pub fn token_count(&self) -> usize {
257        self.tokens.addresses().len()
258    }
259
260    /// Return the number of trades in the settlement.
261    ///
262    /// # Returns
263    ///
264    /// The count of encoded trades.
265    #[must_use]
266    pub const fn trade_count(&self) -> usize {
267        self.trades.len()
268    }
269
270    /// Return the number of interactions for a given stage.
271    ///
272    /// # Arguments
273    ///
274    /// * `stage` - The interaction stage to count.
275    ///
276    /// # Returns
277    ///
278    /// The count of interactions in the specified stage.
279    #[must_use]
280    pub const fn interaction_count(&self, stage: InteractionStage) -> usize {
281        self.interactions[stage as usize].len()
282    }
283
284    /// Check whether the encoder contains any trades or interactions.
285    ///
286    /// # Returns
287    ///
288    /// `true` if there are no trades and no interactions in any stage.
289    #[must_use]
290    pub fn is_empty(&self) -> bool {
291        self.trades.is_empty() && self.interactions.iter().all(Vec::is_empty)
292    }
293
294    /// Reset the encoder, removing all tokens, prices, trades, and interactions.
295    pub fn clear(&mut self) {
296        self.tokens = SettlementTokenRegistry::new();
297        self.clearing_prices.clear();
298        self.trades.clear();
299        for stage in &mut self.interactions {
300            stage.clear();
301        }
302    }
303
304    /// ABI-encode the trades array as a dynamic array of tuples.
305    fn abi_encode_trades(&self) -> Vec<u8> {
306        // Array of dynamic tuples: length + offsets + data.
307        let count = self.trades.len();
308        let mut buf = Vec::new();
309
310        // Array length.
311        buf.extend_from_slice(&abi_u256(U256::from(count)));
312
313        if count == 0 {
314            return buf;
315        }
316
317        // Each trade is a dynamic tuple (contains `bytes signature`).
318        // First write offsets, then data.
319        let mut encoded_trades: Vec<Vec<u8>> = Vec::with_capacity(count);
320        for trade in &self.trades {
321            encoded_trades.push(abi_encode_trade(trade));
322        }
323
324        // Offsets are relative to the start of the offsets block.
325        let offsets_size = count * 32;
326        let mut cumulative = offsets_size;
327        for enc in &encoded_trades {
328            buf.extend_from_slice(&abi_u256(U256::from(cumulative)));
329            cumulative += enc.len();
330        }
331
332        // Actual trade data.
333        for enc in encoded_trades {
334            buf.extend_from_slice(&enc);
335        }
336
337        buf
338    }
339
340    /// ABI-encode the three interaction arrays.
341    fn abi_encode_interactions(&self) -> Vec<u8> {
342        // Fixed-size array of 3 dynamic arrays.
343        // Head: 3 offsets. Tail: 3 encoded arrays.
344        let mut buf = Vec::new();
345
346        let enc0 = abi_encode_interaction_array(&self.interactions[0]);
347        let enc1 = abi_encode_interaction_array(&self.interactions[1]);
348        let enc2 = abi_encode_interaction_array(&self.interactions[2]);
349
350        let head_size = 3 * 32;
351        let offset0 = head_size;
352        let offset1 = offset0 + enc0.len();
353        let offset2 = offset1 + enc1.len();
354
355        buf.extend_from_slice(&abi_u256(U256::from(offset0)));
356        buf.extend_from_slice(&abi_u256(U256::from(offset1)));
357        buf.extend_from_slice(&abi_u256(U256::from(offset2)));
358
359        buf.extend_from_slice(&enc0);
360        buf.extend_from_slice(&enc1);
361        buf.extend_from_slice(&enc2);
362
363        buf
364    }
365}
366
367// ── ABI encoding helpers (private) ─────────────────────────────────────────
368
369/// Encode a [`U256`] as a 32-byte big-endian ABI word.
370const fn abi_u256(v: U256) -> [u8; 32] {
371    v.to_be_bytes()
372}
373
374/// Left-pad an [`Address`] to a 32-byte ABI word.
375fn abi_address(a: Address) -> [u8; 32] {
376    let mut buf = [0u8; 32];
377    buf[12..].copy_from_slice(a.as_slice());
378    buf
379}
380
381/// ABI-encode a dynamic `address[]` array.
382fn abi_encode_address_array(addrs: &[Address]) -> Vec<u8> {
383    let mut buf = Vec::with_capacity(32 + addrs.len() * 32);
384    buf.extend_from_slice(&abi_u256(U256::from(addrs.len())));
385    for addr in addrs {
386        buf.extend_from_slice(&abi_address(*addr));
387    }
388    buf
389}
390
391/// ABI-encode a dynamic `uint256[]` array.
392fn abi_encode_u256_array(values: &[U256]) -> Vec<u8> {
393    let mut buf = Vec::with_capacity(32 + values.len() * 32);
394    buf.extend_from_slice(&abi_u256(U256::from(values.len())));
395    for v in values {
396        buf.extend_from_slice(&abi_u256(*v));
397    }
398    buf
399}
400
401/// ABI-encode a single trade tuple (dynamic due to `bytes signature`).
402fn abi_encode_trade(trade: &EncodedTrade) -> Vec<u8> {
403    // 10 fixed fields (uint256, uint256, address, uint256, uint256, uint32,
404    // bytes32, uint256, uint256, uint256) + 1 dynamic (bytes).
405    let mut buf = Vec::with_capacity(11 * 32 + 32 + trade.signature.len());
406
407    // sell_token_index
408    buf.extend_from_slice(&abi_u256(U256::from(trade.sell_token_index)));
409    // buy_token_index
410    buf.extend_from_slice(&abi_u256(U256::from(trade.buy_token_index)));
411    // receiver
412    buf.extend_from_slice(&abi_address(trade.receiver));
413    // sell_amount
414    buf.extend_from_slice(&abi_u256(trade.sell_amount));
415    // buy_amount
416    buf.extend_from_slice(&abi_u256(trade.buy_amount));
417    // valid_to (uint32, left-padded to 32 bytes)
418    buf.extend_from_slice(&abi_u256(U256::from(trade.valid_to)));
419    // app_data (bytes32)
420    buf.extend_from_slice(trade.app_data.as_slice());
421    // fee_amount
422    buf.extend_from_slice(&abi_u256(trade.fee_amount));
423    // flags (uint256)
424    buf.extend_from_slice(&abi_u256(U256::from(trade.flags)));
425    // executed_amount
426    buf.extend_from_slice(&abi_u256(trade.executed_amount));
427    // signature offset (always 11 * 32 = 352 from tuple start)
428    buf.extend_from_slice(&abi_u256(U256::from(11u64 * 32)));
429    // signature: length + padded data
430    buf.extend_from_slice(&abi_u256(U256::from(trade.signature.len())));
431    buf.extend_from_slice(&trade.signature);
432    // Pad to 32-byte boundary.
433    let padding = (32 - (trade.signature.len() % 32)) % 32;
434    buf.extend_from_slice(&vec![0u8; padding]);
435
436    buf
437}
438
439/// ABI-encode a dynamic array of interaction tuples.
440fn abi_encode_interaction_array(interactions: &[EncodedInteraction]) -> Vec<u8> {
441    let count = interactions.len();
442    let mut buf = Vec::new();
443
444    buf.extend_from_slice(&abi_u256(U256::from(count)));
445
446    if count == 0 {
447        return buf;
448    }
449
450    // Each interaction is a dynamic tuple (address, uint256, bytes).
451    let mut encoded: Vec<Vec<u8>> = Vec::with_capacity(count);
452    for ix in interactions {
453        let mut e = Vec::with_capacity(3 * 32 + 32 + ix.calldata.len());
454        e.extend_from_slice(&abi_address(ix.target));
455        e.extend_from_slice(&abi_u256(ix.value));
456        // Offset to bytes data: 3 * 32 = 96 from tuple start.
457        e.extend_from_slice(&abi_u256(U256::from(3u64 * 32)));
458        // bytes: length + padded data.
459        e.extend_from_slice(&abi_u256(U256::from(ix.calldata.len())));
460        e.extend_from_slice(&ix.calldata);
461        let padding = (32 - (ix.calldata.len() % 32)) % 32;
462        e.extend_from_slice(&vec![0u8; padding]);
463        encoded.push(e);
464    }
465
466    // Offsets then data.
467    let offsets_size = count * 32;
468    let mut cumulative = offsets_size;
469    for enc in &encoded {
470        buf.extend_from_slice(&abi_u256(U256::from(cumulative)));
471        cumulative += enc.len();
472    }
473    for enc in encoded {
474        buf.extend_from_slice(&enc);
475    }
476
477    buf
478}
479
480#[cfg(test)]
481mod tests {
482    use alloy_primitives::{B256, Bytes, address};
483
484    use super::*;
485    use crate::{OrderKind, SigningScheme, TokenBalance};
486
487    fn sample_order() -> UnsignedOrder {
488        UnsignedOrder {
489            sell_token: address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
490            buy_token: address!("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
491            receiver: Address::ZERO,
492            sell_amount: U256::from(1000),
493            buy_amount: U256::from(900),
494            valid_to: 1_000_000,
495            app_data: B256::ZERO,
496            fee_amount: U256::ZERO,
497            kind: OrderKind::Sell,
498            partially_fillable: false,
499            sell_token_balance: TokenBalance::Erc20,
500            buy_token_balance: TokenBalance::Erc20,
501        }
502    }
503
504    fn sample_signature() -> SignatureData {
505        SignatureData { scheme: SigningScheme::Eip712, data: Bytes::from(vec![0u8; 65]) }
506    }
507
508    #[test]
509    fn new_encoder_is_empty() {
510        let enc = SettlementEncoder::new();
511        assert!(enc.is_empty());
512        assert_eq!(enc.token_count(), 0);
513        assert_eq!(enc.trade_count(), 0);
514        assert_eq!(enc.interaction_count(InteractionStage::Pre), 0);
515        assert_eq!(enc.interaction_count(InteractionStage::Intra), 0);
516        assert_eq!(enc.interaction_count(InteractionStage::Post), 0);
517    }
518
519    #[test]
520    fn default_encoder_is_empty() {
521        let enc = SettlementEncoder::default();
522        assert!(enc.is_empty());
523    }
524
525    #[test]
526    fn add_token_registers_and_returns_index() {
527        let mut enc = SettlementEncoder::new();
528        let a = address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
529        let b = address!("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
530
531        assert_eq!(enc.add_token(a), 0);
532        assert_eq!(enc.add_token(b), 1);
533        assert_eq!(enc.add_token(a), 0);
534        assert_eq!(enc.token_count(), 2);
535    }
536
537    #[test]
538    fn set_clearing_price_extends_vec() {
539        let mut enc = SettlementEncoder::new();
540        enc.set_clearing_price(3, U256::from(42));
541        assert_eq!(enc.clearing_prices.len(), 4);
542        assert_eq!(enc.clearing_prices[3], U256::from(42));
543        assert_eq!(enc.clearing_prices[0], U256::ZERO);
544    }
545
546    #[test]
547    fn add_trade_registers_tokens() {
548        let mut enc = SettlementEncoder::new();
549        enc.add_trade(&sample_order(), &sample_signature(), U256::ZERO);
550        assert_eq!(enc.token_count(), 2);
551        assert_eq!(enc.trade_count(), 1);
552        assert!(!enc.is_empty());
553    }
554
555    #[test]
556    fn add_interaction_counts() {
557        let mut enc = SettlementEncoder::new();
558        enc.add_interaction(InteractionStage::Pre, Address::ZERO, U256::ZERO, vec![0xab]);
559        enc.add_interaction(InteractionStage::Post, Address::ZERO, U256::ZERO, vec![]);
560        enc.add_interaction(InteractionStage::Post, Address::ZERO, U256::from(1), vec![0xcd]);
561
562        assert_eq!(enc.interaction_count(InteractionStage::Pre), 1);
563        assert_eq!(enc.interaction_count(InteractionStage::Intra), 0);
564        assert_eq!(enc.interaction_count(InteractionStage::Post), 2);
565        assert!(!enc.is_empty());
566    }
567
568    #[test]
569    fn clear_resets_everything() {
570        let mut enc = SettlementEncoder::new();
571        enc.add_trade(&sample_order(), &sample_signature(), U256::ZERO);
572        enc.add_interaction(InteractionStage::Pre, Address::ZERO, U256::ZERO, vec![]);
573        enc.set_clearing_price(0, U256::from(1000));
574
575        enc.clear();
576        assert!(enc.is_empty());
577        assert_eq!(enc.token_count(), 0);
578        assert_eq!(enc.trade_count(), 0);
579        assert_eq!(enc.interaction_count(InteractionStage::Pre), 0);
580    }
581
582    #[test]
583    fn encode_settlement_starts_with_selector() {
584        let enc = SettlementEncoder::new();
585        let calldata = enc.encode_settlement();
586
587        let expected_selector = &keccak256(
588            b"settle(address[],uint256[],(uint256,uint256,address,uint256,uint256,uint32,bytes32,uint256,uint256,uint256,bytes)[],(address,uint256,bytes)[][3])",
589        )[..4];
590
591        assert_eq!(&calldata[..4], expected_selector);
592    }
593
594    #[test]
595    fn encode_settlement_empty_is_valid() {
596        let enc = SettlementEncoder::new();
597        let calldata = enc.encode_settlement();
598        // 4 (selector) + 4*32 (head offsets) + at least 32*4 (array lengths) = minimum size
599        assert!(calldata.len() >= 4 + 4 * 32);
600    }
601
602    #[test]
603    fn encode_settlement_with_trade() {
604        let mut enc = SettlementEncoder::new();
605        let order = sample_order();
606        let sig = sample_signature();
607
608        let sell_idx = enc.add_token(order.sell_token);
609        let buy_idx = enc.add_token(order.buy_token);
610        enc.set_clearing_price(sell_idx, U256::from(1000));
611        enc.set_clearing_price(buy_idx, U256::from(900));
612        enc.add_trade(&order, &sig, U256::ZERO);
613
614        let calldata = enc.encode_settlement();
615        // Must be non-trivial size with a trade included.
616        assert!(calldata.len() > 4 + 4 * 32 + 2 * 32);
617    }
618
619    #[test]
620    fn encode_settlement_with_interactions() {
621        let mut enc = SettlementEncoder::new();
622        enc.add_interaction(
623            InteractionStage::Pre,
624            address!("cccccccccccccccccccccccccccccccccccccccc"),
625            U256::ZERO,
626            vec![0xde, 0xad, 0xbe, 0xef],
627        );
628        enc.add_interaction(InteractionStage::Post, Address::ZERO, U256::from(1), vec![]);
629
630        let calldata = enc.encode_settlement();
631        assert!(calldata.len() > 4 + 4 * 32);
632    }
633
634    #[test]
635    fn interaction_stage_display() {
636        assert_eq!(format!("{}", InteractionStage::Pre), "Pre");
637        assert_eq!(format!("{}", InteractionStage::Intra), "Intra");
638        assert_eq!(format!("{}", InteractionStage::Post), "Post");
639    }
640
641    #[test]
642    fn interaction_stage_clone_eq() {
643        let a = InteractionStage::Intra;
644        let b = a;
645        assert_eq!(a, b);
646        assert_ne!(InteractionStage::Pre, InteractionStage::Post);
647    }
648
649    #[test]
650    fn encoded_interaction_clone_eq() {
651        let ix = EncodedInteraction {
652            target: Address::ZERO,
653            value: U256::from(42),
654            calldata: vec![0xab, 0xcd],
655        };
656        let ix2 = ix.clone();
657        assert_eq!(ix, ix2);
658    }
659
660    #[test]
661    fn is_empty_with_only_interactions() {
662        let mut enc = SettlementEncoder::new();
663        assert!(enc.is_empty());
664        enc.add_interaction(InteractionStage::Intra, Address::ZERO, U256::ZERO, vec![]);
665        assert!(!enc.is_empty());
666    }
667
668    #[test]
669    fn multiple_trades_encode() {
670        let mut enc = SettlementEncoder::new();
671        let order = sample_order();
672        let sig = sample_signature();
673
674        enc.add_trade(&order, &sig, U256::ZERO);
675        enc.add_trade(&order, &sig, U256::from(100));
676        assert_eq!(enc.trade_count(), 2);
677
678        let calldata = enc.encode_settlement();
679        assert!(calldata.len() > 4 + 4 * 32);
680    }
681
682    #[test]
683    fn add_token_initializes_clearing_price() {
684        let mut enc = SettlementEncoder::new();
685        let idx = enc.add_token(address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
686        assert_eq!(enc.clearing_prices[idx], U256::ZERO);
687    }
688}