Forkchoice Ethereum Mainnet

Address Contract Verified

Address 0x2c776041CCFe903071AF44aa147368a9c8EEA518
Balance 0 ETH
Nonce 1
Code Size 6701 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

6701 bytes
0x6080604052600436101561001b575b361561001957600080fd5b005b60003560e01c806317fcb39b1461016f5780631aa7c53e14610166578063258836fe1461015d578063555029a61461015457806375829def1461014b578063953d7ee2146101425780639860d6e6146101395780639f29291514610130578063b660c15614610127578063b69d34151461011e578063b8ef9d9814610115578063c7f6d7b51461010c578063cc15790514610103578063d1d8bce7146100fa578063d69adac4146100f1578063f340fa01146100e85763f851a4400361000e576100e3610a49565b61000e565b506100e36108b1565b506100e3610875565b506100e361082f565b506100e36107f5565b506100e36107c2565b506100e361078f565b506100e361075c565b506100e3610718565b506100e36106e3565b506100e36106ae565b506100e3610668565b506100e36105cc565b506100e3610327565b506100e3610214565b506100e36101ce565b506100e3610188565b600091031261018357565b600080fd5b5034610183576000366003190112610183576040517f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b03168152602090f35b503461018357600036600319011261018357604051741050d51253d397d514905394d1915497d054d4d155605a1b8152602090f35b6001600160a01b0381160361018357565b50346101835760403660031901126101835760043561023281610203565b60243561023e81610203565b60005461025b906001600160a01b03165b6001600160a01b031690565b33036102e6576040516370a0823160e01b8152610019929091602083806102853060048301610655565b03816001600160a01b0385165afa9283156102d9575b6000936102a9575b50611249565b6102cb91935060203d81116102d2575b6102c38183610a8a565b810190610aba565b91386102a3565b503d6102b9565b6102e1610ac9565b61029b565b6040516282b42960e81b8152600490fd5b9181601f84011215610183578235916001600160401b038311610183576020808501948460051b01011161018357565b506040366003190112610183576001600160401b03600435818111610183576103549036906004016102f7565b916024359081116101835761036d9036906004016102f7565b908184036105ba57349360005b8181106103b757858061038957005b600080809260405190335af161039d610b00565b50156103a557005b60405163d1a4579f60e01b8152600490fd5b6103c2818387610b47565b35721050d51253d397d4d55414131657d054d4d155606a1b81036104185750806104126103fd6103f56001948888610b65565b810190610c31565b9290868060a01b038080921693169116610cb4565b0161037a565b9095907920a1aa24a7a72fa9aaa828262cafa720aa24ab22afaa27a5a2a760311b810361047e575061047760019161047161045f6104578a8989610b65565b810190610c04565b9190868060a01b038091169116610d5f565b90610c68565b955b610412565b9095600191741050d51253d397d514905394d1915497d054d4d155605a1b81036104c957506104796104b46103f5838888610b65565b9290868060a01b038080921693169116610f8b565b741050d51253d397d5d2551211149055d7d054d4d155605a1b810361050f57506104796104fa6103f5838888610b65565b9290868060a01b038080921693169116610fc5565b7b20a1aa24a7a72faba4aa24222920abafa720aa24ab22afaa27a5a2a760211b81036105595750610479610547610457838888610b65565b9190858060a01b038091169116610fff565b721050d51253d397d0d310525357d49155d05491606a1b81036105a55750610479610590610588838888610b65565b810190610bbe565b9290868060a01b0380809216931691166111f7565b610479906105b4838888610b65565b916112f1565b60405163a9cb9e0d60e01b8152600490fd5b5034610183576020366003190112610183576004356105ea81610203565b600054906001600160a01b0380831691338390036102e657168015610643578060009360018060a01b031916178355604051917ff8ccb027dfcd135e000e9d45e6cc2d662578a8825d4c45b5e32e0adf67e79ec68484a3f35b60405163e6c4247b60e01b8152600490fd5b6001600160a01b03909116815260200190565b5034610183576000366003190112610183576040517f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe846001600160a01b03168152602090f35b503461018357600036600319011261018357604051741050d51253d397d5d2551211149055d7d054d4d155605a1b8152602090f35b5034610183576000366003190112610183576040517408286a8929e9cbeae92a89088a482aebea6a88aa89605b1b8152602090f35b50346101835760203660031901126101835760043561073681610203565b6000546001600160a01b031633036102e657600080809247604051915af161039d610b00565b5034610183576000366003190112610183576040517208286a8929e9cbea6aaa0a098b2bea6a88aa89606b1b8152602090f35b503461018357600036600319011261018357604051721050d51253d397d4d55414131657d054d4d155606a1b8152602090f35b503461018357600036600319011261018357604051721050d51253d397d0d310525357d49155d05491606a1b8152602090f35b5034610183576000366003190112610183576040517920a1aa24a7a72fa9aaa828262cafa720aa24ab22afaa27a5a2a760311b8152602090f35b5034610183576000366003190112610183576040517f0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca06001600160a01b03168152602090f35b5034610183576000366003190112610183576040517b20a1aa24a7a72faba4aa24222920abafa720aa24ab22afaa27a5a2a760211b8152602090f35b50602080600319360112610183576004908135906108ce82610203565b600080549092906108e7906001600160a01b031661024f565b3303610a395760405163c55dae6360e01b81526001600160a01b03838287818685165afa918215610a2c575b85926109fd575b507f0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca09181831691829116036109ec578480610954816119c5565b868151910134855af1610965610b00565b50156109db57836109aa95966109909260405180809581946370a0823160e01b835230908301610655565b03915afa9384156109ce575b86946109af575b5050611249565b604051f35b6109c6929450803d106102d2576102c38183610a8a565b9138806109a3565b6109d6610ac9565b61099c565b60405163cefaffeb60e01b81528690fd5b6040516382d6580f60e01b81528690fd5b610a1e919250843d8611610a25575b610a168183610a8a565b810190610d1a565b903861091a565b503d610a0c565b610a34610ac9565b610913565b6040516282b42960e81b81528490fd5b5034610183576000366003190112610183576000546040516001600160a01b039091168152602090f35b50634e487b7160e01b600052604160045260246000fd5b601f909101601f19168101906001600160401b03821190821017610aad57604052565b610ab5610a73565b604052565b90816020910312610183575190565b506040513d6000823e3d90fd5b6020906001600160401b038111610af3575b601f01601f19160190565b610afb610a73565b610ae8565b3d15610b2b573d90610b1182610ad6565b91610b1f6040519384610a8a565b82523d6000602084013e565b606090565b50634e487b7160e01b600052603260045260246000fd5b9190811015610b58575b60051b0190565b610b60610b30565b610b51565b9190811015610ba7575b60051b81013590601e19813603018212156101835701803591906001600160401b038311610183576020018236038113610183579190565b610baf610b30565b610b6f565b8015150361018357565b9190826080910312610183578135610bd581610203565b916020810135610be481610203565b9160606040830135610bf581610203565b920135610c0181610bb4565b90565b90816060910312610183578035610c1a81610203565b9160406020830135610c2b81610203565b92013590565b9190826080910312610183578135610c4881610203565b916020810135610c5781610203565b9160606040830135610c2b81610203565b818110610c73570390565b634e487b7160e01b600052601160045260246000fd5b6001600160a01b03918216815291811660208301529091166040820152606081019190915260800190565b919290916001600160a01b0316803b1561018357610cee936000809460405196879586948593639032317760e01b85523360048601610c89565b03925af18015610d0d575b610d005750565b6000610d0b91610a8a565b565b610d15610ac9565b610cf9565b908160209103126101835751610c0181610203565b908160209103126101835751610c0181610bb4565b6001600160a01b039091168152602081019190915260400190565b60405163c55dae6360e01b81526001600160a01b03808316937f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2939092602092879084836004818b5afa928315610f7e575b600093610f5f575b5080871692168214610eea575b50803b1561018357868391610e1793604051630d0e30db60e41b815260008160048187875af18015610edd575b610ec8575b50600060405180968195829463095ea7b360e01b845260048401610d44565b03925af18015610ebb575b610e8d575b5050823b1561018357610e589260009283869360405196879586948593639032317760e01b85523060048601610c89565b03925af18015610e80575b610e6b575090565b80610e7a6000610c0193610a8a565b80610178565b610e88610ac9565b610e63565b81610eac92903d10610eb4575b610ea48183610a8a565b810190610d2f565b503880610e27565b503d610e9a565b610ec3610ac9565b610e22565b80610e7a6000610ed793610a8a565b38610df8565b610ee5610ac9565b610df3565b60001914610ef9575b38610dc6565b604051630dd3126d60e21b8152909650828180610f193360048301610655565b0381895afa908115610f52575b600091610f35575b5095610ef3565b610f4c9150833d85116102d2576102c38183610a8a565b38610f2e565b610f5a610ac9565b610f26565b610f77919350853d8711610a2557610a168183610a8a565b9138610db9565b610f86610ac9565b610db1565b919290916001600160a01b0316803b1561018357610cee93600080946040519687958694859363183dc58360e31b85523360048601610c89565b919290916001600160a01b0316803b1561018357610cee9360008094604051968795869485936304c8826360e31b85523360048601610c89565b6040805163c55dae6360e01b81529093917f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2906001600160a01b039081169060209085908286600481875afa9586156111ea575b6000966111cb575b5080851695168514611154575b5050803b156101835783600091611097938389518096819582946304c8826360e31b8452303360048601610c89565b03925af18015611147575b611132575b50803b1561018357600092839283928387518092632e1a7d4d60e01b82528183816110da88600483019190602083019252565b03925af18015611125575b611111575b508551915af16110f8610b00565b50156111015750565b5163d1a4579f60e01b8152600490fd5b80610e7a8561111f93610a8a565b386110ea565b61112d610ac9565b6110e5565b80610e7a600061114193610a8a565b386110a7565b61114f610ac9565b6110a2565b60001914611163575b80611068565b86516370a0823160e01b8152919450908181806111833360048301610655565b0381885afa9182156111be575b6000926111a1575b5050923861115d565b6111b79250803d106102d2576102c38183610a8a565b3880611198565b6111c6610ac9565b611190565b6111e3919650833d8511610a2557610a168183610a8a565b943861105b565b6111f2610ac9565b611053565b9192916001600160a01b0391821690813b156101835760006064928195856040519889978896635b81a7bf60e11b8852166004870152166024850152151560448401525af18015610d0d57610d005750565b6001600160a01b03169291833b156101835761128390604051809581809563a9059cbb60e01b825260009889968796879360048401610d44565b03925af180156112e4575b6112d4575b503d905080156112c9576020146112a8575080fd5b90602081803e515b156112b757565b60405163cefaffeb60e01b8152600490fd5b5090506000196112b0565b6112dd91610a8a565b3882611293565b6112ec610ac9565b61128e565b7208286a8929e9cbea6aaa0a098b2bea6a88aa89606b1b8103611332575080610d0b9261131f920190610c04565b916001600160a01b0391821691166115d1565b7408286a8929e9cbeae92a89088a482aebea6a88aa89605b1b036115bf5761135c91810190610c04565b6040805163c55dae6360e01b81526001600160a01b03948516946020949093909291906004908590878484818c5afa9384156115b2575b600094611593575b507f0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca09382851692839116036115835760009088600019820361152657505084516370a0823160e01b815290508781806113f633878301610655565b03818c5afa908115611519575b6000916114fc575b50915b883b15610183576000856114448286610d0b9d8d99836114729c518096819582946304c8826360e31b8452308d33908601610c89565b03925af180156114ef575b6114db575b5051809681958294636f074d1f60e11b845283019190602083019252565b03925af19384156114ce575b6000946114af575b5050167f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe84611249565b6114c6929450803d106102d2576102c38183610a8a565b913880611486565b6114d6610ac9565b61147e565b80610e7a846114e993610a8a565b38611454565b6114f7610ac9565b61144f565b6115139150883d8a116102d2576102c38183610a8a565b3861140b565b611521610ac9565b611403565b865162b0e38960e81b815285810192835291829081906020010381865afa918215611576575b91611559575b509161140e565b6115709150883d8a116102d2576102c38183610a8a565b38611552565b61157e610ac9565b61154c565b84516382d6580f60e01b81528390fd5b6115ab919450883d8a11610a2557610a168183610a8a565b923861139b565b6115ba610ac9565b611393565b60405163fb90de7160e01b8152600490fd5b6040805163c55dae6360e01b81526001600160a01b0395946004949193878116939160209190899083888a818a5afa9788156118fc575b6000986118dd575b507f0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca09782891692839116036118cd57836117419a9b8a928260001982146000146118c2578851630dd3126d60e21b815261171292506116ae90829081818f818061167c338e8301610655565b03915afa9081156118b5575b600091611898575b508b5180938192632eca54bf60e21b83528a83019190602083019252565b03818a5afa90811561188b575b60009161186e575b5080935b8d60007f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe84926116f7853386611909565b8d5180978196829563095ea7b360e01b9d8e85528401610d44565b0393165af18015611861575b611844575b508651809c8192630ea598cb60e41b83528d83019190602083019252565b03816000865af1998a15611837575b60009a61180b575b509183918960008c9561177689519788968795869485528401610d44565b03925af180156117fe575b6117e0575b5050823b15610183576000946117b286925197889687958694639032317760e01b865230908601610c89565b03925af180156117d3575b6117c45750565b80610e7a6000610d0b93610a8a565b6117db610ac9565b6117bd565b816117f692903d10610eb457610ea48183610a8a565b503880611786565b611806610ac9565b611781565b60009a50918961182b8694859694963d87116102d2576102c38183610a8a565b9b505091929092611758565b61183f610ac9565b611750565b61185a90833d8511610eb457610ea48183610a8a565b5038611723565b611869610ac9565b61171e565b6118859150823d84116102d2576102c38183610a8a565b386116c3565b611893610ac9565b6116bb565b6118af9150823d84116102d2576102c38183610a8a565b38611690565b6118bd610ac9565b611688565b6117129180936116c7565b84516382d6580f60e01b81528990fd5b6118f5919850843d8611610a2557610a168183610a8a565b9638611610565b611904610ac9565b611608565b9092916001600160a01b0390911690813b15610183576040516323b872dd60e01b81526001600160a01b0394909416600485015230602485015260448401526000929083908290606490829084905af180156119b8575b6119a4575b503d801561199957602014611978575080fd5b90602081803e515b1561198757565b60405163073d1efd60e51b8152600490fd5b509050600019611980565b80610e7a846119b293610a8a565b38611965565b6119c0610ac9565b611960565b906119cf82610ad6565b6119dc6040519182610a8a565b82815280926119ed601f1991610ad6565b019060203691013756fea2646970667358221220a4fc64974f001904fda47954b6187ef4a86d0f7d6c558db82408678811f85eff64736f6c634300080f0033

