SKIP TO CONTENT

Demo build — mock protocol data. No chain, no ABIs yet.

  • SYNCING: ……

FeeSplitter.t.sol

The 50/30/15/5 split and the 30-day linear team decay.

100 lines3.4 KBSolidity
Source of test/FeeSplitter.t.sol, 100 lines of Solidity
1// SPDX-License-Identifier: MIT
2pragma solidity ^0.8.24;
3 
4import {Base} from "./Base.t.sol";
5import {K401FeeSplitter} from "../src/K401FeeSplitter.sol";
6 
7contract FeeSplitterTest is Base {
8 function _accrueFee(uint256 amount) internal {
9 _giveSeats(alice, (amount / 1e18) + 2);
10 vm.prank(alice);
11 k401.transfer(address(pair), amount);
12 }
13 
14 function test_splitRatios() public view {
15 assertEq(splitter.STOCKDESK_BPS(), 5_000);
16 assertEq(splitter.TREASURY_BPS(), 3_000);
17 assertEq(splitter.LP_BPS(), 1_500);
18 assertEq(splitter.BUYBACK_BPS(), 500);
19 assertEq(
20 splitter.STOCKDESK_BPS() + splitter.TREASURY_BPS() + splitter.LP_BPS() + splitter.BUYBACK_BPS(), 10_000
21 );
22 }
23 
24 function test_teamShareDecaysLinearlyToZeroOver30Days() public {
25 assertEq(splitter.teamShareBps(), 4_000, "40% at t=0");
26 
27 _skip(15 days);
28 assertApproxEqAbs(splitter.teamShareBps(), 2_000, 1, "halfway");
29 
30 _skip(15 days);
31 assertEq(splitter.teamShareBps(), 0, "exactly zero at day 30");
32 
33 _skip(365 days);
34 assertEq(splitter.teamShareBps(), 0, "and zero forever after");
35 }
36 
37 function test_distributeSplitsUsdgAfterTheTeamCut() public {
38 _accrueFee(2_000e18);
39 uint256 fees = k401.balanceOf(address(splitter));
40 assertEq(fees, 100e18, "5% of 2000");
41 
42 _skip(15 days); // team ~20%
43 uint256 teamBps = splitter.teamShareBps();
44 
45 uint256 out = splitter.distribute(0);
46 assertGt(out, 0);
47 
48 uint256 toTeam = (out * teamBps) / 10_000;
49 uint256 rest = out - toTeam;
50 
51 assertEq(usdg.balanceOf(team), toTeam);
52 assertEq(usdg.balanceOf(address(stockDesk)), (rest * 5_000) / 10_000);
53 assertEq(usdg.balanceOf(lpReceiver), (rest * 1_500) / 10_000);
54 // Treasury and buyback also received their cut; treasury already held USDG.
55 assertGt(usdg.balanceOf(address(buyback)), 0);
56 assertEq(k401.balanceOf(address(splitter)), 0, "everything is swept");
57 }
58 
59 function test_afterDay30TheTeamGetsNothing() public {
60 _skip(31 days);
61 _accrueFee(1_000e18);
62 uint256 out = splitter.distribute(0);
63 
64 assertEq(usdg.balanceOf(team), 0);
65 assertEq(usdg.balanceOf(address(stockDesk)), (out * 5_000) / 10_000);
66 assertEq(usdg.balanceOf(lpReceiver), (out * 1_500) / 10_000);
67 }
68 
69 function test_splitterSellsFeeFree() public {
70 // The splitter is whitelisted, so its own sale into the pair pays no second fee.
71 assertTrue(k401.isWhitelisted(address(splitter)));
72 _accrueFee(1_000e18);
73 uint256 fees = k401.balanceOf(address(splitter));
74 splitter.distribute(0);
75 assertEq(k401.balanceOf(address(splitter)), 0);
76 assertGt(fees, 0);
77 }
78 
79 function test_distributeIsPermissionlessAndSlippageBounded() public {
80 _accrueFee(1_000e18);
81 vm.prank(carol);
82 vm.expectRevert(); // min out far too high
83 splitter.distribute(1_000_000e6);
84 
85 vm.prank(carol);
86 splitter.distribute(1);
87 }
88 
89 function test_distributeRevertsWithNothingToDo() public {
90 vm.expectRevert(K401FeeSplitter.NothingToDistribute.selector);
91 splitter.distribute(0);
92 }
93 
94 function test_wiringIsOneShot() public {
95 vm.prank(owner);
96 vm.expectRevert(K401FeeSplitter.AlreadySet.selector);
97 splitter.wire(address(router), alice, alice, alice, alice, alice);
98 }
99}
100 

Click any line number to deep-link to it — the target line highlights on load.