1+ // SPDX-License-Identifier: UNLICENSED
2+ pragma solidity ^ 0.8.13 ;
3+
4+ import "forge-std/src/Test.sol " ;
5+ import "forge-std/src/console.sol " ;
6+ import "../src/CircuitBreakerPower.sol " ;
7+ import "./mock/MockERC20.sol " ;
8+
9+ contract CircuitBreakerPowerTest is Test {
10+ CircuitBreakerPower public circuitBreakerPower;
11+ bool testToken;
12+
13+ function setUp () public {
14+ address underlyingToken = address (new MockERC20 ("mock " , "mock " , 1e28 ));
15+ uint256 minLockAmount = 1000e18 ; // 1k units
16+ uint256 unlockDelaySec = 24 hours ;
17+ uint256 unlockPeriodSec = 48 hours ;
18+ uint8 unlockExponent = 1 ; // linear release
19+
20+ testToken = true ;
21+ if (! testToken)
22+ underlyingToken = address (0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE );
23+
24+ circuitBreakerPower = new CircuitBreakerPower (underlyingToken, minLockAmount, unlockDelaySec, unlockPeriodSec, unlockExponent, address (this ));
25+ }
26+
27+ function testTransferNoFunds () public {
28+ vm.expectRevert ("not enough tokens " );
29+ circuitBreakerPower.transferTo (address (this ), 1234 );
30+ }
31+
32+ function testTransferNoLock () public {
33+ uint256 amount = 1e18 ;
34+ IERC20 (circuitBreakerPower.underlyingToken ()).transfer (address (circuitBreakerPower), amount);
35+
36+ vm.recordLogs ();
37+ circuitBreakerPower.transferTo (address (this ), amount);
38+ Vm.Log[] memory entries = vm.getRecordedLogs ();
39+ assertEq (entries.length , 2 );
40+ assertEq (entries[1 ].topics[0 ], keccak256 ("TransferFulfilled(address,uint256,uint256) " ));
41+ assertEq (uint256 (entries[1 ].topics[1 ]), uint256 (uint160 (address (this ))));
42+
43+ (uint256 withdrawnAmount , uint256 remainingAmount ) = abi.decode (entries[1 ].data, (uint256 , uint256 ));
44+ assertEq (withdrawnAmount, amount);
45+ assertEq (remainingAmount, uint256 (0 ));
46+ }
47+
48+ function testTransferLock () public {
49+ uint256 amount = 1000e18 ;
50+ IERC20 (circuitBreakerPower.underlyingToken ()).transfer (address (circuitBreakerPower), amount);
51+
52+ vm.recordLogs ();
53+ circuitBreakerPower.transferTo (address (this ), amount);
54+ Vm.Log[] memory entries = vm.getRecordedLogs ();
55+
56+ assertEq (entries.length , 1 );
57+ assertEq (entries[0 ].topics[0 ], keccak256 ("TransferLocked(address,uint256,uint256) " ));
58+ assertEq (uint256 (entries[0 ].topics[1 ]), uint256 (uint160 (address (this ))));
59+
60+ (uint256 amountLocked , uint256 lockTimestamp ) = abi.decode (entries[0 ].data, (uint256 , uint256 ));
61+ assertEq (amountLocked, amount);
62+ assertEq (lockTimestamp, uint256 (block .timestamp ));
63+
64+ vm.warp (block .timestamp + 24 hours);
65+ uint256 withdrawableAmount0 = circuitBreakerPower.getWithdrawableAmount (address (this ), lockTimestamp);
66+ assertEq (withdrawableAmount0, 0 );
67+
68+ vm.warp (block .timestamp + 24 hours);
69+ uint256 withdrawableAmount0_5 = circuitBreakerPower.getWithdrawableAmount (address (this ), lockTimestamp);
70+ assertEq (withdrawableAmount0_5, amount / 2 );
71+
72+ vm.warp (block .timestamp + 24 hours);
73+ uint256 withdrawableAmount1 = circuitBreakerPower.getWithdrawableAmount (address (this ), lockTimestamp);
74+ assertEq (withdrawableAmount1, amount);
75+ }
76+
77+ function testTransferLockAndUnlock () public {
78+ uint256 amount = 1000e18 ;
79+ IERC20 (circuitBreakerPower.underlyingToken ()).transfer (address (circuitBreakerPower), amount);
80+
81+ vm.recordLogs ();
82+ circuitBreakerPower.transferTo (address (this ), amount);
83+ Vm.Log[] memory entries = vm.getRecordedLogs ();
84+
85+ assertEq (entries.length , 1 );
86+ assertEq (entries[0 ].topics[0 ], keccak256 ("TransferLocked(address,uint256,uint256) " ));
87+ assertEq (uint256 (entries[0 ].topics[1 ]), uint256 (uint160 (address (this ))));
88+
89+ (uint256 amountLocked , uint256 lockTimestamp ) = abi.decode (entries[0 ].data, (uint256 , uint256 ));
90+ assertEq (amountLocked, amount);
91+ assertEq (lockTimestamp, uint256 (block .timestamp ));
92+
93+ vm.warp (block .timestamp + 24 hours);
94+ uint256 withdrawableAmount0 = circuitBreakerPower.getWithdrawableAmount (address (this ), lockTimestamp);
95+ assertEq (withdrawableAmount0, 0 );
96+
97+ vm.warp (block .timestamp + 24 hours);
98+ uint256 withdrawableAmount0_5 = circuitBreakerPower.getWithdrawableAmount (address (this ), lockTimestamp);
99+ assertEq (withdrawableAmount0_5, amount / 2 );
100+
101+ uint256 balanceBefore = IERC20 (circuitBreakerPower.underlyingToken ()).balanceOf (address (this ));
102+ circuitBreakerPower.unlockFor (address (this ), lockTimestamp);
103+ uint256 balanceAfter = IERC20 (circuitBreakerPower.underlyingToken ()).balanceOf (address (this ));
104+ assertEq (balanceBefore + withdrawableAmount0_5 >= balanceAfter, true );
105+
106+ vm.warp (block .timestamp + 24 hours);
107+ uint256 withdrawableAmount1 = circuitBreakerPower.getWithdrawableAmount (address (this ), lockTimestamp);
108+ assertEq (withdrawableAmount1, amount - (balanceAfter - balanceBefore));
109+
110+ circuitBreakerPower.unlockFor (address (this ), lockTimestamp);
111+ }
112+
113+ function testEarlyUnlock () public {
114+ uint256 amount = 1000e18 ;
115+ IERC20 (circuitBreakerPower.underlyingToken ()).transfer (address (circuitBreakerPower), amount);
116+
117+ vm.recordLogs ();
118+ circuitBreakerPower.transferTo (address (this ), amount);
119+ Vm.Log[] memory entries = vm.getRecordedLogs ();
120+
121+ assertEq (entries.length , 1 );
122+ assertEq (entries[0 ].topics[0 ], keccak256 ("TransferLocked(address,uint256,uint256) " ));
123+ assertEq (uint256 (entries[0 ].topics[1 ]), uint256 (uint160 (address (this ))));
124+
125+ (uint256 amountLocked , uint256 lockTimestamp ) = abi.decode (entries[0 ].data, (uint256 , uint256 ));
126+ assertEq (amountLocked, amount);
127+ assertEq (lockTimestamp, uint256 (block .timestamp ));
128+
129+ vm.warp (block .timestamp + 24 hours);
130+ uint256 withdrawableAmount0 = circuitBreakerPower.getWithdrawableAmount (address (this ), lockTimestamp);
131+ assertEq (withdrawableAmount0, 0 );
132+
133+ vm.warp (block .timestamp + 24 hours);
134+ uint256 withdrawableAmount0_5 = circuitBreakerPower.getWithdrawableAmount (address (this ), lockTimestamp);
135+ assertEq (withdrawableAmount0_5, amount / 2 );
136+
137+ uint256 balanceBefore = IERC20 (circuitBreakerPower.underlyingToken ()).balanceOf (address (this ));
138+ circuitBreakerPower.unlockFor (address (this ), lockTimestamp);
139+ uint256 balanceAfter = IERC20 (circuitBreakerPower.underlyingToken ()).balanceOf (address (this ));
140+ assertEq (balanceBefore + withdrawableAmount0_5 >= balanceAfter, true );
141+
142+ uint256 withdrawableAmount1 = circuitBreakerPower.getWithdrawableAmount (address (this ), lockTimestamp);
143+ console.log (withdrawableAmount1);
144+ assertEq (withdrawableAmount1, 0 );
145+ vm.expectRevert ("no withdrawable funds " );
146+ circuitBreakerPower.unlockFor (address (this ), lockTimestamp);
147+ }
148+
149+ function testTransferNoLock2 () public {
150+ uint256 amount2 = 2e18 ;
151+ IERC20 (circuitBreakerPower.underlyingToken ()).transfer (address (circuitBreakerPower), amount2);
152+
153+ vm.recordLogs ();
154+ circuitBreakerPower.transferTo (address (this ), amount2);
155+ Vm.Log[] memory entries = vm.getRecordedLogs ();
156+ assertEq (entries.length , 2 );
157+ assertEq (
158+ entries[1 ].topics[0 ],
159+ keccak256 ("TransferFulfilled(address,uint256,uint256) " )
160+ );
161+ assertEq (
162+ uint256 (entries[1 ].topics[1 ]),
163+ uint256 (uint160 (address (this )))
164+ );
165+
166+ (uint256 withdrawnAmount , uint256 remainingAmount ) = abi.decode (
167+ entries[1 ].data,
168+ (uint256 , uint256 )
169+ );
170+ assertEq (withdrawnAmount, amount2);
171+ assertEq (remainingAmount, uint256 (0 ));
172+
173+ uint256 amount = 1e18 ;
174+ IERC20 (circuitBreakerPower.underlyingToken ()).transfer (address (circuitBreakerPower), amount);
175+
176+ vm.recordLogs ();
177+
178+ circuitBreakerPower.transferTo (address (this ), amount);
179+ entries = vm.getRecordedLogs ();
180+ assertEq (entries.length , 2 );
181+ assertEq (
182+ entries[1 ].topics[0 ],
183+ keccak256 ("TransferFulfilled(address,uint256,uint256) " )
184+ );
185+ assertEq (
186+ uint256 (entries[1 ].topics[1 ]),
187+ uint256 (uint160 (address (this )))
188+ );
189+
190+ (withdrawnAmount, remainingAmount) = abi.decode (
191+ entries[1 ].data,
192+ (uint256 , uint256 )
193+ );
194+ assertEq (withdrawnAmount, amount);
195+ assertEq (remainingAmount, uint256 (0 ));
196+ }
197+ }
0 commit comments