Verified Source Code Full Match

Compiler: v0.8.15+commit.e14f2714 EVM: london
ERC20.sol 63 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;

/**
 * @title ERC 20 Token Standard Interface
 *  https://eips.ethereum.org/EIPS/eip-20
 */
interface ERC20 {
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function decimals() external view returns (uint8);

    /**
      * @notice Get the total number of tokens in circulation
      * @return The supply of tokens
      */
    function totalSupply() external view returns (uint256);

    /**
     * @notice Gets the balance of the specified address
     * @param owner The address from which the balance will be retrieved
     * @return The balance
     */
    function balanceOf(address owner) external view returns (uint256);

    /**
      * @notice Transfer `amount` tokens from `msg.sender` to `dst`
      * @param dst The address of the destination account
      * @param amount The number of tokens to transfer
      * @return Whether or not the transfer succeeded
      */
    function transfer(address dst, uint256 amount) external returns (bool);

    /**
      * @notice Transfer `amount` tokens from `src` to `dst`
      * @param src The address of the source account
      * @param dst The address of the destination account
      * @param amount The number of tokens to transfer
      * @return Whether or not the transfer succeeded
      */
    function transferFrom(address src, address dst, uint256 amount) external returns (bool);

    /**
      * @notice Approve `spender` to transfer up to `amount` from `src`
      * @dev This will overwrite the approval amount for `spender`
      *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
      * @param spender The address of the account which may transfer tokens
      * @param amount The number of tokens that are approved (-1 means infinite)
      * @return Whether or not the approval succeeded
      */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
      * @notice Get the current allowance from `owner` for `spender`
      * @param owner The address of the account which owns the tokens to be spent
      * @param spender The address of the account which may transfer tokens
      * @return The number of tokens allowed to be spent (-1 means infinite)
      */
    function allowance(address owner, address spender) external view returns (uint256);

