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}