@@ -564,4 +564,83 @@ contract ATokenVaultRevenueSplitterOwnerTest is Test {
564564
565565 assertEq (address (revenueSplitterOwner).balance, 0 );
566566 }
567+
568+ function test_splitRevenue_roundingErrorIsNotAccumulatedAfterManySplits () public {
569+ shareI = 9_000 ; // 90.00%
570+ shareII = 500 ; // 5.00%
571+ shareIII = 500 ; // 5.00%
572+
573+ recipients[0 ].shareInBps = shareI;
574+ recipients[1 ].shareInBps = shareII;
575+ recipients[2 ].shareInBps = shareIII;
576+
577+ revenueSplitterOwner = new ATokenVaultRevenueSplitterOwner (address (vault), owner, recipients);
578+
579+ uint256 accumulatedAmountToSplit;
580+
581+ MockDAI assetToSplit = new MockDAI ();
582+ uint256 assetBalance = 4 ;
583+ uint256 amountToSplit = assetBalance - UNIT_OF_DUST; // = 3
584+ accumulatedAmountToSplit += amountToSplit;
585+ address [] memory assetsToSplit = new address [](1 );
586+ assetsToSplit[0 ] = address (assetToSplit);
587+
588+ /// Split #1: Balance is 4, 3 units of asset to distribute, only recipientI gets revenue
589+
590+ assertEq (assetToSplit.balanceOf (address (revenueSplitterOwner)), 0 , "Unexpected initial splitter balance " );
591+ assetToSplit.mint (address (revenueSplitterOwner), assetBalance);
592+ assertEq (assetToSplit.balanceOf (address (revenueSplitterOwner)), 4 , "Unexpected splitter balance " );
593+
594+ revenueSplitterOwner.splitRevenue (assetsToSplit);
595+
596+ assertEq (assetToSplit.balanceOf (address (recipientI)), 2 , "Unexpected recipientI balance after 1st split " );
597+ assertEq (assetToSplit.balanceOf (address (recipientII)), 0 , "Unexpected recipientII balance after 1st split " );
598+ assertEq (assetToSplit.balanceOf (address (recipientIII)), 0 , "Unexpected recipientIII balance after 1st split " );
599+
600+ /// Split #2 - #10: Balance is 4 (2 new + 1 carried from previous rounding error split dust + 1 from the
601+ /// reserved unit to not fail in aToken transfers), 3 units of asset to distribute, only recipientI gets revenue
602+ for (uint256 i = 1 ; i <= 10 ; i++ ) {
603+ amountToSplit = 2 ;
604+ accumulatedAmountToSplit += amountToSplit;
605+
606+ assetToSplit.mint (address (revenueSplitterOwner), amountToSplit);
607+
608+ revenueSplitterOwner.splitRevenue (assetsToSplit);
609+ }
610+ assertEq (assetToSplit.balanceOf (address (recipientI)), 20 , "Unexpected recipientI balance after 10th split " );
611+ assertEq (assetToSplit.balanceOf (address (recipientII)), 1 , "Unexpected recipientII balance after 10th split " );
612+ assertEq (assetToSplit.balanceOf (address (recipientIII)), 1 , "Unexpected recipientIII balance after 10th split " );
613+
614+ // Split #11: 3 units of asset (2 new + 1 carried from previous split dust), all get revenue
615+
616+ amountToSplit = 2 ;
617+ accumulatedAmountToSplit += amountToSplit;
618+
619+ assetToSplit.mint (address (revenueSplitterOwner), amountToSplit);
620+
621+ revenueSplitterOwner.splitRevenue (assetsToSplit);
622+
623+ assertEq (assetToSplit.balanceOf (address (recipientI)), 22 , "Unexpected recipientI balance after 11th split " );
624+ assertEq (assetToSplit.balanceOf (address (recipientII)), 1 , "Unexpected recipientII balance after 11th split " );
625+ assertEq (assetToSplit.balanceOf (address (recipientIII)), 1 , "Unexpected recipientIII balance after 11th split " );
626+
627+ assertEq (accumulatedAmountToSplit, 25 , "Unexpected accumulated amount to split " );
628+
629+ // The accumulated split is the expected and does not have accumulated rounding errors
630+ assertEq (
631+ assetToSplit.balanceOf (address (recipientI)), accumulatedAmountToSplit * shareI / TOTAL_SHARE_IN_BPS,
632+ "Split has accumulated rounding error for recipientI "
633+ );
634+ assertEq (
635+ assetToSplit.balanceOf (address (recipientII)), accumulatedAmountToSplit * shareII / TOTAL_SHARE_IN_BPS,
636+ "Split has accumulated rounding error for recipientII "
637+ );
638+ assertEq (
639+ assetToSplit.balanceOf (address (recipientIII)), accumulatedAmountToSplit * shareIII / TOTAL_SHARE_IN_BPS,
640+ "Split has accumulated rounding error for recipientIII "
641+ );
642+
643+ // One unit of rounding error dust + one unit of reserved unit to not fail in aToken transfers
644+ assertEq (assetToSplit.balanceOf (address (revenueSplitterOwner)), 2 , "Unexpected final splitter balance " );
645+ }
567646}
0 commit comments