    event Transfer(address indexed from, address indexed to, uint256 amount);
    event Approval(address indexed owner, address indexed spender, uint256 amount);
}
IWETH9.sol 30 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;

interface IWETH9 {
    function name() external view returns (string memory);

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

    function decimals() external view returns (uint8);

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

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

    receive() external payable;

    function deposit() external payable;

    function withdraw(uint wad) external;

    function totalSupply() external view returns (uint);

    function approve(address guy, uint wad) external returns (bool);

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

    function transferFrom(address src, address dst, uint wad)
    external
    returns (bool);
}
IWstETH.sol 23 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;

import "./ERC20.sol";

/**
 * @dev Interface for interacting with WstETH contract
 * Note Not a comprehensive interface
 */
interface IWstETH is ERC20 {
    function stETH() external returns (address);

    function wrap(uint256 _stETHAmount) external returns (uint256);
    function unwrap(uint256 _wstETHAmount) external returns (uint256);

    function receive() external payable;

    function getWstETHByStETH(uint256 _stETHAmount) external view returns (uint256);
    function getStETHByWstETH(uint256 _wstETHAmount) external view returns (uint256);

    function stEthPerToken() external view returns (uint256);
    function tokensPerStEth() external view returns (uint256);
}
CometCore.sol 127 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;

import "./CometConfiguration.sol";
import "./CometStorage.sol";
import "./CometMath.sol";

abstract contract CometCore is CometConfiguration, CometStorage, CometMath {
    struct AssetInfo {
        uint8 offset;
        address asset;
        address priceFeed;
        uint64 scale;
        uint64 borrowCollateralFactor;
        uint64 liquidateCollateralFactor;
        uint64 liquidationFactor;
        uint128 supplyCap;
    }

    /** Internal constants **/

    /// @dev The max number of assets this contract is hardcoded to support
    ///  Do not change this variable without updating all the fields throughout the contract,
    //    including the size of UserBasic.assetsIn and corresponding integer conversions.
    uint8 internal constant MAX_ASSETS = 15;

    /// @dev The max number of decimals base token can have
    ///  Note this cannot just be increased arbitrarily.
    uint8 internal constant MAX_BASE_DECIMALS = 18;

    /// @dev The max value for a collateral factor (1)
    uint64 internal constant MAX_COLLATERAL_FACTOR = FACTOR_SCALE;

    /// @dev Offsets for specific actions in the pause flag bit array
    uint8 internal constant PAUSE_SUPPLY_OFFSET = 0;
    uint8 internal constant PAUSE_TRANSFER_OFFSET = 1;
    uint8 internal constant PAUSE_WITHDRAW_OFFSET = 2;
    uint8 internal constant PAUSE_ABSORB_OFFSET = 3;
    uint8 internal constant PAUSE_BUY_OFFSET = 4;

    /// @dev The decimals required for a price feed
    uint8 internal constant PRICE_FEED_DECIMALS = 8;

    /// @dev 365 days * 24 hours * 60 minutes * 60 seconds
    uint64 internal constant SECONDS_PER_YEAR = 31_536_000;

    /// @dev The scale for base tracking accrual
    uint64 internal constant BASE_ACCRUAL_SCALE = 1e6;

    /// @dev The scale for base index (depends on time/rate scales, not base token)
    uint64 internal constant BASE_INDEX_SCALE = 1e15;

    /// @dev The scale for prices (in USD)
    uint64 internal constant PRICE_SCALE = uint64(10 ** PRICE_FEED_DECIMALS);

    /// @dev The scale for factors
    uint64 internal constant FACTOR_SCALE = 1e18;

    /// @dev The storage slot for reentrancy guard flags
    bytes32 internal constant REENTRANCY_GUARD_FLAG_SLOT = bytes32(keccak256("comet.reentrancy.guard"));

    /// @dev The reentrancy guard statuses
    uint256 internal constant REENTRANCY_GUARD_NOT_ENTERED = 0;
    uint256 internal constant REENTRANCY_GUARD_ENTERED = 1;

    /**
     * @notice Determine if the manager has permission to act on behalf of the owner
     * @param owner The owner account
     * @param manager The manager account
     * @return Whether or not the manager has permission
     */
    function hasPermission(address owner, address manager) public view returns (bool) {
        return owner == manager || isAllowed[owner][manager];
    }

    /**
     * @dev The positive present supply balance if positive or the negative borrow balance if negative
     */
    function presentValue(int104 principalValue_) internal view returns (int256) {
        if (principalValue_ >= 0) {
            return signed256(presentValueSupply(baseSupplyIndex, uint104(principalValue_)));
        } else {
            return -signed256(presentValueBorrow(baseBorrowIndex, uint104(-principalValue_)));
        }
    }

    /**
     * @dev The principal amount projected forward by the supply index
     */
    function presentValueSupply(uint64 baseSupplyIndex_, uint104 principalValue_) internal pure returns (uint256) {
        return uint256(principalValue_) * baseSupplyIndex_ / BASE_INDEX_SCALE;
    }

    /**
     * @dev The principal amount projected forward by the borrow index
     */
    function presentValueBorrow(uint64 baseBorrowIndex_, uint104 principalValue_) internal pure returns (uint256) {
        return uint256(principalValue_) * baseBorrowIndex_ / BASE_INDEX_SCALE;
    }

    /**
     * @dev The positive principal if positive or the negative principal if negative
     */
    function principalValue(int256 presentValue_) internal view returns (int104) {
        if (presentValue_ >= 0) {
            return signed104(principalValueSupply(baseSupplyIndex, uint256(presentValue_)));
        } else {
            return -signed104(principalValueBorrow(baseBorrowIndex, uint256(-presentValue_)));
        }
    }

    /**
     * @dev The present value projected backward by the supply index (rounded down)
     *  Note: This will overflow (revert) at 2^104/1e18=~20 trillion principal for assets with 18 decimals.
     */
    function principalValueSupply(uint64 baseSupplyIndex_, uint256 presentValue_) internal pure returns (uint104) {
        return safe104((presentValue_ * BASE_INDEX_SCALE) / baseSupplyIndex_);
    }

    /**
     * @dev The present value projected backward by the borrow index (rounded up)
     *  Note: This will overflow (revert) at 2^104/1e18=~20 trillion principal for assets with 18 decimals.
     */
    function principalValueBorrow(uint64 baseBorrowIndex_, uint256 presentValue_) internal pure returns (uint104) {
        return safe104((presentValue_ * BASE_INDEX_SCALE + baseBorrowIndex_ - 1) / baseBorrowIndex_);
    }
}
CometMath.sol 61 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;

