Forkchoice Ethereum Mainnet

Address Contract Verified

Address 0x4C871E951228c2f7224416C921e742a86Ef8EECB
Balance 0 ETH
Nonce 1
Code Size 3009 bytes
Indexed Transactions 0 (1 on-chain, 1.4% indexed)
External Etherscan · Sourcify

Contract Bytecode

3009 bytes
0x608060405234801561000f575f80fd5b50600436106100da575f3560e01c806350d25bcd11610088578063d250a48511610063578063d250a485146101dc578063e07b098a146101ef578063efdf0bb014610216578063feaf968c14610229575f80fd5b806350d25bcd146101b55780637284e416146101bd5780637bc6729b146101d2575f80fd5b8063218751b2116100b8578063218751b214610166578063252408101461018d578063313ce567146101ad575f80fd5b806301f59d16146100de57806310347e24146100fa57806312d43a5114610146575b5f80fd5b6100e760015481565b6040519081526020015b60405180910390f35b6101217f00000000000000000000000022390b88c53d1631f673b8dcd91860267137b2c881565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100f1565b6002546101219073ffffffffffffffffffffffffffffffffffffffff1681565b6101217f000000000000000000000000dcd90d866ff9636e5a04768825d05d27b3fb19ec81565b6003546101219073ffffffffffffffffffffffffffffffffffffffff1681565b6100e7601281565b6100e7610268565b6101c561027d565b6040516100f191906108cf565b6101da610308565b005b6101da6101ea366004610938565b610411565b6100e77f000000000000000000000000000000000000000000000000000000000000000181565b6101da61022436600461094f565b61056e565b610231610662565b6040805169ffffffffffffffffffff968716815260208101959095528401929092526060830152909116608082015260a0016100f1565b5f80610272610662565b509195945050505050565b5f805461028990610989565b80601f01602080910402602001604051908101604052809291908181526020018280546102b590610989565b80156103005780601f106102d757610100808354040283529160200191610300565b820191905f5260205f20905b8154815290600101906020018083116102e357829003601f168201915b505050505081565b60035473ffffffffffffffffffffffffffffffffffffffff16331461038e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f4f4e4c592050454e44494e4720474f560000000000000000000000000000000060448201526064015b60405180910390fd5b600380546002805473ffffffffffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff000000000000000000000000000000000000000091821681179092559091169091556040519081527f639717155292ce2c3e699929a8b65d14a637640f75ab5b6d165a4e735d82a4559060200160405180910390a1565b60025473ffffffffffffffffffffffffffffffffffffffff163314610492576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600860248201527f4f4e4c5920474f560000000000000000000000000000000000000000000000006044820152606401610385565b5f81121580156104a757506402540be4008113155b610532576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f4375727665466565643a206d6178466565203e2031303025206f72206e65676160448201527f74697665000000000000000000000000000000000000000000000000000000006064820152608401610385565b60018190556040518181527f65642fa188806dcb12c1f78367586c3052619751b534094568dd4f017c6ba79c906020015b60405180910390a150565b60025473ffffffffffffffffffffffffffffffffffffffff1633146105ef576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600860248201527f4f4e4c5920474f560000000000000000000000000000000000000000000000006044820152606401610385565b600380547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527ff74ae56780e3765c0c0897ef57fb50a10a237584f419631812daf040913e1c9f90602001610563565b5f805f805f807f00000000000000000000000022390b88c53d1631f673b8dcd91860267137b2c873ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa1580156106d1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106f591906109f8565b809650819750829850839550849a5050505050505f7f000000000000000000000000dcd90d866ff9636e5a04768825d05d27b3fb19ec73ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015610773573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107979190610a44565b90506001548113156107a857506001545b5f7f000000000000000000000000dcd90d866ff9636e5a04768825d05d27b3fb19ec73ffffffffffffffffffffffffffffffffffffffff166386fc88d36040518163ffffffff1660e01b8152600401602060405180830381865afa158015610812573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108369190610a44565b90507f00000000000000000000000000000000000000000000000000000000000000011561088057670de0b6b3a76400006108718483610a88565b61087b9190610ad9565b61089d565b8061089384670de0b6b3a7640000610a88565b61089d9190610ad9565b96506402540be4006108af8389610a88565b6108b99190610ad9565b6108c39088610b65565b96505050509091929394565b5f6020808352835180828501525f5b818110156108fa578581018301518582016040015282016108de565b505f6040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b5f60208284031215610948575f80fd5b5035919050565b5f6020828403121561095f575f80fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610982575f80fd5b9392505050565b600181811c9082168061099d57607f821691505b6020821081036109d4577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b50919050565b805169ffffffffffffffffffff811681146109f3575f80fd5b919050565b5f805f805f60a08688031215610a0c575f80fd5b610a15866109da565b9450602086015193506040860151925060608601519150610a38608087016109da565b90509295509295909350565b5f60208284031215610a54575f80fd5b5051919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b8082025f82127f800000000000000000000000000000000000000000000000000000000000000084141615610abf57610abf610a5b565b8181058314821517610ad357610ad3610a5b565b92915050565b5f82610b0c577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f800000000000000000000000000000000000000000000000000000000000000083141615610b6057610b60610a5b565b500590565b8181035f831280158383131683831282161715610b8457610b84610a5b565b509291505056fea2646970667358221220153042fe42eb38e44d54d63297c27189318a2403c3c1b7c4f632772bb85573d664736f6c63430008140033

