11use core :: num :: traits :: Zero ;
2+ use openzeppelin :: token :: erc20 :: interface :: {IERC20Dispatcher , IERC20DispatcherTrait };
3+ use openzeppelin :: utils :: snip12 :: OffchainMessageHash ;
24use snforge_std :: cheatcodes :: events :: {EventSpyTrait , EventsFilterTrait };
5+ use snforge_std :: signature :: stark_curve :: {StarkCurveKeyPairImpl , StarkCurveSignerImpl };
36use snforge_std :: {map_entry_address, store};
47use starknet :: ContractAddress ;
58use starknet_payments :: errors;
69use starknet_payments :: interface :: {
710 IPaymentsDispatcher , IPaymentsDispatcherTrait , IPaymentsSafeDispatcher ,
811 IPaymentsSafeDispatcherTrait ,
912};
13+ use starkware_utils :: constants :: MAX_U128 ;
1014use starkware_utils :: time :: time :: Timestamp ;
1115use starkware_utils_testing :: constants as testing_constants;
1216use starkware_utils_testing :: test_utils :: {
@@ -15,6 +19,7 @@ use starkware_utils_testing::test_utils::{
1519};
1620use crate :: events;
1721use crate :: order :: Order ;
22+ use crate :: payments :: payments :: SNIP12MetadataImpl ;
1823use crate :: tests :: test_utils :: * ;
1924
2025fn default_order () -> Order {
@@ -243,3 +248,291 @@ fn test_failed_handle_order() {
243248 let result = dispatcher . cancel_orders (orders : array! [default_order ()]. span ());
244249 assert_panic_with_error (: result , expected_error : " ONLY_OPERATOR" );
245250}
251+
252+ #[test]
253+ fn test_successful_trade () {
254+ // Setup:
255+ let (contract_address , token_a , token_b , user_a , user_b , key_pair_a , key_pair_b ) = test_setup (
256+ initial_balance : constants :: INITIAL_BALANCE ,
257+ );
258+ let dispatcher = IPaymentsDispatcher { contract_address };
259+ let mut spy = snforge_std :: spy_events ();
260+
261+ // Add tokens.
262+ cheat_caller_address_once (: contract_address , caller_address : testing_constants :: APP_GOVERNOR );
263+ dispatcher . register_token (token : token_a );
264+ cheat_caller_address_once (: contract_address , caller_address : testing_constants :: APP_GOVERNOR );
265+ dispatcher . register_token (token : token_b );
266+
267+ let order_a = Order {
268+ salt : 1 ,
269+ expiry : Timestamp { seconds : 100 },
270+ user : user_a ,
271+ sell_token : token_a ,
272+ buy_token : token_b ,
273+ sell_amount : 10000 ,
274+ buy_amount : 1000 ,
275+ allowed_addresses : array! []. span (),
276+ };
277+
278+ let order_b = Order {
279+ user : user_b ,
280+ sell_token : token_b ,
281+ buy_token : token_a ,
282+ sell_amount : 900 ,
283+ buy_amount : 8000 ,
284+ allowed_addresses : array! [user_a ]. span (),
285+ .. order_a ,
286+ };
287+
288+ let message_hash_a = order_a . get_message_hash (user_a );
289+ let (r , s ) = key_pair_a . sign (message_hash_a ). unwrap ();
290+ let signature_a = array! [r , s ]. span ();
291+
292+ let message_hash_b = order_b . get_message_hash (user_b );
293+ let (r , s ) = key_pair_b . sign (message_hash_b ). unwrap ();
294+ let signature_b = array! [r , s ]. span ();
295+
296+ // Approve tokens:
297+ let token_a_dispatcher = IERC20Dispatcher { contract_address : token_a };
298+ cheat_caller_address_once (token_a , caller_address : user_a );
299+ token_a_dispatcher . approve (spender : contract_address , amount : 10000 );
300+
301+ let token_b_dispatcher = IERC20Dispatcher { contract_address : token_b };
302+ cheat_caller_address_once (token_b , caller_address : user_b );
303+ token_b_dispatcher . approve (spender : contract_address , amount : 10000 );
304+
305+ // Test:
306+ // 8000/900 <= 7560/850 <= 10000/1000
307+ dispatcher
308+ . trade (
309+ : order_a ,
310+ : order_b ,
311+ : signature_a ,
312+ : signature_b ,
313+ order_a_actual_sell_amount : 7560 ,
314+ order_a_actual_buy_amount : 850 ,
315+ );
316+
317+ // Checks:
318+ assert_eq! (token_a_dispatcher . balance_of (user_a ), 2440 );
319+ assert_eq! (token_a_dispatcher . balance_of (user_b ), 7484 );
320+ assert_eq! (token_a_dispatcher . balance_of (constants :: FEE_RECIPIENT ), 76 );
321+
322+ assert_eq! (token_b_dispatcher . balance_of (user_a ), 841 );
323+ assert_eq! (token_b_dispatcher . balance_of (user_b ), 9150 );
324+ assert_eq! (token_b_dispatcher . balance_of (constants :: FEE_RECIPIENT ), 9 );
325+
326+ // Catch the events.
327+ let events = spy . get_events (). emitted_by (contract_address ). events;
328+ let expected_event = events :: TradeExecuted {
329+ user_a ,
330+ user_b ,
331+ sell_token : token_a ,
332+ buy_token : token_b ,
333+ order_a_sell_amount : 7560 ,
334+ order_a_buy_amount : 850 ,
335+ };
336+ assert_expected_event_emitted (
337+ spied_event : events [2 ],
338+ expected_event : expected_event ,
339+ expected_event_selector : @ selector! (" TradeExecuted" ),
340+ expected_event_name : " TradeExecuted" ,
341+ );
342+ }
343+
344+ #[test]
345+ fn test_successful_trade_large_numbers () {
346+ // Setup:
347+ let (contract_address , token_a , token_b , user_a , user_b , key_pair_a , key_pair_b ) = test_setup (
348+ initial_balance : MAX_U128 . into (),
349+ );
350+ let dispatcher = IPaymentsDispatcher { contract_address };
351+
352+ // Add tokens.
353+ cheat_caller_address_once (: contract_address , caller_address : testing_constants :: APP_GOVERNOR );
354+ dispatcher . register_token (token : token_a );
355+ cheat_caller_address_once (: contract_address , caller_address : testing_constants :: APP_GOVERNOR );
356+ dispatcher . register_token (token : token_b );
357+
358+ let order_a = Order {
359+ salt : 1 ,
360+ expiry : Timestamp { seconds : 100 },
361+ user : user_a ,
362+ sell_token : token_a ,
363+ buy_token : token_b ,
364+ sell_amount : MAX_U128 ,
365+ buy_amount : MAX_U128 ,
366+ allowed_addresses : array! [user_b ]. span (),
367+ };
368+
369+ let order_b = Order {
370+ user : user_b ,
371+ sell_token : token_b ,
372+ buy_token : token_a ,
373+ sell_amount : MAX_U128 ,
374+ buy_amount : MAX_U128 ,
375+ allowed_addresses : array! [user_a ]. span (),
376+ .. order_a ,
377+ };
378+
379+ let message_hash_a = order_a . get_message_hash (user_a );
380+ let (r , s ) = key_pair_a . sign (message_hash_a ). unwrap ();
381+ let signature_a = array! [r , s ]. span ();
382+
383+ let message_hash_b = order_b . get_message_hash (user_b );
384+ let (r , s ) = key_pair_b . sign (message_hash_b ). unwrap ();
385+ let signature_b = array! [r , s ]. span ();
386+
387+ // Approve tokens:
388+ let token_a_dispatcher = IERC20Dispatcher { contract_address : token_a };
389+ cheat_caller_address_once (token_a , caller_address : user_a );
390+ token_a_dispatcher . approve (spender : contract_address , amount : MAX_U128 . into ());
391+
392+ let token_b_dispatcher = IERC20Dispatcher { contract_address : token_b };
393+ cheat_caller_address_once (token_b , caller_address : user_b );
394+ token_b_dispatcher . approve (spender : contract_address , amount : MAX_U128 . into ());
395+
396+ // Test:
397+ dispatcher
398+ . trade (
399+ : order_a ,
400+ : order_b ,
401+ : signature_a ,
402+ : signature_b ,
403+ order_a_actual_sell_amount : MAX_U128 ,
404+ order_a_actual_buy_amount : MAX_U128 ,
405+ );
406+
407+ // Checks:
408+ // Plus 1 for rounding up.
409+ let fee : u256 = (MAX_U128 / 100 + 1 ). into ();
410+ assert_eq! (token_a_dispatcher . balance_of (user_a ), 0 );
411+ assert_eq! (token_a_dispatcher . balance_of (user_b ), MAX_U128 . into () - fee );
412+ assert_eq! (token_a_dispatcher . balance_of (constants :: FEE_RECIPIENT ), fee );
413+
414+ assert_eq! (token_b_dispatcher . balance_of (user_a ), MAX_U128 . into () - fee );
415+ assert_eq! (token_b_dispatcher . balance_of (user_b ), 0 );
416+ assert_eq! (token_b_dispatcher . balance_of (constants :: FEE_RECIPIENT ), fee );
417+ }
418+
419+ #[test]
420+ fn test_successful_flow () {
421+ // Setup:
422+ let (contract_address , token_a , token_b , user_a , user_b , key_pair_a , key_pair_b ) = test_setup (
423+ initial_balance : constants :: INITIAL_BALANCE ,
424+ );
425+ let dispatcher = IPaymentsDispatcher { contract_address };
426+
427+ // Add tokens.
428+ cheat_caller_address_once (: contract_address , caller_address : testing_constants :: APP_GOVERNOR );
429+ dispatcher . register_token (token : token_a );
430+ cheat_caller_address_once (: contract_address , caller_address : testing_constants :: APP_GOVERNOR );
431+ dispatcher . register_token (token : token_b );
432+
433+ // Set fee:
434+ cheat_caller_address_once (: contract_address , caller_address : testing_constants :: APP_GOVERNOR );
435+ dispatcher . set_fee_limit (fee_limit : 1000 );
436+
437+ cheat_caller_address_once (: contract_address , caller_address : testing_constants :: OPERATOR );
438+ dispatcher . set_fee (fee : 900 ); // 9%, in this case it would be rounded to 10%.
439+
440+ let order_a = Order {
441+ salt : 1 ,
442+ expiry : Timestamp { seconds : 100 },
443+ user : user_a ,
444+ sell_token : token_a ,
445+ buy_token : token_b ,
446+ sell_amount : 100 ,
447+ buy_amount : 10 ,
448+ allowed_addresses : array! []. span (),
449+ };
450+
451+ let order_b = Order {
452+ user : user_b ,
453+ sell_token : token_b ,
454+ buy_token : token_a ,
455+ sell_amount : 5 ,
456+ buy_amount : 50 ,
457+ allowed_addresses : array! [user_a ]. span (),
458+ .. order_a ,
459+ };
460+
461+ let message_hash_a = order_a . get_message_hash (user_a );
462+ let (r , s ) = key_pair_a . sign (message_hash_a ). unwrap ();
463+ let signature_a = array! [r , s ]. span ();
464+
465+ let message_hash_b = order_b . get_message_hash (user_b );
466+ let (r , s ) = key_pair_b . sign (message_hash_b ). unwrap ();
467+ let signature_b = array! [r , s ]. span ();
468+
469+ // Approve tokens:
470+ let token_a_dispatcher = IERC20Dispatcher { contract_address : token_a };
471+ cheat_caller_address_once (token_a , caller_address : user_a );
472+ token_a_dispatcher . approve (spender : contract_address , amount : 10000 );
473+
474+ let token_b_dispatcher = IERC20Dispatcher { contract_address : token_b };
475+ cheat_caller_address_once (token_b , caller_address : user_b );
476+ token_b_dispatcher . approve (spender : contract_address , amount : 10000 );
477+
478+ // Stage 1:
479+
480+ // Test:
481+ dispatcher
482+ . trade (
483+ : order_a ,
484+ : order_b ,
485+ : signature_a ,
486+ : signature_b ,
487+ order_a_actual_sell_amount : 50 ,
488+ order_a_actual_buy_amount : 5 ,
489+ );
490+
491+ // Checks:
492+ assert_eq! (token_a_dispatcher . balance_of (user_a ), 9950 );
493+ assert_eq! (token_a_dispatcher . balance_of (user_b ), 45 );
494+ assert_eq! (token_a_dispatcher . balance_of (constants :: FEE_RECIPIENT ), 5 );
495+
496+ assert_eq! (token_b_dispatcher . balance_of (user_a ), 4 );
497+ assert_eq! (token_b_dispatcher . balance_of (user_b ), 9995 );
498+ assert_eq! (token_b_dispatcher . balance_of (constants :: FEE_RECIPIENT ), 1 );
499+
500+ assert_eq! (dispatcher . get_order_fulfillment (message_hash_a ), 50 );
501+ assert_eq! (dispatcher . get_order_fulfillment (message_hash_b ), 5 );
502+
503+ // Stage 2:
504+
505+ let new_fee_recipient : ContractAddress = ' NEW_FEE_RECIPIENT' . try_into (). unwrap ();
506+ cheat_caller_address_once (: contract_address , caller_address : testing_constants :: OPERATOR );
507+ dispatcher . set_fee_recipient (recipient : new_fee_recipient );
508+
509+ let order_b = Order { salt : 2 , .. order_b };
510+ let message_hash_b = order_b . get_message_hash (user_b );
511+ let (r , s ) = key_pair_b . sign (message_hash_b ). unwrap ();
512+ let signature_b = array! [r , s ]. span ();
513+
514+ dispatcher
515+ . trade (
516+ : order_a ,
517+ : order_b ,
518+ : signature_a ,
519+ : signature_b ,
520+ order_a_actual_sell_amount : 50 ,
521+ order_a_actual_buy_amount : 5 ,
522+ );
523+
524+ // Checks:
525+ assert_eq! (token_a_dispatcher . balance_of (user_a ), 9900 );
526+ assert_eq! (token_a_dispatcher . balance_of (user_b ), 90 );
527+ assert_eq! (token_a_dispatcher . balance_of (constants :: FEE_RECIPIENT ), 5 );
528+ assert_eq! (token_a_dispatcher . balance_of (new_fee_recipient ), 5 );
529+
530+ assert_eq! (token_b_dispatcher . balance_of (user_a ), 8 );
531+ assert_eq! (token_b_dispatcher . balance_of (user_b ), 9990 );
532+ assert_eq! (token_b_dispatcher . balance_of (constants :: FEE_RECIPIENT ), 1 );
533+ assert_eq! (token_b_dispatcher . balance_of (new_fee_recipient ), 1 );
534+
535+ assert_eq! (dispatcher . get_order_fulfillment (message_hash_a ), 100 );
536+ assert_eq! (dispatcher . get_order_fulfillment (message_hash_b ), 5 );
537+ }
538+
0 commit comments