@inco/lightning 0.8.0-devnet-2 → 0.8.0-devnet-3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -57,4 +57,3 @@ contract TestFeeWithdrawal is IncoTest {
57
57
  }
58
58
 
59
59
  }
60
-
@@ -239,4 +239,54 @@ contract TestIncoUtils is Test, IncoUtils {
239
239
  assertEq(address(this).balance, balanceBefore - spendAmount);
240
240
  }
241
241
 
242
+ /// @notice Test reentrancy guard catches direct reentrant call from function body
243
+ function testRefundUnspentReentrancyFromFunctionBody() public {
244
+ vm.expectRevert(IncoUtils.ReentrantCall.selector);
245
+ this.mockFunctionThatReenters{value: 1 ether}();
246
+ }
247
+
248
+ function mockFunctionThatReenters() external payable refundUnspent {
249
+ // Try to call another protected function while inside the first one
250
+ this.mockFunctionNoSpend();
251
+ }
252
+
253
+ /// @notice Test refund=0 when msg.value equals spent exactly
254
+ function testRefundUnspentWhenMsgValueEqualsSpent() public {
255
+ // This explicitly tests the refund=0 branch when msg.value == spent
256
+ uint256 msgValue = 1 ether;
257
+
258
+ vm.deal(address(this), msgValue);
259
+ uint256 balanceBefore = address(this).balance;
260
+
261
+ // Spend exactly msg.value, so refund = msg.value - spent = 0
262
+ this.mockFunctionPartialSpend{value: msgValue}(msgValue);
263
+
264
+ // All ETH was spent, no refund
265
+ assertEq(address(this).balance, balanceBefore - msgValue);
266
+ }
267
+
268
+ /// @notice Test when contract receives more ETH during execution than was sent
269
+ /// This triggers the balanceAfter > balanceBefore branch (spent = 0)
270
+ function testRefundUnspentWhenReceivingMoreThanSent() public {
271
+ uint256 msgValue = 0.5 ether;
272
+ uint256 incomingEth = 1 ether; // More than msg.value
273
+
274
+ vm.deal(address(sender), incomingEth);
275
+ vm.deal(address(this), msgValue);
276
+ uint256 balanceBefore = address(this).balance;
277
+
278
+ // During execution, we receive 1 ETH (more than we sent)
279
+ this.mockFunctionReceivesEthDuringExecution{value: msgValue}(payable(address(sender)), incomingEth);
280
+
281
+ // Execution trace:
282
+ // 1. balanceBefore (modifier) = 0.5 ETH (self-call doesn't change balance)
283
+ // 2. Sender sends 1 ETH during execution
284
+ // 3. balanceAfter (modifier) = 0.5 + 1 = 1.5 ETH
285
+ // 4. spent = balanceBefore > balanceAfter ? ... : 0 = 0 (since 0.5 < 1.5)
286
+ // 5. refund = msg.value - spent = 0.5 - 0 = 0.5 ETH
287
+ // 6. Refund sent to msg.sender (this contract) = self-transfer, no balance change
288
+ // 7. Final balance = 1.5 ETH = balanceBefore (test) + incomingEth
289
+ assertEq(address(this).balance, balanceBefore + incomingEth);
290
+ }
291
+
242
292
  }