/**
 * @title Compound's Comet Math Contract
 * @dev Pure math functions
 * @author Compound
 */
contract CometMath {
    /** Custom errors **/

    error InvalidUInt64();
    error InvalidUInt104();
    error InvalidUInt128();
    error InvalidInt104();
    error InvalidInt256();
    error NegativeNumber();

    function safe64(uint n) internal pure returns (uint64) {
        if (n > type(uint64).max) revert InvalidUInt64();
        return uint64(n);
    }

    function safe104(uint n) internal pure returns (uint104) {
        if (n > type(uint104).max) revert InvalidUInt104();
        return uint104(n);
    }

    function safe128(uint n) internal pure returns (uint128) {
        if (n > type(uint128).max) revert InvalidUInt128();
        return uint128(n);
    }

    function signed104(uint104 n) internal pure returns (int104) {
        if (n > uint104(type(int104).max)) revert InvalidInt104();
        return int104(n);
    }

    function signed256(uint256 n) internal pure returns (int256) {
        if (n > uint256(type(int256).max)) revert InvalidInt256();
        return int256(n);
    }

    function unsigned104(int104 n) internal pure returns (uint104) {
        if (n < 0) revert NegativeNumber();
        return uint104(n);
    }

    function unsigned256(int256 n) internal pure returns (uint256) {
        if (n < 0) revert NegativeNumber();
        return uint256(n);
    }

    function toUInt8(bool x) internal pure returns (uint8) {
        return x ? 1 : 0;
    }

    function toBool(uint8 x) internal pure returns (bool) {
        return x != 0;
    }
}
CometStorage.sol 76 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;

/**
 * @title Compound's Comet Storage Interface
 * @dev Versions can enforce append-only storage slots via inheritance.
 * @author Compound
 */
contract CometStorage {
    // 512 bits total = 2 slots
    struct TotalsBasic {
        // 1st slot
        uint64 baseSupplyIndex;
        uint64 baseBorrowIndex;
        uint64 trackingSupplyIndex;
        uint64 trackingBorrowIndex;
        // 2nd slot
        uint104 totalSupplyBase;
        uint104 totalBorrowBase;
        uint40 lastAccrualTime;
        uint8 pauseFlags;
    }

    struct TotalsCollateral {
        uint128 totalSupplyAsset;
        uint128 _reserved;
    }

    struct UserBasic {
        int104 principal;
        uint64 baseTrackingIndex;
        uint64 baseTrackingAccrued;
        uint16 assetsIn;
        uint8 _reserved;
    }

    struct UserCollateral {
        uint128 balance;
        uint128 _reserved;
    }

    struct LiquidatorPoints {
        uint32 numAbsorbs;
        uint64 numAbsorbed;
        uint128 approxSpend;
        uint32 _reserved;
    }

    /// @dev Aggregate variables tracked for the entire market
    uint64 internal baseSupplyIndex;
    uint64 internal baseBorrowIndex;
    uint64 internal trackingSupplyIndex;
    uint64 internal trackingBorrowIndex;
    uint104 internal totalSupplyBase;
    uint104 internal totalBorrowBase;
    uint40 internal lastAccrualTime;
    uint8 internal pauseFlags;

    /// @notice Aggregate variables tracked for each collateral asset
    mapping(address => TotalsCollateral) public totalsCollateral;

    /// @notice Mapping of users to accounts which may be permitted to manage the user account
    mapping(address => mapping(address => bool)) public isAllowed;

    /// @notice The next expected nonce for an address, for validating authorizations via signature
    mapping(address => uint) public userNonce;

    /// @notice Mapping of users to base principal and other basic data
    mapping(address => UserBasic) public userBasic;

    /// @notice Mapping of users to collateral data per collateral asset
    mapping(address => mapping(address => UserCollateral)) public userCollateral;

    /// @notice Mapping of magic liquidator points
    mapping(address => LiquidatorPoints) public liquidatorPoints;
}
CometInterface.sol 12 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;

import "./CometMainInterface.sol";
import "./CometExtInterface.sol";

/**
 * @title Compound's Comet Interface
 * @notice An efficient monolithic money market protocol
 * @author Compound
 */
abstract contract CometInterface is CometMainInterface, CometExtInterface {}
CometExtInterface.sol 68 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;

import "./CometCore.sol";

/**
 * @title Compound's Comet Ext Interface
 * @notice An efficient monolithic money market protocol
 * @author Compound
 */
