Skip to main content

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