// Copyright 2021-2022, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE // SPDX-License-Identifier: BUSL-1.1 // solhint-disable-next-line compiler-version pragma solidity >=0.6.9 <0.9.0; interface IGasRefunder { function onGasSpent( address payable spender, uint256 gasUsed, uint256 calldataSize ) external returns (bool success); } abstract contract GasRefundEnabled { /// @dev this refunds the sender for execution costs of the tx /// calldata costs are only refunded if `msg.sender == tx.origin` to guarantee the value refunded relates to charging /// for the `tx.input`. this avoids a possible attack where you generate large calldata from a contract and get over-refunded modifier refundsGas(IGasRefunder gasRefunder) { uint256 startGasLeft = gasleft(); _; if (address(gasRefunder) != address(0)) { uint256 calldataSize = msg.data.length; uint256 calldataWords = (calldataSize + 31) / 32; // account for the CALLDATACOPY cost of the proxy contract, including the memory expansion cost startGasLeft += calldataWords * 6 + (calldataWords**2) / 512; // if triggered in a contract call, the spender may be overrefunded by appending dummy data to the call // so we check if it is a top level call, which would mean the sender paid calldata as part of tx.input // solhint-disable-next-line avoid-tx-origin if (msg.sender != tx.origin) { // We can't be sure if this calldata came from the top level tx, // so to be safe we tell the gas refunder there was no calldata. calldataSize = 0; } gasRefunder.onGasSpent(payable(msg.sender), startGasLeft - gasleft(), calldataSize); } } }