abstract contract CometExtInterface is CometCore {
    error BadAmount();
    error BadNonce();
    error BadSignatory();
    error InvalidValueS();
    error InvalidValueV();
    error SignatureExpired();

    function allow(address manager, bool isAllowed) virtual external;
    function allowBySig(address owner, address manager, bool isAllowed, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) virtual external;

    function collateralBalanceOf(address account, address asset) virtual external view returns (uint128);
    function baseTrackingAccrued(address account) virtual external view returns (uint64);

    function baseAccrualScale() virtual external view returns (uint64);
    function baseIndexScale() virtual external view returns (uint64);
    function factorScale() virtual external view returns (uint64);
    function priceScale() virtual external view returns (uint64);

    function maxAssets() virtual external view returns (uint8);

    function totalsBasic() virtual external view returns (TotalsBasic memory);

    function version() virtual external view returns (string memory);

    /**
      * ===== ERC20 interfaces =====
      * Does not include the following functions/events, which are defined in `CometMainInterface` instead:
      * - function decimals() virtual external view returns (uint8)
      * - function totalSupply() virtual external view returns (uint256)
      * - function transfer(address dst, uint amount) virtual external returns (bool)
      * - function transferFrom(address src, address dst, uint amount) virtual external returns (bool)
      * - function balanceOf(address owner) virtual external view returns (uint256)
      * - event Transfer(address indexed from, address indexed to, uint256 amount)
      */
    function name() virtual external view returns (string memory);
    function symbol() virtual external view returns (string memory);

    /**
      * @notice Approve `spender` to transfer up to `amount` from `src`
      * @dev This will overwrite the approval amount for `spender`
      *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
      * @param spender The address of the account which may transfer tokens
      * @param amount The number of tokens that are approved (-1 means infinite)
      * @return Whether or not the approval succeeded
      */
    function approve(address spender, uint256 amount) virtual external returns (bool);

    /**
      * @notice Get the current allowance from `owner` for `spender`
      * @param owner The address of the account which owns the tokens to be spent
      * @param spender The address of the account which may transfer tokens
      * @return The number of tokens allowed to be spent (-1 means infinite)
      */
    function allowance(address owner, address spender) virtual external view returns (uint256);

    event Approval(address indexed owner, address indexed spender, uint256 amount);
}
IERC20NonStandard.sol 43 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;

/**
 * @title IERC20NonStandard
 * @dev Version of ERC20 with no return values for `approve`, `transfer`, and `transferFrom`
 *  See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
 */
interface IERC20NonStandard {
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function decimals() external view returns (uint8);

    /**
     * @notice Approve `spender` to transfer up to `amount` from `src`
     * @dev This will overwrite the approval amount for `spender`
     *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
     * @param spender The address of the account which may transfer tokens
     * @param amount The number of tokens that are approved (-1 means infinite)
     */
    function approve(address spender, uint256 amount) external;

    /**
     * @notice Transfer `value` tokens from `msg.sender` to `to`
     * @param to The address of the destination account
     * @param value The number of tokens to transfer
     */
    function transfer(address to, uint256 value) external;

    /**
     * @notice Transfer `value` tokens from `from` to `to`
     * @param from The address of the source account
     * @param to The address of the destination account
     * @param value The number of tokens to transfer
     */
    function transferFrom(address from, address to, uint256 value) external;

    /**
     * @notice Gets the balance of the specified address
     * @param account The address from which the balance will be retrieved
     */
    function balanceOf(address account) external view returns (uint256);
}
CometConfiguration.sol 49 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;

/**
 * @title Compound's Comet Configuration Interface
 * @author Compound
 */
contract CometConfiguration {
    struct ExtConfiguration {
        bytes32 name32;
        bytes32 symbol32;
    }

    struct Configuration {
        address governor;
        address pauseGuardian;
        address baseToken;
        address baseTokenPriceFeed;
        address extensionDelegate;

        uint64 supplyKink;
        uint64 supplyPerYearInterestRateSlopeLow;
        uint64 supplyPerYearInterestRateSlopeHigh;
        uint64 supplyPerYearInterestRateBase;
        uint64 borrowKink;
        uint64 borrowPerYearInterestRateSlopeLow;
        uint64 borrowPerYearInterestRateSlopeHigh;
        uint64 borrowPerYearInterestRateBase;
        uint64 storeFrontPriceFactor;
        uint64 trackingIndexScale;
        uint64 baseTrackingSupplySpeed;
        uint64 baseTrackingBorrowSpeed;
        uint104 baseMinForRewards;
        uint104 baseBorrowMin;
        uint104 targetReserves;

        AssetConfig[] assetConfigs;
    }

    struct AssetConfig {
        address asset;
        address priceFeed;
        uint8 decimals;
        uint64 borrowCollateralFactor;
        uint64 liquidateCollateralFactor;
        uint64 liquidationFactor;
        uint128 supplyCap;
    }
}
CometMainInterface.sol 152 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;

import "./CometCore.sol";

/**
 * @title Compound's Comet Main Interface (without Ext)
 * @notice An efficient monolithic money market protocol
 * @author Compound
 */
