ETH Price: $3,692.95 (+1.55%)

Contract Diff Checker

Contract Name:
BasefeeOSMDeviationCallBundler

Contract Source Code:

File 1 of 1 : BasefeeOSMDeviationCallBundler

pragma solidity 0.8.19;

abstract contract StabilityFeeTreasuryLike {
    function systemCoin() external view virtual returns (address);

    function pullFunds(address, address, uint) external virtual;
}

abstract contract OracleLike {
    function read() external view virtual returns (uint256);
}

abstract contract BaseFeeIncentive {
    StabilityFeeTreasuryLike public immutable treasury; // The stability fee treasury
    address public immutable coin; // The system coin
    OracleLike public ethOracle; // eth oracle
    OracleLike public coinOracle; // coin oracle
    uint256 public fixedReward; // The fixed reward sent by the treasury to a fee receiver (wad)
    uint256 public callDelay; // delay between incentivized calls (seconds)
    uint256 public lastCallMade; // last time a call to target was made (UNIX timestamp)

    // --- Events ---
    event AddAuthorization(address account);
    event RemoveAuthorization(address account);
    event ModifyParameters(bytes32 parameter, address addr);
    event ModifyParameters(bytes32 parameter, uint256 val);
    event RewardCaller(address indexed finalFeeReceiver, uint256 fixedReward);
    event FailRewardCaller(
        bytes revertReason,
        address feeReceiver,
        uint256 amount
    );

    // --- Auth ---
    mapping(address => uint256) public authorizedAccounts;

    /**
     * @notice Add auth to an account
     * @param account Account to add auth to
     */
    function addAuthorization(address account) external isAuthorized {
        authorizedAccounts[account] = 1;
        emit AddAuthorization(account);
    }

    /**
     * @notice Remove auth from an account
     * @param account Account to remove auth from
     */
    function removeAuthorization(address account) external isAuthorized {
        authorizedAccounts[account] = 0;
        emit RemoveAuthorization(account);
    }

    /**
     * @notice Checks whether msg.sender can call an authed function
     **/
    modifier isAuthorized() {
        require(
            authorizedAccounts[msg.sender] == 1,
            "StabilityFeeTreasury/account-not-authorized"
        );
        _;
    }

    // --- Constructor ---
    constructor(
        address treasury_,
        uint256 reward_,
        uint256 delay_,
        address coinOracle_,
        address ethOracle_
    ) {
        require(treasury_ != address(0), "invalid-treasury");
        require(reward_ != 0, "invalid-reward");
        require(coinOracle_ != address(0), "invalid-coin-oracle");
        require(ethOracle_ != address(0), "invalid-eth-oracle");

        authorizedAccounts[msg.sender] = 1;

        treasury = StabilityFeeTreasuryLike(treasury_);
        fixedReward = reward_;
        callDelay = delay_;
        coin = StabilityFeeTreasuryLike(treasury_).systemCoin();
        coinOracle = OracleLike(coinOracle_);
        ethOracle = OracleLike(ethOracle_);

        emit AddAuthorization(msg.sender);
        emit ModifyParameters("fixedReward", reward_);
        emit ModifyParameters("callDelay", delay_);
        emit ModifyParameters("coinOracle", coinOracle_);
        emit ModifyParameters("ethOracle", ethOracle_);
    }

    // -- Admin --
    function modifyParameters(
        bytes32 parameter,
        uint256 val
    ) public isAuthorized virtual {
        if (parameter == "fixedReward") fixedReward = val;
        else if (parameter == "callDelay") callDelay = val;
        else revert("invalid-param");
        emit ModifyParameters(parameter, val);
    }

    function modifyParameters(
        bytes32 parameter,
        address val
    ) external isAuthorized {
        require(val != address(0), "invalid-data");
        if (parameter == "coinOracle") coinOracle = OracleLike(val);
        else if (parameter == "ethOracle") ethOracle = OracleLike(val);
        else revert("invalid-param");
        emit ModifyParameters(parameter, val);
    }

    modifier payRewards() {
        uint256 gas = gasleft();
        _;

        if (block.timestamp >= lastCallMade + callDelay) {
            gas = gas - gasleft();
            uint256 coinCost = (gas * block.basefee * ethOracle.read()) /
                coinOracle.read();

            try treasury.pullFunds(msg.sender, coin, coinCost + fixedReward) {
                emit RewardCaller(msg.sender, coinCost + fixedReward);
            } catch (bytes memory revertReason) {
                emit FailRewardCaller(
                    revertReason,
                    msg.sender,
                    coinCost + fixedReward
                );
            }
        }

        lastCallMade = block.timestamp;
    }
}