Verified Source Code Full Match

Compiler: v0.8.20+commit.a1b79de6 EVM: shanghai Optimization: Yes (10000 runs)
DynamicFeeCurveFeed.sol 126 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {ICurvePool} from "src/interfaces/ICurvePool.sol";
import {IChainlinkBasePriceFeed} from "src/interfaces/IChainlinkFeed.sol";
import {IERC20} from "src/interfaces/IERC20.sol";

// Combined Chainlink and Curve price_oracle taking dynamic fees into consideration, allows for additional fallback to be set via ChainlinkBasePriceFeed
contract DynamicFeeCurveFeed {
    /// @dev Chainlink base price feed implementation for the pairedToken to USD
    IChainlinkBasePriceFeed public immutable pairedTokenToUsd;
    /// @dev Curve 2-pool
    ICurvePool public immutable curvePool;
    /// @dev FiRM asset index in Curve pool `coins` array
    uint256 public immutable assetIndex;
    /// @dev Description of the feed
    string public description;
    /// @dev Price decimals of this feed
    uint public constant decimals = 18;
    /// @dev Max fee initially set to 2%
    int public maxFee = 2e8;

    address public gov;

    address public pendingGov;

    constructor(
        address _pairedTokenToUsd,
        address _curvePool,
        address _asset,
        address _gov
    ) {
        gov = _gov;
        pairedTokenToUsd = IChainlinkBasePriceFeed(_pairedTokenToUsd);
        require(
            pairedTokenToUsd.decimals() == 18,
            "ChainlinkCurveFeed: DECIMALS_MISMATCH"
        );
        curvePool = ICurvePool(_curvePool);
        uint _index;
        if(ICurvePool(_curvePool).coins(0) == _asset)
            _index = 0;
        else if(ICurvePool(_curvePool).coins(1) == _asset)
            _index = 1;
        else
            revert("CurveFeed: ASSET NOT IN TWO POOL");
        assetIndex = _index;

        string memory coin = IERC20(_asset).symbol();
        description = string(abi.encodePacked(coin, " / USD"));
    }

    event NewMaxFee(int maxFee);
    event NewGov(address newGov);
    event NewPendingGov(address newPendingGov);

    /**
     * @notice Retrieves the latest round data for the pairedToken token price feed
     * @return roundId The round ID of the Chainlink price feed for the feed with the lowest updatedAt feed
     * @return usdPrice The latest FiRM asset price in USD
     * @return startedAt The timestamp when the latest round of Chainlink price feed started of the lowest last updatedAt feed
     * @return updatedAt The lowest timestamp when either of the latest round of Chainlink price feed was updated
     * @return answeredInRound The round ID in which the answer was computed of the lowest updatedAt feed
     */
    function latestRoundData()
        public
        view
        returns (
            uint80 roundId,
            int256 usdPrice,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answeredInRound
        )
    {
        int256 pairedTokenToUsdPrice;
        (
            roundId,
            pairedTokenToUsdPrice,
            startedAt,
            updatedAt,
            answeredInRound
        ) = pairedTokenToUsd.latestRoundData();
        
        int256 fee = int256(curvePool.fee());
        if(fee > maxFee) fee = maxFee;
        //crv oracle price is either asset/pairedToken or pairedToken/asset depending on asset index
        int256 crvOraclePrice = int256(curvePool.price_oracle());
        //Depending on assetIndex we either divide or multiply by crv oracle price
        usdPrice = assetIndex == 0 ?
            pairedTokenToUsdPrice * 1e18 / crvOraclePrice :
            crvOraclePrice * pairedTokenToUsdPrice / 1e18;
        //Reduce the price by the dynamic fee amount
        usdPrice -= usdPrice * fee / 1e10; 
    }

    /**
     * @notice Returns the latest price only
     * @dev Unlike chainlink oracles, the latestAnswer will always be the same as in the latestRoundData
     * @return int256 Returns the last finalized price of the chainlink oracle
     */
    function latestAnswer() external view returns (int256) {
        (, int256 latestPrice, , , ) = latestRoundData();
        return latestPrice;
    }

    function setMaxFee(int _maxFee) external {
        require(msg.sender == gov, "ONLY GOV");
        require(_maxFee >= 0 && _maxFee <= 1e10, "CurveFeed: maxFee > 100% or negative");
        maxFee = _maxFee;
        emit NewMaxFee(_maxFee);
    }

    function setPendingGov(address _gov) external {
        require(msg.sender == gov, "ONLY GOV");
        pendingGov = _gov;
        emit NewPendingGov(_gov);
    }

    function acceptGov() external {
        require(msg.sender == pendingGov, "ONLY PENDING GOV");
        gov = pendingGov;
        pendingGov = address(0);
        emit NewGov(gov);
    }
}
ICurvePool.sol 84 lines
pragma solidity ^0.8.13;

