Contract Diff Checker

Contract Name:
ProtocolActions

Contract Source Code:

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.26;
pragma abicoder v2;

import {PoolStorage} from './PoolStorage.sol';
import {TransferHelper} from './TransferHelper.sol';
import {IRamsesV3Factory} from '../interfaces/IRamsesV3Factory.sol';

library ProtocolActions {
    error NOT_AUTHORIZED();
    error INVALID_FEE();

    /// @notice Emitted when the protocol fee is changed by the pool
    /// @param feeProtocolOld The previous value of the token0 protocol fee
    /// @param feeProtocolNew The updated value of the token1 protocol fee
    event SetFeeProtocol(uint8 feeProtocolOld, uint8 feeProtocolNew);

    /// @notice Emitted when the collected protocol fees are withdrawn by the fee collector
    /// @param sender The address that collects the protocol fees
    /// @param recipient The address that receives the collected protocol fees
    /// @param amount0 The amount of token0 protocol fees that is withdrawn
    /// @param amount0 The amount of token1 protocol fees that is withdrawn
    event CollectProtocol(address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1);

    event FeeAdjustment(uint24 oldFee, uint24 newFee);

    /// @notice Set % share of the fees that do not go to liquidity providers
    /// @dev Fetches from factory directly
    function setFeeProtocol(address factory) external {
        PoolStorage.PoolState storage $ = PoolStorage.getStorage();

        uint8 feeProtocolOld = $.slot0.feeProtocol;
        /// @dev fetch from factory mapping
        uint8 feeProtocol = IRamsesV3Factory(factory).poolFeeProtocol(address(this));
        /// @dev if the two values are not the same, the factory mapping takes precedent
        if (feeProtocol != feeProtocolOld) {
            /// @dev set the storage feeProtocol to the factory's
            $.slot0.feeProtocol = feeProtocol;
            emit SetFeeProtocol(feeProtocolOld, feeProtocol);
        }
    }

    /// @notice Collect the protocol fee accrued to the pool
    /// @param recipient The address to which collected protocol fees should be sent
    /// @param amount0Requested The maximum amount of token0 to send, can be 0 to collect fees in only token1
    /// @param amount1Requested The maximum amount of token1 to send, can be 0 to collect fees in only token0
    /// @return amount0 The protocol fee collected in token0
    /// @return amount1 The protocol fee collected in token1
    function collectProtocol(
        address recipient,
        uint128 amount0Requested,
        uint128 amount1Requested,
        address token0,
        address token1
    ) external returns (uint128 amount0, uint128 amount1) {
        PoolStorage.PoolState storage $ = PoolStorage.getStorage();

        amount0 = amount0Requested > $.protocolFees.token0 ? $.protocolFees.token0 : amount0Requested;
        amount1 = amount1Requested > $.protocolFees.token1 ? $.protocolFees.token1 : amount1Requested;

        unchecked {
            if (amount0 > 0) {
                if (amount0 == $.protocolFees.token0) amount0--; /// @dev ensure that the slot is not cleared, for gas savings
                $.protocolFees.token0 -= amount0;
                TransferHelper.safeTransfer(token0, recipient, amount0);
            }
            if (amount1 > 0) {
                if (amount1 == $.protocolFees.token1) amount1--; /// @dev ensure that the slot is not cleared, for gas savings
                $.protocolFees.token1 -= amount1;
                TransferHelper.safeTransfer(token1, recipient, amount1);
            }
        }
        emit CollectProtocol(msg.sender, recipient, amount0, amount1);
    }

    function setFee(uint24 _fee, address factory) external {
        PoolStorage.PoolState storage $ = PoolStorage.getStorage();
        if (msg.sender != factory) revert NOT_AUTHORIZED();
        if (_fee > 100000) revert INVALID_FEE();
        uint24 _oldFee = $.fee;
        $.fee = _fee;
        emit FeeAdjustment(_oldFee, _fee);
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.26;

struct Slot0 {
    /// @dev the current price
    uint160 sqrtPriceX96;
    /// @dev the current tick
    int24 tick;
    /// @dev the most-recently updated index of the observations array
    uint16 observationIndex;
    /// @dev the current maximum number of observations that are being stored
    uint16 observationCardinality;
    /// @dev the next maximum number of observations to store, triggered in observations.write
    uint16 observationCardinalityNext;
    /// @dev the current protocol fee as a percentage of the swap fee taken on withdrawal
    /// @dev represented as an integer denominator (1/x)%
    uint8 feeProtocol;
    /// @dev whether the pool is locked
    bool unlocked;
}

struct Observation {
    /// @dev the block timestamp of the observation
    uint32 blockTimestamp;
    /// @dev the tick accumulator, i.e. tick * time elapsed since the pool was first initialized
    int56 tickCumulative;
    /// @dev the seconds per liquidity, i.e. seconds elapsed / max(1, liquidity) since the pool was first initialized
    uint160 secondsPerLiquidityCumulativeX128;
    /// @dev whether or not the observation is initialized
    bool initialized;
}

struct RewardInfo {
    /// @dev used to account for changes in the deposit amount
    int256 secondsDebtX96;
    /// @dev used to check if starting seconds have already been written
    bool initialized;
    /// @dev used to account for changes in secondsPerLiquidity
    int160 secondsPerLiquidityPeriodStartX128;
}

/// @dev info stored for each user's position
struct PositionInfo {
    /// @dev the amount of liquidity owned by this position
    uint128 liquidity;
    /// @dev fee growth per unit of liquidity as of the last update to liquidity or fees owed
    uint256 feeGrowthInside0LastX128;
    uint256 feeGrowthInside1LastX128;
    /// @dev the fees owed to the position owner in token0/token1
    uint128 tokensOwed0;
    uint128 tokensOwed1;
    mapping(uint256 => RewardInfo) periodRewardInfo;
}

/// @dev info stored for each initialized individual tick
struct TickInfo {
    /// @dev the total position liquidity that references this tick
    uint128 liquidityGross;
    /// @dev amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left),
    int128 liquidityNet;
    /// @dev fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)
    /// @dev only has relative meaning, not absolute — the value depends on when the tick is initialized
    uint256 feeGrowthOutside0X128;
    uint256 feeGrowthOutside1X128;
    /// @dev the cumulative tick value on the other side of the tick
    int56 tickCumulativeOutside;
    /// @dev the seconds per unit of liquidity on the _other_ side of this tick (relative to the current tick)
    /// @dev only has relative meaning, not absolute — the value depends on when the tick is initialized
    uint160 secondsPerLiquidityOutsideX128;
    /// @dev the seconds spent on the other side of the tick (relative to the current tick)
    /// @dev only has relative meaning, not absolute — the value depends on when the tick is initialized
    uint32 secondsOutside;
    /// @dev true iff the tick is initialized, i.e. the value is exactly equivalent to the expression liquidityGross != 0
    /// @dev these 8 bits are set to prevent fresh sstores when crossing newly initialized ticks
    bool initialized;
    /// @dev secondsPerLiquidityOutsideX128 separated into periods, placed here to preserve struct slots
    mapping(uint256 => uint256) periodSecondsPerLiquidityOutsideX128;
}

/// @dev info stored for each period
struct PeriodInfo {
    uint32 previousPeriod;
    int24 startTick;
    int24 lastTick;
    uint160 endSecondsPerLiquidityPeriodX128;
}

/// @dev accumulated protocol fees in token0/token1 units
struct ProtocolFees {
    uint128 token0;
    uint128 token1;
}

/// @dev Position period and liquidity
struct PositionCheckpoint {
    uint256 period;
    uint256 liquidity;
}

library PoolStorage {
    /// @dev keccak256(abi.encode(uint256(keccak256("pool.storage")) - 1)) & ~bytes32(uint256(0xff));
    bytes32 public constant POOL_STORAGE_LOCATION = 0xf047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb2800;

    /// @custom꞉storage‑location erc7201꞉pool.storage
    struct PoolState {
        Slot0 slot0;
        uint24 fee;
        uint256 feeGrowthGlobal0X128;
        uint256 feeGrowthGlobal1X128;
        ProtocolFees protocolFees;
        uint128 liquidity;
        mapping(int24 => TickInfo) _ticks;
        mapping(int16 => uint256) tickBitmap;
        mapping(bytes32 => PositionInfo) positions;
        Observation[65535] observations;
        mapping(uint256 => PeriodInfo) periods;
        uint256 lastPeriod;
        mapping(bytes32 => PositionCheckpoint[]) positionCheckpoints;
        bool initialized;
        address nfpManager;
    }

    /// @dev Return state storage struct for reading and writing
    function getStorage() internal pure returns (PoolState storage $) {
        assembly {
            $.slot := POOL_STORAGE_LOCATION
        }
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.6.0;

import {IERC20Minimal} from '../interfaces/IERC20Minimal.sol';

/// @title TransferHelper
/// @notice Contains helper methods for interacting with ERC20 tokens that do not consistently return true/false
library TransferHelper {
    error TF();

    /// @notice Transfers tokens from msg.sender to a recipient
    /// @dev Calls transfer on token contract, errors with TF if transfer fails
    /// @param token The contract address of the token which will be transferred
    /// @param to The recipient of the transfer
    /// @param value The value of the transfer
    function safeTransfer(address token, address to, uint256 value) internal {
        (bool success, bytes memory data) = token.call(
            abi.encodeWithSelector(IERC20Minimal.transfer.selector, to, value)
        );
        if (!(success && (data.length == 0 || abi.decode(data, (bool))))) revert TF();
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title The interface for the Ramses V3 Factory
/// @notice The Ramses V3 Factory facilitates creation of Ramses V3 pools and control over the protocol fees
interface IRamsesV3Factory {
    error IT();
    /// @dev Fee Too Large
    error FTL();
    error A0();
    error F0();
    error PE();

    /// @notice Emitted when a pool is created
    /// @param token0 The first token of the pool by address sort order
    /// @param token1 The second token of the pool by address sort order
    /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
    /// @param tickSpacing The minimum number of ticks between initialized ticks
    /// @param pool The address of the created pool
    event PoolCreated(
        address indexed token0,
        address indexed token1,
        uint24 indexed fee,
        int24 tickSpacing,
        address pool
    );

    /// @notice Emitted when a new tickspacing amount is enabled for pool creation via the factory
    /// @dev unlike UniswapV3, we map via the tickSpacing rather than the fee tier
    /// @param tickSpacing The minimum number of ticks between initialized ticks
    /// @param fee The fee, denominated in hundredths of a bip
    event TickSpacingEnabled(int24 indexed tickSpacing, uint24 indexed fee);

    /// @notice Emitted when the protocol fee is changed
    /// @param feeProtocolOld The previous value of the protocol fee
    /// @param feeProtocolNew The updated value of the protocol fee
    event SetFeeProtocol(uint8 feeProtocolOld, uint8 feeProtocolNew);

    /// @notice Emitted when the protocol fee is changed
    /// @param pool The pool address
    /// @param feeProtocolOld The previous value of the protocol fee
    /// @param feeProtocolNew The updated value of the protocol fee
    event SetPoolFeeProtocol(address pool, uint8 feeProtocolOld, uint8 feeProtocolNew);

    /// @notice Emitted when a pool's fee is changed
    /// @param pool The pool address
    /// @param newFee The updated value of the protocol fee
    event FeeAdjustment(address pool, uint24 newFee);

    /// @notice Emitted when the fee collector is changed
    /// @param oldFeeCollector The previous implementation
    /// @param newFeeCollector The new implementation
    event FeeCollectorChanged(address indexed oldFeeCollector, address indexed newFeeCollector);

    /// @notice Returns the PoolDeployer address
    /// @return The address of the PoolDeployer contract
    function ramsesV3PoolDeployer() external returns (address);

    /// @notice Returns the fee amount for a given tickSpacing, if enabled, or 0 if not enabled
    /// @dev A tickSpacing can never be removed, so this value should be hard coded or cached in the calling context
    /// @dev unlike UniswapV3, we map via the tickSpacing rather than the fee tier
    /// @param tickSpacing The enabled tickSpacing. Returns 0 in case of unenabled tickSpacing
    /// @return initialFee The initial fee
    function tickSpacingInitialFee(int24 tickSpacing) external view returns (uint24 initialFee);

    /// @notice Returns the pool address for a given pair of tokens and a tickSpacing, or address 0 if it does not exist
    /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order
    /// @dev unlike UniswapV3, we map via the tickSpacing rather than the fee tier
    /// @param tokenA The contract address of either token0 or token1
    /// @param tokenB The contract address of the other token
    /// @param tickSpacing The tickSpacing of the pool
    /// @return pool The pool address
    function getPool(address tokenA, address tokenB, int24 tickSpacing) external view returns (address pool);

    /// @notice Creates a pool for the given two tokens and fee
    /// @dev unlike UniswapV3, we map via the tickSpacing rather than the fee tier
    /// @param tokenA One of the two tokens in the desired pool
    /// @param tokenB The other of the two tokens in the desired pool
    /// @param tickSpacing The desired tickSpacing for the pool
    /// @param sqrtPriceX96 initial sqrtPriceX96 of the pool
    /// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0.
    /// @dev The call will revert if the pool already exists, the tickSpacing is invalid, or the token arguments are invalid.
    /// @return pool The address of the newly created pool
    function createPool(
        address tokenA,
        address tokenB,
        int24 tickSpacing,
        uint160 sqrtPriceX96
    ) external returns (address pool);

    /// @notice Enables a tickSpacing with the given initialFee amount
    /// @dev unlike UniswapV3, we map via the tickSpacing rather than the fee tier
    /// @dev tickSpacings may never be removed once enabled
    /// @param tickSpacing The spacing between ticks to be enforced for all pools created
    /// @param initialFee The initial fee amount, denominated in hundredths of a bip (i.e. 1e-6)
    function enableTickSpacing(int24 tickSpacing, uint24 initialFee) external;

    /// @notice returns the default protocol fee.
    /// @return _feeProtocol the default feeProtocol
    function feeProtocol() external view returns (uint8 _feeProtocol);

    /// @notice returns the % of fees directed to governance
    /// @dev if the fee is 0, or the pool is uninitialized this will return the Factory's default feeProtocol
    /// @param pool the address of the pool
    /// @return _feeProtocol the feeProtocol for the pool
    function poolFeeProtocol(address pool) external view returns (uint8 _feeProtocol);

    /// @notice Sets the default protocol's % share of the fees
    /// @param _feeProtocol new default protocol fee for token0 and token1
    function setFeeProtocol(uint8 _feeProtocol) external;

    /// @notice Get the parameters to be used in constructing the pool, set transiently during pool creation.
    /// @dev Called by the pool constructor to fetch the parameters of the pool
    /// @return factory The factory address
    /// @return token0 The first token of the pool by address sort order
    /// @return token1 The second token of the pool by address sort order
    /// @return fee The initialized feetier of the pool, denominated in hundredths of a bip
    /// @return tickSpacing The minimum number of ticks between initialized ticks
    function parameters()
        external
        view
        returns (address factory, address token0, address token1, uint24 fee, int24 tickSpacing);

    /// @notice Sets the fee collector address
    /// @param _feeCollector the fee collector address
    function setFeeCollector(address _feeCollector) external;

    /// @notice sets the swap fee for a specific pool
    /// @param _pool address of the pool
    /// @param _fee the fee to be assigned to the pool, scaled to 1_000_000 = 100%
    function setFee(address _pool, uint24 _fee) external;

    /// @notice Returns the address of the fee collector contract
    /// @dev Fee collector decides where the protocol fees go (fee distributor, treasury, etc.)
    function feeCollector() external view returns (address);

    /// @notice sets the feeProtocol of a specific pool
    /// @param pool address of the pool
    /// @param _feeProtocol the fee protocol to assign
    function setPoolFeeProtocol(address pool, uint8 _feeProtocol) external;

    /// @notice sets the feeProtocol upon a gauge's creation
    /// @param pool address of the pool
    function gaugeFeeSplitEnable(address pool) external;

    /// @notice sets the the voter address
    /// @param _voter the address of the voter
    function setVoter(address _voter) external;

    function initialize(address poolDeployer) external;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Minimal ERC20 interface for Ramses
/// @notice Contains a subset of the full ERC20 interface that is used in Ramses V3
interface IERC20Minimal {
    /// @notice Returns the balance of a token
    /// @param account The account for which to look up the number of tokens it has, i.e. its balance
    /// @return The number of tokens held by the account
    function balanceOf(address account) external view returns (uint256);

    /// @notice Transfers the amount of token from the `msg.sender` to the recipient
    /// @param recipient The account that will receive the amount transferred
    /// @param amount The number of tokens to send from the sender to the recipient
    /// @return Returns true for a successful transfer, false for an unsuccessful transfer
    function transfer(address recipient, uint256 amount) external returns (bool);

    /// @notice Returns the current allowance given to a spender by an owner
    /// @param owner The account of the token owner
    /// @param spender The account of the token spender
    /// @return The current allowance granted by `owner` to `spender`
    function allowance(address owner, address spender) external view returns (uint256);

    /// @notice Sets the allowance of a spender from the `msg.sender` to the value `amount`
    /// @param spender The account which will be allowed to spend a given amount of the owners tokens
    /// @param amount The amount of tokens allowed to be used by `spender`
    /// @return Returns true for a successful approval, false for unsuccessful
    function approve(address spender, uint256 amount) external returns (bool);

    /// @notice Transfers `amount` tokens from `sender` to `recipient` up to the allowance given to the `msg.sender`
    /// @param sender The account from which the transfer will be initiated
    /// @param recipient The recipient of the transfer
    /// @param amount The amount of the transfer
    /// @return Returns true for a successful transfer, false for unsuccessful
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    /// @notice Event emitted when tokens are transferred from one address to another, either via `#transfer` or `#transferFrom`.
    /// @param from The account from which the tokens were sent, i.e. the balance decreased
    /// @param to The account to which the tokens were sent, i.e. the balance increased
    /// @param value The amount of tokens that were transferred
    event Transfer(address indexed from, address indexed to, uint256 value);

    /// @notice Event emitted when the approval amount for the spender of a given owner's tokens changes.
    /// @param owner The account that approved spending of its tokens
    /// @param spender The account for which the spending allowance was modified
    /// @param value The new allowance from the owner to the spender
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):