abstract contract OSMLike {
    function updateResult() external virtual; // OSM Call

    function read() external view virtual returns (uint256);

    function priceSource() external view virtual returns (OracleLike);

    function getNextResultWithValidity() external view virtual returns (uint256, bool);
}

abstract contract OracleRelayerLike {
    function updateCollateralPrice(bytes32) external virtual; // Oracle relayer call

    function orcl(bytes32) external view virtual returns (address);
}

// @notice: Unobtrusive incentives for any call on a TAI like system.
// @dev: Assumes an allowance from the stability fee treasury, all oracles return quotes with 18 decimal places.
// @dev: Assumes all collateral types use the same OSM
contract BasefeeOSMDeviationCallBundler is BaseFeeIncentive {
    OSMLike public immutable osm;
    OracleRelayerLike public immutable oracleRelayer;
    bytes32 public immutable collateralA;
    bytes32 public immutable collateralB;
    bytes32 public immutable collateralC;

    uint256 public acceptedDeviation = 50; // 1000 = 100%, default 5%

    // --- Constructor ---
    constructor(
        address treasury_,
        address osm_,
        address oracleRelayer_,
        bytes32[3] memory collateral_,
        uint256 reward_,
        uint256 delay_,
        address coinOracle_,
        address ethOracle_
    ) BaseFeeIncentive(treasury_, reward_, delay_, coinOracle_, ethOracle_) {
        require(osm_ != address(0), "invalid-osm");
        require(oracleRelayer_ != address(0), "invalid-oracle-relayer");

        osm = OSMLike(osm_);
        oracleRelayer = OracleRelayerLike(oracleRelayer_);

        for (uint i; i < 3; i++) {
            if (collateral_[i] != bytes32(0)) require(oracleRelayer.orcl(collateral_[i]) == osm_, "invalid-collateral");
        }
        collateralA = collateral_[0];
        collateralB = collateral_[1];
        collateralC = collateral_[2];
    }

    function modifyParameters(bytes32 parameter, uint256 data) public override isAuthorized {
        if (parameter == "acceptedDeviation") {
            require(data <= 500, "invalid-deviation");
            acceptedDeviation = data;
            emit ModifyParameters(parameter, data);
        } else super.modifyParameters(parameter, data);
    }

    // @dev Calls are made through the fallback function
    fallback() external payRewards {
        uint256 currentPrice = osm.read();
        (uint256 nextPrice, ) = osm.getNextResultWithValidity();
        uint256 marketPrice = osm.priceSource().read();

        uint256 deviation = (currentPrice * acceptedDeviation) / 1000;

        // will pay if either current vs nextPrice or current vs marketPrice deviates by more than deviation
        require(
            nextPrice >= currentPrice + deviation ||
            nextPrice <= currentPrice - deviation ||
            marketPrice >= currentPrice + deviation ||
            marketPrice <= currentPrice - deviation,
            "not-enough-deviation"
        );

        osm.updateResult();

        if (collateralA != bytes32(0)) oracleRelayer.updateCollateralPrice(collateralA);
        if (collateralB != bytes32(0)) oracleRelayer.updateCollateralPrice(collateralB);
        if (collateralC != bytes32(0)) oracleRelayer.updateCollateralPrice(collateralC);
    }
}

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

Context size (optional):