@@ -30,6 +30,16 @@ contract FVMRewardWireTest is MockRewardWireTest {
3030 return rewardActor ().mockLastParams ();
3131 }
3232
33+ /// @dev `vm.expectRevert` cannot see a revert that happens after `_invoke`'s delegatecall,
34+ /// since that delegatecall runs inlined in this contract's own frame rather than behind a
35+ /// real CALL boundary. Routing through an external self-call gives `expectRevert` one to catch.
36+ function _externalClaim (uint64 id , address [] memory wallets )
37+ external
38+ returns (int256 exitCode , uint256 [] memory amounts )
39+ {
40+ return FVMRewards.tryClaim (id, wallets);
41+ }
42+
3343 // [[
3444 // [23,[23,-24,23,0,23]], [24,[24,-25,24,0,24]],
3545 // [255,[255,-256,255,0,255]], [256,[256,-257,256,0,256]],
@@ -242,6 +252,42 @@ contract FVMRewardWireTest is MockRewardWireTest {
242252 }
243253 }
244254
255+ /// @dev A lone sign byte with an empty magnitude, distinct from an empty byte string.
256+ // [[byte[00]]]
257+ function test_ClaimReturn_SignByteWithEmptyMagnitudeIsZero () public {
258+ rewardActor ().mockSetClaimReturn (hex "81814100 " );
259+ (int256 exitCode , uint256 [] memory amounts ) = FVMRewards.tryClaim (1 , new address [](1 ));
260+ assertEq (exitCode, 0 );
261+ assertEq (amounts[0 ], 0 );
262+ }
263+
264+ /// @dev The widest magnitude `_readBigInt` accepts: 32 bytes, forcing its shift amount to zero.
265+ // [[byte[00,ff*32]]]
266+ function test_ClaimReturn_WidestMagnitude () public {
267+ rewardActor ()
268+ .mockSetClaimReturn (hex "8181582100ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff " );
269+ (int256 exitCode , uint256 [] memory amounts ) = FVMRewards.tryClaim (1 , new address [](1 ));
270+ assertEq (exitCode, 0 );
271+ assertEq (amounts[0 ], type (uint256 ).max);
272+ }
273+
274+ /// @dev A nonzero sign byte, which `_readBigInt` rejects even though f02 never emits one.
275+ // [[byte[01,01]]]
276+ function test_ClaimReturn_NegativeSignByte_Reverts () public {
277+ rewardActor ().mockSetClaimReturn (hex "8181420101 " );
278+ vm.expectRevert (bytes ("" ));
279+ this ._externalClaim (1 , new address [](1 ));
280+ }
281+
282+ /// @dev One byte past `_readBigInt`'s 32-byte magnitude cap.
283+ // [[byte[00,00*33]]]
284+ function test_ClaimReturn_OversizedMagnitude_Reverts () public {
285+ rewardActor ()
286+ .mockSetClaimReturn (hex "8181582200000000000000000000000000000000000000000000000000000000000000000000 " );
287+ vm.expectRevert (bytes ("" ));
288+ this ._externalClaim (1 , new address [](1 ));
289+ }
290+
245291 // [null,op] for the two schedule-wide weight slots, [id,op] for everything per stream.
246292 function test_CancelPending_NullIdAddressesTheWeightSlot () public {
247293 FVMRewards.tryCancelPendingWeight (PendingOp.SET_WEIGHT);
0 commit comments