abstract contract CometMainInterface is CometCore {
    error Absurd();
    error AlreadyInitialized();
    error BadAsset();
    error BadDecimals();
    error BadDiscount();
    error BadMinimum();
    error BadPrice();
    error BorrowTooSmall();
    error BorrowCFTooLarge();
    error InsufficientReserves();
    error LiquidateCFTooLarge();
    error NoSelfTransfer();
    error NotCollateralized();
    error NotForSale();
    error NotLiquidatable();
    error Paused();
    error ReentrantCallBlocked();
    error SupplyCapExceeded();
    error TimestampTooLarge();
    error TooManyAssets();
    error TooMuchSlippage();
    error TransferInFailed();
    error TransferOutFailed();
    error Unauthorized();

    event Supply(address indexed from, address indexed dst, uint amount);
    event Transfer(address indexed from, address indexed to, uint amount);
    event Withdraw(address indexed src, address indexed to, uint amount);

    event SupplyCollateral(address indexed from, address indexed dst, address indexed asset, uint amount);
    event TransferCollateral(address indexed from, address indexed to, address indexed asset, uint amount);
    event WithdrawCollateral(address indexed src, address indexed to, address indexed asset, uint amount);

    /// @notice Event emitted when a borrow position is absorbed by the protocol
    event AbsorbDebt(address indexed absorber, address indexed borrower, uint basePaidOut, uint usdValue);

    /// @notice Event emitted when a user's collateral is absorbed by the protocol
    event AbsorbCollateral(address indexed absorber, address indexed borrower, address indexed asset, uint collateralAbsorbed, uint usdValue);

    /// @notice Event emitted when a collateral asset is purchased from the protocol
    event BuyCollateral(address indexed buyer, address indexed asset, uint baseAmount, uint collateralAmount);

    /// @notice Event emitted when an action is paused/unpaused
    event PauseAction(bool supplyPaused, bool transferPaused, bool withdrawPaused, bool absorbPaused, bool buyPaused);

    /// @notice Event emitted when reserves are withdrawn by the governor
    event WithdrawReserves(address indexed to, uint amount);

    function supply(address asset, uint amount) virtual external;
    function supplyTo(address dst, address asset, uint amount) virtual external;
    function supplyFrom(address from, address dst, address asset, uint amount) virtual external;

    function transfer(address dst, uint amount) virtual external returns (bool);
    function transferFrom(address src, address dst, uint amount) virtual external returns (bool);

    function transferAsset(address dst, address asset, uint amount) virtual external;
    function transferAssetFrom(address src, address dst, address asset, uint amount) virtual external;

    function withdraw(address asset, uint amount) virtual external;
    function withdrawTo(address to, address asset, uint amount) virtual external;
    function withdrawFrom(address src, address to, address asset, uint amount) virtual external;

    function approveThis(address manager, address asset, uint amount) virtual external;
    function withdrawReserves(address to, uint amount) virtual external;

    function absorb(address absorber, address[] calldata accounts) virtual external;
    function buyCollateral(address asset, uint minAmount, uint baseAmount, address recipient) virtual external;
    function quoteCollateral(address asset, uint baseAmount) virtual public view returns (uint);

    function getAssetInfo(uint8 i) virtual public view returns (AssetInfo memory);
    function getAssetInfoByAddress(address asset) virtual public view returns (AssetInfo memory);
    function getCollateralReserves(address asset) virtual public view returns (uint);
    function getReserves() virtual public view returns (int);
    function getPrice(address priceFeed) virtual public view returns (uint);

    function isBorrowCollateralized(address account) virtual public view returns (bool);
    function isLiquidatable(address account) virtual public view returns (bool);

    function totalSupply() virtual external view returns (uint256);
    function totalBorrow() virtual external view returns (uint256);
    function balanceOf(address owner) virtual public view returns (uint256);
    function borrowBalanceOf(address account) virtual public view returns (uint256);

    function pause(bool supplyPaused, bool transferPaused, bool withdrawPaused, bool absorbPaused, bool buyPaused) virtual external;
    function isSupplyPaused() virtual public view returns (bool);
    function isTransferPaused() virtual public view returns (bool);
    function isWithdrawPaused() virtual public view returns (bool);
    function isAbsorbPaused() virtual public view returns (bool);
    function isBuyPaused() virtual public view returns (bool);

    function accrueAccount(address account) virtual external;
    function getSupplyRate(uint utilization) virtual public view returns (uint64);
    function getBorrowRate(uint utilization) virtual public view returns (uint64);
    function getUtilization() virtual public view returns (uint);

    function governor() virtual external view returns (address);
    function pauseGuardian() virtual external view returns (address);
    function baseToken() virtual external view returns (address);
    function baseTokenPriceFeed() virtual external view returns (address);
    function extensionDelegate() virtual external view returns (address);

    /// @dev uint64
    function supplyKink() virtual external view returns (uint);
    /// @dev uint64
    function supplyPerSecondInterestRateSlopeLow() virtual external view returns (uint);
    /// @dev uint64
    function supplyPerSecondInterestRateSlopeHigh() virtual external view returns (uint);
    /// @dev uint64
    function supplyPerSecondInterestRateBase() virtual external view returns (uint);
    /// @dev uint64
    function borrowKink() virtual external view returns (uint);
    /// @dev uint64
    function borrowPerSecondInterestRateSlopeLow() virtual external view returns (uint);
    /// @dev uint64
    function borrowPerSecondInterestRateSlopeHigh() virtual external view returns (uint);
    /// @dev uint64
    function borrowPerSecondInterestRateBase() virtual external view returns (uint);
    /// @dev uint64
    function storeFrontPriceFactor() virtual external view returns (uint);

    /// @dev uint64
    function baseScale() virtual external view returns (uint);
    /// @dev uint64
    function trackingIndexScale() virtual external view returns (uint);

    /// @dev uint64
    function baseTrackingSupplySpeed() virtual external view returns (uint);
    /// @dev uint64
    function baseTrackingBorrowSpeed() virtual external view returns (uint);
    /// @dev uint104
    function baseMinForRewards() virtual external view returns (uint);
    /// @dev uint104
    function baseBorrowMin() virtual external view returns (uint);
    /// @dev uint104
    function targetReserves() virtual external view returns (uint);

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

    function initializeStorage() virtual external;
}
BaseBulker.sol 289 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;

import "../CometInterface.sol";
import "../IERC20NonStandard.sol";
import "../IWETH9.sol";

/**
 * @dev Interface for claiming rewards from the CometRewards contract
 */
interface IClaimable {
    function claim(address comet, address src, bool shouldAccrue) external;

    function claimTo(address comet, address src, address to, bool shouldAccrue) external;
}

/**
 * @title Compound's Bulker contract
 * @notice Executes multiple Comet-related actions in a single transaction
 * @author Compound
 * @dev Note: Only intended to be used on EVM chains that have a native token and wrapped native token that implements the IWETH interface
 */
