@@ -439,70 +439,6 @@ contract TestRateLimit is Test, IRateLimiterEvents {
439
439
nttManager.completeOutboundQueuedTransfer (0 );
440
440
}
441
441
442
- function testFuzz_outboundRateLimitShouldQueue (uint256 limitAmt , uint256 transferAmt ) public {
443
- // setup
444
- address user_A = address (0x123 );
445
- address user_B = address (0x456 );
446
- DummyToken token = DummyToken (nttManager.token ());
447
- uint8 decimals = token.decimals ();
448
-
449
- // inputs
450
- uint256 totalAmt = (type (uint64 ).max) / (10 ** decimals);
451
- // avoids the ZeroAmount() error
452
- // cannot transfer more than what's available
453
- vm.assume (transferAmt > 0 && transferAmt <= totalAmt);
454
- // this ensures that the transfer is always queued up
455
- vm.assume (limitAmt < transferAmt);
456
-
457
- // mint
458
- token.mintDummy (address (user_A), totalAmt * (10 ** decimals));
459
- uint256 outboundLimit = limitAmt * (10 ** decimals);
460
- nttManager.setOutboundLimit (outboundLimit);
461
-
462
- vm.startPrank (user_A);
463
-
464
- // initiate transfer
465
- uint256 transferAmount = transferAmt * (10 ** decimals);
466
- token.approve (address (nttManager), transferAmount);
467
-
468
- // shouldQueue == true
469
- uint64 qSeq = nttManager.transfer (
470
- transferAmount, chainId, toWormholeFormat (user_B), true , new bytes (1 )
471
- );
472
-
473
- // assert that the transfer got queued up
474
- assertEq (qSeq, 0 );
475
- IRateLimiter.OutboundQueuedTransfer memory qt = nttManager.getOutboundQueuedTransfer (0 );
476
- assertEq (qt.amount.getAmount (), transferAmount.trim (decimals, decimals).getAmount ());
477
- assertEq (qt.recipientChain, chainId);
478
- assertEq (qt.recipient, toWormholeFormat (user_B));
479
- assertEq (qt.txTimestamp, initialBlockTimestamp);
480
-
481
- // assert that the contract also locked funds from the user
482
- assertEq (token.balanceOf (address (user_A)), totalAmt * (10 ** decimals) - transferAmount);
483
- assertEq (token.balanceOf (address (nttManager)), transferAmount);
484
-
485
- // elapse rate limit duration - 1
486
- uint256 durationElapsedTime = initialBlockTimestamp + nttManager.rateLimitDuration ();
487
- vm.warp (durationElapsedTime - 1 );
488
-
489
- // assert that transfer still can't be completed
490
- bytes4 stillQueuedSelector =
491
- bytes4 (keccak256 ("OutboundQueuedTransferStillQueued(uint64,uint256) " ));
492
- vm.expectRevert (abi.encodeWithSelector (stillQueuedSelector, 0 , initialBlockTimestamp));
493
- nttManager.completeOutboundQueuedTransfer (0 );
494
-
495
- // now complete transfer
496
- vm.warp (durationElapsedTime);
497
- uint64 seq = nttManager.completeOutboundQueuedTransfer (0 );
498
- assertEq (seq, 0 );
499
-
500
- // now ensure transfer was removed from queue
501
- bytes4 notFoundSelector = bytes4 (keccak256 ("OutboundQueuedTransferNotFound(uint64) " ));
502
- vm.expectRevert (abi.encodeWithSelector (notFoundSelector, 0 ));
503
- nttManager.completeOutboundQueuedTransfer (0 );
504
- }
505
-
506
442
function test_inboundRateLimit_simple () public {
507
443
address user_B = address (0x456 );
508
444
@@ -762,4 +698,178 @@ contract TestRateLimit is Test, IRateLimiterEvents {
762
698
assertEq (inboundLimitParams.lastTxTimestamp, sendAgainTime);
763
699
}
764
700
}
701
+
702
+ function testFuzz_outboundRateLimitShouldQueue (uint256 limitAmt , uint256 transferAmt ) public {
703
+ // setup
704
+ address user_A = address (0x123 );
705
+ address user_B = address (0x456 );
706
+ DummyToken token = DummyToken (nttManager.token ());
707
+ uint8 decimals = token.decimals ();
708
+
709
+ // inputs
710
+ uint256 totalAmt = (type (uint64 ).max) / (10 ** decimals);
711
+ // avoids the ZeroAmount() error
712
+ // cannot transfer more than what's available
713
+ vm.assume (transferAmt > 0 && transferAmt <= totalAmt);
714
+ // this ensures that the transfer is always queued up
715
+ vm.assume (limitAmt < transferAmt);
716
+
717
+ // mint
718
+ token.mintDummy (address (user_A), totalAmt * (10 ** decimals));
719
+ uint256 outboundLimit = limitAmt * (10 ** decimals);
720
+ nttManager.setOutboundLimit (outboundLimit);
721
+
722
+ vm.startPrank (user_A);
723
+
724
+ // initiate transfer
725
+ uint256 transferAmount = transferAmt * (10 ** decimals);
726
+ token.approve (address (nttManager), transferAmount);
727
+
728
+ // shouldQueue == true
729
+ uint64 qSeq = nttManager.transfer (
730
+ transferAmount, chainId, toWormholeFormat (user_B), true , new bytes (1 )
731
+ );
732
+
733
+ // assert that the transfer got queued up
734
+ assertEq (qSeq, 0 );
735
+ IRateLimiter.OutboundQueuedTransfer memory qt = nttManager.getOutboundQueuedTransfer (0 );
736
+ assertEq (qt.amount.getAmount (), transferAmount.trim (decimals, decimals).getAmount ());
737
+ assertEq (qt.recipientChain, chainId);
738
+ assertEq (qt.recipient, toWormholeFormat (user_B));
739
+ assertEq (qt.txTimestamp, initialBlockTimestamp);
740
+
741
+ // assert that the contract also locked funds from the user
742
+ assertEq (token.balanceOf (address (user_A)), totalAmt * (10 ** decimals) - transferAmount);
743
+ assertEq (token.balanceOf (address (nttManager)), transferAmount);
744
+
745
+ // elapse rate limit duration - 1
746
+ uint256 durationElapsedTime = initialBlockTimestamp + nttManager.rateLimitDuration ();
747
+ vm.warp (durationElapsedTime - 1 );
748
+
749
+ // assert that transfer still can't be completed
750
+ bytes4 stillQueuedSelector =
751
+ bytes4 (keccak256 ("OutboundQueuedTransferStillQueued(uint64,uint256) " ));
752
+ vm.expectRevert (abi.encodeWithSelector (stillQueuedSelector, 0 , initialBlockTimestamp));
753
+ nttManager.completeOutboundQueuedTransfer (0 );
754
+
755
+ // now complete transfer
756
+ vm.warp (durationElapsedTime);
757
+ uint64 seq = nttManager.completeOutboundQueuedTransfer (0 );
758
+ assertEq (seq, 0 );
759
+
760
+ // now ensure transfer was removed from queue
761
+ bytes4 notFoundSelector = bytes4 (keccak256 ("OutboundQueuedTransferNotFound(uint64) " ));
762
+ vm.expectRevert (abi.encodeWithSelector (notFoundSelector, 0 ));
763
+ nttManager.completeOutboundQueuedTransfer (0 );
764
+ }
765
+
766
+ function testFuzz_inboundRateLimitShouldQueue (uint256 inboundLimitAmt , uint256 amount ) public {
767
+ vm.assume (amount > 0 && amount <= type (uint64 ).max);
768
+ vm.assume (inboundLimitAmt < amount);
769
+
770
+ address user_B = address (0x456 );
771
+
772
+ (DummyTransceiver e1 , DummyTransceiver e2 ) =
773
+ TransceiverHelpersLib.setup_transceivers (nttManager);
774
+
775
+ DummyToken token = DummyToken (nttManager.token ());
776
+
777
+ ITransceiverReceiver[] memory transceivers = new ITransceiverReceiver [](1 );
778
+ transceivers[0 ] = e1;
779
+
780
+ TransceiverStructs.NttManagerMessage memory m;
781
+ bytes memory encodedEm;
782
+ uint256 inboundLimit = inboundLimitAmt;
783
+ TrimmedAmount memory trimmedAmount = TrimmedAmount (uint64 (amount), 8 );
784
+ {
785
+ TransceiverStructs.TransceiverMessage memory em;
786
+ (m, em) = TransceiverHelpersLib.attestTransceiversHelper (
787
+ user_B,
788
+ 0 ,
789
+ chainId,
790
+ nttManager,
791
+ nttManager,
792
+ trimmedAmount,
793
+ // TrimmedAmount(50, 8),
794
+ inboundLimit.trim (token.decimals (), token.decimals ()),
795
+ transceivers
796
+ );
797
+ encodedEm = TransceiverStructs.encodeTransceiverMessage (
798
+ TransceiverHelpersLib.TEST_TRANSCEIVER_PAYLOAD_PREFIX, em
799
+ );
800
+ }
801
+
802
+ bytes32 digest =
803
+ TransceiverStructs.nttManagerMessageDigest (TransceiverHelpersLib.SENDING_CHAIN_ID, m);
804
+
805
+ // no quorum yet
806
+ assertEq (token.balanceOf (address (user_B)), 0 );
807
+
808
+ vm.expectEmit (address (nttManager));
809
+ emit InboundTransferQueued (digest);
810
+ e2.receiveMessage (encodedEm);
811
+
812
+ {
813
+ // now we have quorum but it'll hit limit
814
+ IRateLimiter.InboundQueuedTransfer memory qt =
815
+ nttManager.getInboundQueuedTransfer (digest);
816
+ assertEq (qt.amount.getAmount (), trimmedAmount.getAmount ());
817
+ assertEq (qt.txTimestamp, initialBlockTimestamp);
818
+ assertEq (qt.recipient, user_B);
819
+ }
820
+
821
+ // assert that the user doesn't have funds yet
822
+ assertEq (token.balanceOf (address (user_B)), 0 );
823
+
824
+ // change block time to (duration - 1) seconds later
825
+ uint256 durationElapsedTime = initialBlockTimestamp + nttManager.rateLimitDuration ();
826
+ vm.warp (durationElapsedTime - 1 );
827
+
828
+ {
829
+ // assert that transfer still can't be completed
830
+ bytes4 stillQueuedSelector =
831
+ bytes4 (keccak256 ("InboundQueuedTransferStillQueued(bytes32,uint256) " ));
832
+ vm.expectRevert (
833
+ abi.encodeWithSelector (stillQueuedSelector, digest, initialBlockTimestamp)
834
+ );
835
+ nttManager.completeInboundQueuedTransfer (digest);
836
+ }
837
+
838
+ // now complete transfer
839
+ vm.warp (durationElapsedTime);
840
+ nttManager.completeInboundQueuedTransfer (digest);
841
+
842
+ {
843
+ // assert transfer no longer in queue
844
+ bytes4 notQueuedSelector = bytes4 (keccak256 ("InboundQueuedTransferNotFound(bytes32) " ));
845
+ vm.expectRevert (abi.encodeWithSelector (notQueuedSelector, digest));
846
+ nttManager.completeInboundQueuedTransfer (digest);
847
+ }
848
+
849
+ // assert user now has funds
850
+ assertEq (
851
+ token.balanceOf (address (user_B)),
852
+ trimmedAmount.getAmount () * 10 ** (token.decimals () - 8 )
853
+ );
854
+
855
+ // replay protection on executeMsg
856
+ vm.recordLogs ();
857
+ nttManager.executeMsg (
858
+ TransceiverHelpersLib.SENDING_CHAIN_ID, toWormholeFormat (address (nttManager)), m
859
+ );
860
+
861
+ {
862
+ Vm.Log[] memory entries = vm.getRecordedLogs ();
863
+ assertEq (entries.length , 1 );
864
+ assertEq (entries[0 ].topics.length , 3 );
865
+ assertEq (entries[0 ].topics[0 ], keccak256 ("MessageAlreadyExecuted(bytes32,bytes32) " ));
866
+ assertEq (entries[0 ].topics[1 ], toWormholeFormat (address (nttManager)));
867
+ assertEq (
868
+ entries[0 ].topics[2 ],
869
+ TransceiverStructs.nttManagerMessageDigest (
870
+ TransceiverHelpersLib.SENDING_CHAIN_ID, m
871
+ )
872
+ );
873
+ }
874
+ }
765
875
}
0 commit comments