From de2e49c877e5bc77983eb544e803b5929f675d4a Mon Sep 17 00:00:00 2001 From: Uncle Fatso Date: Thu, 24 Sep 2026 15:23:05 +0300 Subject: [PATCH] cap bounty payout gas to prevent receiver gas units abuse Signed-off-by: Uncle Fatso --- src/Gatekeeper.sol | 21 ++++++++++++++------- src/interfaces/IGatekeeper.sol | 4 ---- test/bonding/BondDepositorty.t.sol | 2 +- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/Gatekeeper.sol b/src/Gatekeeper.sol index 54de0e5..285a15b 100644 --- a/src/Gatekeeper.sol +++ b/src/Gatekeeper.sol @@ -89,7 +89,7 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard { return IStorageHistory(storageHistory).bridgeImbalance(); } - function latestPublicKeyInfo() external override view returns (bytes32, uint8, uint64) { + function latestPublicKeyInfo() external view returns (bytes32, uint8, uint64) { bytes32 latestPublicKey = bytes32(_aggregatedPublicKeys.latest()); uint256 packed = _packedRotationStates[latestPublicKey]; @@ -152,7 +152,8 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard { RequestPacking.RequestPayload memory payload = RequestPacking.unpack(packed); if (payload.chainId != block.chainid) revert WrongChainId(); - uint256 bountyAmount = FullMath.mulDiv(amount, uint256(payload.bounty), BOUNTY_DIVISOR); + uint256 requestedBountyAmount = FullMath.mulDiv(amount, uint256(payload.bounty), BOUNTY_DIVISOR); + uint256 bountyAmount = _isContract(payload.receiver) ? 0 : requestedBountyAmount; uint256 receiverAmount = amount - bountyAmount; StorageHistory(storageHistory).trySetTransactionExecuted(exodusSession); @@ -163,7 +164,7 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard { (address token, uint256 sent) = IStaking(staking).recall(bountyAmount, REGISTRY_INDEX); IWETH9(token).withdraw(sent); - (bool sentSuccess,) = payload.receiver.call{ value: sent }(""); + (bool sentSuccess,) = payload.receiver.call{ value: sent, gas: 3000 }(""); if (!sentSuccess) revert SendFailed(); } @@ -192,7 +193,7 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard { uint256 exodusSession, uint256 packed, bytes calldata - ) external returns (bytes memory) { + ) external { if (msg.sender != address(this)) revert NotGatekeeper(); GovernancePacking.GovernancePayload memory payload = GovernancePacking.unpack(packed); @@ -200,10 +201,8 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard { StorageHistory(storageHistory).trySetTransactionExecuted(exodusSession); - (bool success, bytes memory data) = payload.target.call(msg.data[132:]); + (bool success,) = payload.target.call(msg.data[132:]); if (!success) revert ExecutionReverted(); - - return data; } function _extractPublicKey(bytes calldata call) internal view returns (uint256) { @@ -217,4 +216,12 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard { return uint256(publicKey); } + + function _isContract(address account) internal view returns (bool) { + uint256 size; + assembly { + size := extcodesize(account) + } + return size > 0; + } } diff --git a/src/interfaces/IGatekeeper.sol b/src/interfaces/IGatekeeper.sol index 839df8e..0fd2487 100644 --- a/src/interfaces/IGatekeeper.sol +++ b/src/interfaces/IGatekeeper.sol @@ -22,12 +22,8 @@ interface IGatekeeper { function deployer() external view returns (address); function ghostedSupply() external view returns (uint256); function storageHistory() external view returns (address); - function latestPublicKeyInfo() external view returns (bytes32, uint8, uint64); function getRotationInfoAt(uint256 session) external view returns (bytes32, uint8, uint64); - function govern(uint256 session, uint256 payload, bytes calldata call) external returns (bytes calldata); - function recall(uint256 session, uint256 amount, uint256 payload) external; - function rotate(uint256 session, bytes32 publicKey, uint8 parity) external; function ghost(bytes32 receiver, uint256 amount) external; function initialize(address previousAddress) external; } diff --git a/test/bonding/BondDepositorty.t.sol b/test/bonding/BondDepositorty.t.sol index 6953101..c0ca212 100644 --- a/test/bonding/BondDepositorty.t.sol +++ b/test/bonding/BondDepositorty.t.sol @@ -195,7 +195,7 @@ contract GhostBondDepositoryTest is Test { [CAPACITY, initialPrice, minPrice, BUFFER], [VESTING, TIME_TO_CONCLUSION], address(reserve), - [uint32(DEPOSIT_INTERVAL), uint32(TUNE_INTERVAL)], + [uint32(DEPOSIT_INTERVAL), uint32(TUNE_INTERVAL)], // forge-lint: disable-line(unsafe-typecast) [false, true] );