contract BaseBulker {
    /** Custom events **/

    event AdminTransferred(address indexed oldAdmin, address indexed newAdmin);

    /** General configuration constants **/

    /// @notice The admin of the Bulker contract
    address public admin;

    /// @notice The address of the wrapped representation of the chain's native asset
    address payable public immutable wrappedNativeToken;

    /** Actions **/

    /// @notice The action for supplying an asset to Comet
    bytes32 public constant ACTION_SUPPLY_ASSET = "ACTION_SUPPLY_ASSET";

    /// @notice The action for supplying a native asset (e.g. ETH on Ethereum mainnet) to Comet
    bytes32 public constant ACTION_SUPPLY_NATIVE_TOKEN = "ACTION_SUPPLY_NATIVE_TOKEN";

    /// @notice The action for transferring an asset within Comet
    bytes32 public constant ACTION_TRANSFER_ASSET = "ACTION_TRANSFER_ASSET";

    /// @notice The action for withdrawing an asset from Comet
    bytes32 public constant ACTION_WITHDRAW_ASSET = "ACTION_WITHDRAW_ASSET";

    /// @notice The action for withdrawing a native asset from Comet
    bytes32 public constant ACTION_WITHDRAW_NATIVE_TOKEN = "ACTION_WITHDRAW_NATIVE_TOKEN";

    /// @notice The action for claiming rewards from the Comet rewards contract
    bytes32 public constant ACTION_CLAIM_REWARD = "ACTION_CLAIM_REWARD";

    /** Custom errors **/

    error InvalidAddress();
    error InvalidArgument();
    error FailedToSendNativeToken();
    error TransferInFailed();
    error TransferOutFailed();
    error Unauthorized();
    error UnhandledAction();

    /**
     * @notice Construct a new BaseBulker instance
     * @param admin_ The admin of the Bulker contract
     * @param wrappedNativeToken_ The address of the wrapped representation of the chain's native asset
     **/
    constructor(address admin_, address payable wrappedNativeToken_) {
        admin = admin_;
        wrappedNativeToken = wrappedNativeToken_;
    }

    /**
     * @notice Fallback for receiving native token. Needed for ACTION_WITHDRAW_NATIVE_TOKEN
     */
    receive() external payable {}

    /**
     * @notice A public function to sweep accidental ERC-20 transfers to this contract
     * @dev Note: Make sure to check that the asset being swept out is not malicious
     * @param recipient The address that will receive the swept funds
     * @param asset The address of the ERC-20 token to sweep
     */
    function sweepToken(address recipient, address asset) external {
        if (msg.sender != admin) revert Unauthorized();

        uint256 balance = IERC20NonStandard(asset).balanceOf(address(this));
        doTransferOut(asset, recipient, balance);
    }

    /**
     * @notice A public function to sweep accidental native token transfers to this contract
     * @param recipient The address that will receive the swept funds
     */
    function sweepNativeToken(address recipient) external {
        if (msg.sender != admin) revert Unauthorized();

        uint256 balance = address(this).balance;
        (bool success, ) = recipient.call{ value: balance }("");
        if (!success) revert FailedToSendNativeToken();
    }

    /**
     * @notice Transfers the admin rights to a new address
     * @param newAdmin The address that will become the new admin
     */
    function transferAdmin(address newAdmin) external {
        if (msg.sender != admin) revert Unauthorized();
        if (newAdmin == address(0)) revert InvalidAddress();

        address oldAdmin = admin;
        admin = newAdmin;
        emit AdminTransferred(oldAdmin, newAdmin);
    }

    /**
     * @notice Executes a list of actions in order
     * @param actions The list of actions to execute in order
     * @param data The list of calldata to use for each action
     */
    function invoke(bytes32[] calldata actions, bytes[] calldata data) external payable {
        if (actions.length != data.length) revert InvalidArgument();

        uint unusedNativeToken = msg.value;
        for (uint i = 0; i < actions.length; ) {
            bytes32 action = actions[i];
            if (action == ACTION_SUPPLY_ASSET) {
                (address comet, address to, address asset, uint amount) = abi.decode(data[i], (address, address, address, uint));
                supplyTo(comet, to, asset, amount);
            } else if (action == ACTION_SUPPLY_NATIVE_TOKEN) {
                (address comet, address to, uint amount) = abi.decode(data[i], (address, address, uint));
                uint256 nativeTokenUsed = supplyNativeTokenTo(comet, to, amount);
                unusedNativeToken -= nativeTokenUsed;
            } else if (action == ACTION_TRANSFER_ASSET) {
                (address comet, address to, address asset, uint amount) = abi.decode(data[i], (address, address, address, uint));
                transferTo(comet, to, asset, amount);
            } else if (action == ACTION_WITHDRAW_ASSET) {
                (address comet, address to, address asset, uint amount) = abi.decode(data[i], (address, address, address, uint));
                withdrawTo(comet, to, asset, amount);
            } else if (action == ACTION_WITHDRAW_NATIVE_TOKEN) {
                (address comet, address to, uint amount) = abi.decode(data[i], (address, address, uint));
                withdrawNativeTokenTo(comet, to, amount);
            } else if (action == ACTION_CLAIM_REWARD) {
                (address comet, address rewards, address src, bool shouldAccrue) = abi.decode(data[i], (address, address, address, bool));
                claimReward(comet, rewards, src, shouldAccrue);
            } else {
                handleAction(action, data[i]);
            }
            unchecked { i++; }
        }

        // Refund unused native token back to msg.sender
        if (unusedNativeToken > 0) {
            (bool success, ) = msg.sender.call{ value: unusedNativeToken }("");
            if (!success) revert FailedToSendNativeToken();
        }
    }

    /**
     * @notice Handles any actions not handled by the BaseBulker implementation
     * @dev Note: Meant to be overridden by contracts that extend BaseBulker and want to support more actions
     */
    function handleAction(bytes32 action, bytes calldata data) virtual internal {
        revert UnhandledAction();
    }

    /**
     * @notice Supplies an asset to a user in Comet
     * @dev Note: This contract must have permission to manage msg.sender's Comet account
     */
    function supplyTo(address comet, address to, address asset, uint amount) internal {
        CometInterface(comet).supplyFrom(msg.sender, to, asset, amount);
    }

    /**
     * @notice Wraps the native token and supplies wrapped native token to a user in Comet
     * @return The amount of the native token wrapped and supplied to Comet
     * @dev Note: Supports `amount` of `uint256.max` implies max only for base asset
     */
    function supplyNativeTokenTo(address comet, address to, uint amount) internal returns (uint256) {
        uint256 supplyAmount = amount;
        if (wrappedNativeToken == CometInterface(comet).baseToken()) {
            if (amount == type(uint256).max)
                supplyAmount = CometInterface(comet).borrowBalanceOf(msg.sender);
        }
        IWETH9(wrappedNativeToken).deposit{ value: supplyAmount }();
        IWETH9(wrappedNativeToken).approve(comet, supplyAmount);
        CometInterface(comet).supplyFrom(address(this), to, wrappedNativeToken, supplyAmount);
        return supplyAmount;
    }

    /**
     * @notice Transfers an asset to a user in Comet
     * @dev Note: This contract must have permission to manage msg.sender's Comet account
     */
    function transferTo(address comet, address to, address asset, uint amount) internal {
        CometInterface(comet).transferAssetFrom(msg.sender, to, asset, amount);
    }

    /**
     * @notice Withdraws an asset to a user in Comet
     * @dev Note: This contract must have permission to manage msg.sender's Comet account
     */
    function withdrawTo(address comet, address to, address asset, uint amount) internal {
        CometInterface(comet).withdrawFrom(msg.sender, to, asset, amount);
    }

    /**
     * @notice Withdraws wrapped native token from Comet, unwraps it to the native token, and transfers it to a user
     * @dev Note: This contract must have permission to manage msg.sender's Comet account
     * @dev Note: Supports `amount` of `uint256.max` only for the base asset. Should revert for a collateral asset
     */
    function withdrawNativeTokenTo(address comet, address to, uint amount) internal {
        uint256 withdrawAmount = amount;
        if (wrappedNativeToken == CometInterface(comet).baseToken()) {
            if (amount == type(uint256).max)
                withdrawAmount = CometInterface(comet).balanceOf(msg.sender);
        }
        CometInterface(comet).withdrawFrom(msg.sender, address(this), wrappedNativeToken, withdrawAmount);
        IWETH9(wrappedNativeToken).withdraw(withdrawAmount);
        (bool success, ) = to.call{ value: withdrawAmount }("");
        if (!success) revert FailedToSendNativeToken();
    }

    /**
     * @notice Claims rewards for a user
     */
    function claimReward(address comet, address rewards, address src, bool shouldAccrue) internal {
        IClaimable(rewards).claim(comet, src, shouldAccrue);
    }

    /**
     * @notice Similar to ERC-20 transfer, except it properly handles `transferFrom` from non-standard ERC-20 tokens
     * @param asset The ERC-20 token to transfer in
     * @param from The address to transfer from
     * @param amount The amount of the token to transfer
     * @dev Note: This does not check that the amount transferred in is actually equals to the amount specified (e.g. fee tokens will not revert)
     * @dev Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value. See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
     */
    function doTransferIn(address asset, address from, uint amount) internal {
        IERC20NonStandard(asset).transferFrom(from, address(this), amount);

        bool success;
        assembly {
            switch returndatasize()
                case 0 {                       // This is a non-standard ERC-20
                    success := not(0)          // set success to true
                }
                case 32 {                      // This is a compliant ERC-20
                    returndatacopy(0, 0, 32)
                    success := mload(0)        // Set `success = returndata` of override external call
                }
                default {                      // This is an excessively non-compliant ERC-20, revert.
                    revert(0, 0)
                }
        }
        if (!success) revert TransferInFailed();
    }

    /**
     * @notice Similar to ERC-20 transfer, except it properly handles `transfer` from non-standard ERC-20 tokens
     * @param asset The ERC-20 token to transfer out
     * @param to The recipient of the token transfer
     * @param amount The amount of the token to transfer
     * @dev Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value. See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
     */
    function doTransferOut(address asset, address to, uint amount) internal {
        IERC20NonStandard(asset).transfer(to, amount);

        bool success;
        assembly {
            switch returndatasize()
                case 0 {                      // This is a non-standard ERC-20
                    success := not(0)         // set success to true
                }
                case 32 {                     // This is a compliant ERC-20
                    returndatacopy(0, 0, 32)
                    success := mload(0)       // Set `success = returndata` of override external call
                }
                default {                     // This is an excessively non-compliant ERC-20, revert.
                    revert(0, 0)
                }
        }
        if (!success) revert TransferOutFailed();
    }
}
MainnetBulkerWithWstETHSupport.sol 109 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;

