Skip to main content

cow_types/
trade.rs

1//! Trade and swap encoding for `CoW` Protocol settlements.
2//!
3//! Mirrors `encodeTrade`, `encodeSwapStep`, `decodeOrder`, and the
4//! `TokenRegistry` class from the `TypeScript` `contracts-ts` package.
5
6use alloy_primitives::{Address, B256, Bytes, U256};
7use cow_errors::CowError;
8use foldhash::HashMap;
9
10use crate::{
11    SigningScheme, UnsignedOrder,
12    flags::{
13        OrderFlags, TradeFlags, decode_order_flags, encode_trade_flags, normalize_buy_token_balance,
14    },
15};
16
17/// Encoded trade data as used in the settlement contract.
18///
19/// Corresponds to the `TypeScript` `Trade` type.
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct EncodedTrade {
22    /// Index of the sell token in the settlement token array.
23    pub sell_token_index: u64,
24    /// Index of the buy token in the settlement token array.
25    pub buy_token_index: u64,
26    /// Address that receives the bought tokens.
27    pub receiver: Address,
28    /// Amount of sell token.
29    pub sell_amount: U256,
30    /// Amount of buy token.
31    pub buy_amount: U256,
32    /// Order expiry as Unix timestamp.
33    pub valid_to: u32,
34    /// App-data hash.
35    pub app_data: B256,
36    /// Fee amount.
37    pub fee_amount: U256,
38    /// Encoded trade flags.
39    pub flags: u8,
40    /// The executed trade amount.
41    pub executed_amount: U256,
42    /// Signature data.
43    pub signature: Bytes,
44}
45
46/// An encoded Balancer batch swap step.
47///
48/// Corresponds to the `TypeScript` `BatchSwapStep` type.
49#[derive(Debug, Clone, PartialEq, Eq)]
50pub struct BatchSwapStep {
51    /// The Balancer pool ID.
52    pub pool_id: B256,
53    /// Index of the input token in the token array.
54    pub asset_in_index: u64,
55    /// Index of the output token in the token array.
56    pub asset_out_index: u64,
57    /// The amount to swap.
58    pub amount: U256,
59    /// Additional pool user data.
60    pub user_data: Bytes,
61}
62
63/// A Balancer swap used for settling an order against Balancer pools.
64///
65/// Corresponds to the `TypeScript` `Swap` type.
66#[derive(Debug, Clone)]
67pub struct Swap {
68    /// The Balancer pool ID.
69    pub pool_id: B256,
70    /// The swap input token address.
71    pub asset_in: Address,
72    /// The swap output token address.
73    pub asset_out: Address,
74    /// The amount to swap.
75    pub amount: U256,
76    /// Optional additional pool user data.
77    pub user_data: Option<Bytes>,
78}
79
80/// A registry for tracking token addresses by index in a settlement.
81///
82/// Mirrors the `TypeScript` `TokenRegistry` class. Tokens are indexed by their
83/// checksummed address to ensure consistent lookups.
84#[derive(Debug, Clone, Default)]
85pub struct SettlementTokenRegistry {
86    tokens: Vec<Address>,
87    token_map: HashMap<Address, u64>,
88}
89
90impl SettlementTokenRegistry {
91    /// Create a new empty token registry.
92    ///
93    /// # Returns
94    ///
95    /// An empty [`SettlementTokenRegistry`] with no tokens registered.
96    #[must_use]
97    pub fn new() -> Self {
98        Self::default()
99    }
100
101    /// Get the list of token addresses in the registry.
102    ///
103    /// # Returns
104    ///
105    /// A slice of [`Address`] values in registration order.
106    #[must_use]
107    pub fn addresses(&self) -> &[Address] {
108        &self.tokens
109    }
110
111    /// Get the index for a token, adding it to the registry if not yet seen.
112    ///
113    /// # Arguments
114    ///
115    /// * `token` — the token address to look up or register.
116    ///
117    /// # Returns
118    ///
119    /// The zero-based index of `token` in the registry. If the token was not
120    /// previously registered, it is appended and its new index is returned.
121    ///
122    /// ```
123    /// use alloy_primitives::address;
124    /// use cow_types::trade::SettlementTokenRegistry;
125    ///
126    /// let mut registry = SettlementTokenRegistry::new();
127    /// let token_a = address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
128    /// let token_b = address!("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
129    ///
130    /// assert_eq!(registry.index(token_a), 0);
131    /// assert_eq!(registry.index(token_b), 1);
132    /// assert_eq!(registry.index(token_a), 0); // same token returns same index
133    /// assert_eq!(registry.addresses().len(), 2);
134    /// ```
135    pub fn index(&mut self, token: Address) -> u64 {
136        if let Some(&idx) = self.token_map.get(&token) {
137            return idx;
138        }
139        let idx = self.tokens.len() as u64;
140        self.tokens.push(token);
141        self.token_map.insert(token, idx);
142        idx
143    }
144}
145
146/// Signature data that includes a signing scheme.
147///
148/// This is a simplified Rust version of the `TypeScript` `Signature` union type.
149#[derive(Debug, Clone)]
150pub struct SignatureData {
151    /// The signing scheme.
152    pub scheme: SigningScheme,
153    /// The encoded signature bytes.
154    pub data: Bytes,
155}
156
157/// Encode a trade for the settlement contract.
158///
159/// Mirrors `encodeTrade` from the `TypeScript` SDK.
160///
161/// # Arguments
162///
163/// * `tokens` — the settlement token registry (tokens are added as needed).
164/// * `order` — the unsigned order to encode.
165/// * `signature` — the signature data including scheme and encoded bytes.
166/// * `executed_amount` — the amount already executed for this trade.
167///
168/// # Returns
169///
170/// An [`EncodedTrade`] ready for inclusion in a settlement transaction.
171///
172/// ```ignore
173/// use alloy_primitives::{Address, B256, Bytes, U256, address};
174/// use cow_types::{
175///         trade::{EncodedTrade, SettlementTokenRegistry, SignatureData, encode_trade},
176///         types::UnsignedOrder,
177///     };
178/// use cow_rs::types::{OrderKind, SigningScheme, TokenBalance};  // UNRESOLVED
179///
180/// let mut tokens = SettlementTokenRegistry::new();
181/// let sell = address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
182/// let buy = address!("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
183///
184/// let order = UnsignedOrder {
185///     sell_token: sell,
186///     buy_token: buy,
187///     receiver: Address::ZERO,
188///     sell_amount: U256::from(1000),
189///     buy_amount: U256::from(900),
190///     valid_to: 1_000_000,
191///     app_data: B256::ZERO,
192///     fee_amount: U256::ZERO,
193///     kind: OrderKind::Sell,
194///     partially_fillable: false,
195///     sell_token_balance: TokenBalance::Erc20,
196///     buy_token_balance: TokenBalance::Erc20,
197/// };
198///
199/// let signature =
200///     SignatureData { scheme: SigningScheme::Eip712, data: Bytes::from(vec![0u8; 65]) };
201///
202/// let trade = encode_trade(&mut tokens, &order, &signature, U256::ZERO);
203/// assert_eq!(trade.sell_token_index, 0);
204/// assert_eq!(trade.buy_token_index, 1);
205/// ```
206#[must_use]
207pub fn encode_trade(
208    tokens: &mut SettlementTokenRegistry,
209    order: &UnsignedOrder,
210    signature: &SignatureData,
211    executed_amount: U256,
212) -> EncodedTrade {
213    let trade_flags = TradeFlags {
214        order_flags: OrderFlags {
215            kind: order.kind,
216            partially_fillable: order.partially_fillable,
217            sell_token_balance: order.sell_token_balance,
218            buy_token_balance: normalize_buy_token_balance(order.buy_token_balance),
219        },
220        signing_scheme: signature.scheme,
221    };
222
223    EncodedTrade {
224        sell_token_index: tokens.index(order.sell_token),
225        buy_token_index: tokens.index(order.buy_token),
226        receiver: order.receiver,
227        sell_amount: order.sell_amount,
228        buy_amount: order.buy_amount,
229        valid_to: order.valid_to,
230        app_data: order.app_data,
231        fee_amount: order.fee_amount,
232        flags: encode_trade_flags(&trade_flags),
233        executed_amount,
234        signature: signature.data.clone(),
235    }
236}
237
238/// Decode an order from a settlement trade and token list.
239///
240/// Mirrors `decodeOrder` from the `TypeScript` SDK.
241///
242/// # Arguments
243///
244/// * `trade` — the encoded trade to decode.
245/// * `tokens` — the token address list used to resolve token indices.
246///
247/// # Returns
248///
249/// The reconstructed [`UnsignedOrder`].
250///
251/// # Errors
252///
253/// Returns [`CowError::Parse`] if token indices are out of bounds or flags are
254/// invalid.
255///
256/// ```ignore
257/// use alloy_primitives::{Address, B256, Bytes, U256, address};
258/// use cow_types::trade::{EncodedTrade, decode_order};
259/// use cow_rs::types::{OrderKind, TokenBalance};  // UNRESOLVED
260///
261/// let sell = address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
262/// let buy = address!("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
263///
264/// let trade = EncodedTrade {
265///     sell_token_index: 0,
266///     buy_token_index: 1,
267///     receiver: Address::ZERO,
268///     sell_amount: U256::from(1000),
269///     buy_amount: U256::from(900),
270///     valid_to: 1_000_000,
271///     app_data: B256::ZERO,
272///     fee_amount: U256::ZERO,
273///     flags: 0,
274///     executed_amount: U256::ZERO,
275///     signature: Bytes::new(),
276/// };
277///
278/// let tokens = vec![sell, buy];
279/// let order = decode_order(&trade, &tokens).unwrap();
280/// assert_eq!(order.sell_token, sell);
281/// assert_eq!(order.buy_token, buy);
282/// assert_eq!(order.kind, OrderKind::Sell);
283/// ```
284pub fn decode_order(trade: &EncodedTrade, tokens: &[Address]) -> Result<UnsignedOrder, CowError> {
285    let sell_index = trade.sell_token_index as usize;
286    let buy_index = trade.buy_token_index as usize;
287
288    if sell_index >= tokens.len() || buy_index >= tokens.len() {
289        return Err(CowError::Parse {
290            field: "trade",
291            reason: format!(
292                "token index out of bounds: sell={sell_index}, buy={buy_index}, tokens={}",
293                tokens.len()
294            ),
295        });
296    }
297
298    let flags = decode_order_flags(trade.flags)?;
299
300    Ok(UnsignedOrder {
301        sell_token: tokens[sell_index],
302        buy_token: tokens[buy_index],
303        receiver: trade.receiver,
304        sell_amount: trade.sell_amount,
305        buy_amount: trade.buy_amount,
306        valid_to: trade.valid_to,
307        app_data: trade.app_data,
308        fee_amount: trade.fee_amount,
309        kind: flags.kind,
310        partially_fillable: flags.partially_fillable,
311        sell_token_balance: flags.sell_token_balance,
312        buy_token_balance: flags.buy_token_balance,
313    })
314}
315
316/// Encode a Balancer swap step for the settlement contract.
317///
318/// Mirrors `encodeSwapStep` from the `TypeScript` SDK.
319///
320/// # Arguments
321///
322/// * `tokens` — the settlement token registry (tokens are added as needed).
323/// * `swap` — the swap to encode.
324///
325/// # Returns
326///
327/// A [`BatchSwapStep`] with token addresses resolved to registry indices.
328///
329/// ```
330/// use alloy_primitives::{B256, U256, address};
331/// use cow_types::trade::{SettlementTokenRegistry, Swap, encode_swap_step};
332///
333/// let mut tokens = SettlementTokenRegistry::new();
334/// let swap = Swap {
335///     pool_id: B256::ZERO,
336///     asset_in: address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
337///     asset_out: address!("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
338///     amount: U256::from(1000),
339///     user_data: None,
340/// };
341///
342/// let step = encode_swap_step(&mut tokens, &swap);
343/// assert_eq!(step.asset_in_index, 0);
344/// assert_eq!(step.asset_out_index, 1);
345/// ```
346#[must_use]
347pub fn encode_swap_step(tokens: &mut SettlementTokenRegistry, swap: &Swap) -> BatchSwapStep {
348    BatchSwapStep {
349        pool_id: swap.pool_id,
350        asset_in_index: tokens.index(swap.asset_in),
351        asset_out_index: tokens.index(swap.asset_out),
352        amount: swap.amount,
353        user_data: swap.user_data.clone().unwrap_or_default(),
354    }
355}
356
357/// Encode signature data based on the signing scheme.
358///
359/// Mirrors `encodeSignatureData` from the `TypeScript` SDK.
360///
361/// - For ECDSA schemes (EIP-712, EIP-191): returns the raw signature bytes.
362/// - For EIP-1271: returns the verifier address + signature (see
363///   [`encode_eip1271_signature_data`]).
364/// - For `PreSign`: returns the signer's address as 20 bytes.
365///
366/// # Arguments
367///
368/// * `signature` — the signature data to encode, including its scheme.
369///
370/// # Returns
371///
372/// The encoded signature as [`Bytes`].
373#[must_use]
374pub fn encode_signature_data(signature: &SignatureData) -> Bytes {
375    signature.data.clone()
376}
377
378/// EIP-1271 signature data: a verifier address and the actual signature bytes.
379///
380/// Corresponds to the `TypeScript` `Eip1271SignatureData` type.
381#[derive(Debug, Clone, PartialEq, Eq)]
382pub struct Eip1271SignatureData {
383    /// The verifying contract address.
384    pub verifier: Address,
385    /// The arbitrary signature bytes used for verification.
386    pub signature: Bytes,
387}
388
389/// Encode EIP-1271 signature data as `abi.encodePacked(address, bytes)`.
390///
391/// The `CoW` Protocol settlement contract expects EIP-1271 signatures to be
392/// encoded as the 20-byte verifier address followed by the arbitrary
393/// signature bytes.
394///
395/// Mirrors `encodeEip1271SignatureData` from the `TypeScript` SDK.
396///
397/// # Arguments
398///
399/// * `data` — the EIP-1271 verifier address and signature bytes.
400///
401/// # Returns
402///
403/// The packed [`Bytes`] containing `verifier ++ signature`.
404///
405/// ```
406/// use alloy_primitives::{Bytes, address};
407/// use cow_types::trade::{Eip1271SignatureData, encode_eip1271_signature_data};
408///
409/// let data = Eip1271SignatureData {
410///     verifier: address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
411///     signature: Bytes::from(vec![0xde, 0xad, 0xbe, 0xef]),
412/// };
413/// let encoded = encode_eip1271_signature_data(&data);
414/// assert_eq!(encoded.len(), 24); // 20 + 4
415/// ```
416#[must_use]
417pub fn encode_eip1271_signature_data(data: &Eip1271SignatureData) -> Bytes {
418    let mut buf = Vec::with_capacity(20 + data.signature.len());
419    buf.extend_from_slice(data.verifier.as_slice());
420    buf.extend_from_slice(&data.signature);
421    Bytes::from(buf)
422}
423
424/// Extract the owner (verifier) address from an EIP-1271 packed signature.
425///
426/// The first 20 bytes of an EIP-1271 signature encode the verifying contract
427/// address. This function returns that address without parsing the remaining
428/// signature bytes.
429///
430/// Mirrors `decodeSignatureOwner` from the `TypeScript` `contracts-ts` package.
431///
432/// # Arguments
433///
434/// * `data` — the packed EIP-1271 signature bytes.
435///
436/// # Returns
437///
438/// The 20-byte verifier [`Address`] extracted from the start of `data`.
439///
440/// # Errors
441///
442/// Returns [`CowError::Parse`] if the input is less than 20 bytes.
443///
444/// ```
445/// use alloy_primitives::{Bytes, address};
446/// use cow_types::trade::{
447///     Eip1271SignatureData, decode_signature_owner, encode_eip1271_signature_data,
448/// };
449///
450/// let data = Eip1271SignatureData {
451///     verifier: address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
452///     signature: Bytes::from(vec![0xde, 0xad]),
453/// };
454/// let encoded = encode_eip1271_signature_data(&data);
455/// let owner = decode_signature_owner(&encoded).unwrap();
456/// assert_eq!(owner, address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
457/// ```
458pub fn decode_signature_owner(data: &[u8]) -> Result<Address, CowError> {
459    if data.len() < 20 {
460        return Err(CowError::Parse {
461            field: "eip1271_signature",
462            reason: format!("data too short: {} bytes, need at least 20", data.len()),
463        });
464    }
465    Ok(Address::from_slice(&data[..20]))
466}
467
468/// Decode EIP-1271 signature data from the packed format.
469///
470/// The first 20 bytes are the verifier address, and the remaining bytes are
471/// the signature data.
472///
473/// Mirrors `decodeEip1271SignatureData` from the `TypeScript` SDK.
474///
475/// # Arguments
476///
477/// * `data` — the packed EIP-1271 signature bytes to decode.
478///
479/// # Returns
480///
481/// An [`Eip1271SignatureData`] with the verifier address and signature bytes
482/// separated.
483///
484/// # Errors
485///
486/// Returns [`CowError::Parse`] if the input is less than 20 bytes.
487///
488/// ```
489/// use alloy_primitives::{Bytes, address};
490/// use cow_types::trade::{
491///     Eip1271SignatureData, decode_eip1271_signature_data, encode_eip1271_signature_data,
492/// };
493///
494/// let original = Eip1271SignatureData {
495///     verifier: address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
496///     signature: Bytes::from(vec![0xde, 0xad]),
497/// };
498/// let encoded = encode_eip1271_signature_data(&original);
499/// let decoded = decode_eip1271_signature_data(&encoded).unwrap();
500/// assert_eq!(decoded, original);
501/// ```
502pub fn decode_eip1271_signature_data(data: &[u8]) -> Result<Eip1271SignatureData, CowError> {
503    if data.len() < 20 {
504        return Err(CowError::Parse {
505            field: "eip1271_signature",
506            reason: format!("data too short: {} bytes, need at least 20", data.len()),
507        });
508    }
509    let verifier = Address::from_slice(&data[..20]);
510    let signature = Bytes::from(data[20..].to_vec());
511    Ok(Eip1271SignatureData { verifier, signature })
512}
513
514#[cfg(test)]
515mod tests {
516    use super::*;
517    use crate::{OrderKind, TokenBalance};
518    use alloy_primitives::address;
519
520    #[test]
521    fn token_registry_basic() {
522        let mut reg = SettlementTokenRegistry::new();
523        let a = address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
524        let b = address!("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
525
526        assert_eq!(reg.index(a), 0);
527        assert_eq!(reg.index(b), 1);
528        assert_eq!(reg.index(a), 0);
529        assert_eq!(reg.addresses().len(), 2);
530    }
531
532    #[test]
533    fn encode_decode_trade_roundtrip() {
534        let sell = address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
535        let buy = address!("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
536        let mut tokens = SettlementTokenRegistry::new();
537
538        let order = UnsignedOrder {
539            sell_token: sell,
540            buy_token: buy,
541            receiver: Address::ZERO,
542            sell_amount: U256::from(1000),
543            buy_amount: U256::from(900),
544            valid_to: 1_000_000,
545            app_data: B256::ZERO,
546            fee_amount: U256::ZERO,
547            kind: OrderKind::Sell,
548            partially_fillable: false,
549            sell_token_balance: TokenBalance::Erc20,
550            buy_token_balance: TokenBalance::Erc20,
551        };
552
553        let signature =
554            SignatureData { scheme: SigningScheme::Eip712, data: Bytes::from(vec![0u8; 65]) };
555
556        let encoded = encode_trade(&mut tokens, &order, &signature, U256::ZERO);
557        let decoded = decode_order(&encoded, tokens.addresses()).unwrap();
558
559        assert_eq!(decoded.sell_token, sell);
560        assert_eq!(decoded.buy_token, buy);
561        assert_eq!(decoded.kind, OrderKind::Sell);
562        assert_eq!(decoded.sell_amount, U256::from(1000));
563    }
564
565    #[test]
566    fn decode_order_out_of_bounds() {
567        let trade = EncodedTrade {
568            sell_token_index: 5,
569            buy_token_index: 0,
570            receiver: Address::ZERO,
571            sell_amount: U256::ZERO,
572            buy_amount: U256::ZERO,
573            valid_to: 0,
574            app_data: B256::ZERO,
575            fee_amount: U256::ZERO,
576            flags: 0,
577            executed_amount: U256::ZERO,
578            signature: Bytes::new(),
579        };
580        let tokens = vec![Address::ZERO];
581        assert!(decode_order(&trade, &tokens).is_err());
582    }
583
584    #[test]
585    fn eip1271_roundtrip() {
586        let data = Eip1271SignatureData {
587            verifier: address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
588            signature: Bytes::from(vec![1, 2, 3, 4, 5]),
589        };
590        let encoded = encode_eip1271_signature_data(&data);
591        let decoded = decode_eip1271_signature_data(&encoded).unwrap();
592        assert_eq!(decoded, data);
593    }
594
595    #[test]
596    fn eip1271_too_short() {
597        assert!(decode_eip1271_signature_data(&[0u8; 19]).is_err());
598    }
599
600    #[test]
601    fn encode_swap_step_basic() {
602        let mut tokens = SettlementTokenRegistry::new();
603        let swap = Swap {
604            pool_id: B256::ZERO,
605            asset_in: address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
606            asset_out: address!("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
607            amount: U256::from(500),
608            user_data: None,
609        };
610        let step = encode_swap_step(&mut tokens, &swap);
611        assert_eq!(step.asset_in_index, 0);
612        assert_eq!(step.asset_out_index, 1);
613        assert_eq!(step.amount, U256::from(500));
614        assert!(step.user_data.is_empty());
615    }
616
617    #[test]
618    fn encode_swap_step_with_user_data() {
619        let mut tokens = SettlementTokenRegistry::new();
620        let swap = Swap {
621            pool_id: B256::ZERO,
622            asset_in: address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
623            asset_out: address!("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
624            amount: U256::from(500),
625            user_data: Some(Bytes::from(vec![0xDE, 0xAD])),
626        };
627        let step = encode_swap_step(&mut tokens, &swap);
628        assert_eq!(step.user_data, Bytes::from(vec![0xDE, 0xAD]));
629    }
630
631    #[test]
632    fn encode_signature_data_returns_clone() {
633        let sig = SignatureData { scheme: SigningScheme::Eip712, data: Bytes::from(vec![1, 2, 3]) };
634        let encoded = encode_signature_data(&sig);
635        assert_eq!(encoded, Bytes::from(vec![1, 2, 3]));
636    }
637
638    #[test]
639    fn decode_signature_owner_too_short() {
640        assert!(decode_signature_owner(&[0u8; 19]).is_err());
641    }
642
643    #[test]
644    fn decode_signature_owner_exact_20_bytes() {
645        let mut data = [0u8; 20];
646        data[19] = 0x42;
647        let owner = decode_signature_owner(&data).unwrap();
648        assert_eq!(owner.as_slice()[19], 0x42);
649    }
650
651    #[test]
652    fn decode_eip1271_exact_20_bytes_empty_sig() {
653        let data = [0u8; 20];
654        let result = decode_eip1271_signature_data(&data).unwrap();
655        assert_eq!(result.verifier, Address::ZERO);
656        assert!(result.signature.is_empty());
657    }
658
659    #[test]
660    fn encode_decode_trade_partially_fillable() {
661        let sell = address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
662        let buy = address!("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
663        let mut tokens = SettlementTokenRegistry::new();
664
665        let order = UnsignedOrder {
666            sell_token: sell,
667            buy_token: buy,
668            receiver: Address::ZERO,
669            sell_amount: U256::from(1000),
670            buy_amount: U256::from(900),
671            valid_to: 1_000_000,
672            app_data: B256::ZERO,
673            fee_amount: U256::ZERO,
674            kind: OrderKind::Buy,
675            partially_fillable: true,
676            sell_token_balance: TokenBalance::Erc20,
677            buy_token_balance: TokenBalance::Erc20,
678        };
679
680        let signature =
681            SignatureData { scheme: SigningScheme::Eip712, data: Bytes::from(vec![0u8; 65]) };
682
683        let encoded = encode_trade(&mut tokens, &order, &signature, U256::from(100u64));
684        assert_eq!(encoded.executed_amount, U256::from(100u64));
685        let decoded = decode_order(&encoded, tokens.addresses()).unwrap();
686        assert_eq!(decoded.kind, OrderKind::Buy);
687        assert!(decoded.partially_fillable);
688    }
689}