SKIP TO CONTENT

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

  • SYNCING: ……

Base.t.sol

Shared test fixture: deploys the system with mocks and funds the actors.

271 lines10.4 KBSolidity
Source of test/Base.t.sol, 271 lines of Solidity
1// SPDX-License-Identifier: MIT
2pragma solidity ^0.8.24;
3 
4import {Test, console2} from "forge-std/Test.sol";
5 
6import {K401} from "../src/K401.sol";
7import {K401Treasury} from "../src/K401Treasury.sol";
8import {K401Oracle} from "../src/K401Oracle.sol";
9import {K401Distributor} from "../src/K401Distributor.sol";
10import {K401Staking} from "../src/K401Staking.sol";
11import {K401SeatRegistry} from "../src/K401SeatRegistry.sol";
12import {K401Seat6551, K401Seat6551Account} from "../src/K401Seat6551.sol";
13import {ERC6551Registry} from "../src/ERC6551Registry.sol";
14import {K401FeeSplitter} from "../src/K401FeeSplitter.sol";
15import {K401StockDesk} from "../src/K401StockDesk.sol";
16import {K401BondDepository} from "../src/K401BondDepository.sol";
17import {K401Buyback} from "../src/K401Buyback.sol";
18import {K401Lens} from "../src/K401Lens.sol";
19import {Symbol, SeatMode} from "../src/interfaces/IK401Interfaces.sol";
20 
21import {MockERC20} from "./mocks/MockERC20.sol";
22import {MockPriceFeed} from "./mocks/MockPriceFeed.sol";
23import {MockUniV2Pair, MockUniV2Router} from "./mocks/MockUniV2.sol";
24 
25interface IMirror {
26 function ownerOf(uint256) external view returns (address);
27 function balanceOf(address) external view returns (uint256);
28 function totalSupply() external view returns (uint256);
29 function transferFrom(address, address, uint256) external;
30 function approve(address, uint256) external;
31 function setApprovalForAll(address, bool) external;
32}
33 
34/// @dev Full-protocol fixture: real contracts, mock USDG / equities / feeds / UniV2.
35abstract contract Base is Test {
36 // ---- actors ----
37 address internal owner = makeAddr("owner");
38 address internal team = makeAddr("team");
39 address internal lpReceiver = makeAddr("lpReceiver");
40 address internal alice = makeAddr("alice");
41 address internal bob = makeAddr("bob");
42 address internal carol = makeAddr("carol");
43 address internal keeper = makeAddr("keeper");
44 
45 // ---- protocol ----
46 K401 internal k401;
47 IMirror internal mirror;
48 K401Treasury internal treasury;
49 K401Oracle internal oracle;
50 K401SeatRegistry internal registry;
51 K401Staking internal staking;
52 K401Distributor internal distributor;
53 ERC6551Registry internal tbaRegistry;
54 K401Seat6551Account internal seatAccountImpl;
55 K401Seat6551 internal seat6551;
56 K401StockDesk internal stockDesk;
57 K401FeeSplitter internal splitter;
58 K401BondDepository internal bonds;
59 K401Buyback internal buyback;
60 K401Lens internal lens;
61 
62 // ---- externals ----
63 MockERC20 internal usdg;
64 MockERC20 internal nvda;
65 MockERC20 internal aapl;
66 MockPriceFeed internal nvdaFeed;
67 MockPriceFeed internal aaplFeed;
68 MockUniV2Pair internal pair; // 401K / USDG
69 MockUniV2Pair internal nvdaPair; // USDG / NVDA
70 MockUniV2Pair internal aaplPair; // USDG / AAPL
71 MockUniV2Router internal router;
72 
73 uint8 internal constant USDG_DECIMALS = 6;
74 uint256 internal constant INITIAL_SUPPLY = 1_000_000e18;
75 
76 function setUp() public virtual {
77 vm.warp(1_700_000_000);
78 
79 usdg = new MockERC20("Global Dollar", "USDG", USDG_DECIMALS);
80 nvda = new MockERC20("Tokenized NVDA", "tNVDA", 18);
81 aapl = new MockERC20("Tokenized AAPL", "tAAPL", 18);
82 nvdaFeed = new MockPriceFeed(8, 100e8, "NVDA/USD");
83 aaplFeed = new MockPriceFeed(8, 200e8, "AAPL/USD");
84 
85 // 1. Token. Owner holds the initial supply.
86 k401 = new K401(uint96(INITIAL_SUPPLY), owner, owner);
87 mirror = IMirror(k401.mirrorERC721());
88 
89 // 2. Pair + router.
90 pair = new MockUniV2Pair(address(k401), address(usdg));
91 nvdaPair = new MockUniV2Pair(address(usdg), address(nvda));
92 aaplPair = new MockUniV2Pair(address(usdg), address(aapl));
93 router = new MockUniV2Router();
94 router.setPair(address(k401), address(usdg), address(pair));
95 router.setPair(address(usdg), address(nvda), address(nvdaPair));
96 router.setPair(address(usdg), address(aapl), address(aaplPair));
97 
98 // 3. Core modules.
99 treasury = new K401Treasury(address(k401), address(usdg), owner);
100 oracle = new K401Oracle(address(pair), address(k401), address(usdg));
101 registry = new K401SeatRegistry(address(k401), owner);
102 staking = new K401Staking(address(k401), address(registry), owner);
103 distributor = new K401Distributor(address(k401), address(oracle), address(treasury), address(staking), owner);
104 
105 // 4. ERC-6551.
106 tbaRegistry = new ERC6551Registry();
107 seatAccountImpl = new K401Seat6551Account();
108 seat6551 = new K401Seat6551(address(tbaRegistry), address(seatAccountImpl), address(mirror));
109 
110 // 5. Revenue plumbing.
111 stockDesk = new K401StockDesk(address(usdg), address(oracle), address(registry), address(seat6551), owner);
112 splitter = new K401FeeSplitter(address(k401), address(usdg), owner);
113 bonds = new K401BondDepository(address(k401), address(oracle), address(treasury), owner);
114 buyback = new K401Buyback(address(k401), address(usdg), address(treasury), address(oracle), owner);
115 
116 // 6. Wiring.
117 vm.startPrank(owner);
118 k401.setSeatRegistry(address(registry));
119 k401.setStaking(address(staking));
120 k401.setFeeSplitter(address(splitter));
121 k401.mapPair(address(pair), true);
122 k401.setWhitelist(address(staking), true);
123 k401.setWhitelist(address(treasury), true);
124 k401.setWhitelist(address(stockDesk), true);
125 k401.setWhitelist(address(bonds), true);
126 k401.setWhitelist(address(buyback), true);
127 k401.setWhitelist(address(distributor), true);
128 // The deployer seeds POL and distributes the genesis allocation fee-free.
129 k401.setWhitelist(owner, true);
130 k401.addMinter(address(treasury));
131 k401.addMinter(address(distributor));
132 k401.addBurner(address(registry));
133 k401.addBurner(address(buyback));
134 
135 registry.setStaking(address(staking));
136 registry.setStockDesk(address(stockDesk));
137 staking.setDistributor(address(distributor));
138 
139 treasury.addDepositor(address(bonds));
140 treasury.addPolToken(address(pair));
141 treasury.addEquityToken(address(nvda), address(nvdaFeed));
142 treasury.addEquityToken(address(aapl), address(aaplFeed));
143 
144 stockDesk.setRouter(address(router));
145 stockDesk.listSymbol(uint8(Symbol.NVDA), address(nvda), address(nvdaFeed));
146 stockDesk.listSymbol(uint8(Symbol.AAPL), address(aapl), address(aaplFeed));
147 
148 splitter.wire(
149 address(router), address(stockDesk), address(treasury), lpReceiver, address(buyback), team
150 );
151 vm.stopPrank();
152 
153 lens = new K401Lens(
154 address(k401),
155 address(treasury),
156 address(oracle),
157 address(distributor),
158 address(staking),
159 address(registry),
160 address(seat6551),
161 address(stockDesk),
162 address(bonds)
163 );
164 
165 _seedLiquidity();
166 // 1.20 USDG of RFV per token: floor intact, 200k of mint headroom,
167 // and NAV (1.20) below the pool price (2.00) so there is a real premium.
168 _fundTreasury(1_200_000e6);
169 _seedEquityPools();
170 }
171 
172 /*//////////////////////////////////////////////////////////////
173 HELPERS
174 //////////////////////////////////////////////////////////////*/
175 
176 /// @dev Seeds the 401K/USDG pair at ~2.00 USDG per 401K.
177 function _seedLiquidity() internal {
178 vm.startPrank(owner);
179 k401.transfer(address(pair), 100_000e18);
180 vm.stopPrank();
181 usdg.mint(address(pair), 200_000e6);
182 pair.mint(owner);
183 }
184 
185 function _seedEquityPools() internal {
186 // NVDA @ $100, AAPL @ $200 with deep-enough mock pools for the batch tests.
187 usdg.mint(address(nvdaPair), 10_000_000e6);
188 nvda.mint(address(nvdaPair), 100_000e18);
189 nvdaPair.mint(owner);
190 
191 usdg.mint(address(aaplPair), 10_000_000e6);
192 aapl.mint(address(aaplPair), 50_000e18);
193 aaplPair.mint(owner);
194 }
195 
196 function _fundTreasury(uint256 usdgAmount) internal {
197 usdg.mint(address(treasury), usdgAmount);
198 }
199 
200 /// @dev `vm.warp(block.timestamp + x)` is unsafe under via-ir (the TIMESTAMP opcode
201 /// gets CSE'd), so always read the clock back through the cheatcode.
202 function _skip(uint256 secs) internal {
203 vm.warp(vm.getBlockTimestamp() + secs);
204 }
205 
206 function _now() internal view returns (uint256) {
207 return vm.getBlockTimestamp();
208 }
209 
210 /// @dev Moves time forward and rolls two oracle windows so `consult()` is valid.
211 function _primeOracle() internal {
212 _skip(31 minutes);
213 pair.sync();
214 oracle.checkpoint();
215 _skip(31 minutes);
216 pair.sync();
217 oracle.checkpoint();
218 }
219 
220 function _giveSeats(address to, uint256 count) internal {
221 vm.prank(owner);
222 k401.transfer(to, count * 1e18);
223 }
224 
225 /// @dev Fund an address with 401K without minting Seats (skipNFT), for gas-sane
226 /// tests of the 25_000 401K upgrade cost.
227 function _fundNoSeats(address to, uint256 amount) internal {
228 vm.prank(to);
229 k401.setSkipNFT(true);
230 vm.prank(owner);
231 k401.transfer(to, amount);
232 }
233 
234 /// @dev Give `to` exactly `count` Seat NFTs by minting them elsewhere and moving the
235 /// ids across with an ERC721 transfer (which preserves tokenIds).
236 function _giveSeatNfts(address to, uint256 count) internal returns (uint256[] memory ids) {
237 address mule = address(uint160(uint256(keccak256(abi.encode("mule", to, count, _now())))));
238 vm.prank(owner);
239 k401.transfer(mule, count * 1e18);
240 ids = k401.seatsOf(mule);
241 vm.startPrank(mule);
242 for (uint256 i; i < ids.length; ++i) {
243 mirror.transferFrom(mule, to, ids[i]);
244 }
245 vm.stopPrank();
246 }
247 
248 function _ids(uint256 a) internal pure returns (uint256[] memory out) {
249 out = new uint256[](1);
250 out[0] = a;
251 }
252 
253 function _ids(uint256 a, uint256 b) internal pure returns (uint256[] memory out) {
254 out = new uint256[](2);
255 out[0] = a;
256 out[1] = b;
257 }
258 
259 function _ids(uint256 a, uint256 b, uint256 c) internal pure returns (uint256[] memory out) {
260 out = new uint256[](3);
261 out[0] = a;
262 out[1] = b;
263 out[2] = c;
264 }
265 
266 function _syms(uint8 a) internal pure returns (uint8[] memory out) {
267 out = new uint8[](1);
268 out[0] = a;
269 }
270}
271 

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