Address Contract Verified
Address
0x2c776041CCFe903071AF44aa147368a9c8EEA518
Balance
0 ETH
Nonce
1
Code Size
6701 bytes
Creator
0x2501713A...4EAb at tx 0x4a7c6cb4...82cac4
Indexed Transactions
0
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