import "./BaseBulker.sol";
import "../IWstETH.sol";

/**
 * @title Compound's Bulker contract for Ethereum mainnet
 * @notice Executes multiple Comet-related actions in a single transaction
 * @author Compound
 */
contract MainnetBulkerWithWstETHSupport is BaseBulker {
    /** General configuration constants **/

    /// @notice The address of Lido staked ETH
    address public immutable steth;

    /// @notice The address of Lido wrapped staked ETH
    address public immutable wsteth;

    /** Actions **/

    /// @notice The action for supplying staked ETH to Comet
    bytes32 public constant ACTION_SUPPLY_STETH = "ACTION_SUPPLY_STETH";

    /// @notice The action for withdrawing staked ETH from Comet
    bytes32 public constant ACTION_WITHDRAW_STETH = "ACTION_WITHDRAW_STETH";

    /** Custom errors **/

    error UnsupportedBaseAsset();

    /**
     * @notice Construct a new MainnetBulker instance
     * @param admin_ The admin of the Bulker contract
     * @param weth_ The address of wrapped ETH
     * @param wsteth_ The address of Lido wrapped staked ETH
     **/
    constructor(
        address admin_,
        address payable weth_,
        address wsteth_
    ) BaseBulker(admin_, weth_) {
        wsteth = wsteth_;
        steth = IWstETH(wsteth_).stETH();
    }

    /**
     * @notice Handles actions specific to the Ethereum mainnet version of Bulker, specifically supplying and withdrawing stETH
     */
    function handleAction(bytes32 action, bytes calldata data) override internal {
        if (action == ACTION_SUPPLY_STETH) {
            (address comet, address to, uint stETHAmount) = abi.decode(data, (address, address, uint));
            supplyStEthTo(comet, to, stETHAmount);
        } else if (action == ACTION_WITHDRAW_STETH) {
            (address comet, address to, uint wstETHAmount) = abi.decode(data, (address, address, uint));
            withdrawStEthTo(comet, to, wstETHAmount);
        } else {
            revert UnhandledAction();
        }
    }

    /**
     * @notice Wraps stETH to wstETH and supplies to a user in Comet
     * @dev Note: This contract must have permission to manage msg.sender's Comet account
     * @dev Note: Supports `stETHAmount` of `uint256.max` to fully repay the wstETH debt
     * @dev Note: Only for the cwstETHv3 market
     */
    function supplyStEthTo(address comet, address to, uint stETHAmount) internal {
        if(CometInterface(comet).baseToken() != wsteth) revert UnsupportedBaseAsset();
        uint256 _stETHAmount = stETHAmount == type(uint256).max
            ? IWstETH(wsteth).getStETHByWstETH(CometInterface(comet).borrowBalanceOf(msg.sender))
            : stETHAmount;
        doTransferIn(steth, msg.sender, _stETHAmount);
        ERC20(steth).approve(wsteth, _stETHAmount);
        uint wstETHAmount = IWstETH(wsteth).wrap(_stETHAmount);
        ERC20(wsteth).approve(comet, wstETHAmount);
        CometInterface(comet).supplyFrom(address(this), to, wsteth, wstETHAmount);
    }

    /**
     * @notice Withdraws wstETH from Comet, unwraps it to stETH, and transfers it to a user
     * @dev Note: This contract must have permission to manage msg.sender's Comet account
     * @dev Note: Supports `amount` of `uint256.max` to withdraw all wstETH from Comet
     * @dev Note: Only for the cwstETHv3 market
     */
    function withdrawStEthTo(address comet, address to, uint stETHAmount) internal {
        if(CometInterface(comet).baseToken() != wsteth) revert UnsupportedBaseAsset();
        uint wstETHAmount = stETHAmount == type(uint256).max
            ? CometInterface(comet).balanceOf(msg.sender)
            : IWstETH(wsteth).getWstETHByStETH(stETHAmount);
        CometInterface(comet).withdrawFrom(msg.sender, address(this), wsteth, wstETHAmount);
        uint unwrappedStETHAmount = IWstETH(wsteth).unwrap(wstETHAmount);
        doTransferOut(steth, to, unwrappedStETHAmount);
    }
    
    /**
     * @notice Submits received ether to get stETH and wraps it to wstETH, received wstETH is transferred to Comet
     */
    function deposit(address comet) external payable {
        if(msg.sender != admin) revert Unauthorized();
        if(CometInterface(comet).baseToken() != wsteth) revert UnsupportedBaseAsset();
        (bool success, ) = payable(wsteth).call{value: msg.value}(new bytes(0));
        if(!success) revert TransferOutFailed();

        uint wstETHAmount = ERC20(wsteth).balanceOf(address(this));
        doTransferOut(wsteth, comet, wstETHAmount);
    }
}

Read Contract

ACTION_CLAIM_REWARD 0xc7f6d7b5 → bytes32
ACTION_SUPPLY_ASSET 0xb8ef9d98 → bytes32
ACTION_SUPPLY_NATIVE_TOKEN 0xcc157905 → bytes32
ACTION_SUPPLY_STETH 0xb69d3415 → bytes32
ACTION_TRANSFER_ASSET 0x1aa7c53e → bytes32
ACTION_WITHDRAW_ASSET 0x9860d6e6 → bytes32
ACTION_WITHDRAW_NATIVE_TOKEN 0xd69adac4 → bytes32
ACTION_WITHDRAW_STETH 0x9f292915 → bytes32
admin 0xf851a440 → address
steth 0x953d7ee2 → address
wrappedNativeToken 0x17fcb39b → address
wsteth 0xd1d8bce7 → address

Write Contract 5 functions

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

deposit 0xf340fa01
address comet
invoke 0x555029a6
bytes32[] actions
bytes[] data
sweepNativeToken 0xb660c156
address recipient
sweepToken 0x258836fe
address recipient
address asset
transferAdmin 0x75829def
address newAdmin

Recent Transactions

No transactions found for this address