interface ICurvePool {
    function price_oracle(uint256 k) external view returns (uint256);

    function get_virtual_price() external view returns (uint256);

    function price_oracle() external view returns (uint256);

    function add_liquidity(
        uint256[2] memory _amounts,
        uint256 _min_mint_amount,
        address _receiver
    ) external returns (uint256);

    function add_liquidity(
        uint256[] memory _amounts,
        uint256 _min_mint_amount,
        address _receiver
    ) external returns (uint256);

    function add_liquidity(
        uint256[2] memory _amounts,
        uint256 _min_mint_amount
    ) external returns (uint256);

    function add_liquidity(
        uint256[] memory _amounts,
        uint256 _min_mint_amount
    ) external returns (uint256);

    function add_liquidity(
        uint256[3] memory _amounts,
        uint256 _min_mint_amount,
        address _receiver
    ) external returns (uint256);

    function add_liquidity(
        uint256[3] memory _amounts,
        uint256 _min_mint_amount
    ) external returns (uint256);

    function remove_liquidity_one_coin(
        uint256 _burn_amount,
        int128 i,
        uint256 _min_received,
        address _receiver
    ) external returns (uint256);

    function coins(uint index) external view returns (address);

    function exchange(
        uint i,
        uint j,
        uint dx,
        uint min_dy,
        bool use_eth,
        address receiver
    ) external payable returns (uint);

    function calc_token_amount(
        uint256[2] memory _amounts,
        bool _is_deposit
    ) external view returns (uint256);
    function calc_token_amount(
        uint256[] memory _amounts,
        bool _is_deposit
    ) external view returns (uint256);

    function calc_withdraw_one_coin(
        uint256 _burn_amount,
        int128 i
    ) external view returns (uint256);

    function symbol() external view returns (string memory);

    function lp_token() external view returns (address);

    function decimals() external view returns (uint256);

    function fee() external view returns(uint);

    function out_fee() external view returns(uint);
}
IChainlinkFeed.sol 30 lines
pragma solidity ^0.8.13;

interface IChainlinkFeed {
    function aggregator() external view returns (address aggregator);

    function decimals() external view returns (uint8 decimals);

    function latestRoundData()
        external
        view
        returns (
            uint80 roundId,
            int256 crvUsdPrice,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answeredInRound
        );

    function latestAnswer() external view returns (int256 price);

    function description() external view returns (string memory description);
}

interface IChainlinkBasePriceFeed is IChainlinkFeed {
    function assetToUsd() external view returns (IChainlinkFeed);

    function assetToUsdFallback() external view returns (IChainlinkFeed);

    function assetToUsdHeartbeat() external view returns (uint256 heartbeat);
}
IERC20.sol 31 lines
pragma solidity ^0.8.13;

interface IERC20 {
    function approve(address, uint) external;

    function transfer(address, uint) external returns (bool);

    function transferFrom(address, address, uint) external returns (bool);

    function balanceOf(address) external view returns (uint);

    function allowance(address from, address to) external view returns (uint);

    function symbol() external view returns (string memory);
}

interface IMintable is IERC20 {
    function mint(address, uint) external;

    function burn(uint) external;

    function addMinter(address minter) external;
}

interface IDelegateableERC20 is IERC20 {
    function delegate(address delegatee) external;

    function delegates(
        address delegator
    ) external view returns (address delegatee);
}

Read Contract

assetIndex 0xe07b098a → uint256
curvePool 0x218751b2 → address
decimals 0x313ce567 → uint256
description 0x7284e416 → string
gov 0x12d43a51 → address
latestAnswer 0x50d25bcd → int256
latestRoundData 0xfeaf968c → uint80, int256, uint256, uint256, uint80
maxFee 0x01f59d16 → int256
pairedTokenToUsd 0x10347e24 → address
pendingGov 0x25240810 → address

Write Contract 3 functions

These functions modify contract state and require a wallet transaction to execute.

acceptGov 0x7bc6729b
No parameters
setMaxFee 0xd250a485
int256 _maxFee
setPendingGov 0xefdf0bb0
address _gov

Recent Transactions

This address has 1 on-chain transactions, but only 1.4% of the chain is indexed. Transactions will appear as indexing progresses. View on Etherscan →