Feature Tip: Add private address tag to any address under My Name Tag !
ERC-20
Overview
Max Total Supply
69,420,000,000 MEMEME
Holders
2,834
Market
Price
$0.00 @ 0.000000 ETH (-1.02%)
Onchain Market Cap
$205,375.49
Circulating Supply Market Cap
$0.00
Other Info
Token Contract (WITH 18 Decimals)
Balance
851,618.707001841823193877 MEMEMEValue
$2.52 ( ~0.000768416700104675 Eth) [0.0012%]Loading...
Loading
Loading...
Loading
Loading...
Loading
# | Exchange | Pair | Price | 24H Volume | % Volume |
---|
Contract Name:
TradCoin
Compiler Version
v0.8.11+commit.d7f03943
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.10; import {ERC20} from "lib/solmate/src/tokens/ERC20.sol"; contract TradCoin is ERC20 { constructor(string memory name, string memory id) ERC20(name, id, 18) { _mint(msg.sender, 69420000000 * 10 ** 18); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol) /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. abstract contract ERC20 { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); /*////////////////////////////////////////////////////////////// METADATA STORAGE //////////////////////////////////////////////////////////////*/ string public name; string public symbol; uint8 public immutable decimals; /*////////////////////////////////////////////////////////////// ERC20 STORAGE //////////////////////////////////////////////////////////////*/ uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; /*////////////////////////////////////////////////////////////// EIP-2612 STORAGE //////////////////////////////////////////////////////////////*/ uint256 internal immutable INITIAL_CHAIN_ID; bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; mapping(address => uint256) public nonces; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor( string memory _name, string memory _symbol, uint8 _decimals ) { name = _name; symbol = _symbol; decimals = _decimals; INITIAL_CHAIN_ID = block.chainid; INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); } /*////////////////////////////////////////////////////////////// ERC20 LOGIC //////////////////////////////////////////////////////////////*/ function approve(address spender, uint256 amount) public virtual returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function transfer(address to, uint256 amount) public virtual returns (bool) { balanceOf[msg.sender] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(msg.sender, to, amount); return true; } function transferFrom( address from, address to, uint256 amount ) public virtual returns (bool) { uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; balanceOf[from] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(from, to, amount); return true; } /*////////////////////////////////////////////////////////////// EIP-2612 LOGIC //////////////////////////////////////////////////////////////*/ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual { require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); // Unchecked because the only math done is incrementing // the owner's nonce which cannot realistically overflow. unchecked { address recoveredAddress = ecrecover( keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256( abi.encode( keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ), owner, spender, value, nonces[owner]++, deadline ) ) ) ), v, r, s ); require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); allowance[recoveredAddress][spender] = value; } emit Approval(owner, spender, value); } function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); } function computeDomainSeparator() internal view virtual returns (bytes32) { return keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(name)), keccak256("1"), block.chainid, address(this) ) ); } /*////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to, uint256 amount) internal virtual { totalSupply += amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(address(0), to, amount); } function _burn(address from, uint256 amount) internal virtual { balanceOf[from] -= amount; // Cannot underflow because a user's balance // will never be larger than the total supply. unchecked { totalSupply -= amount; } emit Transfer(from, address(0), amount); } }
pragma solidity ^0.8.10; import "./AccountantInterfaces.sol"; import "../ExponentialNoError.sol"; import "../ErrorReporter.sol"; import "../Treasury/TreasuryInterfaces.sol"; contract AccountantDelegate is AccountantInterface, ExponentialNoError, TokenErrorReporter, ComptrollerErrorReporter, AccountantErrors { /** * @notice Method used to initialize the contract during delegator contructor * @param cnoteAddress_ The address of the CNoteDelegator * @param noteAddress_ The address of the note contract * @param comptrollerAddress_ The address of the comptroller contract */ function initialize( address treasury_, address cnoteAddress_, address noteAddress_, address comptrollerAddress_ ) external { //AccountantDelegate can only be initialized once if (msg.sender != admin) { revert SenderNotAdmin(msg.sender); } if ( address(treasury) != address(0) || address(note) != address(0) || address(cnote) != address(0) ) { revert AccountantInitializedAgain(); } treasury = treasury_; // set the current treasury address (address of TreasuryDelegator) address[] memory MarketEntered = new address[](1); // first entry into lending market MarketEntered[0] = cnoteAddress_; comptroller = ComptrollerInterface(comptrollerAddress_); note = Note(noteAddress_); cnote = CNote(cnoteAddress_); uint256[] memory err = comptroller.enterMarkets(MarketEntered); // check if market entry returns without error if (err[0] != 0) { revert ErrorMarketEntering(err[0]); } emit AcctInit(cnoteAddress_); note.approve(cnoteAddress_, type(uint256).max); // approve lending market, to transferFrom Accountant as needed } /** * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. * @param newPendingAdmin New pending admin. */ function _setPendingAdmin(address newPendingAdmin) external override { // Check caller = admin require( msg.sender == admin, "TreasuryDelegator:_setPendingAdmin: admin only" ); // Save current value, if any, for inclusion in log address oldPendingAdmin = pendingAdmin; // Store pendingAdmin with value newPendingAdmin pendingAdmin = newPendingAdmin; // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin) emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin); } /** * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin * @dev Admin function for pending admin to accept role and update admin */ function _acceptAdmin() external override { // Check caller is pendingAdmin and pendingAdmin ≠ address(0), msg.sender cannot == address(0) require( msg.sender == pendingAdmin, "TreasuryDelegator:_acceptAdmin: pending admin only" ); // Save current values for inclusion in log address oldAdmin = admin; address oldPendingAdmin = pendingAdmin; // Store admin with value pendingAdmin admin = pendingAdmin; // Clear the pending value pendingAdmin = address(0); emit NewAdmin(oldAdmin, admin); emit NewPendingAdmin(oldPendingAdmin, pendingAdmin); } /** * @notice Method to supply markets * @param amount the amount to supply * @return uint error code from CNote mint() */ function supplyMarket(uint256 amount) external override returns (uint256) { if (msg.sender != address(cnote)) { revert SenderNotCNote(address(cnote)); } uint256 err = cnote.mint(amount); emit AcctSupplied(amount, uint256(err)); return err; } /** * @notice Method to redeem account CNote from lending market * @param amount Amount to redeem (Note) * @return uint Amount of cnote redeemed (amount * exchange rate) */ function redeemMarket(uint256 amount) external override returns (uint256) { if (msg.sender != address(cnote)) { revert SenderNotCNote(address(cnote)); } emit AcctRedeemed(amount); return cnote.redeemUnderlying(amount); // redeem the amount of Note calculated via current CNote exchange rate } /** * @notice Method to sweep interest earned from accountant depositing note in lending market to the treasury */ function sweepInterest() external override { if (msg.sender != admin) { revert SenderNotAdmin(msg.sender); } //Total balance of Treasury => Note + CNote Balance, Exp memory exRate = Exp({mantissa: cnote.exchangeRateStored()}); //used stored interest rates in determining amount to sweep //underflow impossible uint256 noteDiff = sub_(note.totalSupply(), note.balanceOf(address(this))); //Note deficit in Accountant uint256 cNoteBal = cnote.balanceOf(address(this)); //current cNote Balance uint256 cNoteAmt = mul_(cNoteBal, exRate); // cNote Balance converted to Note require( cNoteAmt >= noteDiff, "AccountantDelegate::sweepInterest:Error calculating interest to sweep" ); uint256 amtToSweep = sub_(cNoteAmt, noteDiff); // amount to sweep in Note, uint256 cNoteToSweep = div_(amtToSweep, exRate); // amount of cNote to sweep = amtToSweep(Note) / exRate cNoteToSweep = cNoteToSweep > cNoteBal ? cNoteBal : cNoteToSweep; bool success = cnote.transfer(treasury, amtToSweep); if (!success) { revert SweepError(treasury, amtToSweep); //handles if transfer of tokens is not successful } TreasuryInterface Treas = TreasuryInterface(treasury); Treas.redeem(address(cnote), amtToSweep); } }
pragma solidity ^0.8.10; import "./AccountantInterfaces.sol"; contract AccountantDelegator is AccountantInterface, AccountantDelegatorInterface { /** * @param implementation_ implementation address (the AccountantDelegate) * @param admin_ admin address (Timelock) * @param cnoteAddress_ lending market address (CNote) * @param noteAddress_ note address (note erc20 contract) * @param comptrollerAddress_, address of Comptroller Delegator(Unitroller) * @param treasury_ treasury address (TreasuryDelegator) */ constructor( address implementation_, address admin_, address cnoteAddress_, address noteAddress_, address comptrollerAddress_, address treasury_ ) { require(admin_ != address(0)); // Admin set to msg.sender for initialization admin = msg.sender; delegateTo( implementation_, abi.encodeWithSignature( "initialize(address,address,address,address)", treasury_, cnoteAddress_, noteAddress_, comptrollerAddress_ ) ); setImplementation(implementation_); admin = admin_; } /** * @notice Called by the admin to update the implementation of the delegator * @param implementation_ The address of the new implementation for delegation */ function setImplementation(address implementation_) public override { require( msg.sender == admin, "AccountantDelegator::_setImplementation: admin only" ); require( implementation_ != address(0), "AccountantDelegator::_setImplementation: invalid implementation address" ); emit NewImplementation(implementation, implementation_); implementation = implementation_; } function _setPendingAdmin(address newPendingAdmin) external override { require(msg.sender == admin, "AccountantDelegator::admin only"); delegateToImplementation( abi.encodeWithSignature("_setPendingAdmin(address)", newPendingAdmin) ); } function _acceptAdmin() external override { require( msg.sender == pendingAdmin, "AccountantDelegator::sender not pendingAdmin" ); delegateToImplementation(abi.encodeWithSignature("_acceptAdmin()")); } /** * @notice A public function to sweep accidental ERC-20 transfers to this contract. Tokens are sent to admin (timelock) * @param amount amount of Note Accountant is supplying to market */ function supplyMarket(uint256 amount) external override returns (uint256) { bytes memory data = delegateToImplementation( abi.encodeWithSignature("supplyMarket(uint256)", amount) ); return abi.decode(data, (uint256)); } /** * @notice A public function to sweep accidental ERC-20 transfers to this contract. Tokens are sent to admin (timelock) * @param amount The address of the ERC-20 token to sweep */ function redeemMarket(uint256 amount) external override returns (uint256) { bytes memory data = delegateToImplementation( abi.encodeWithSignature("redeemMarket(uint256)", amount) ); return abi.decode(data, (uint256)); } /** * @notice A public function to sweep accidental ERC-20 transfers to this contract. Tokens are sent to admin (timelock) */ function sweepInterest() external override { delegateToImplementation(abi.encodeWithSignature("sweepInterest()")); } /** * @notice Internal method to delegate execution to another contract * @dev It returns to the external caller whatever the implementation returns or forwards reverts * @param callee The contract to delegatecall * @param data The raw data to delegatecall */ function delegateTo(address callee, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returnData) = callee.delegatecall(data); assembly { if eq(success, 0) { revert(add(returnData, 0x20), returndatasize()) } } return returnData; } /** * @notice Delegates execution to the implementation contract * @dev It returns to the external caller whatever the implementation returns or forwards reverts * @param data The raw data to delegatecall * @return The returned bytes from the delegatecall */ function delegateToImplementation(bytes memory data) public returns (bytes memory) { return delegateTo(implementation, data); } /** * @notice Delegates execution to an implementation contract * @dev It returns to the external caller whatever the implementation returns or forwards reverts * There are an additional 2 prefix uints from the wrapper returndata, which we ignore since we make an extra hop. * @param data The raw data to delegatecall * @return The returned bytes from the delegatecall */ function delegateToViewImplementation(bytes memory data) public view returns (bytes memory) { (bool success, bytes memory returnData) = address(this).staticcall( abi.encodeWithSignature("delegateToImplementation(bytes)", data) ); assembly { if eq(success, 0) { revert(add(returnData, 0x20), returndatasize()) } } return abi.decode(returnData, (bytes)); } /** * @notice Delegates execution to an implementation contract * @dev It returns to the external caller whatever the implementation returns or forwards reverts */ fallback() external payable { require( msg.value == 0, "AccountantDelegator:fallback: cannot send value to fallback" ); (bool success,) = implementation.delegatecall(msg.data); // delegate all other functions to current implementation assembly { let free_mem_ptr := mload(0x40) returndatacopy(free_mem_ptr, 0, returndatasize()) switch success case 0 { revert(free_mem_ptr, returndatasize()) } default { return(free_mem_ptr, returndatasize()) } } } }
pragma solidity ^0.8.10; import "../Note.sol"; import "../CNote.sol"; import "../ComptrollerInterface.sol"; contract AccountantErrors { error SenderNotAdmin(address sender); //emitted in admin only methods error SenderNotCNote(address sender); // emitted in CNote only events error InvalidAddress(address addr); error ErrorMarketEntering(uint errCode); error AccountantInitializedAgain(); } contract AccountantDelegatorStorage { address public pendingAdmin; address public admin; // admin address (Timelock) address public implementation; // implementation address } contract AccountantStorageV1 is AccountantDelegatorStorage{ event AcctInit(address lendingMarketAddress); event AcctSupplied(uint amount, uint err); event AcctRedeemed(uint amount); event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin); event NewAdmin(address oldAdmin, address admin); error SweepError(address treasury, uint amount); Note public note; // note address CNote public cnote; // lending market address ComptrollerInterface public comptroller; // comptroller address address public treasury; // treasury address } abstract contract AccountantDelegatorInterface { event NewImplementation(address oldImplementation, address newImplementation); function setImplementation(address implementation_) public virtual; } abstract contract AccountantInterface is AccountantStorageV1 { function _setPendingAdmin(address newPendingAdmin) external virtual; function _acceptAdmin() external virtual; function supplyMarket(uint amount) external virtual returns(uint); function redeemMarket(uint amount) external virtual returns(uint); function sweepInterest() external virtual; }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./InterestRateModel.sol"; /** * @title Logic for Compound's JumpRateModel Contract V2. * @author Compound (modified by Dharma Labs, refactored by Arr00) * @notice Version 2 modifies Version 1 by enabling updateable parameters. */ abstract contract BaseJumpRateModelV2 is InterestRateModel { event NewInterestParams(uint baseRatePerBlock, uint multiplierPerBlock, uint jumpMultiplierPerBlock, uint kink); uint256 private constant BASE = 1e18; /** * @notice The address of the owner, i.e. the Timelock contract, which can update parameters directly */ address public owner; /** * @notice The approximate number of blocks per year that is assumed by the interest rate model */ uint public constant blocksPerYear = 2102400; /** * @notice The multiplier of utilization rate that gives the slope of the interest rate */ uint public multiplierPerBlock; /** * @notice The base interest rate which is the y-intercept when utilization rate is 0 */ uint public baseRatePerBlock; /** * @notice The multiplierPerBlock after hitting a specified utilization point */ uint public jumpMultiplierPerBlock; /** * @notice The utilization point at which the jump multiplier is applied */ uint public kink; /** * @notice Construct an interest rate model * @param baseRatePerYear The approximate target base APR, as a mantissa (scaled by BASE) * @param multiplierPerYear The rate of increase in interest rate wrt utilization (scaled by BASE) * @param jumpMultiplierPerYear The multiplierPerBlock after hitting a specified utilization point * @param kink_ The utilization point at which the jump multiplier is applied * @param owner_ The address of the owner, i.e. the Timelock contract (which has the ability to update parameters directly) */ constructor(uint baseRatePerYear, uint multiplierPerYear, uint jumpMultiplierPerYear, uint kink_, address owner_) internal { owner = owner_; updateJumpRateModelInternal(baseRatePerYear, multiplierPerYear, jumpMultiplierPerYear, kink_); } /** * @notice Update the parameters of the interest rate model (only callable by owner, i.e. Timelock) * @param baseRatePerYear The approximate target base APR, as a mantissa (scaled by BASE) * @param multiplierPerYear The rate of increase in interest rate wrt utilization (scaled by BASE) * @param jumpMultiplierPerYear The multiplierPerBlock after hitting a specified utilization point * @param kink_ The utilization point at which the jump multiplier is applied */ function updateJumpRateModel(uint baseRatePerYear, uint multiplierPerYear, uint jumpMultiplierPerYear, uint kink_) virtual external { require(msg.sender == owner, "only the owner may call this function."); updateJumpRateModelInternal(baseRatePerYear, multiplierPerYear, jumpMultiplierPerYear, kink_); } /** * @notice Calculates the utilization rate of the market: `borrows / (cash + borrows - reserves)` * @param cash The amount of cash in the market * @param borrows The amount of borrows in the market * @param reserves The amount of reserves in the market (currently unused) * @return The utilization rate as a mantissa between [0, BASE] */ function utilizationRate(uint cash, uint borrows, uint reserves) public pure returns (uint) { // Utilization rate is 0 when there are no borrows if (borrows == 0) { return 0; } return borrows * BASE / (cash + borrows - reserves); } /** * @notice Calculates the current borrow rate per block, with the error code expected by the market * @param cash The amount of cash in the market * @param borrows The amount of borrows in the market * @param reserves The amount of reserves in the market * @return The borrow rate percentage per block as a mantissa (scaled by BASE) */ function getBorrowRateInternal(uint cash, uint borrows, uint reserves) internal view returns (uint) { uint util = utilizationRate(cash, borrows, reserves); if (util <= kink) { return ((util * multiplierPerBlock) / BASE) + baseRatePerBlock; } else { uint normalRate = ((kink * multiplierPerBlock) / BASE) + baseRatePerBlock; uint excessUtil = util - kink; return ((excessUtil * jumpMultiplierPerBlock) / BASE) + normalRate; } } /** * @notice Calculates the current supply rate per block * @param cash The amount of cash in the market * @param borrows The amount of borrows in the market * @param reserves The amount of reserves in the market * @param reserveFactorMantissa The current reserve factor for the market * @return The supply rate percentage per block as a mantissa (scaled by BASE) */ function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) virtual override public view returns (uint) { uint oneMinusReserveFactor = BASE - reserveFactorMantissa; uint borrowRate = getBorrowRateInternal(cash, borrows, reserves); uint rateToPool = borrowRate * oneMinusReserveFactor / BASE; return utilizationRate(cash, borrows, reserves) * rateToPool / BASE; } /** * @notice Internal function to update the parameters of the interest rate model * @param baseRatePerYear The approximate target base APR, as a mantissa (scaled by BASE) * @param multiplierPerYear The rate of increase in interest rate wrt utilization (scaled by BASE) * @param jumpMultiplierPerYear The multiplierPerBlock after hitting a specified utilization point * @param kink_ The utilization point at which the jump multiplier is applied */ function updateJumpRateModelInternal(uint baseRatePerYear, uint multiplierPerYear, uint jumpMultiplierPerYear, uint kink_) internal { baseRatePerBlock = baseRatePerYear / blocksPerYear; multiplierPerBlock = (multiplierPerYear * BASE) / (blocksPerYear * kink_); jumpMultiplierPerBlock = jumpMultiplierPerYear / blocksPerYear; kink = kink_; emit NewInterestParams(baseRatePerBlock, multiplierPerBlock, jumpMultiplierPerBlock, kink); } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./CErc20Delegate.sol"; /** * @title Compound's CDai Contract * @notice CToken which wraps Multi-Collateral DAI * @author Compound */ contract CDaiDelegate is CErc20Delegate { /** * @notice DAI adapter address */ address public daiJoinAddress; /** * @notice DAI Savings Rate (DSR) pot address */ address public potAddress; /** * @notice DAI vat address */ address public vatAddress; /** * @notice Delegate interface to become the implementation * @param data The encoded arguments for becoming */ function _becomeImplementation(bytes memory data) public override { require( msg.sender == admin, "only the admin may initialize the implementation" ); (address daiJoinAddress_, address potAddress_) = abi.decode(data, (address, address)); return _becomeImplementation(daiJoinAddress_, potAddress_); } /** * @notice Explicit interface to become the implementation * @param daiJoinAddress_ DAI adapter address * @param potAddress_ DAI Savings Rate (DSR) pot address */ function _becomeImplementation(address daiJoinAddress_, address potAddress_) internal { // Get dai and vat and sanity check the underlying DaiJoinLike daiJoin = DaiJoinLike(daiJoinAddress_); PotLike pot = PotLike(potAddress_); GemLike dai = daiJoin.dai(); VatLike vat = daiJoin.vat(); require( address(dai) == underlying, "DAI must be the same as underlying" ); // Remember the relevant addresses daiJoinAddress = daiJoinAddress_; potAddress = potAddress_; vatAddress = address(vat); // Approve moving our DAI into the vat through daiJoin dai.approve(daiJoinAddress, type(uint256).max); // Approve the pot to transfer our funds within the vat vat.hope(potAddress); vat.hope(daiJoinAddress); // Accumulate DSR interest -- must do this in order to doTransferIn pot.drip(); // Transfer all cash in (doTransferIn does this regardless of amount) doTransferIn(address(this), 0); } /** * @notice Delegate interface to resign the implementation */ function _resignImplementation() public override { require( msg.sender == admin, "only the admin may abandon the implementation" ); // Transfer all cash out of the DSR - note that this relies on self-transfer DaiJoinLike daiJoin = DaiJoinLike(daiJoinAddress); PotLike pot = PotLike(potAddress); VatLike vat = VatLike(vatAddress); // Accumulate interest pot.drip(); // Calculate the total amount in the pot, and move it out uint256 pie = pot.pie(address(this)); pot.exit(pie); // Checks the actual balance of DAI in the vat after the pot exit uint256 bal = vat.dai(address(this)); // Remove our whole balance daiJoin.exit(address(this), bal / RAY); } /** ** CToken Overrides ** */ /** * @notice Accrues DSR then applies accrued interest to total borrows and reserves * @dev This calculates interest accrued from the last checkpointed block * up to the current block and writes new checkpoint to storage. */ function accrueInterest() public override returns (uint256) { // Accumulate DSR interest PotLike(potAddress).drip(); // Accumulate CToken interest return super.accrueInterest(); } /** ** Safe Token ** */ /** * @notice Gets balance of this contract in terms of the underlying * @dev This excludes the value of the current message, if any * @return The quantity of underlying tokens owned by this contract */ function getCashPrior() internal view override returns (uint256) { PotLike pot = PotLike(potAddress); uint256 pie = pot.pie(address(this)); return mul(pot.chi(), pie) / RAY; } /** * @notice Transfer the underlying to this contract and sweep into DSR pot * @param from Address to transfer funds from * @param amount Amount of underlying to transfer * @return The actual amount that is transferred */ function doTransferIn(address from, uint256 amount) internal override returns (uint256) { // Read from storage once address underlying_ = underlying; // Perform the EIP-20 transfer in EIP20Interface token = EIP20Interface(underlying_); require( token.transferFrom(from, address(this), amount), "unexpected EIP-20 transfer in return" ); DaiJoinLike daiJoin = DaiJoinLike(daiJoinAddress); GemLike dai = GemLike(underlying_); PotLike pot = PotLike(potAddress); VatLike vat = VatLike(vatAddress); // Convert all our DAI to internal DAI in the vat daiJoin.join(address(this), dai.balanceOf(address(this))); // Checks the actual balance of DAI in the vat after the join uint256 bal = vat.dai(address(this)); // Calculate the percentage increase to th pot for the entire vat, and move it in // Note: We may leave a tiny bit of DAI in the vat...but we do the whole thing every time uint256 pie = bal / pot.chi(); pot.join(pie); return amount; } /** * @notice Transfer the underlying from this contract, after sweeping out of DSR pot * @param to Address to transfer funds to * @param amount Amount of underlying to transfer */ function doTransferOut(address payable to, uint256 amount) internal override { DaiJoinLike daiJoin = DaiJoinLike(daiJoinAddress); PotLike pot = PotLike(potAddress); // Calculate the percentage decrease from the pot, and move that much out // Note: Use a slightly larger pie size to ensure that we get at least amount in the vat uint256 pie = add(mul(amount, RAY) / pot.chi(), 1); pot.exit(pie); daiJoin.exit(to, amount); } /** ** Maker Internals ** */ uint256 constant RAY = 10 ** 27; function add(uint256 x, uint256 y) internal pure returns (uint256 z) { require((z = x + y) >= x, "add-overflow"); } function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { require(y == 0 || (z = x * y) / y == x, "mul-overflow"); } } /** ** Maker Interfaces ** */ interface PotLike { function chi() external view returns (uint256); function pie(address) external view returns (uint256); function drip() external returns (uint256); function join(uint256) external; function exit(uint256) external; } interface GemLike { function approve(address, uint256) external; function balanceOf(address) external view returns (uint256); function transferFrom(address, address, uint256) external returns (bool); } interface VatLike { function dai(address) external view returns (uint256); function hope(address) external; } interface DaiJoinLike { function vat() external returns (VatLike); function dai() external returns (GemLike); function join(address, uint256) external payable; function exit(address, uint256) external; }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./CToken.sol"; interface CompLike { function delegate(address delegatee) external; } /** * @title Compound's CErc20 Contract * @notice CTokens which wrap an EIP-20 underlying * @author Compound */ contract CErc20 is CToken, CErc20Interface { /** * @notice Initialize the new money market * @param underlying_ The address of the underlying asset * @param comptroller_ The address of the Comptroller * @param interestRateModel_ The address of the interest rate model * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18 * @param name_ ERC-20 name of this token * @param symbol_ ERC-20 symbol of this token * @param decimals_ ERC-20 decimal precision of this token */ function initialize( address underlying_, ComptrollerInterface comptroller_, InterestRateModel interestRateModel_, uint256 initialExchangeRateMantissa_, string memory name_, string memory symbol_, uint8 decimals_ ) public { // CToken initialize does the bulk of the work super.initialize( comptroller_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_ ); // Set underlying and sanity check it underlying = underlying_; EIP20Interface(underlying).totalSupply(); } /** ** User Interface ** */ /** * @notice Sender supplies assets into the market and receives cTokens in exchange * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param mintAmount The amount of the underlying asset to supply * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function mint(uint256 mintAmount) external override returns (uint256) { mintInternal(mintAmount); return NO_ERROR; } /** * @notice Sender redeems cTokens in exchange for the underlying asset * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param redeemTokens The number of cTokens to redeem into underlying * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function redeem(uint256 redeemTokens) external override returns (uint256) { redeemInternal(redeemTokens); return NO_ERROR; } /** * @notice Sender redeems cTokens in exchange for a specified amount of underlying asset * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param redeemAmount The amount of underlying to redeem * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function redeemUnderlying(uint256 redeemAmount) external override returns (uint256) { redeemUnderlyingInternal(redeemAmount); return NO_ERROR; } /** * @notice Sender borrows assets from the protocol to their own address * @param borrowAmount The amount of the underlying asset to borrow * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function borrow(uint256 borrowAmount) external override returns (uint256) { borrowInternal(borrowAmount); return NO_ERROR; } /** * @notice Sender repays their own borrow * @param repayAmount The amount to repay, or -1 for the full outstanding amount * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function repayBorrow(uint256 repayAmount) external override returns (uint256) { repayBorrowInternal(repayAmount); return NO_ERROR; } /** * @notice Sender repays a borrow belonging to borrower * @param borrower the account with the debt being payed off * @param repayAmount The amount to repay, or -1 for the full outstanding amount * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function repayBorrowBehalf(address borrower, uint256 repayAmount) external override returns (uint256) { repayBorrowBehalfInternal(borrower, repayAmount); return NO_ERROR; } /** * @notice The sender liquidates the borrowers collateral. * The collateral seized is transferred to the liquidator. * @param borrower The borrower of this cToken to be liquidated * @param repayAmount The amount of the underlying borrowed asset to repay * @param cTokenCollateral The market in which to seize collateral from the borrower * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function liquidateBorrow( address borrower, uint256 repayAmount, CTokenInterface cTokenCollateral ) external override returns (uint256) { liquidateBorrowInternal(borrower, repayAmount, cTokenCollateral); return NO_ERROR; } /** * @notice A public function to sweep accidental ERC-20 transfers to this contract. Tokens are sent to admin (timelock) * @param token The address of the ERC-20 token to sweep */ function sweepToken(EIP20NonStandardInterface token) external override { require( msg.sender == admin, "CErc20::sweepToken: only admin can sweep tokens" ); require( address(token) != underlying, "CErc20::sweepToken: can not sweep underlying token" ); uint256 balance = token.balanceOf(address(this)); token.transfer(admin, balance); } /** * @notice The sender adds to reserves. * @param addAmount The amount fo underlying token to add as reserves * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _addReserves(uint256 addAmount) external override returns (uint256) { return _addReservesInternal(addAmount); } /** ** Safe Token ** */ /** * @notice Gets balance of this contract in terms of the underlying * @dev This excludes the value of the current message, if any * @return The quantity of underlying tokens owned by this contract */ function getCashPrior() internal view virtual override returns (uint256) { EIP20Interface token = EIP20Interface(underlying); return token.balanceOf(address(this)); } /** * @dev Similar to EIP20 transfer, except it handles a False result from `transferFrom` and reverts in that case. * This will revert due to insufficient balance or insufficient allowance. * This function returns the actual amount received, * which may be less than `amount` if there is a fee attached to the transfer. * * 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 from, uint256 amount) internal virtual override returns (uint256) { // Read from storage once address underlying_ = underlying; EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying_); uint256 balanceBefore = EIP20Interface(underlying_).balanceOf(address(this)); token.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) } } require(success, "TOKEN_TRANSFER_IN_FAILED"); // Calculate the amount that was *actually* transferred uint256 balanceAfter = EIP20Interface(underlying_).balanceOf(address(this)); return balanceAfter - balanceBefore; // underflow already checked above, just subtract } /** * @dev Similar to EIP20 transfer, except it handles a False success from `transfer` and returns an explanatory * error code rather than reverting. If caller has not called checked protocol's balance, this may revert due to * insufficient cash held in this contract. If caller has checked protocol's balance prior to this call, and verified * it is >= amount, this should not revert in normal conditions. * * 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 payable to, uint256 amount) internal virtual override { EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying); token.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) } } require(success, "TOKEN_TRANSFER_OUT_FAILED"); } /** * @notice Admin call to delegate the votes of the COMP-like underlying * @param compLikeDelegatee The address to delegate votes to * @dev CTokens whose underlying are not CompLike should revert here */ function _delegateCompLikeTo(address compLikeDelegatee) external { require( msg.sender == admin, "only the admin may set the comp-like delegate" ); CompLike(underlying).delegate(compLikeDelegatee); } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./CErc20.sol"; /** * @title Compound's CErc20Delegate Contract * @notice CTokens which wrap an EIP-20 underlying and are delegated to * @author Compound */ contract CErc20Delegate is CErc20, CDelegateInterface { /** * @notice Construct an empty delegate */ constructor() {} /** * @notice Called by the delegator on a delegate to initialize it for duty * @param data The encoded bytes data for any initialization */ function _becomeImplementation(bytes memory data) public virtual override { // Shh -- currently unused data; // Shh -- we don't ever want this hook to be marked pure if (false) { implementation = address(0); } require( msg.sender == admin, "only the admin may call _becomeImplementation" ); } /** * @notice Called by the delegator on a delegate to forfeit its responsibility */ function _resignImplementation() public virtual override { // Shh -- we don't ever want this hook to be marked pure if (false) { implementation = address(0); } require( msg.sender == admin, "only the admin may call _resignImplementation" ); } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./CTokenInterfaces.sol"; /** * @title Compound's CErc20Delegator Contract * @notice CTokens which wrap an EIP-20 underlying and delegate to an implementation * @author Compound */ contract CErc20Delegator is CTokenInterface, CErc20Interface, CDelegatorInterface { /** * @notice Construct a new money market * @param underlying_ The address of the underlying asset * @param comptroller_ The address of the Comptroller * @param interestRateModel_ The address of the interest rate model * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18 * @param name_ ERC-20 name of this token * @param symbol_ ERC-20 symbol of this token * @param decimals_ ERC-20 decimal precision of this token * @param admin_ Address of the administrator of this token * @param implementation_ The address of the implementation the contract delegates to * @param becomeImplementationData The encoded args for becomeImplementation */ constructor( address underlying_, ComptrollerInterface comptroller_, InterestRateModel interestRateModel_, uint256 initialExchangeRateMantissa_, string memory name_, string memory symbol_, uint8 decimals_, address payable admin_, address implementation_, bytes memory becomeImplementationData ) { // Creator of the contract is admin during initialization admin = payable(msg.sender); // First delegate gets to initialize the delegator (i.e. storage contract) delegateTo( implementation_, abi.encodeWithSignature( "initialize(address,address,address,uint256,string,string,uint8)", underlying_, comptroller_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_ ) ); // New implementations always get set via the settor (post-initialize) _setImplementation(implementation_, false, becomeImplementationData); // Set the proper admin now that initialization is done admin = admin_; } /** * @notice Called by the admin to update the implementation of the delegator * @param implementation_ The address of the new implementation for delegation * @param allowResign Flag to indicate whether to call _resignImplementation on the old implementation * @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation */ function _setImplementation( address implementation_, bool allowResign, bytes memory becomeImplementationData ) public override { require( msg.sender == admin, "CErc20Delegator::_setImplementation: Caller must be admin" ); if (allowResign) { delegateToImplementation( abi.encodeWithSignature("_resignImplementation()") ); } address oldImplementation = implementation; implementation = implementation_; delegateToImplementation( abi.encodeWithSignature( "_becomeImplementation(bytes)", becomeImplementationData ) ); emit NewImplementation(oldImplementation, implementation); } /** * @notice Sender supplies assets into the market and receives cTokens in exchange * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param mintAmount The amount of the underlying asset to supply * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function mint(uint256 mintAmount) external override returns (uint256) { bytes memory data = delegateToImplementation( abi.encodeWithSignature("mint(uint256)", mintAmount) ); return abi.decode(data, (uint256)); } /** * @notice Sender redeems cTokens in exchange for the underlying asset * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param redeemTokens The number of cTokens to redeem into underlying * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function redeem(uint256 redeemTokens) external override returns (uint256) { bytes memory data = delegateToImplementation( abi.encodeWithSignature("redeem(uint256)", redeemTokens) ); return abi.decode(data, (uint256)); } /** * @notice Sender redeems cTokens in exchange for a specified amount of underlying asset * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param redeemAmount The amount of underlying to redeem * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function redeemUnderlying(uint256 redeemAmount) external override returns (uint256) { bytes memory data = delegateToImplementation( abi.encodeWithSignature("redeemUnderlying(uint256)", redeemAmount) ); return abi.decode(data, (uint256)); } /** * @notice Sender borrows assets from the protocol to their own address * @param borrowAmount The amount of the underlying asset to borrow * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function borrow(uint256 borrowAmount) external override returns (uint256) { bytes memory data = delegateToImplementation( abi.encodeWithSignature("borrow(uint256)", borrowAmount) ); return abi.decode(data, (uint256)); } /** * @notice Sender repays their own borrow * @param repayAmount The amount to repay, or -1 for the full outstanding amount * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function repayBorrow(uint256 repayAmount) external override returns (uint256) { bytes memory data = delegateToImplementation( abi.encodeWithSignature("repayBorrow(uint256)", repayAmount) ); return abi.decode(data, (uint256)); } /** * @notice Sender repays a borrow belonging to borrower * @param borrower the account with the debt being payed off * @param repayAmount The amount to repay, or -1 for the full outstanding amount * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function repayBorrowBehalf(address borrower, uint256 repayAmount) external override returns (uint256) { bytes memory data = delegateToImplementation( abi.encodeWithSignature( "repayBorrowBehalf(address,uint256)", borrower, repayAmount ) ); return abi.decode(data, (uint256)); } /** * @notice The sender liquidates the borrowers collateral. * The collateral seized is transferred to the liquidator. * @param borrower The borrower of this cToken to be liquidated * @param cTokenCollateral The market in which to seize collateral from the borrower * @param repayAmount The amount of the underlying borrowed asset to repay * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function liquidateBorrow( address borrower, uint256 repayAmount, CTokenInterface cTokenCollateral ) external override returns (uint256) { bytes memory data = delegateToImplementation( abi.encodeWithSignature( "liquidateBorrow(address,uint256,address)", borrower, repayAmount, cTokenCollateral ) ); return abi.decode(data, (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 override returns (bool) { bytes memory data = delegateToImplementation( abi.encodeWithSignature("transfer(address,uint256)", dst, amount) ); return abi.decode(data, (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 override returns (bool) { bytes memory data = delegateToImplementation( abi.encodeWithSignature( "transferFrom(address,address,uint256)", src, dst, amount ) ); return abi.decode(data, (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 override returns (bool) { bytes memory data = delegateToImplementation( abi.encodeWithSignature("approve(address,uint256)", spender, amount) ); return abi.decode(data, (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 override returns (uint256) { bytes memory data = delegateToViewImplementation( abi.encodeWithSignature("allowance(address,address)", owner, spender) ); return abi.decode(data, (uint256)); } /** * @notice Get the token balance of the `owner` * @param owner The address of the account to query * @return The number of tokens owned by `owner` */ function balanceOf(address owner) external view override returns (uint256) { bytes memory data = delegateToViewImplementation( abi.encodeWithSignature("balanceOf(address)", owner) ); return abi.decode(data, (uint256)); } /** * @notice Get the underlying balance of the `owner` * @dev This also accrues interest in a transaction * @param owner The address of the account to query * @return The amount of underlying owned by `owner` */ function balanceOfUnderlying(address owner) external override returns (uint256) { bytes memory data = delegateToImplementation( abi.encodeWithSignature("balanceOfUnderlying(address)", owner) ); return abi.decode(data, (uint256)); } /** * @notice Get a snapshot of the account's balances, and the cached exchange rate * @dev This is used by comptroller to more efficiently perform liquidity checks. * @param account Address of the account to snapshot * @return (possible error, token balance, borrow balance, exchange rate mantissa) */ function getAccountSnapshot(address account) external view override returns (uint256, uint256, uint256, uint256) { bytes memory data = delegateToViewImplementation( abi.encodeWithSignature("getAccountSnapshot(address)", account) ); return abi.decode(data, (uint256, uint256, uint256, uint256)); } /** * @notice Returns the current per-block borrow interest rate for this cToken * @return The borrow interest rate per block, scaled by 1e18 */ function borrowRatePerBlock() external view override returns (uint256) { bytes memory data = delegateToViewImplementation( abi.encodeWithSignature("borrowRatePerBlock()") ); return abi.decode(data, (uint256)); } /** * @notice Returns the current per-block supply interest rate for this cToken * @return The supply interest rate per block, scaled by 1e18 */ function supplyRatePerBlock() external view override returns (uint256) { bytes memory data = delegateToViewImplementation( abi.encodeWithSignature("supplyRatePerBlock()") ); return abi.decode(data, (uint256)); } /** * @notice Returns the current total borrows plus accrued interest * @return The total borrows with interest */ function totalBorrowsCurrent() external override returns (uint256) { bytes memory data = delegateToImplementation( abi.encodeWithSignature("totalBorrowsCurrent()") ); return abi.decode(data, (uint256)); } /** * @notice Accrue interest to updated borrowIndex and then calculate account's borrow balance using the updated borrowIndex * @param account The address whose balance should be calculated after updating borrowIndex * @return The calculated balance */ function borrowBalanceCurrent(address account) external override returns (uint256) { bytes memory data = delegateToImplementation( abi.encodeWithSignature("borrowBalanceCurrent(address)", account) ); return abi.decode(data, (uint256)); } /** * @notice Return the borrow balance of account based on stored data * @param account The address whose balance should be calculated * @return The calculated balance */ function borrowBalanceStored(address account) public view override returns (uint256) { bytes memory data = delegateToViewImplementation( abi.encodeWithSignature("borrowBalanceStored(address)", account) ); return abi.decode(data, (uint256)); } /** * @notice Accrue interest then return the up-to-date exchange rate * @return Calculated exchange rate scaled by 1e18 */ function exchangeRateCurrent() public override returns (uint256) { bytes memory data = delegateToImplementation( abi.encodeWithSignature("exchangeRateCurrent()") ); return abi.decode(data, (uint256)); } /** * @notice Calculates the exchange rate from the underlying to the CToken * @dev This function does not accrue interest before calculating the exchange rate * @return Calculated exchange rate scaled by 1e18 */ function exchangeRateStored() public view override returns (uint256) { bytes memory data = delegateToViewImplementation( abi.encodeWithSignature("exchangeRateStored()") ); return abi.decode(data, (uint256)); } /** * @notice Get cash balance of this cToken in the underlying asset * @return The quantity of underlying asset owned by this contract */ function getCash() external view override returns (uint256) { bytes memory data = delegateToViewImplementation(abi.encodeWithSignature("getCash()")); return abi.decode(data, (uint256)); } /** * @notice Applies accrued interest to total borrows and reserves. * @dev This calculates interest accrued from the last checkpointed block * up to the current block and writes new checkpoint to storage. */ function accrueInterest() public override returns (uint256) { bytes memory data = delegateToImplementation(abi.encodeWithSignature("accrueInterest()")); return abi.decode(data, (uint256)); } /** * @notice Transfers collateral tokens (this market) to the liquidator. * @dev Will fail unless called by another cToken during the process of liquidation. * Its absolutely critical to use msg.sender as the borrowed cToken and not a parameter. * @param liquidator The account receiving seized collateral * @param borrower The account having collateral seized * @param seizeTokens The number of cTokens to seize * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function seize(address liquidator, address borrower, uint256 seizeTokens) external override returns (uint256) { bytes memory data = delegateToImplementation( abi.encodeWithSignature( "seize(address,address,uint256)", liquidator, borrower, seizeTokens ) ); return abi.decode(data, (uint256)); } /** * @notice A public function to sweep accidental ERC-20 transfers to this contract. Tokens are sent to admin (timelock) * @param token The address of the ERC-20 token to sweep */ function sweepToken(EIP20NonStandardInterface token) external override { delegateToImplementation( abi.encodeWithSignature("sweepToken(address)", token) ); } /** ** Admin Functions ** */ /** * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. * @param newPendingAdmin New pending admin. * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setPendingAdmin(address payable newPendingAdmin) external override returns (uint256) { bytes memory data = delegateToImplementation( abi.encodeWithSignature("_setPendingAdmin(address)", newPendingAdmin) ); return abi.decode(data, (uint256)); } /** * @notice Sets a new comptroller for the market * @dev Admin function to set a new comptroller * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setComptroller(ComptrollerInterface newComptroller) public override returns (uint256) { bytes memory data = delegateToImplementation( abi.encodeWithSignature("_setComptroller(address)", newComptroller) ); return abi.decode(data, (uint256)); } /** * @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh * @dev Admin function to accrue interest and set a new reserve factor * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setReserveFactor(uint256 newReserveFactorMantissa) external override returns (uint256) { bytes memory data = delegateToImplementation( abi.encodeWithSignature("_setReserveFactor(uint256)", newReserveFactorMantissa) ); return abi.decode(data, (uint256)); } /** * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin * @dev Admin function for pending admin to accept role and update admin * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _acceptAdmin() external override returns (uint256) { bytes memory data = delegateToImplementation(abi.encodeWithSignature("_acceptAdmin()")); return abi.decode(data, (uint256)); } /** * @notice Accrues interest and adds reserves by transferring from admin * @param addAmount Amount of reserves to add * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _addReserves(uint256 addAmount) external override returns (uint256) { bytes memory data = delegateToImplementation( abi.encodeWithSignature("_addReserves(uint256)", addAmount) ); return abi.decode(data, (uint256)); } /** * @notice Accrues interest and reduces reserves by transferring to admin * @param reduceAmount Amount of reduction to reserves * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _reduceReserves(uint256 reduceAmount) external override returns (uint256) { bytes memory data = delegateToImplementation( abi.encodeWithSignature("_reduceReserves(uint256)", reduceAmount) ); return abi.decode(data, (uint256)); } /** * @notice Accrues interest and updates the interest rate model using _setInterestRateModelFresh * @dev Admin function to accrue interest and update the interest rate model * @param newInterestRateModel the new interest rate model to use * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setInterestRateModel(InterestRateModel newInterestRateModel) public override returns (uint256) { bytes memory data = delegateToImplementation( abi.encodeWithSignature("_setInterestRateModel(address)", newInterestRateModel) ); return abi.decode(data, (uint256)); } /** * @notice Internal method to delegate execution to another contract * @dev It returns to the external caller whatever the implementation returns or forwards reverts * @param callee The contract to delegatecall * @param data The raw data to delegatecall * @return The returned bytes from the delegatecall */ function delegateTo(address callee, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returnData) = callee.delegatecall(data); assembly { if eq(success, 0) { revert(add(returnData, 0x20), returndatasize()) } } return returnData; } /** * @notice Delegates execution to the implementation contract * @dev It returns to the external caller whatever the implementation returns or forwards reverts * @param data The raw data to delegatecall * @return The returned bytes from the delegatecall */ function delegateToImplementation(bytes memory data) public returns (bytes memory) { return delegateTo(implementation, data); } /** * @notice Delegates execution to an implementation contract * @dev It returns to the external caller whatever the implementation returns or forwards reverts * There are an additional 2 prefix uints from the wrapper returndata, which we ignore since we make an extra hop. * @param data The raw data to delegatecall * @return The returned bytes from the delegatecall */ function delegateToViewImplementation(bytes memory data) public view returns (bytes memory) { (bool success, bytes memory returnData) = address(this).staticcall( abi.encodeWithSignature("delegateToImplementation(bytes)", data) ); assembly { if eq(success, 0) { revert(add(returnData, 0x20), returndatasize()) } } return abi.decode(returnData, (bytes)); } /** * @notice Delegates execution to an implementation contract * @dev It returns to the external caller whatever the implementation returns or forwards reverts */ fallback() external payable { require( msg.value == 0, "CErc20Delegator:fallback: cannot send value to fallback" ); // delegate all other functions to current implementation (bool success,) = implementation.delegatecall(msg.data); assembly { let free_mem_ptr := mload(0x40) returndatacopy(free_mem_ptr, 0, returndatasize()) switch success case 0 { revert(free_mem_ptr, returndatasize()) } default { return(free_mem_ptr, returndatasize()) } } } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./CErc20.sol"; /** * @title Compound's CErc20Immutable Contract * @notice CTokens which wrap an EIP-20 underlying and are immutable * @author Compound */ contract CErc20Immutable is CErc20 { /** * @notice Construct a new money market * @param underlying_ The address of the underlying asset * @param comptroller_ The address of the Comptroller * @param interestRateModel_ The address of the interest rate model * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18 * @param name_ ERC-20 name of this token * @param symbol_ ERC-20 symbol of this token * @param decimals_ ERC-20 decimal precision of this token * @param admin_ Address of the administrator of this token */ constructor(address underlying_, ComptrollerInterface comptroller_, InterestRateModel interestRateModel_, uint initialExchangeRateMantissa_, string memory name_, string memory symbol_, uint8 decimals_, address payable admin_ ) { // Creator of the contract is admin during initialization admin = payable(msg.sender); // Initialize the market initialize(underlying_, comptroller_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_); // Set the proper admin now that initialization is done admin = admin_; } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./CToken.sol"; /** * @title Compound's CEther Contract * @notice CToken which wraps Ether * @author Compound */ contract CEther is CToken { /** * @notice Construct a new CEther money market * @param comptroller_ The address of the Comptroller * @param interestRateModel_ The address of the interest rate model * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18 * @param name_ ERC-20 name of this token * @param symbol_ ERC-20 symbol of this token * @param decimals_ ERC-20 decimal precision of this token * @param admin_ Address of the administrator of this token */ constructor(ComptrollerInterface comptroller_, InterestRateModel interestRateModel_, uint initialExchangeRateMantissa_, string memory name_, string memory symbol_, uint8 decimals_, address payable admin_) { // Creator of the contract is admin during initialization admin = payable(msg.sender); initialize(comptroller_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_); // Set the proper admin now that initialization is done admin = admin_; } /*** User Interface ***/ /** * @notice Sender supplies assets into the market and receives cTokens in exchange * @dev Reverts upon any failure */ function mint() external payable { mintInternal(msg.value); } /** * @notice Sender redeems cTokens in exchange for the underlying asset * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param redeemTokens The number of cTokens to redeem into underlying * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function redeem(uint redeemTokens) external returns (uint) { redeemInternal(redeemTokens); return NO_ERROR; } /** * @notice Sender redeems cTokens in exchange for a specified amount of underlying asset * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param redeemAmount The amount of underlying to redeem * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function redeemUnderlying(uint redeemAmount) external returns (uint) { redeemUnderlyingInternal(redeemAmount); return NO_ERROR; } /** * @notice Sender borrows assets from the protocol to their own address * @param borrowAmount The amount of the underlying asset to borrow * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function borrow(uint borrowAmount) external returns (uint) { borrowInternal(borrowAmount); return NO_ERROR; } /** * @notice Sender repays their own borrow * @dev Reverts upon any failure */ function repayBorrow() external payable { repayBorrowInternal(msg.value); } /** * @notice Sender repays a borrow belonging to borrower * @dev Reverts upon any failure * @param borrower the account with the debt being payed off */ function repayBorrowBehalf(address borrower) external payable { repayBorrowBehalfInternal(borrower, msg.value); } /** * @notice The sender liquidates the borrowers collateral. * The collateral seized is transferred to the liquidator. * @dev Reverts upon any failure * @param borrower The borrower of this cToken to be liquidated * @param cTokenCollateral The market in which to seize collateral from the borrower */ function liquidateBorrow(address borrower, CToken cTokenCollateral) external payable { liquidateBorrowInternal(borrower, msg.value, cTokenCollateral); } /** * @notice The sender adds to reserves. * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _addReserves() external payable returns (uint) { return _addReservesInternal(msg.value); } /** * @notice Send Ether to CEther to mint */ receive() external payable { mintInternal(msg.value); } /*** Safe Token ***/ /** * @notice Gets balance of this contract in terms of Ether, before this message * @dev This excludes the value of the current message, if any * @return The quantity of Ether owned by this contract */ function getCashPrior() override internal view returns (uint) { return address(this).balance - msg.value; } /** * @notice Perform the actual transfer in, which is a no-op * @param from Address sending the Ether * @param amount Amount of Ether being sent * @return The actual amount of Ether transferred */ function doTransferIn(address from, uint amount) override internal returns (uint) { // Sanity checks require(msg.sender == from, "sender mismatch"); require(msg.value == amount, "value mismatch"); return amount; } function doTransferOut(address payable to, uint amount) virtual override internal { /* Send the Ether, with minimal gas and revert on failure */ to.transfer(amount); } }
// SPDX-License-Identifier: BSD-3-Clause import {Comptroller} from "src/Comptroller.sol"; import {PriceOracle} from "src/PriceOracle.sol"; import {IBaseV1Pair} from "src/Swap/BaseV1-periphery.sol"; import {erc20, Math} from "src/Swap/BaseV1-libs.sol"; import {CToken} from "src/CToken.sol"; pragma solidity 0.8.11; interface IBaseV1Router { function pairFor(address, address, bool) external view returns(address); function isPair(address) external view returns(bool); function isStable(address) external view returns(bool); } interface ICErc20 { function underlying() external view returns(address); } contract CLMPriceOracle is PriceOracle { // address of cUsdc (underlying asset will be statically priced) address public immutable usdc; // address of cUsdt (underlying asset will be statically priced) address public immutable usdt; // address of cCanto (so we can set the underlying to wcanto) address public immutable cCanto; // address of note address public immutable note; // address of wcanto address public immutable wcanto; // address of comptroller address public immutable comptroller; // address of router contract address public immutable router; // modifier to prevent other contracts from using the data from this oracle modifier onlyComptroller(address sender) { if (sender != comptroller) { // function returns default value (0) return; } _; } /// @dev Initializes PriceOracle, by setting immutable addresses /// @param _comptroller, address of protocol comptroller /// @param _router, address of protocol router /// @param _cCanto, address of CCanto lm /// @param _usdt, address of usdt /// @param _usdc, address of usdc /// @param _wcanto, address of wcanto /// @param _note, address of note constructor( address _comptroller, address _router, address _cCanto, address _usdt, address _usdc, address _wcanto, address _note ) { comptroller = _comptroller; router = _router; usdc = _usdc; usdt = _usdt; note = _note; wcanto = _wcanto; cCanto = _cCanto; } /// @param cToken, the cToken (treated as CErc20) that is being priced, in the case of cCanto, although it is not a cErc20 it is treated as such. /// @return price, the price of the asset in Note, scaled by 1e18 function getUnderlyingPrice(CToken cToken) external override view onlyComptroller(msg.sender) returns(uint) { address underlying; IBaseV1Router router_ = IBaseV1Router(router); // first check whether the cToken is cCanto if (address(cToken) == cCanto) { // return price from wcanto/note pool return getPriceNote(wcanto, false); } else { // this is a CErc20, get the underlying address underlying = address(ICErc20(address(cToken)).underlying()); } // if the underlying is note if (underlying == note) { return 1e18; } // if the underlying is usdc or usdt if ((underlying == usdc) || (underlying == usdt)) { uint decimals = 10 ** erc20(underlying).decimals(); return 1e18 * 1e18 / (decimals); } // if the underlying is a pair if (router_.isPair(underlying)) { return getPriceLp(IBaseV1Pair(underlying)); } else { // treat this as a stable asset if (router_.isStable(underlying)) { return getPriceNote(underlying, true); } else { return getPriceCanto(underlying) * getPriceNote(wcanto, false) / 1e18; } } } /// @param pair, the address of the pair that the lpToken was minted from /// @return price, the price of the lpToken function getPriceLp(IBaseV1Pair pair) internal view returns(uint) { uint[] memory supply = pair.sampleSupply(12, 1); uint[] memory prices; uint[] memory unitReserves; uint[] memory assetReserves; address token0 = pair.token0(); address token1 = pair.token1(); uint decimals; // stables will be traded between note (unit asset is note) if (pair.stable()) { if (token0 == note) { //token0 is the unit, token1 will be priced with respect to this asset initially decimals = 10 ** (erc20(token1).decimals()); // we must normalize the price of token1 to 18 decimals prices = pair.sample(token1, decimals, 12, 1); (unitReserves, assetReserves) = pair.sampleReserves(12, 1); } else { decimals = 10 ** (erc20(token0).decimals()); prices = pair.sample(token0, decimals, 12, 1); (assetReserves, unitReserves) = pair.sampleReserves(12, 1); } } else { // the unit reserve will be Canto if (token0 == address(wcanto)) { // token0 is Canto, and the unit asset of this pair is Canto decimals = 10 ** (erc20(token1).decimals()); prices = pair.sample(token1, decimals, 12, 1); (unitReserves, assetReserves) = pair.sampleReserves(12, 1); } else { decimals = 10 ** (erc20(token0)).decimals(); prices = pair.sample(token0, decimals, 12, 1); (assetReserves, unitReserves) = pair.sampleReserves(12, 1); } } // now calcuate TVL from twaps and average uint LpPricesCumulative; // average over most recent 12 TWAPS for(uint i; i < 12; ++i) { uint token0TVL = (assetReserves[i] * prices[i]) / decimals; uint token1TVL = unitReserves[i]; // price of the unit asset is always 1 LpPricesCumulative += (token0TVL + token1TVL) * 1e18 / supply[i]; } uint LpPrice = LpPricesCumulative / 12; // take the average of the cumulative prices if (pair.stable()) { // this asset has been priced in terms of Note return LpPrice; } // this asset has been priced in terms of Canto return LpPrice * getPriceNote(address(wcanto), false) / 1e18; // return the price in terms of Note } /// @param token_, the asset to be priced in terms of Canto /// @return price, the price of the asset in terms of canto, in the case of failure, return 0 function getPriceCanto(address token_) internal view returns(uint) { erc20 token = erc20(token_); address pair = getVolatilePair(token_); // this pair does not exist, return 0 uint price; if (pair == address(0)) { // price has already been initialized to zero return price; } // pair exists, now return the quoted amount of Canto for 10**token_decimals uint decimals = 10 ** token.decimals(); // return 0 if there aren't enough observations if (IBaseV1Pair(pair).observationLength() < 8) { return 0; } price = IBaseV1Pair(pair).quote(token_, decimals, 8); // we now have the returned value, in the case of failed require return 0 // return the price scaled by 1e18, and divided by the amtIn, (this is a vol-pair so operations are roughly linear) return price * 1e18 / decimals; } /// @param token_, the asset to be priced in terms of Canto /// @return price, the price of the asset in terms of canto, in the case of failure, return 0 function getPriceNote(address token_, bool stable) internal view returns(uint) { erc20 token = erc20(token_); address pair; if (stable) { pair = getStablePair(token_); } else { // this pair is between wcanto / note (the only pair of this form) pair = getVolatilePair(note); } // this pair does not exist, return 0 uint price; if (pair == address(0)) { // price has already been initialized to zero return price; } // pair exists, now return the quoted amount of Canto for 10**token_decimals uint decimals = 10 ** token.decimals(); // return 0 if there aren't enough observations if (IBaseV1Pair(pair).observationLength() < 8) { return 0; } price = IBaseV1Pair(pair).quote(token_, decimals, 8); // we now have the returned value, in the case of failed require return 0 // return the price scaled by 1e18, and divided by the amtIn, (this is a vol-pair so operations are roughly linear) return price * 1e18 / decimals; } /// @param token_, asset token in stable pair /// @return pair, address of pair if it is to exist, otherwise return address(0) function getStablePair(address token_) internal view returns(address) { IBaseV1Router router_ = IBaseV1Router(router); // return address of pair if it was to be deployed through the router's CREATE2 method address pair = router_.pairFor(note, token_, true); // if the pair does not exist, return address(0) if (!router_.isPair(pair)) { pair = address(0); } return pair; } /// @param token_, asset token in non-stable pair /// @return pair, address of pair if it is to exist function getVolatilePair(address token_) internal view returns(address) { IBaseV1Router router_ = IBaseV1Router(router); address pair = router_.pairFor(wcanto, token_, false); // if the pair does not exist return address(0) if (!router_.isPair(pair)) { pair = address(0); } return pair; } }
pragma solidity ^0.8.10; import "./CErc20Delegate.sol"; import "./Accountant/AccountantInterfaces.sol"; import "./Treasury/TreasuryInterfaces.sol"; import "./ErrorReporter.sol"; import "./NoteInterest.sol"; contract CNote is CErc20Delegate { event AccountantSet(address accountant, address accountantPrior); error FailedTransfer(uint256 amount); AccountantInterface public _accountant; // accountant private _accountant = Accountant(address(0)); function setAccountantContract(address accountant_) public { require( msg.sender == admin, "CNote::_setAccountantContract:Only admin may call this function" ); emit AccountantSet(accountant_, address(_accountant)); _accountant = AccountantInterface(accountant_); } /** * @dev return the current address of the Accounant */ function getAccountant() external view returns (address) { return address(_accountant); } /** * @dev getCashPrior retrieves balance of the accountant (not cNote contract) */ function getCashPrior() internal view virtual override returns (uint256) { EIP20Interface token = EIP20Interface(underlying); return token.balanceOf(address(_accountant)); } function accrueInterest() public virtual override returns (uint256) { NoteRateModel(address(interestRateModel)).updateBaseRate(); //update the baseRate of Note return super.accrueInterest(); } /** * @notice Calculates the exchange rate from Note to cNote * @dev This function does not accrue efore calculating the exchange rate * @return calculated exchange rate scaled by 1e18 */ function exchangeRateStoredInternal() internal view virtual override returns (uint256) { uint256 _totalSupply = totalSupply; if (_totalSupply == 0) { /* * If there are no tokens minted: * exchangeRate = initialExchangeRate */ return initialExchangeRateMantissa; } else { /* * Otherwise: * exchangeRate = (totalCash + totalBorrows - totalReserves) / totalSupply */ uint256 cashPlusBorrowsMinusReserves = totalBorrows - totalReserves; // totalCash in cNote Lending Market is zero, thus it is not factored into the exchangeRate uint256 exchangeRate = cashPlusBorrowsMinusReserves * expScale / _totalSupply; return exchangeRate; } } /** * @dev Similar to EIP20 transfer, except it handles a False result from `transferFrom` and reverts in that case. * This will revert due to insufficient balance or insufficient allowance. * This function returns the actual amount received, * which may be less than `amount` if there is a fee attached to the transfer. * * 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 from, uint256 amount) internal virtual override returns (uint256) { require(address(_accountant) != address(0)); //check that the accountant has been set EIP20Interface token = EIP20Interface(underlying); token.transferFrom(from, address(this), amount); //allowance set before //revert if transfer fails 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) } } require(success, "CNote::TOKEN_TRANSFER_IN_FAILED"); uint256 balanceAfter = token.balanceOf(address(this)); // Calculate the amount that was *actually* transferred if (from != address(_accountant)) { uint256 err = _accountant.redeemMarket(balanceAfter); //Whatever is transferred into cNote is then redeemed by the accountant if (err != 0) { revert AccountantSupplyError(balanceAfter); } } return balanceAfter; // underflow already checked above, just subtract } /** * @dev Similar to EIP20 transfer, except it handles a False success from `transfer` and returns an explanatory * error code rather than reverting. If caller has not called checked protocol's balance, this may revert due to * insufficient cash held in this contract. If caller has checked protocol's balance prior to this call, and verified * it is >= amount, this should not revert in normal conditions. * * 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 payable to, uint256 amount) internal virtual override { require(address(_accountant) != address(0)); //check that the accountant has been set EIP20Interface token = EIP20Interface(underlying); if (to != address(_accountant)) { uint256 err = _accountant.supplyMarket(amount); //Accountant redeems requisite cNote to supply this market if (err != 0) { revert AccountantRedeemError(amount); } } token.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) } } require(success, "TOKEN_TRANSFER_OUT_FAILED"); } /** * @notice Users borrow assets from the protocol to their own address * @param borrowAmount The amount of the underlying asset to borrow */ function borrowFresh(address payable borrower, uint256 borrowAmount) internal override { /* Fail if borrow not allowed */ uint256 allowed = comptroller.borrowAllowed(address(this), borrower, borrowAmount); if (allowed != 0) { revert BorrowComptrollerRejection(allowed); } /* Verify market's block number equals current block number */ if (accrualBlockNumber != getBlockNumber()) { revert BorrowFreshnessCheck(); } /* Fail gracefully if protocol has insufficient underlying cash */ if (getCashPrior() < borrowAmount) { revert BorrowCashNotAvailable(); } /* * We calculate the new borrower and total borrow balances, failing on overflow: * accountBorrowNew = accountBorrow + borrowAmount * totalBorrowsNew = totalBorrows + borrowAmount */ uint256 accountBorrowsPrev = borrowBalanceStoredInternal(borrower); uint256 accountBorrowsNew = accountBorrowsPrev + borrowAmount; uint256 totalBorrowsNew = totalBorrows + borrowAmount; ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* * We invoke doTransferOut for the borrower and the borrowAmount. * Note: The cToken must handle variations between ERC-20 and ETH underlying. * On success, the cToken borrowAmount less of cash. * doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred. */ doTransferOut(borrower, borrowAmount); /* * We write the previously calculated values into storage. * These values must be updated after the accountant has received cTokens at the previous exchangeRate (without totalBorrows being updated) `*/ accountBorrows[borrower].principal = accountBorrowsNew; accountBorrows[borrower].interestIndex = borrowIndex; totalBorrows = totalBorrowsNew; /* We emit a Borrow event */ emit Borrow(borrower, borrowAmount, accountBorrowsNew, totalBorrowsNew); } /** * @notice User redeems cTokens in exchange for the underlying asset * @dev Assumes interest has already been accrued up to the current block * @param redeemer The address of the account which is redeeming the tokens * @param redeemTokensIn The number of cTokens to redeem into underlying (only one of redeemTokensIn or redeemAmountIn may be non-zero) * @param redeemAmountIn The number of underlying tokens to receive from redeeming cTokens (only one of redeemTokensIn or redeemAmountIn may be non-zero) */ function redeemFresh( address payable redeemer, uint256 redeemTokensIn, uint256 redeemAmountIn ) internal override { require( redeemTokensIn == 0 || redeemAmountIn == 0, "one of redeemTokensIn or redeemAmountIn must be zero" ); /* exchangeRate = invoke Exchange Rate Stored() */ Exp memory exchangeRate = Exp({mantissa: exchangeRateStoredInternal()}); uint256 redeemTokens; uint256 redeemAmount; /* If redeemTokensIn > 0: */ if (redeemTokensIn > 0) { /* * We calculate the exchange rate and the amount of underlying to be redeemed: * redeemTokens = redeemTokensIn * redeemAmount = redeemTokensIn x exchangeRateCurrent */ redeemTokens = redeemTokensIn; redeemAmount = mul_ScalarTruncate(exchangeRate, redeemTokensIn); } else { /* * We get the current exchange rate and calculate the amount to be redeemed: * redeemTokens = redeemAmountIn / exchangeRate * redeemAmount = redeemAmountIn */ redeemTokens = div_(redeemAmountIn, exchangeRate); redeemAmount = redeemAmountIn; } /* Fail if redeem not allowed */ uint256 allowed = comptroller.redeemAllowed(address(this), redeemer, redeemTokens); if (allowed != 0) { revert RedeemComptrollerRejection(allowed); } /* Verify market's block number equals current block number */ if (accrualBlockNumber != getBlockNumber()) { revert RedeemFreshnessCheck(); } /* Fail gracefully if protocol has insufficient cash */ if (getCashPrior() < redeemAmount) { revert RedeemTransferOutNotPossible(); } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* * We invoke doTransferOut for the redeemer and the redeemAmount. * Note: The cToken must handle variations between ERC-20 and ETH underlying. * On success, the cToken has redeemAmount less of cash. * doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred. * accountant supplies market and receives cTokens at current exchange Rate */ doTransferOut(redeemer, redeemAmount); /* * We write the previously calculated values into storage. * totalSupply is updated after the accountant has supplied enough tokens for the transfer, and has received cTokens at the prior exchange Rate (without totalSupply being updated) */ totalSupply = totalSupply - redeemTokens; accountTokens[redeemer] = accountTokens[redeemer] - redeemTokens; /* We emit a Transfer event, and a Redeem event */ emit Transfer(redeemer, address(this), redeemTokens); emit Redeem(redeemer, redeemAmount, redeemTokens); /* We call the defense hook */ comptroller.redeemVerify( address(this), redeemer, redeemAmount, redeemTokens ); } /** ** Reentrancy Guard ** */ /** * @dev Prevents a contract from calling itself, directly or indirectly. */ modifier nonReentrant() override { if (msg.sender != address(_accountant)) { require(_notEntered, "re-entered"); //this is required as the Accountant must redeem / mint before users are able to borrow / repayBorrow } _notEntered = false; _; _notEntered = true; // get a gas-refund post-Istanbul } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./CToken.sol"; import "./ErrorReporter.sol"; import "./PriceOracle.sol"; import "./ComptrollerInterface.sol"; import "./ComptrollerStorage.sol"; import "./Unitroller.sol"; import "./Governance/Comp.sol"; import "./WETH.sol"; /** * @title Compound's Comptroller Contract * @author Compound */ contract Comptroller is ComptrollerV7Storage, ComptrollerInterface, ComptrollerErrorReporter, ExponentialNoError { /// @notice Emitted when an admin supports a market event MarketListed(CToken cToken); /// @notice Emitted when an account enters a market event MarketEntered(CToken cToken, address account); /// @notice Emitted when an account exits a market event MarketExited(CToken cToken, address account); /// @notice Emitted when close factor is changed by admin event NewCloseFactor( uint256 oldCloseFactorMantissa, uint256 newCloseFactorMantissa ); /// @notice Emitted when a collateral factor is changed by admin event NewCollateralFactor( CToken cToken, uint256 oldCollateralFactorMantissa, uint256 newCollateralFactorMantissa ); /// @notice Emitted when liquidation incentive is changed by admin event NewLiquidationIncentive( uint256 oldLiquidationIncentiveMantissa, uint256 newLiquidationIncentiveMantissa ); /// @notice Emitted when price oracle is changed event NewPriceOracle(PriceOracle oldPriceOracle, PriceOracle newPriceOracle); /// @notice Emitted when pause guardian is changed event NewPauseGuardian(address oldPauseGuardian, address newPauseGuardian); /// @notice Emitted when an action is paused globally event ActionPaused(string action, bool pauseState); /// @notice Emitted when an action is paused on a market event ActionPaused(CToken cToken, string action, bool pauseState); /// @notice Emitted when a new borrow-side COMP speed is calculated for a market event CompBorrowSpeedUpdated(CToken indexed cToken, uint256 newSpeed); /// @notice Emitted when a new supply-side COMP speed is calculated for a market event CompSupplySpeedUpdated(CToken indexed cToken, uint256 newSpeed); /// @notice Emitted when a new COMP speed is set for a contributor event ContributorCompSpeedUpdated( address indexed contributor, uint256 newSpeed ); /// @notice Emitted when COMP is distributed to a supplier event DistributedSupplierComp( CToken indexed cToken, address indexed supplier, uint256 compDelta, uint256 compSupplyIndex ); /// @notice Emitted when COMP is distributed to a borrower event DistributedBorrowerComp( CToken indexed cToken, address indexed borrower, uint256 compDelta, uint256 compBorrowIndex ); /// @notice Emitted when borrow cap for a cToken is changed event NewBorrowCap(CToken indexed cToken, uint256 newBorrowCap); /// @notice Emitted when borrow cap guardian is changed event NewBorrowCapGuardian( address oldBorrowCapGuardian, address newBorrowCapGuardian ); /// @notice Emitted when COMP is granted by admin event CompGranted(address recipient, uint256 amount); /// @notice Emitted when COMP accrued for a user has been manually adjusted. event CompAccruedAdjusted( address indexed user, uint256 oldCompAccrued, uint256 newCompAccrued ); /// @notice Emitted when COMP receivable for a user has been updated. event CompReceivableUpdated( address indexed user, uint256 oldCompReceivable, uint256 newCompReceivable ); /// @notice The initial COMP index for a market uint224 public constant compInitialIndex = 1e36; // closeFactorMantissa must be strictly greater than this value uint256 internal constant closeFactorMinMantissa = 0.05e18; // 0.05 // closeFactorMantissa must not exceed this value uint256 internal constant closeFactorMaxMantissa = 0.9e18; // 0.9 // No collateralFactorMantissa may exceed this value uint256 internal constant collateralFactorMaxMantissa = 0.9e18; // 0.9 //here for testing purposes address public WethAddr; constructor() { admin = msg.sender; } /** ** Assets You Are In ** */ /** * @notice Returns the assets an account has entered * @param account The address of the account to pull assets for * @return A dynamic list with the assets the account has entered */ function getAssetsIn(address account) external view returns (CToken[] memory) { CToken[] memory assetsIn = accountAssets[account]; return assetsIn; } /** * @notice Returns whether the given account is entered in the given asset * @param account The address of the account to check * @param cToken The cToken to check * @return True if the account is in the asset, otherwise false. */ function checkMembership(address account, CToken cToken) external view returns (bool) { return markets[address(cToken)].accountMembership[account]; } /** * @notice Add assets to be included in account liquidity calculation * @param cTokens The list of addresses of the cToken markets to be enabled * @return Success indicator for whether each corresponding market was entered */ function enterMarkets(address[] memory cTokens) public override returns (uint256[] memory) { uint256 len = cTokens.length; uint256[] memory results = new uint[](len); for (uint256 i = 0; i < len; i++) { CToken cToken = CToken(cTokens[i]); results[i] = uint256(addToMarketInternal(cToken, msg.sender)); } return results; } /** * @notice Add the market to the borrower's "assets in" for liquidity calculations * @param cToken The market to enter * @param borrower The address of the account to modify * @return Success indicator for whether the market was entered */ function addToMarketInternal(CToken cToken, address borrower) internal returns (Error) { Market storage marketToJoin = markets[address(cToken)]; if (!marketToJoin.isListed) { // market is not listed, cannot join return Error.MARKET_NOT_LISTED; } if (marketToJoin.accountMembership[borrower] == true) { // already joined return Error.NO_ERROR; } // survived the gauntlet, add to list // NOTE: we store these somewhat redundantly as a significant optimization // this avoids having to iterate through the list for the most common use cases // that is, only when we need to perform liquidity checks // and not whenever we want to check if an account is in a particular market marketToJoin.accountMembership[borrower] = true; accountAssets[borrower].push(cToken); emit MarketEntered(cToken, borrower); return Error.NO_ERROR; } /** * @notice Removes asset from sender's account liquidity calculation * @dev Sender must not have an outstanding borrow balance in the asset, * or be providing necessary collateral for an outstanding borrow. * @param cTokenAddress The address of the asset to be removed * @return Whether or not the account successfully exited the market */ function exitMarket(address cTokenAddress) external override returns (uint256) { CToken cToken = CToken(cTokenAddress); /* Get sender tokensHeld and amountOwed underlying from the cToken */ (uint256 oErr, uint256 tokensHeld, uint256 amountOwed,) = cToken.getAccountSnapshot(msg.sender); require(oErr == 0, "exitMarket: getAccountSnapshot failed"); // semi-opaque error code /* Fail if the sender has a borrow balance */ if (amountOwed != 0) { return fail( Error.NONZERO_BORROW_BALANCE, FailureInfo.EXIT_MARKET_BALANCE_OWED ); } /* Fail if the sender is not permitted to redeem all of their tokens */ uint256 allowed = redeemAllowedInternal(cTokenAddress, msg.sender, tokensHeld); if (allowed != 0) { return failOpaque( Error.REJECTION, FailureInfo.EXIT_MARKET_REJECTION, allowed ); } Market storage marketToExit = markets[address(cToken)]; /* Return true if the sender is not already ‘in’ the market */ if (!marketToExit.accountMembership[msg.sender]) { return uint256(Error.NO_ERROR); } /* Set cToken account membership to false */ delete marketToExit.accountMembership[msg.sender]; /* Delete cToken from the account’s list of assets */ // load into memory for faster iteration CToken[] memory userAssetList = accountAssets[msg.sender]; uint256 len = userAssetList.length; uint256 assetIndex = len; for (uint256 i = 0; i < len; i++) { if (userAssetList[i] == cToken) { assetIndex = i; break; } } // We *must* have found the asset in the list or our redundant data structure is broken assert(assetIndex < len); // copy last item in list to location of item to be removed, reduce length by 1 CToken[] storage storedList = accountAssets[msg.sender]; storedList[assetIndex] = storedList[storedList.length - 1]; storedList.pop(); emit MarketExited(cToken, msg.sender); return uint256(Error.NO_ERROR); } /** ** Policy Hooks ** */ /** * @notice Checks if the account should be allowed to mint tokens in the given market * @param cToken The market to verify the mint against * @param minter The account which would get the minted tokens * @param mintAmount The amount of underlying being supplied to the market in exchange for tokens * @return 0 if the mint is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ function mintAllowed(address cToken, address minter, uint256 mintAmount) external override returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!mintGuardianPaused[cToken], "mint is paused"); // Shh - currently unused minter; mintAmount; if (!markets[cToken].isListed) { return uint256(Error.MARKET_NOT_LISTED); } // Keep the flywheel moving updateCompSupplyIndex(cToken); distributeSupplierComp(cToken, minter); return uint256(Error.NO_ERROR); } /** * @notice Validates mint and reverts on rejection. May emit logs. * @param cToken Asset being minted * @param minter The address minting the tokens * @param actualMintAmount The amount of the underlying asset being minted * @param mintTokens The number of tokens being minted */ function mintVerify( address cToken, address minter, uint256 actualMintAmount, uint256 mintTokens ) external override { // Shh - currently unused cToken; minter; actualMintAmount; mintTokens; // Shh - we don't ever want this hook to be marked pure if (false) { maxAssets = maxAssets; } } /** * @notice Checks if the account should be allowed to redeem tokens in the given market * @param cToken The market to verify the redeem against * @param redeemer The account which would redeem the tokens * @param redeemTokens The number of cTokens to exchange for the underlying asset in the market * @return 0 if the redeem is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ function redeemAllowed( address cToken, address redeemer, uint256 redeemTokens ) external override returns (uint256) { uint256 allowed = redeemAllowedInternal(cToken, redeemer, redeemTokens); if (allowed != uint256(Error.NO_ERROR)) { return allowed; } // Keep the flywheel moving updateCompSupplyIndex(cToken); distributeSupplierComp(cToken, redeemer); return uint256(Error.NO_ERROR); } function redeemAllowedInternal( address cToken, address redeemer, uint256 redeemTokens ) internal view returns (uint256) { if (!markets[cToken].isListed) { return uint256(Error.MARKET_NOT_LISTED); } /* If the redeemer is not 'in' the market, then we can bypass the liquidity check */ if (!markets[cToken].accountMembership[redeemer]) { return uint256(Error.NO_ERROR); } /* Otherwise, perform a hypothetical liquidity check to guard against shortfall */ (Error err,, uint256 shortfall) = getHypotheticalAccountLiquidityInternal( redeemer, CToken(cToken), redeemTokens, 0 ); if (err != Error.NO_ERROR) { return uint256(err); } if (shortfall > 0) { return uint256(Error.INSUFFICIENT_LIQUIDITY); } return uint256(Error.NO_ERROR); } /** * @notice Validates redeem and reverts on rejection. May emit logs. * @param cToken Asset being redeemed * @param redeemer The address redeeming the tokens * @param redeemAmount The amount of the underlying asset being redeemed * @param redeemTokens The number of tokens being redeemed */ function redeemVerify( address cToken, address redeemer, uint256 redeemAmount, uint256 redeemTokens ) external override { // Shh - currently unused cToken; redeemer; // Require tokens is zero or amount is also zero if (redeemTokens == 0 && redeemAmount > 0) { revert("redeemTokens zero"); } } /** * @notice Checks if the account should be allowed to borrow the underlying asset of the given market * @param cToken The market to verify the borrow against * @param borrower The account which would borrow the asset * @param borrowAmount The amount of underlying the account would borrow * @return 0 if the borrow is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ function borrowAllowed( address cToken, address borrower, uint256 borrowAmount ) external override returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!borrowGuardianPaused[cToken], "borrow is paused"); if (!markets[cToken].isListed) { return uint256(Error.MARKET_NOT_LISTED); } if (!markets[cToken].accountMembership[borrower]) { // only cTokens may call borrowAllowed if borrower not in market require(msg.sender == cToken, "sender must be cToken"); // attempt to add borrower to the market Error err = addToMarketInternal(CToken(msg.sender), borrower); if (err != Error.NO_ERROR) { return uint256(err); } // it should be impossible to break the important invariant assert(markets[cToken].accountMembership[borrower]); } if (oracle.getUnderlyingPrice(CToken(cToken)) == 0) { return uint256(Error.PRICE_ERROR); } uint256 borrowCap = borrowCaps[cToken]; // Borrow cap of 0 corresponds to unlimited borrowing if (borrowCap != 0) { uint256 totalBorrows = CToken(cToken).totalBorrows(); uint256 nextTotalBorrows = add_(totalBorrows, borrowAmount); require(nextTotalBorrows < borrowCap, "market borrow cap reached"); } (Error err,, uint256 shortfall) = getHypotheticalAccountLiquidityInternal( borrower, CToken(cToken), 0, borrowAmount ); if (err != Error.NO_ERROR) { return uint256(err); } if (shortfall > 0) { return uint256(Error.INSUFFICIENT_LIQUIDITY); } // Keep the flywheel moving Exp memory borrowIndex = Exp({mantissa: CToken(cToken).borrowIndex()}); updateCompBorrowIndex(cToken, borrowIndex); distributeBorrowerComp(cToken, borrower, borrowIndex); return uint256(Error.NO_ERROR); } /** * @notice Validates borrow and reverts on rejection. May emit logs. * @param cToken Asset whose underlying is being borrowed * @param borrower The address borrowing the underlying * @param borrowAmount The amount of the underlying asset requested to borrow */ function borrowVerify( address cToken, address borrower, uint256 borrowAmount ) external override { // Shh - currently unused cToken; borrower; borrowAmount; // Shh - we don't ever want this hook to be marked pure if (false) { maxAssets = maxAssets; } } /** * @notice Checks if the account should be allowed to repay a borrow in the given market * @param cToken The market to verify the repay against * @param payer The account which would repay the asset * @param borrower The account which would borrowed the asset * @param repayAmount The amount of the underlying asset the account would repay * @return 0 if the repay is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ function repayBorrowAllowed( address cToken, address payer, address borrower, uint256 repayAmount ) external override returns (uint256) { // Shh - currently unused payer; borrower; repayAmount; if (!markets[cToken].isListed) { return uint256(Error.MARKET_NOT_LISTED); } // Keep the flywheel moving Exp memory borrowIndex = Exp({mantissa: CToken(cToken).borrowIndex()}); updateCompBorrowIndex(cToken, borrowIndex); distributeBorrowerComp(cToken, borrower, borrowIndex); return uint256(Error.NO_ERROR); } /** * @notice Validates repayBorrow and reverts on rejection. May emit logs. * @param cToken Asset being repaid * @param payer The address repaying the borrow * @param borrower The address of the borrower * @param actualRepayAmount The amount of underlying being repaid */ function repayBorrowVerify( address cToken, address payer, address borrower, uint256 actualRepayAmount, uint256 borrowerIndex ) external override { // Shh - currently unused cToken; payer; borrower; actualRepayAmount; borrowerIndex; // Shh - we don't ever want this hook to be marked pure if (false) { maxAssets = maxAssets; } } /** * @notice Checks if the liquidation should be allowed to occur * @param cTokenBorrowed Asset which was borrowed by the borrower * @param cTokenCollateral Asset which was used as collateral and will be seized * @param liquidator The address repaying the borrow and seizing the collateral * @param borrower The address of the borrower * @param repayAmount The amount of underlying being repaid */ function liquidateBorrowAllowed( address cTokenBorrowed, address cTokenCollateral, address liquidator, address borrower, uint256 repayAmount ) external override returns (uint256) { // Shh - currently unused liquidator; if ( !markets[cTokenBorrowed].isListed || !markets[cTokenCollateral].isListed ) { return uint256(Error.MARKET_NOT_LISTED); } uint256 borrowBalance = CToken(cTokenBorrowed).borrowBalanceStored(borrower); /* allow accounts to be liquidated if the market is deprecated */ if (isDeprecated(CToken(cTokenBorrowed))) { require( borrowBalance >= repayAmount, "Can not repay more than the total borrow" ); } else { /* The borrower must have shortfall in order to be liquidatable */ (Error err,, uint256 shortfall) = getAccountLiquidityInternal(borrower); if (err != Error.NO_ERROR) { return uint256(err); } if (shortfall == 0) { return uint256(Error.INSUFFICIENT_SHORTFALL); } /* The liquidator may not repay more than what is allowed by the closeFactor */ uint256 maxClose = mul_ScalarTruncate( Exp({mantissa: closeFactorMantissa}), borrowBalance ); if (repayAmount > maxClose) { return uint256(Error.TOO_MUCH_REPAY); } } return uint256(Error.NO_ERROR); } /** * @notice Validates liquidateBorrow and reverts on rejection. May emit logs. * @param cTokenBorrowed Asset which was borrowed by the borrower * @param cTokenCollateral Asset which was used as collateral and will be seized * @param liquidator The address repaying the borrow and seizing the collateral * @param borrower The address of the borrower * @param actualRepayAmount The amount of underlying being repaid */ function liquidateBorrowVerify( address cTokenBorrowed, address cTokenCollateral, address liquidator, address borrower, uint256 actualRepayAmount, uint256 seizeTokens ) external override { // Shh - currently unused cTokenBorrowed; cTokenCollateral; liquidator; borrower; actualRepayAmount; seizeTokens; // Shh - we don't ever want this hook to be marked pure if (false) { maxAssets = maxAssets; } } /** * @notice Checks if the seizing of assets should be allowed to occur * @param cTokenCollateral Asset which was used as collateral and will be seized * @param cTokenBorrowed Asset which was borrowed by the borrower * @param liquidator The address repaying the borrow and seizing the collateral * @param borrower The address of the borrower * @param seizeTokens The number of collateral tokens to seize */ function seizeAllowed( address cTokenCollateral, address cTokenBorrowed, address liquidator, address borrower, uint256 seizeTokens ) external override returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!seizeGuardianPaused, "seize is paused"); // Shh - currently unused seizeTokens; if ( !markets[cTokenCollateral].isListed || !markets[cTokenBorrowed].isListed ) { return uint256(Error.MARKET_NOT_LISTED); } if ( CToken(cTokenCollateral).comptroller() != CToken(cTokenBorrowed).comptroller() ) { return uint256(Error.COMPTROLLER_MISMATCH); } // Keep the flywheel moving updateCompSupplyIndex(cTokenCollateral); distributeSupplierComp(cTokenCollateral, borrower); distributeSupplierComp(cTokenCollateral, liquidator); return uint256(Error.NO_ERROR); } /** * @notice Validates seize and reverts on rejection. May emit logs. * @param cTokenCollateral Asset which was used as collateral and will be seized * @param cTokenBorrowed Asset which was borrowed by the borrower * @param liquidator The address repaying the borrow and seizing the collateral * @param borrower The address of the borrower * @param seizeTokens The number of collateral tokens to seize */ function seizeVerify( address cTokenCollateral, address cTokenBorrowed, address liquidator, address borrower, uint256 seizeTokens ) external override { // Shh - currently unused cTokenCollateral; cTokenBorrowed; liquidator; borrower; seizeTokens; // Shh - we don't ever want this hook to be marked pure if (false) { maxAssets = maxAssets; } } /** * @notice Checks if the account should be allowed to transfer tokens in the given market * @param cToken The market to verify the transfer against * @param src The account which sources the tokens * @param dst The account which receives the tokens * @param transferTokens The number of cTokens to transfer * @return 0 if the transfer is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ function transferAllowed( address cToken, address src, address dst, uint256 transferTokens ) external override returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!transferGuardianPaused, "transfer is paused"); // Currently the only consideration is whether or not // the src is allowed to redeem this many tokens uint256 allowed = redeemAllowedInternal(cToken, src, transferTokens); if (allowed != uint256(Error.NO_ERROR)) { return allowed; } // Keep the flywheel moving updateCompSupplyIndex(cToken); distributeSupplierComp(cToken, src); distributeSupplierComp(cToken, dst); return uint256(Error.NO_ERROR); } /** * @notice Validates transfer and reverts on rejection. May emit logs. * @param cToken Asset being transferred * @param src The account which sources the tokens * @param dst The account which receives the tokens * @param transferTokens The number of cTokens to transfer */ function transferVerify( address cToken, address src, address dst, uint256 transferTokens ) external override { // Shh - currently unused cToken; src; dst; transferTokens; // Shh - we don't ever want this hook to be marked pure if (false) { maxAssets = maxAssets; } } /** ** Liquidity/Liquidation Calculations ** */ /** * @dev Local vars for avoiding stack-depth limits in calculating account liquidity. * Note that `cTokenBalance` is the number of cTokens the account owns in the market, * whereas `borrowBalance` is the amount of underlying that the account has borrowed. */ struct AccountLiquidityLocalVars { uint256 sumCollateral; uint256 sumBorrowPlusEffects; uint256 cTokenBalance; uint256 borrowBalance; uint256 exchangeRateMantissa; uint256 oraclePriceMantissa; Exp collateralFactor; Exp exchangeRate; Exp oraclePrice; Exp tokensToDenom; } /** * @notice Determine the current account liquidity wrt collateral requirements * @return (possible error code (semi-opaque), account liquidity in excess of collateral requirements, * account shortfall below collateral requirements) */ function getAccountLiquidity(address account) public view returns (uint256, uint256, uint256) { (Error err, uint256 liquidity, uint256 shortfall) = getHypotheticalAccountLiquidityInternal( account, CToken(address(0)), 0, 0 ); return (uint256(err), liquidity, shortfall); } /** * @notice Determine the current account liquidity wrt collateral requirements * @return (possible error code, account liquidity in excess of collateral requirements, * account shortfall below collateral requirements) */ function getAccountLiquidityInternal(address account) internal view returns (Error, uint256, uint256) { return getHypotheticalAccountLiquidityInternal( account, CToken(address(0)), 0, 0 ); } /** * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed * @param cTokenModify The market to hypothetically redeem/borrow in * @param account The account to determine liquidity for * @param redeemTokens The number of tokens to hypothetically redeem * @param borrowAmount The amount of underlying to hypothetically borrow * @return (possible error code (semi-opaque), hypothetical account liquidity in excess of collateral requirements, * hypothetical account shortfall below collateral requirements) */ function getHypotheticalAccountLiquidity( address account, address cTokenModify, uint256 redeemTokens, uint256 borrowAmount ) public view returns (uint256, uint256, uint256) { (Error err, uint256 liquidity, uint256 shortfall) = getHypotheticalAccountLiquidityInternal( account, CToken(cTokenModify), redeemTokens, borrowAmount ); return (uint256(err), liquidity, shortfall); } /** * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed * @param cTokenModify The market to hypothetically redeem/borrow in * @param account The account to determine liquidity for * @param redeemTokens The number of tokens to hypothetically redeem * @param borrowAmount The amount of underlying to hypothetically borrow * @dev Note that we calculate the exchangeRateStored for each collateral cToken using stored data, * without calculating accumulated interest. * @return (possible error code, hypothetical account liquidity in excess of collateral requirements, * hypothetical account shortfall below collateral requirements) */ function getHypotheticalAccountLiquidityInternal( address account, CToken cTokenModify, uint256 redeemTokens, uint256 borrowAmount ) internal view returns (Error, uint256, uint256) { AccountLiquidityLocalVars memory vars; // Holds all our calculation results uint256 oErr; // For each asset the account is in CToken[] memory assets = accountAssets[account]; for (uint256 i = 0; i < assets.length; i++) { CToken asset = assets[i]; // Read the balances and exchange rate from the cToken ( oErr, vars.cTokenBalance, vars.borrowBalance, vars.exchangeRateMantissa ) = asset.getAccountSnapshot(account); if (oErr != 0) { // semi-opaque error code, we assume NO_ERROR == 0 is invariant between upgrades return (Error.SNAPSHOT_ERROR, 0, 0); } vars.collateralFactor = Exp({mantissa: markets[address(asset)].collateralFactorMantissa}); vars.exchangeRate = Exp({mantissa: vars.exchangeRateMantissa}); // Get the normalized price of the asset vars.oraclePriceMantissa = oracle.getUnderlyingPrice(asset); if (vars.oraclePriceMantissa == 0) { return (Error.PRICE_ERROR, 0, 0); } vars.oraclePrice = Exp({mantissa: vars.oraclePriceMantissa}); // Pre-compute a conversion factor from tokens -> note (normalized price value) vars.tokensToDenom = mul_( mul_(vars.collateralFactor, vars.exchangeRate), vars.oraclePrice ); // sumCollateral += tokensToDenom * cTokenBalance vars.sumCollateral = mul_ScalarTruncateAddUInt( vars.tokensToDenom, vars.cTokenBalance, vars.sumCollateral ); // sumBorrowPlusEffects += oraclePrice * borrowBalance vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt( vars.oraclePrice, vars.borrowBalance, vars.sumBorrowPlusEffects ); // Calculate effects of interacting with cTokenModify if (asset == cTokenModify) { // redeem effect // sumBorrowPlusEffects += tokensToDenom * redeemTokens vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt( vars.tokensToDenom, redeemTokens, vars.sumBorrowPlusEffects ); // borrow effect // sumBorrowPlusEffects += oraclePrice * borrowAmount vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt( vars.oraclePrice, borrowAmount, vars.sumBorrowPlusEffects ); } } // These are safe, as the underflow condition is checked first if (vars.sumCollateral > vars.sumBorrowPlusEffects) { return ( Error.NO_ERROR, vars.sumCollateral - vars.sumBorrowPlusEffects, 0 ); } else { return ( Error.NO_ERROR, 0, vars.sumBorrowPlusEffects - vars.sumCollateral ); } } /** * @notice Calculate number of tokens of collateral asset to seize given an underlying amount * @dev Used in liquidation (called in cToken.liquidateBorrowFresh) * @param cTokenBorrowed The address of the borrowed cToken * @param cTokenCollateral The address of the collateral cToken * @param actualRepayAmount The amount of cTokenBorrowed underlying to convert into cTokenCollateral tokens * @return (errorCode, number of cTokenCollateral tokens to be seized in a liquidation) */ function liquidateCalculateSeizeTokens( address cTokenBorrowed, address cTokenCollateral, uint256 actualRepayAmount ) external view override returns (uint256, uint256) { /* Read oracle prices for borrowed and collateral markets */ uint256 priceBorrowedMantissa = oracle.getUnderlyingPrice(CToken(cTokenBorrowed)); uint256 priceCollateralMantissa = oracle.getUnderlyingPrice(CToken(cTokenCollateral)); if (priceBorrowedMantissa == 0 || priceCollateralMantissa == 0) { return (uint256(Error.PRICE_ERROR), 0); } /* * Get the exchange rate and calculate the number of collateral tokens to seize: * seizeAmount = actualRepayAmount * liquidationIncentive * priceBorrowed / priceCollateral * seizeTokens = seizeAmount / exchangeRate * = actualRepayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate) */ uint256 exchangeRateMantissa = CToken(cTokenCollateral).exchangeRateStored(); // Note: reverts on error uint256 seizeTokens; Exp memory numerator; Exp memory denominator; Exp memory ratio; numerator = mul_( Exp({mantissa: liquidationIncentiveMantissa}), Exp({mantissa: priceBorrowedMantissa}) ); denominator = mul_( Exp({mantissa: priceCollateralMantissa}), Exp({mantissa: exchangeRateMantissa}) ); ratio = div_(numerator, denominator); seizeTokens = mul_ScalarTruncate(ratio, actualRepayAmount); return (uint256(Error.NO_ERROR), seizeTokens); } /** ** Admin Functions ** */ /** * @notice Sets a new price oracle for the comptroller * @dev Admin function to set a new price oracle * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setPriceOracle(PriceOracle newOracle) public returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail( Error.UNAUTHORIZED, FailureInfo.SET_PRICE_ORACLE_OWNER_CHECK ); } // Track the old oracle for the comptroller PriceOracle oldOracle = oracle; // Set comptroller's oracle to newOracle oracle = newOracle; // Emit NewPriceOracle(oldOracle, newOracle) emit NewPriceOracle(oldOracle, newOracle); return uint256(Error.NO_ERROR); } /** * @notice Sets the closeFactor used when liquidating borrows * @dev Admin function to set closeFactor * @param newCloseFactorMantissa New close factor, scaled by 1e18 * @return uint 0=success, otherwise a failure */ function _setCloseFactor(uint256 newCloseFactorMantissa) external returns (uint256) { // Check caller is admin require(msg.sender == admin, "only admin can set close factor"); uint256 oldCloseFactorMantissa = closeFactorMantissa; closeFactorMantissa = newCloseFactorMantissa; emit NewCloseFactor(oldCloseFactorMantissa, closeFactorMantissa); return uint256(Error.NO_ERROR); } /** * @notice Sets the collateralFactor for a market * @dev Admin function to set per-market collateralFactor * @param cToken The market to set the factor on * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18 * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) */ function _setCollateralFactor( CToken cToken, uint256 newCollateralFactorMantissa ) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail( Error.UNAUTHORIZED, FailureInfo.SET_COLLATERAL_FACTOR_OWNER_CHECK ); } // Verify market is listed Market storage market = markets[address(cToken)]; if (!market.isListed) { return fail( Error.MARKET_NOT_LISTED, FailureInfo.SET_COLLATERAL_FACTOR_NO_EXISTS ); } Exp memory newCollateralFactorExp = Exp({mantissa: newCollateralFactorMantissa}); // Check collateral factor <= 0.9 Exp memory highLimit = Exp({mantissa: collateralFactorMaxMantissa}); if (lessThanExp(highLimit, newCollateralFactorExp)) { return fail( Error.INVALID_COLLATERAL_FACTOR, FailureInfo.SET_COLLATERAL_FACTOR_VALIDATION ); } // If collateral factor != 0, fail if price == 0 if ( newCollateralFactorMantissa != 0 && oracle.getUnderlyingPrice(cToken) == 0 ) { return fail( Error.PRICE_ERROR, FailureInfo.SET_COLLATERAL_FACTOR_WITHOUT_PRICE ); } // Set market's collateral factor to new collateral factor, remember old value uint256 oldCollateralFactorMantissa = market.collateralFactorMantissa; market.collateralFactorMantissa = newCollateralFactorMantissa; // Emit event with asset, old collateral factor, and new collateral factor emit NewCollateralFactor( cToken, oldCollateralFactorMantissa, newCollateralFactorMantissa ); return uint256(Error.NO_ERROR); } /** * @notice Sets liquidationIncentive * @dev Admin function to set liquidationIncentive * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18 * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) */ function _setLiquidationIncentive(uint256 newLiquidationIncentiveMantissa) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail( Error.UNAUTHORIZED, FailureInfo.SET_LIQUIDATION_INCENTIVE_OWNER_CHECK ); } // Save current value for use in log uint256 oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa; // Set liquidation incentive to new incentive liquidationIncentiveMantissa = newLiquidationIncentiveMantissa; // Emit event with old incentive, new incentive emit NewLiquidationIncentive( oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa ); return uint256(Error.NO_ERROR); } /** * @notice Add the market to the markets mapping and set it as listed * @dev Admin function to set isListed and add support for the market * @param cToken The address of the market (token) to list * @return uint 0=success, otherwise a failure. (See enum Error for details) */ function _supportMarket(CToken cToken) external returns (uint256) { if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SUPPORT_MARKET_OWNER_CHECK); } if (markets[address(cToken)].isListed) { return fail( Error.MARKET_ALREADY_LISTED, FailureInfo.SUPPORT_MARKET_EXISTS ); } cToken.isCToken(); // Sanity check to make sure its really a CToken // Note that isComped is not in active use anymore Market storage newMarket = markets[address(cToken)]; newMarket.isListed = true; newMarket.isComped = false; newMarket.collateralFactorMantissa = 0; _addMarketInternal(address(cToken)); _initializeMarket(address(cToken)); emit MarketListed(cToken); return uint256(Error.NO_ERROR); } function _addMarketInternal(address cToken) internal { for (uint256 i = 0; i < allMarkets.length; i++) { require(allMarkets[i] != CToken(cToken), "market already added"); } allMarkets.push(CToken(cToken)); } function _initializeMarket(address cToken) internal { uint32 blockNumber = safe32(getBlockNumber(), "block number exceeds 32 bits"); CompMarketState storage supplyState = compSupplyState[cToken]; CompMarketState storage borrowState = compBorrowState[cToken]; /* * Update market state indices */ if (supplyState.index == 0) { // Initialize supply state index with default value supplyState.index = compInitialIndex; } if (borrowState.index == 0) { // Initialize borrow state index with default value borrowState.index = compInitialIndex; } /* * Update market state block numbers */ supplyState.block = (borrowState.block = blockNumber); } /** * @notice Set the given borrow caps for the given cToken markets. Borrowing that brings total borrows to or above borrow cap will revert. * @dev Admin or borrowCapGuardian function to set the borrow caps. A borrow cap of 0 corresponds to unlimited borrowing. * @param cTokens The addresses of the markets (tokens) to change the borrow caps for * @param newBorrowCaps The new borrow cap values in underlying to be set. A value of 0 corresponds to unlimited borrowing. */ function _setMarketBorrowCaps( CToken[] calldata cTokens, uint256[] calldata newBorrowCaps ) external { require( msg.sender == admin || msg.sender == borrowCapGuardian, "only admin or borrow cap guardian can set borrow caps" ); uint256 numMarkets = cTokens.length; uint256 numBorrowCaps = newBorrowCaps.length; require(numMarkets != 0 && numMarkets == numBorrowCaps, "invalid input"); for (uint256 i = 0; i < numMarkets; i++) { borrowCaps[address(cTokens[i])] = newBorrowCaps[i]; emit NewBorrowCap(cTokens[i], newBorrowCaps[i]); } } /** * @notice Admin function to change the Borrow Cap Guardian * @param newBorrowCapGuardian The address of the new Borrow Cap Guardian */ function _setBorrowCapGuardian(address newBorrowCapGuardian) external { require(msg.sender == admin, "only admin can set borrow cap guardian"); // Save current value for inclusion in log address oldBorrowCapGuardian = borrowCapGuardian; // Store borrowCapGuardian with value newBorrowCapGuardian borrowCapGuardian = newBorrowCapGuardian; // Emit NewBorrowCapGuardian(OldBorrowCapGuardian, NewBorrowCapGuardian) emit NewBorrowCapGuardian(oldBorrowCapGuardian, newBorrowCapGuardian); } /** * @notice Admin function to change the Pause Guardian * @param newPauseGuardian The address of the new Pause Guardian * @return uint 0=success, otherwise a failure. (See enum Error for details) */ function _setPauseGuardian(address newPauseGuardian) public returns (uint256) { if (msg.sender != admin) { return fail( Error.UNAUTHORIZED, FailureInfo.SET_PAUSE_GUARDIAN_OWNER_CHECK ); } // Save current value for inclusion in log address oldPauseGuardian = pauseGuardian; // Store pauseGuardian with value newPauseGuardian pauseGuardian = newPauseGuardian; // Emit NewPauseGuardian(OldPauseGuardian, NewPauseGuardian) emit NewPauseGuardian(oldPauseGuardian, pauseGuardian); return uint256(Error.NO_ERROR); } function _setMintPaused(CToken cToken, bool state) public returns (bool) { require( markets[address(cToken)].isListed, "cannot pause a market that is not listed" ); require( msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause" ); require(msg.sender == admin || state == true, "only admin can unpause"); mintGuardianPaused[address(cToken)] = state; emit ActionPaused(cToken, "Mint", state); return state; } function _setBorrowPaused(CToken cToken, bool state) public returns (bool) { require( markets[address(cToken)].isListed, "cannot pause a market that is not listed" ); require( msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause" ); require(msg.sender == admin || state == true, "only admin can unpause"); borrowGuardianPaused[address(cToken)] = state; emit ActionPaused(cToken, "Borrow", state); return state; } function _setTransferPaused(bool state) public returns (bool) { require( msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause" ); require(msg.sender == admin || state == true, "only admin can unpause"); transferGuardianPaused = state; emit ActionPaused("Transfer", state); return state; } function _setSeizePaused(bool state) public returns (bool) { require( msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause" ); require(msg.sender == admin || state == true, "only admin can unpause"); seizeGuardianPaused = state; emit ActionPaused("Seize", state); return state; } function _become(Unitroller unitroller) public { require( msg.sender == unitroller.admin(), "only unitroller admin can change brains" ); require( unitroller._acceptImplementation() == 0, "change not authorized" ); } /// @notice Delete this function after proposal 65 is executed function fixBadAccruals( address[] calldata affectedUsers, uint256[] calldata amounts ) external { require(msg.sender == admin, "Only admin can call this function"); // Only the timelock can call this function require( !proposal65FixExecuted, "Already executed this one-off function" ); // Require that this function is only called once require(affectedUsers.length == amounts.length, "Invalid input"); // Loop variables address user; uint256 currentAccrual; uint256 amountToSubtract; uint256 newAccrual; // Iterate through all affected users for (uint256 i = 0; i < affectedUsers.length; ++i) { user = affectedUsers[i]; currentAccrual = compAccrued[user]; amountToSubtract = amounts[i]; // The case where the user has claimed and received an incorrect amount of COMP. // The user has less currently accrued than the amount they incorrectly received. if (amountToSubtract > currentAccrual) { // Amount of COMP the user owes the protocol uint256 accountReceivable = amountToSubtract - currentAccrual; // Underflow safe since amountToSubtract > currentAccrual uint256 oldReceivable = compReceivable[user]; uint256 newReceivable = add_(oldReceivable, accountReceivable); // Accounting: record the COMP debt for the user compReceivable[user] = newReceivable; emit CompReceivableUpdated(user, oldReceivable, newReceivable); amountToSubtract = currentAccrual; } if (amountToSubtract > 0) { // Subtract the bad accrual amount from what they have accrued. // Users will keep whatever they have correctly accrued. compAccrued[user] = ( newAccrual = sub_(currentAccrual, amountToSubtract) ); emit CompAccruedAdjusted(user, currentAccrual, newAccrual); } } proposal65FixExecuted = true; // Makes it so that this function cannot be called again } /** * @notice Checks caller is admin, or this contract is becoming the new implementation */ function adminOrInitializing() internal view returns (bool) { return msg.sender == admin || msg.sender == comptrollerImplementation; } /** ** Comp Distribution ** */ /** * @notice Set COMP speed for a single market * @param cToken The market whose COMP speed to update * @param supplySpeed New supply-side COMP speed for market * @param borrowSpeed New borrow-side COMP speed for market */ function setCompSpeedInternal( CToken cToken, uint256 supplySpeed, uint256 borrowSpeed ) internal { Market storage market = markets[address(cToken)]; require(market.isListed, "comp market is not listed"); if (compSupplySpeeds[address(cToken)] != supplySpeed) { // Supply speed updated so let's update supply state to ensure that // 1. COMP accrued properly for the old speed, and // 2. COMP accrued at the new speed starts after this block. updateCompSupplyIndex(address(cToken)); // Update speed and emit event compSupplySpeeds[address(cToken)] = supplySpeed; emit CompSupplySpeedUpdated(cToken, supplySpeed); } if (compBorrowSpeeds[address(cToken)] != borrowSpeed) { // Borrow speed updated so let's update borrow state to ensure that // 1. COMP accrued properly for the old speed, and // 2. COMP accrued at the new speed starts after this block. Exp memory borrowIndex = Exp({mantissa: cToken.borrowIndex()}); updateCompBorrowIndex(address(cToken), borrowIndex); // Update speed and emit event compBorrowSpeeds[address(cToken)] = borrowSpeed; emit CompBorrowSpeedUpdated(cToken, borrowSpeed); } } /** * @notice Accrue COMP to the market by updating the supply index * @param cToken The market whose supply index to update * @dev Index is a cumulative sum of the COMP per cToken accrued. */ function updateCompSupplyIndex(address cToken) internal { CompMarketState storage supplyState = compSupplyState[cToken]; uint256 supplySpeed = compSupplySpeeds[cToken]; uint32 blockNumber = safe32(getBlockNumber(), "block number exceeds 32 bits"); uint256 deltaBlocks = sub_(uint256(blockNumber), uint256(supplyState.block)); if (deltaBlocks > 0 && supplySpeed > 0) { uint256 supplyTokens = CToken(cToken).totalSupply(); uint256 compAccrued = mul_(deltaBlocks, supplySpeed); Double memory ratio = supplyTokens > 0 ? fraction(compAccrued, supplyTokens) : Double({mantissa: 0}); supplyState.index = safe224( add_(Double({mantissa: supplyState.index}), ratio).mantissa, "new index exceeds 224 bits" ); supplyState.block = blockNumber; } else if (deltaBlocks > 0) { supplyState.block = blockNumber; } } /** * @notice Accrue COMP to the market by updating the borrow index * @param cToken The market whose borrow index to update * @dev Index is a cumulative sum of the COMP per cToken accrued. */ function updateCompBorrowIndex(address cToken, Exp memory marketBorrowIndex) internal { CompMarketState storage borrowState = compBorrowState[cToken]; uint256 borrowSpeed = compBorrowSpeeds[cToken]; uint32 blockNumber = safe32(getBlockNumber(), "block number exceeds 32 bits"); uint256 deltaBlocks = sub_(uint256(blockNumber), uint256(borrowState.block)); if (deltaBlocks > 0 && borrowSpeed > 0) { uint256 borrowAmount = div_(CToken(cToken).totalBorrows(), marketBorrowIndex); uint256 compAccrued = mul_(deltaBlocks, borrowSpeed); Double memory ratio = borrowAmount > 0 ? fraction(compAccrued, borrowAmount) : Double({mantissa: 0}); borrowState.index = safe224( add_(Double({mantissa: borrowState.index}), ratio).mantissa, "new index exceeds 224 bits" ); borrowState.block = blockNumber; } else if (deltaBlocks > 0) { borrowState.block = blockNumber; } } /** * @notice Calculate COMP accrued by a supplier and possibly transfer it to them * @param cToken The market in which the supplier is interacting * @param supplier The address of the supplier to distribute COMP to */ function distributeSupplierComp(address cToken, address supplier) internal { // TODO: Don't distribute supplier COMP if the user is not in the supplier market. // This check should be as gas efficient as possible as distributeSupplierComp is called in many places. // - We really don't want to call an external contract as that's quite expensive. CompMarketState storage supplyState = compSupplyState[cToken]; uint256 supplyIndex = supplyState.index; uint256 supplierIndex = compSupplierIndex[cToken][supplier]; // Update supplier's index to the current index since we are distributing accrued COMP compSupplierIndex[cToken][supplier] = supplyIndex; if (supplierIndex == 0 && supplyIndex >= compInitialIndex) { // Covers the case where users supplied tokens before the market's supply state index was set. // Rewards the user with COMP accrued from the start of when supplier rewards were first // set for the market. supplierIndex = compInitialIndex; } // Calculate change in the cumulative sum of the COMP per cToken accrued Double memory deltaIndex = Double({mantissa: sub_(supplyIndex, supplierIndex)}); uint256 supplierTokens = CToken(cToken).balanceOf(supplier); // Calculate COMP accrued: cTokenAmount * accruedPerCToken uint256 supplierDelta = mul_(supplierTokens, deltaIndex); uint256 supplierAccrued = add_(compAccrued[supplier], supplierDelta); compAccrued[supplier] = supplierAccrued; emit DistributedSupplierComp( CToken(cToken), supplier, supplierDelta, supplyIndex ); } /** * @notice Calculate COMP accrued by a borrower and possibly transfer it to them * @dev Borrowers will not begin to accrue until after the first interaction with the protocol. * @param cToken The market in which the borrower is interacting * @param borrower The address of the borrower to distribute COMP to */ function distributeBorrowerComp( address cToken, address borrower, Exp memory marketBorrowIndex ) internal { // TODO: Don't distribute supplier COMP if the user is not in the borrower market. // This check should be as gas efficient as possible as distributeBorrowerComp is called in many places. // - We really don't want to call an external contract as that's quite expensive. CompMarketState storage borrowState = compBorrowState[cToken]; uint256 borrowIndex = borrowState.index; uint256 borrowerIndex = compBorrowerIndex[cToken][borrower]; // Update borrowers's index to the current index since we are distributing accrued COMP compBorrowerIndex[cToken][borrower] = borrowIndex; if (borrowerIndex == 0 && borrowIndex >= compInitialIndex) { // Covers the case where users borrowed tokens before the market's borrow state index was set. // Rewards the user with COMP accrued from the start of when borrower rewards were first // set for the market. borrowerIndex = compInitialIndex; } // Calculate change in the cumulative sum of the COMP per borrowed unit accrued Double memory deltaIndex = Double({mantissa: sub_(borrowIndex, borrowerIndex)}); uint256 borrowerAmount = div_(CToken(cToken).borrowBalanceStored(borrower), marketBorrowIndex); // Calculate COMP accrued: cTokenAmount * accruedPerBorrowedUnit uint256 borrowerDelta = mul_(borrowerAmount, deltaIndex); uint256 borrowerAccrued = add_(compAccrued[borrower], borrowerDelta); compAccrued[borrower] = borrowerAccrued; emit DistributedBorrowerComp( CToken(cToken), borrower, borrowerDelta, borrowIndex ); } /** * @notice Calculate additional accrued COMP for a contributor since last accrual * @param contributor The address to calculate contributor rewards for */ function updateContributorRewards(address contributor) public { uint256 compSpeed = compContributorSpeeds[contributor]; uint256 blockNumber = getBlockNumber(); uint256 deltaBlocks = sub_(blockNumber, lastContributorBlock[contributor]); if (deltaBlocks > 0 && compSpeed > 0) { uint256 newAccrued = mul_(deltaBlocks, compSpeed); uint256 contributorAccrued = add_(compAccrued[contributor], newAccrued); compAccrued[contributor] = contributorAccrued; lastContributorBlock[contributor] = blockNumber; } } /** * @notice Claim all the comp accrued by holder in all markets * @param holder The address to claim COMP for */ function claimComp(address holder) public { return claimComp(holder, allMarkets); } /** * @notice Claim all the comp accrued by holder in the specified markets * @param holder The address to claim COMP for * @param cTokens The list of markets to claim COMP in */ function claimComp(address holder, CToken[] memory cTokens) public { address[] memory holders = new address[](1); holders[0] = holder; claimComp(holders, cTokens, true, true); } /** * @notice Claim all comp accrued by the holders * @param holders The addresses to claim COMP for * @param cTokens The list of markets to claim COMP in * @param borrowers Whether or not to claim COMP earned by borrowing * @param suppliers Whether or not to claim COMP earned by supplying */ function claimComp( address[] memory holders, CToken[] memory cTokens, bool borrowers, bool suppliers ) public { for (uint256 i = 0; i < cTokens.length; i++) { CToken cToken = cTokens[i]; require(markets[address(cToken)].isListed, "market must be listed"); if (borrowers == true) { Exp memory borrowIndex = Exp({mantissa: cToken.borrowIndex()}); updateCompBorrowIndex(address(cToken), borrowIndex); for (uint256 j = 0; j < holders.length; j++) { distributeBorrowerComp( address(cToken), holders[j], borrowIndex ); } } if (suppliers == true) { updateCompSupplyIndex(address(cToken)); for (uint256 j = 0; j < holders.length; j++) { distributeSupplierComp(address(cToken), holders[j]); } } } for (uint256 j = 0; j < holders.length; j++) { compAccrued[holders[j]] = grantCompInternal(holders[j], compAccrued[holders[j]]); } } /** * @notice Transfer WETH to the user * @dev Note: If there is not enough WETH, we do not perform the transfer all. * @param user The address of the user to transfer WETH to * @param amount The amount of WETH to (possibly) transfer * @return The amount of WETH which was NOT transferred to the user */ function grantCompInternal(address user, uint256 amount) internal returns (uint256) { WETH comp = WETH(payable(getWETHAddress())); uint256 compRemaining = comp.balanceOf(address(this)); if (amount > 0 && amount <= compRemaining) { comp.transfer(user, amount); return 0; } return amount; } /** ** Comp Distribution Admin ** */ /** * @notice Transfer COMP to the recipient * @dev Note: If there is not enough COMP, we do not perform the transfer all. * @param recipient The address of the recipient to transfer COMP to * @param amount The amount of COMP to (possibly) transfer */ function _grantComp(address recipient, uint256 amount) public { require(adminOrInitializing(), "only admin can grant comp"); uint256 amountLeft = grantCompInternal(recipient, amount); require(amountLeft == 0, "insufficient comp for grant"); emit CompGranted(recipient, amount); } /** * @notice Set COMP borrow and supply speeds for the specified markets. * @param cTokens The markets whose COMP speed to update. * @param supplySpeeds New supply-side COMP speed for the corresponding market. * @param borrowSpeeds New borrow-side COMP speed for the corresponding market. */ function _setCompSpeeds( CToken[] memory cTokens, uint256[] memory supplySpeeds, uint256[] memory borrowSpeeds ) public { require(adminOrInitializing(), "only admin can set comp speed"); uint256 numTokens = cTokens.length; require( numTokens == supplySpeeds.length && numTokens == borrowSpeeds.length, "Comptroller::_setCompSpeeds invalid input" ); for (uint256 i = 0; i < numTokens; ++i) { setCompSpeedInternal(cTokens[i], supplySpeeds[i], borrowSpeeds[i]); } } /** * @notice Set COMP speed for a single contributor * @param contributor The contributor whose COMP speed to update * @param compSpeed New COMP speed for contributor */ function _setContributorCompSpeed(address contributor, uint256 compSpeed) public { require(adminOrInitializing(), "only admin can set comp speed"); // note that COMP speed could be set to 0 to halt liquidity rewards for a contributor updateContributorRewards(contributor); if (compSpeed == 0) { // release storage delete lastContributorBlock[contributor]; } else { lastContributorBlock[contributor] = getBlockNumber(); } compContributorSpeeds[contributor] = compSpeed; emit ContributorCompSpeedUpdated(contributor, compSpeed); } /** * @notice Return all of the markets * @dev The automatic getter may be used to access an individual market. * @return The list of market addresses */ function getAllMarkets() public view returns (CToken[] memory) { return allMarkets; } /** * @notice Returns true if the given cToken market has been deprecated * @dev All borrows in a deprecated cToken market can be immediately liquidated * @param cToken The market to check if deprecated */ function isDeprecated(CToken cToken) public view returns (bool) { return markets[address(cToken)].collateralFactorMantissa == 0 && borrowGuardianPaused[address(cToken)] == true && cToken.reserveFactorMantissa() == 1e18; } function getBlockNumber() public view virtual returns (uint256) { return block.number; } /** * @notice Return the address of the COMP token * @return The address of COMP */ function getWETHAddress() public view virtual returns (address) { return WethAddr; } function setWETHAddress(address wethAddr) public virtual { require(msg.sender == admin, "Only admin may initialize Weth Address"); WethAddr = wethAddr; } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./CToken.sol"; import "./ErrorReporter.sol"; import "./PriceOracle.sol"; import "./ComptrollerInterface.sol"; import "./ComptrollerStorage.sol"; import "./Unitroller.sol"; import "./Governance/Comp.sol"; /** * @title Compound's Comptroller Contract * @author Compound */ contract ComptrollerG7 is ComptrollerV5Storage, ComptrollerInterface, ComptrollerErrorReporter, ExponentialNoError { /// @notice Emitted when an admin supports a market event MarketListed(CToken cToken); /// @notice Emitted when an account enters a market event MarketEntered(CToken cToken, address account); /// @notice Emitted when an account exits a market event MarketExited(CToken cToken, address account); /// @notice Emitted when close factor is changed by admin event NewCloseFactor( uint256 oldCloseFactorMantissa, uint256 newCloseFactorMantissa ); /// @notice Emitted when a collateral factor is changed by admin event NewCollateralFactor( CToken cToken, uint256 oldCollateralFactorMantissa, uint256 newCollateralFactorMantissa ); /// @notice Emitted when liquidation incentive is changed by admin event NewLiquidationIncentive( uint256 oldLiquidationIncentiveMantissa, uint256 newLiquidationIncentiveMantissa ); /// @notice Emitted when price oracle is changed event NewPriceOracle(PriceOracle oldPriceOracle, PriceOracle newPriceOracle); /// @notice Emitted when pause guardian is changed event NewPauseGuardian(address oldPauseGuardian, address newPauseGuardian); /// @notice Emitted when an action is paused globally event ActionPaused(string action, bool pauseState); /// @notice Emitted when an action is paused on a market event ActionPaused(CToken cToken, string action, bool pauseState); /// @notice Emitted when a new COMP speed is calculated for a market event CompSpeedUpdated(CToken indexed cToken, uint256 newSpeed); /// @notice Emitted when a new COMP speed is set for a contributor event ContributorCompSpeedUpdated( address indexed contributor, uint256 newSpeed ); /// @notice Emitted when COMP is distributed to a supplier event DistributedSupplierComp( CToken indexed cToken, address indexed supplier, uint256 compDelta, uint256 compSupplyIndex ); /// @notice Emitted when COMP is distributed to a borrower event DistributedBorrowerComp( CToken indexed cToken, address indexed borrower, uint256 compDelta, uint256 compBorrowIndex ); /// @notice Emitted when borrow cap for a cToken is changed event NewBorrowCap(CToken indexed cToken, uint256 newBorrowCap); /// @notice Emitted when borrow cap guardian is changed event NewBorrowCapGuardian( address oldBorrowCapGuardian, address newBorrowCapGuardian ); /// @notice Emitted when COMP is granted by admin event CompGranted(address recipient, uint256 amount); /// @notice The initial COMP index for a market uint224 public constant compInitialIndex = 1e36; // closeFactorMantissa must be strictly greater than this value uint256 internal constant closeFactorMinMantissa = 0.05e18; // 0.05 // closeFactorMantissa must not exceed this value uint256 internal constant closeFactorMaxMantissa = 0.9e18; // 0.9 // No collateralFactorMantissa may exceed this value uint256 internal constant collateralFactorMaxMantissa = 0.9e18; // 0.9 constructor() public { admin = msg.sender; } /** ** Assets You Are In ** */ /** * @notice Returns the assets an account has entered * @param account The address of the account to pull assets for * @return A dynamic list with the assets the account has entered */ function getAssetsIn(address account) external view returns (CToken[] memory) { CToken[] memory assetsIn = accountAssets[account]; return assetsIn; } /** * @notice Returns whether the given account is entered in the given asset * @param account The address of the account to check * @param cToken The cToken to check * @return True if the account is in the asset, otherwise false. */ function checkMembership(address account, CToken cToken) external view returns (bool) { return markets[address(cToken)].accountMembership[account]; } /** * @notice Add assets to be included in account liquidity calculation * @param cTokens The list of addresses of the cToken markets to be enabled * @return Success indicator for whether each corresponding market was entered */ function enterMarkets(address[] memory cTokens) public override returns (uint256[] memory) { uint256 len = cTokens.length; uint256[] memory results = new uint[](len); for (uint256 i = 0; i < len; i++) { CToken cToken = CToken(cTokens[i]); results[i] = uint256(addToMarketInternal(cToken, msg.sender)); } return results; } /** * @notice Add the market to the borrower's "assets in" for liquidity calculations * @param cToken The market to enter * @param borrower The address of the account to modify * @return Success indicator for whether the market was entered */ function addToMarketInternal(CToken cToken, address borrower) internal returns (Error) { Market storage marketToJoin = markets[address(cToken)]; if (!marketToJoin.isListed) { // market is not listed, cannot join return Error.MARKET_NOT_LISTED; } if (marketToJoin.accountMembership[borrower] == true) { // already joined return Error.NO_ERROR; } // survived the gauntlet, add to list // NOTE: we store these somewhat redundantly as a significant optimization // this avoids having to iterate through the list for the most common use cases // that is, only when we need to perform liquidity checks // and not whenever we want to check if an account is in a particular market marketToJoin.accountMembership[borrower] = true; accountAssets[borrower].push(cToken); emit MarketEntered(cToken, borrower); return Error.NO_ERROR; } /** * @notice Removes asset from sender's account liquidity calculation * @dev Sender must not have an outstanding borrow balance in the asset, * or be providing necessary collateral for an outstanding borrow. * @param cTokenAddress The address of the asset to be removed * @return Whether or not the account successfully exited the market */ function exitMarket(address cTokenAddress) external override returns (uint256) { CToken cToken = CToken(cTokenAddress); /* Get sender tokensHeld and amountOwed underlying from the cToken */ (uint256 oErr, uint256 tokensHeld, uint256 amountOwed,) = cToken.getAccountSnapshot(msg.sender); require(oErr == 0, "exitMarket: getAccountSnapshot failed"); // semi-opaque error code /* Fail if the sender has a borrow balance */ if (amountOwed != 0) { return fail( Error.NONZERO_BORROW_BALANCE, FailureInfo.EXIT_MARKET_BALANCE_OWED ); } /* Fail if the sender is not permitted to redeem all of their tokens */ uint256 allowed = redeemAllowedInternal(cTokenAddress, msg.sender, tokensHeld); if (allowed != 0) { return failOpaque( Error.REJECTION, FailureInfo.EXIT_MARKET_REJECTION, allowed ); } Market storage marketToExit = markets[address(cToken)]; /* Return true if the sender is not already ‘in’ the market */ if (!marketToExit.accountMembership[msg.sender]) { return uint256(Error.NO_ERROR); } /* Set cToken account membership to false */ delete marketToExit.accountMembership[msg.sender]; /* Delete cToken from the account’s list of assets */ // load into memory for faster iteration CToken[] memory userAssetList = accountAssets[msg.sender]; uint256 len = userAssetList.length; uint256 assetIndex = len; for (uint256 i = 0; i < len; i++) { if (userAssetList[i] == cToken) { assetIndex = i; break; } } // We *must* have found the asset in the list or our redundant data structure is broken assert(assetIndex < len); // copy last item in list to location of item to be removed, reduce length by 1 CToken[] storage storedList = accountAssets[msg.sender]; storedList[assetIndex] = storedList[storedList.length - 1]; storedList.pop(); emit MarketExited(cToken, msg.sender); return uint256(Error.NO_ERROR); } /** ** Policy Hooks ** */ /** * @notice Checks if the account should be allowed to mint tokens in the given market * @param cToken The market to verify the mint against * @param minter The account which would get the minted tokens * @param mintAmount The amount of underlying being supplied to the market in exchange for tokens * @return 0 if the mint is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ function mintAllowed(address cToken, address minter, uint256 mintAmount) external override returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!mintGuardianPaused[cToken], "mint is paused"); // Shh - currently unused minter; mintAmount; if (!markets[cToken].isListed) { return uint256(Error.MARKET_NOT_LISTED); } // Keep the flywheel moving updateCompSupplyIndex(cToken); distributeSupplierComp(cToken, minter); return uint256(Error.NO_ERROR); } /** * @notice Validates mint and reverts on rejection. May emit logs. * @param cToken Asset being minted * @param minter The address minting the tokens * @param actualMintAmount The amount of the underlying asset being minted * @param mintTokens The number of tokens being minted */ function mintVerify( address cToken, address minter, uint256 actualMintAmount, uint256 mintTokens ) external override { // Shh - currently unused cToken; minter; actualMintAmount; mintTokens; // Shh - we don't ever want this hook to be marked pure if (false) { maxAssets = maxAssets; } } /** * @notice Checks if the account should be allowed to redeem tokens in the given market * @param cToken The market to verify the redeem against * @param redeemer The account which would redeem the tokens * @param redeemTokens The number of cTokens to exchange for the underlying asset in the market * @return 0 if the redeem is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ function redeemAllowed( address cToken, address redeemer, uint256 redeemTokens ) external override returns (uint256) { uint256 allowed = redeemAllowedInternal(cToken, redeemer, redeemTokens); if (allowed != uint256(Error.NO_ERROR)) { return allowed; } // Keep the flywheel moving updateCompSupplyIndex(cToken); distributeSupplierComp(cToken, redeemer); return uint256(Error.NO_ERROR); } function redeemAllowedInternal( address cToken, address redeemer, uint256 redeemTokens ) internal view returns (uint256) { if (!markets[cToken].isListed) { return uint256(Error.MARKET_NOT_LISTED); } /* If the redeemer is not 'in' the market, then we can bypass the liquidity check */ if (!markets[cToken].accountMembership[redeemer]) { return uint256(Error.NO_ERROR); } /* Otherwise, perform a hypothetical liquidity check to guard against shortfall */ (Error err,, uint256 shortfall) = getHypotheticalAccountLiquidityInternal( redeemer, CToken(cToken), redeemTokens, 0 ); if (err != Error.NO_ERROR) { return uint256(err); } if (shortfall > 0) { return uint256(Error.INSUFFICIENT_LIQUIDITY); } return uint256(Error.NO_ERROR); } /** * @notice Validates redeem and reverts on rejection. May emit logs. * @param cToken Asset being redeemed * @param redeemer The address redeeming the tokens * @param redeemAmount The amount of the underlying asset being redeemed * @param redeemTokens The number of tokens being redeemed */ function redeemVerify( address cToken, address redeemer, uint256 redeemAmount, uint256 redeemTokens ) external override { // Shh - currently unused cToken; redeemer; // Require tokens is zero or amount is also zero if (redeemTokens == 0 && redeemAmount > 0) { revert("redeemTokens zero"); } } /** * @notice Checks if the account should be allowed to borrow the underlying asset of the given market * @param cToken The market to verify the borrow against * @param borrower The account which would borrow the asset * @param borrowAmount The amount of underlying the account would borrow * @return 0 if the borrow is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ function borrowAllowed( address cToken, address borrower, uint256 borrowAmount ) external override returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!borrowGuardianPaused[cToken], "borrow is paused"); if (!markets[cToken].isListed) { return uint256(Error.MARKET_NOT_LISTED); } if (!markets[cToken].accountMembership[borrower]) { // only cTokens may call borrowAllowed if borrower not in market require(msg.sender == cToken, "sender must be cToken"); // attempt to add borrower to the market Error err = addToMarketInternal(CToken(msg.sender), borrower); if (err != Error.NO_ERROR) { return uint256(err); } // it should be impossible to break the important invariant assert(markets[cToken].accountMembership[borrower]); } if (oracle.getUnderlyingPrice(CToken(cToken)) == 0) { return uint256(Error.PRICE_ERROR); } uint256 borrowCap = borrowCaps[cToken]; // Borrow cap of 0 corresponds to unlimited borrowing if (borrowCap != 0) { uint256 totalBorrows = CToken(cToken).totalBorrows(); uint256 nextTotalBorrows = add_(totalBorrows, borrowAmount); require(nextTotalBorrows < borrowCap, "market borrow cap reached"); } (Error err,, uint256 shortfall) = getHypotheticalAccountLiquidityInternal( borrower, CToken(cToken), 0, borrowAmount ); if (err != Error.NO_ERROR) { return uint256(err); } if (shortfall > 0) { return uint256(Error.INSUFFICIENT_LIQUIDITY); } // Keep the flywheel moving Exp memory borrowIndex = Exp({mantissa: CToken(cToken).borrowIndex()}); updateCompBorrowIndex(cToken, borrowIndex); distributeBorrowerComp(cToken, borrower, borrowIndex); return uint256(Error.NO_ERROR); } /** * @notice Validates borrow and reverts on rejection. May emit logs. * @param cToken Asset whose underlying is being borrowed * @param borrower The address borrowing the underlying * @param borrowAmount The amount of the underlying asset requested to borrow */ function borrowVerify( address cToken, address borrower, uint256 borrowAmount ) external override { // Shh - currently unused cToken; borrower; borrowAmount; // Shh - we don't ever want this hook to be marked pure if (false) { maxAssets = maxAssets; } } /** * @notice Checks if the account should be allowed to repay a borrow in the given market * @param cToken The market to verify the repay against * @param payer The account which would repay the asset * @param borrower The account which would borrowed the asset * @param repayAmount The amount of the underlying asset the account would repay * @return 0 if the repay is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ function repayBorrowAllowed( address cToken, address payer, address borrower, uint256 repayAmount ) external override returns (uint256) { // Shh - currently unused payer; borrower; repayAmount; if (!markets[cToken].isListed) { return uint256(Error.MARKET_NOT_LISTED); } // Keep the flywheel moving Exp memory borrowIndex = Exp({mantissa: CToken(cToken).borrowIndex()}); updateCompBorrowIndex(cToken, borrowIndex); distributeBorrowerComp(cToken, borrower, borrowIndex); return uint256(Error.NO_ERROR); } /** * @notice Validates repayBorrow and reverts on rejection. May emit logs. * @param cToken Asset being repaid * @param payer The address repaying the borrow * @param borrower The address of the borrower * @param actualRepayAmount The amount of underlying being repaid */ function repayBorrowVerify( address cToken, address payer, address borrower, uint256 actualRepayAmount, uint256 borrowerIndex ) external override { // Shh - currently unused cToken; payer; borrower; actualRepayAmount; borrowerIndex; // Shh - we don't ever want this hook to be marked pure if (false) { maxAssets = maxAssets; } } /** * @notice Checks if the liquidation should be allowed to occur * @param cTokenBorrowed Asset which was borrowed by the borrower * @param cTokenCollateral Asset which was used as collateral and will be seized * @param liquidator The address repaying the borrow and seizing the collateral * @param borrower The address of the borrower * @param repayAmount The amount of underlying being repaid */ function liquidateBorrowAllowed( address cTokenBorrowed, address cTokenCollateral, address liquidator, address borrower, uint256 repayAmount ) external override returns (uint256) { // Shh - currently unused liquidator; if ( !markets[cTokenBorrowed].isListed || !markets[cTokenCollateral].isListed ) { return uint256(Error.MARKET_NOT_LISTED); } /* The borrower must have shortfall in order to be liquidatable */ (Error err,, uint256 shortfall) = getAccountLiquidityInternal(borrower); if (err != Error.NO_ERROR) { return uint256(err); } if (shortfall == 0) { return uint256(Error.INSUFFICIENT_SHORTFALL); } /* The liquidator may not repay more than what is allowed by the closeFactor */ uint256 borrowBalance = CToken(cTokenBorrowed).borrowBalanceStored(borrower); uint256 maxClose = mul_ScalarTruncate(Exp({mantissa: closeFactorMantissa}), borrowBalance); if (repayAmount > maxClose) { return uint256(Error.TOO_MUCH_REPAY); } return uint256(Error.NO_ERROR); } /** * @notice Validates liquidateBorrow and reverts on rejection. May emit logs. * @param cTokenBorrowed Asset which was borrowed by the borrower * @param cTokenCollateral Asset which was used as collateral and will be seized * @param liquidator The address repaying the borrow and seizing the collateral * @param borrower The address of the borrower * @param actualRepayAmount The amount of underlying being repaid */ function liquidateBorrowVerify( address cTokenBorrowed, address cTokenCollateral, address liquidator, address borrower, uint256 actualRepayAmount, uint256 seizeTokens ) external override { // Shh - currently unused cTokenBorrowed; cTokenCollateral; liquidator; borrower; actualRepayAmount; seizeTokens; // Shh - we don't ever want this hook to be marked pure if (false) { maxAssets = maxAssets; } } /** * @notice Checks if the seizing of assets should be allowed to occur * @param cTokenCollateral Asset which was used as collateral and will be seized * @param cTokenBorrowed Asset which was borrowed by the borrower * @param liquidator The address repaying the borrow and seizing the collateral * @param borrower The address of the borrower * @param seizeTokens The number of collateral tokens to seize */ function seizeAllowed( address cTokenCollateral, address cTokenBorrowed, address liquidator, address borrower, uint256 seizeTokens ) external override returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!seizeGuardianPaused, "seize is paused"); // Shh - currently unused seizeTokens; if ( !markets[cTokenCollateral].isListed || !markets[cTokenBorrowed].isListed ) { return uint256(Error.MARKET_NOT_LISTED); } if ( CToken(cTokenCollateral).comptroller() != CToken(cTokenBorrowed).comptroller() ) { return uint256(Error.COMPTROLLER_MISMATCH); } // Keep the flywheel moving updateCompSupplyIndex(cTokenCollateral); distributeSupplierComp(cTokenCollateral, borrower); distributeSupplierComp(cTokenCollateral, liquidator); return uint256(Error.NO_ERROR); } /** * @notice Validates seize and reverts on rejection. May emit logs. * @param cTokenCollateral Asset which was used as collateral and will be seized * @param cTokenBorrowed Asset which was borrowed by the borrower * @param liquidator The address repaying the borrow and seizing the collateral * @param borrower The address of the borrower * @param seizeTokens The number of collateral tokens to seize */ function seizeVerify( address cTokenCollateral, address cTokenBorrowed, address liquidator, address borrower, uint256 seizeTokens ) external override { // Shh - currently unused cTokenCollateral; cTokenBorrowed; liquidator; borrower; seizeTokens; // Shh - we don't ever want this hook to be marked pure if (false) { maxAssets = maxAssets; } } /** * @notice Checks if the account should be allowed to transfer tokens in the given market * @param cToken The market to verify the transfer against * @param src The account which sources the tokens * @param dst The account which receives the tokens * @param transferTokens The number of cTokens to transfer * @return 0 if the transfer is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol) */ function transferAllowed( address cToken, address src, address dst, uint256 transferTokens ) external override returns (uint256) { // Pausing is a very serious situation - we revert to sound the alarms require(!transferGuardianPaused, "transfer is paused"); // Currently the only consideration is whether or not // the src is allowed to redeem this many tokens uint256 allowed = redeemAllowedInternal(cToken, src, transferTokens); if (allowed != uint256(Error.NO_ERROR)) { return allowed; } // Keep the flywheel moving updateCompSupplyIndex(cToken); distributeSupplierComp(cToken, src); distributeSupplierComp(cToken, dst); return uint256(Error.NO_ERROR); } /** * @notice Validates transfer and reverts on rejection. May emit logs. * @param cToken Asset being transferred * @param src The account which sources the tokens * @param dst The account which receives the tokens * @param transferTokens The number of cTokens to transfer */ function transferVerify( address cToken, address src, address dst, uint256 transferTokens ) external override { // Shh - currently unused cToken; src; dst; transferTokens; // Shh - we don't ever want this hook to be marked pure if (false) { maxAssets = maxAssets; } } /** ** Liquidity/Liquidation Calculations ** */ /** * @dev Local vars for avoiding stack-depth limits in calculating account liquidity. * Note that `cTokenBalance` is the number of cTokens the account owns in the market, * whereas `borrowBalance` is the amount of underlying that the account has borrowed. */ struct AccountLiquidityLocalVars { uint256 sumCollateral; uint256 sumBorrowPlusEffects; uint256 cTokenBalance; uint256 borrowBalance; uint256 exchangeRateMantissa; uint256 oraclePriceMantissa; Exp collateralFactor; Exp exchangeRate; Exp oraclePrice; Exp tokensToDenom; } /** * @notice Determine the current account liquidity wrt collateral requirements * @return (possible error code (semi-opaque), account liquidity in excess of collateral requirements, * account shortfall below collateral requirements) */ function getAccountLiquidity(address account) public view returns (uint256, uint256, uint256) { (Error err, uint256 liquidity, uint256 shortfall) = getHypotheticalAccountLiquidityInternal( account, CToken(address(0)), 0, 0 ); return (uint256(err), liquidity, shortfall); } /** * @notice Determine the current account liquidity wrt collateral requirements * @return (possible error code, account liquidity in excess of collateral requirements, * account shortfall below collateral requirements) */ function getAccountLiquidityInternal(address account) internal view returns (Error, uint256, uint256) { return getHypotheticalAccountLiquidityInternal( account, CToken(address(0)), 0, 0 ); } /** * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed * @param cTokenModify The market to hypothetically redeem/borrow in * @param account The account to determine liquidity for * @param redeemTokens The number of tokens to hypothetically redeem * @param borrowAmount The amount of underlying to hypothetically borrow * @return (possible error code (semi-opaque), hypothetical account liquidity in excess of collateral requirements, * hypothetical account shortfall below collateral requirements) */ function getHypotheticalAccountLiquidity( address account, address cTokenModify, uint256 redeemTokens, uint256 borrowAmount ) public view returns (uint256, uint256, uint256) { (Error err, uint256 liquidity, uint256 shortfall) = getHypotheticalAccountLiquidityInternal( account, CToken(cTokenModify), redeemTokens, borrowAmount ); return (uint256(err), liquidity, shortfall); } /** * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed * @param cTokenModify The market to hypothetically redeem/borrow in * @param account The account to determine liquidity for * @param redeemTokens The number of tokens to hypothetically redeem * @param borrowAmount The amount of underlying to hypothetically borrow * @dev Note that we calculate the exchangeRateStored for each collateral cToken using stored data, * without calculating accumulated interest. * @return (possible error code, hypothetical account liquidity in excess of collateral requirements, * hypothetical account shortfall below collateral requirements) */ function getHypotheticalAccountLiquidityInternal( address account, CToken cTokenModify, uint256 redeemTokens, uint256 borrowAmount ) internal view returns (Error, uint256, uint256) { AccountLiquidityLocalVars memory vars; // Holds all our calculation results uint256 oErr; // For each asset the account is in CToken[] memory assets = accountAssets[account]; for (uint256 i = 0; i < assets.length; i++) { CToken asset = assets[i]; // Read the balances and exchange rate from the cToken ( oErr, vars.cTokenBalance, vars.borrowBalance, vars.exchangeRateMantissa ) = asset.getAccountSnapshot(account); if (oErr != 0) { // semi-opaque error code, we assume NO_ERROR == 0 is invariant between upgrades return (Error.SNAPSHOT_ERROR, 0, 0); } vars.collateralFactor = Exp({mantissa: markets[address(asset)].collateralFactorMantissa}); vars.exchangeRate = Exp({mantissa: vars.exchangeRateMantissa}); // Get the normalized price of the asset vars.oraclePriceMantissa = oracle.getUnderlyingPrice(asset); if (vars.oraclePriceMantissa == 0) { return (Error.PRICE_ERROR, 0, 0); } vars.oraclePrice = Exp({mantissa: vars.oraclePriceMantissa}); // Pre-compute a conversion factor from tokens -> ether (normalized price value) vars.tokensToDenom = mul_( mul_(vars.collateralFactor, vars.exchangeRate), vars.oraclePrice ); // sumCollateral += tokensToDenom * cTokenBalance vars.sumCollateral = mul_ScalarTruncateAddUInt( vars.tokensToDenom, vars.cTokenBalance, vars.sumCollateral ); // sumBorrowPlusEffects += oraclePrice * borrowBalance vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt( vars.oraclePrice, vars.borrowBalance, vars.sumBorrowPlusEffects ); // Calculate effects of interacting with cTokenModify if (asset == cTokenModify) { // redeem effect // sumBorrowPlusEffects += tokensToDenom * redeemTokens vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt( vars.tokensToDenom, redeemTokens, vars.sumBorrowPlusEffects ); // borrow effect // sumBorrowPlusEffects += oraclePrice * borrowAmount vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt( vars.oraclePrice, borrowAmount, vars.sumBorrowPlusEffects ); } } // These are safe, as the underflow condition is checked first if (vars.sumCollateral > vars.sumBorrowPlusEffects) { return ( Error.NO_ERROR, vars.sumCollateral - vars.sumBorrowPlusEffects, 0 ); } else { return ( Error.NO_ERROR, 0, vars.sumBorrowPlusEffects - vars.sumCollateral ); } } /** * @notice Calculate number of tokens of collateral asset to seize given an underlying amount * @dev Used in liquidation (called in cToken.liquidateBorrowFresh) * @param cTokenBorrowed The address of the borrowed cToken * @param cTokenCollateral The address of the collateral cToken * @param actualRepayAmount The amount of cTokenBorrowed underlying to convert into cTokenCollateral tokens * @return (errorCode, number of cTokenCollateral tokens to be seized in a liquidation) */ function liquidateCalculateSeizeTokens( address cTokenBorrowed, address cTokenCollateral, uint256 actualRepayAmount ) external view override returns (uint256, uint256) { /* Read oracle prices for borrowed and collateral markets */ uint256 priceBorrowedMantissa = oracle.getUnderlyingPrice(CToken(cTokenBorrowed)); uint256 priceCollateralMantissa = oracle.getUnderlyingPrice(CToken(cTokenCollateral)); if (priceBorrowedMantissa == 0 || priceCollateralMantissa == 0) { return (uint256(Error.PRICE_ERROR), 0); } /* * Get the exchange rate and calculate the number of collateral tokens to seize: * seizeAmount = actualRepayAmount * liquidationIncentive * priceBorrowed / priceCollateral * seizeTokens = seizeAmount / exchangeRate * = actualRepayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate) */ uint256 exchangeRateMantissa = CToken(cTokenCollateral).exchangeRateStored(); // Note: reverts on error uint256 seizeTokens; Exp memory numerator; Exp memory denominator; Exp memory ratio; numerator = mul_( Exp({mantissa: liquidationIncentiveMantissa}), Exp({mantissa: priceBorrowedMantissa}) ); denominator = mul_( Exp({mantissa: priceCollateralMantissa}), Exp({mantissa: exchangeRateMantissa}) ); ratio = div_(numerator, denominator); seizeTokens = mul_ScalarTruncate(ratio, actualRepayAmount); return (uint256(Error.NO_ERROR), seizeTokens); } /** ** Admin Functions ** */ /** * @notice Sets a new price oracle for the comptroller * @dev Admin function to set a new price oracle * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setPriceOracle(PriceOracle newOracle) public returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail( Error.UNAUTHORIZED, FailureInfo.SET_PRICE_ORACLE_OWNER_CHECK ); } // Track the old oracle for the comptroller PriceOracle oldOracle = oracle; // Set comptroller's oracle to newOracle oracle = newOracle; // Emit NewPriceOracle(oldOracle, newOracle) emit NewPriceOracle(oldOracle, newOracle); return uint256(Error.NO_ERROR); } /** * @notice Sets the closeFactor used when liquidating borrows * @dev Admin function to set closeFactor * @param newCloseFactorMantissa New close factor, scaled by 1e18 * @return uint 0=success, otherwise a failure */ function _setCloseFactor(uint256 newCloseFactorMantissa) external returns (uint256) { // Check caller is admin require(msg.sender == admin, "only admin can set close factor"); uint256 oldCloseFactorMantissa = closeFactorMantissa; closeFactorMantissa = newCloseFactorMantissa; emit NewCloseFactor(oldCloseFactorMantissa, closeFactorMantissa); return uint256(Error.NO_ERROR); } /** * @notice Sets the collateralFactor for a market * @dev Admin function to set per-market collateralFactor * @param cToken The market to set the factor on * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18 * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) */ function _setCollateralFactor( CToken cToken, uint256 newCollateralFactorMantissa ) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail( Error.UNAUTHORIZED, FailureInfo.SET_COLLATERAL_FACTOR_OWNER_CHECK ); } // Verify market is listed Market storage market = markets[address(cToken)]; if (!market.isListed) { return fail( Error.MARKET_NOT_LISTED, FailureInfo.SET_COLLATERAL_FACTOR_NO_EXISTS ); } Exp memory newCollateralFactorExp = Exp({mantissa: newCollateralFactorMantissa}); // Check collateral factor <= 0.9 Exp memory highLimit = Exp({mantissa: collateralFactorMaxMantissa}); if (lessThanExp(highLimit, newCollateralFactorExp)) { return fail( Error.INVALID_COLLATERAL_FACTOR, FailureInfo.SET_COLLATERAL_FACTOR_VALIDATION ); } // If collateral factor != 0, fail if price == 0 if ( newCollateralFactorMantissa != 0 && oracle.getUnderlyingPrice(cToken) == 0 ) { return fail( Error.PRICE_ERROR, FailureInfo.SET_COLLATERAL_FACTOR_WITHOUT_PRICE ); } // Set market's collateral factor to new collateral factor, remember old value uint256 oldCollateralFactorMantissa = market.collateralFactorMantissa; market.collateralFactorMantissa = newCollateralFactorMantissa; // Emit event with asset, old collateral factor, and new collateral factor emit NewCollateralFactor( cToken, oldCollateralFactorMantissa, newCollateralFactorMantissa ); return uint256(Error.NO_ERROR); } /** * @notice Sets liquidationIncentive * @dev Admin function to set liquidationIncentive * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18 * @return uint 0=success, otherwise a failure. (See ErrorReporter for details) */ function _setLiquidationIncentive(uint256 newLiquidationIncentiveMantissa) external returns (uint256) { // Check caller is admin if (msg.sender != admin) { return fail( Error.UNAUTHORIZED, FailureInfo.SET_LIQUIDATION_INCENTIVE_OWNER_CHECK ); } // Save current value for use in log uint256 oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa; // Set liquidation incentive to new incentive liquidationIncentiveMantissa = newLiquidationIncentiveMantissa; // Emit event with old incentive, new incentive emit NewLiquidationIncentive( oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa ); return uint256(Error.NO_ERROR); } /** * @notice Add the market to the markets mapping and set it as listed * @dev Admin function to set isListed and add support for the market * @param cToken The address of the market (token) to list * @return uint 0=success, otherwise a failure. (See enum Error for details) */ function _supportMarket(CToken cToken) external returns (uint256) { if (msg.sender != admin) { return fail(Error.UNAUTHORIZED, FailureInfo.SUPPORT_MARKET_OWNER_CHECK); } if (markets[address(cToken)].isListed) { return fail( Error.MARKET_ALREADY_LISTED, FailureInfo.SUPPORT_MARKET_EXISTS ); } cToken.isCToken(); // Sanity check to make sure its really a CToken // Note that isComped is not in active use anymore Market storage market = markets[address(cToken)]; market.isListed = true; market.isComped = false; market.collateralFactorMantissa = 0; _addMarketInternal(address(cToken)); emit MarketListed(cToken); return uint256(Error.NO_ERROR); } function _addMarketInternal(address cToken) internal { for (uint256 i = 0; i < allMarkets.length; i++) { require(allMarkets[i] != CToken(cToken), "market already added"); } allMarkets.push(CToken(cToken)); } /** * @notice Set the given borrow caps for the given cToken markets. Borrowing that brings total borrows to or above borrow cap will revert. * @dev Admin or borrowCapGuardian function to set the borrow caps. A borrow cap of 0 corresponds to unlimited borrowing. * @param cTokens The addresses of the markets (tokens) to change the borrow caps for * @param newBorrowCaps The new borrow cap values in underlying to be set. A value of 0 corresponds to unlimited borrowing. */ function _setMarketBorrowCaps( CToken[] calldata cTokens, uint256[] calldata newBorrowCaps ) external { require( msg.sender == admin || msg.sender == borrowCapGuardian, "only admin or borrow cap guardian can set borrow caps" ); uint256 numMarkets = cTokens.length; uint256 numBorrowCaps = newBorrowCaps.length; require(numMarkets != 0 && numMarkets == numBorrowCaps, "invalid input"); for (uint256 i = 0; i < numMarkets; i++) { borrowCaps[address(cTokens[i])] = newBorrowCaps[i]; emit NewBorrowCap(cTokens[i], newBorrowCaps[i]); } } /** * @notice Admin function to change the Borrow Cap Guardian * @param newBorrowCapGuardian The address of the new Borrow Cap Guardian */ function _setBorrowCapGuardian(address newBorrowCapGuardian) external { require(msg.sender == admin, "only admin can set borrow cap guardian"); // Save current value for inclusion in log address oldBorrowCapGuardian = borrowCapGuardian; // Store borrowCapGuardian with value newBorrowCapGuardian borrowCapGuardian = newBorrowCapGuardian; // Emit NewBorrowCapGuardian(OldBorrowCapGuardian, NewBorrowCapGuardian) emit NewBorrowCapGuardian(oldBorrowCapGuardian, newBorrowCapGuardian); } /** * @notice Admin function to change the Pause Guardian * @param newPauseGuardian The address of the new Pause Guardian * @return uint 0=success, otherwise a failure. (See enum Error for details) */ function _setPauseGuardian(address newPauseGuardian) public returns (uint256) { if (msg.sender != admin) { return fail( Error.UNAUTHORIZED, FailureInfo.SET_PAUSE_GUARDIAN_OWNER_CHECK ); } // Save current value for inclusion in log address oldPauseGuardian = pauseGuardian; // Store pauseGuardian with value newPauseGuardian pauseGuardian = newPauseGuardian; // Emit NewPauseGuardian(OldPauseGuardian, NewPauseGuardian) emit NewPauseGuardian(oldPauseGuardian, pauseGuardian); return uint256(Error.NO_ERROR); } function _setMintPaused(CToken cToken, bool state) public returns (bool) { require( markets[address(cToken)].isListed, "cannot pause a market that is not listed" ); require( msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause" ); require(msg.sender == admin || state == true, "only admin can unpause"); mintGuardianPaused[address(cToken)] = state; emit ActionPaused(cToken, "Mint", state); return state; } function _setBorrowPaused(CToken cToken, bool state) public returns (bool) { require( markets[address(cToken)].isListed, "cannot pause a market that is not listed" ); require( msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause" ); require(msg.sender == admin || state == true, "only admin can unpause"); borrowGuardianPaused[address(cToken)] = state; emit ActionPaused(cToken, "Borrow", state); return state; } function _setTransferPaused(bool state) public returns (bool) { require( msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause" ); require(msg.sender == admin || state == true, "only admin can unpause"); transferGuardianPaused = state; emit ActionPaused("Transfer", state); return state; } function _setSeizePaused(bool state) public returns (bool) { require( msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause" ); require(msg.sender == admin || state == true, "only admin can unpause"); seizeGuardianPaused = state; emit ActionPaused("Seize", state); return state; } function _become(Unitroller unitroller) public { require( msg.sender == unitroller.admin(), "only unitroller admin can change brains" ); require( unitroller._acceptImplementation() == 0, "change not authorized" ); } /** * @notice Checks caller is admin, or this contract is becoming the new implementation */ function adminOrInitializing() internal view returns (bool) { return msg.sender == admin || msg.sender == comptrollerImplementation; } /** ** Comp Distribution ** */ /** * @notice Set COMP speed for a single market * @param cToken The market whose COMP speed to update * @param compSpeed New COMP speed for market */ function setCompSpeedInternal(CToken cToken, uint256 compSpeed) internal { uint256 currentCompSpeed = compSpeeds[address(cToken)]; if (currentCompSpeed != 0) { // note that COMP speed could be set to 0 to halt liquidity rewards for a market Exp memory borrowIndex = Exp({mantissa: cToken.borrowIndex()}); updateCompSupplyIndex(address(cToken)); updateCompBorrowIndex(address(cToken), borrowIndex); } else if (compSpeed != 0) { // Add the COMP market Market storage market = markets[address(cToken)]; require(market.isListed == true, "comp market is not listed"); if ( compSupplyState[address(cToken)].index == 0 && compSupplyState[address(cToken)].block == 0 ) { compSupplyState[address(cToken)] = CompMarketState({ index: compInitialIndex, block: safe32( getBlockNumber(), "block number exceeds 32 bits" ) }); } if ( compBorrowState[address(cToken)].index == 0 && compBorrowState[address(cToken)].block == 0 ) { compBorrowState[address(cToken)] = CompMarketState({ index: compInitialIndex, block: safe32( getBlockNumber(), "block number exceeds 32 bits" ) }); } } if (currentCompSpeed != compSpeed) { compSpeeds[address(cToken)] = compSpeed; emit CompSpeedUpdated(cToken, compSpeed); } } /** * @notice Accrue COMP to the market by updating the supply index * @param cToken The market whose supply index to update */ function updateCompSupplyIndex(address cToken) internal { CompMarketState storage supplyState = compSupplyState[cToken]; uint256 supplySpeed = compSpeeds[cToken]; uint256 blockNumber = getBlockNumber(); uint256 deltaBlocks = sub_(blockNumber, uint256(supplyState.block)); if (deltaBlocks > 0 && supplySpeed > 0) { uint256 supplyTokens = CToken(cToken).totalSupply(); uint256 compAccrued = mul_(deltaBlocks, supplySpeed); Double memory ratio = supplyTokens > 0 ? fraction(compAccrued, supplyTokens) : Double({mantissa: 0}); Double memory index = add_(Double({mantissa: supplyState.index}), ratio); compSupplyState[cToken] = CompMarketState({ index: safe224( index.mantissa, "new index exceeds 224 bits" ), block: safe32( blockNumber, "block number exceeds 32 bits" ) }); } else if (deltaBlocks > 0) { supplyState.block = safe32(blockNumber, "block number exceeds 32 bits"); } } /** * @notice Accrue COMP to the market by updating the borrow index * @param cToken The market whose borrow index to update */ function updateCompBorrowIndex(address cToken, Exp memory marketBorrowIndex) internal { CompMarketState storage borrowState = compBorrowState[cToken]; uint256 borrowSpeed = compSpeeds[cToken]; uint256 blockNumber = getBlockNumber(); uint256 deltaBlocks = sub_(blockNumber, uint256(borrowState.block)); if (deltaBlocks > 0 && borrowSpeed > 0) { uint256 borrowAmount = div_(CToken(cToken).totalBorrows(), marketBorrowIndex); uint256 compAccrued = mul_(deltaBlocks, borrowSpeed); Double memory ratio = borrowAmount > 0 ? fraction(compAccrued, borrowAmount) : Double({mantissa: 0}); Double memory index = add_(Double({mantissa: borrowState.index}), ratio); compBorrowState[cToken] = CompMarketState({ index: safe224( index.mantissa, "new index exceeds 224 bits" ), block: safe32( blockNumber, "block number exceeds 32 bits" ) }); } else if (deltaBlocks > 0) { borrowState.block = safe32(blockNumber, "block number exceeds 32 bits"); } } /** * @notice Calculate COMP accrued by a supplier and possibly transfer it to them * @param cToken The market in which the supplier is interacting * @param supplier The address of the supplier to distribute COMP to */ function distributeSupplierComp(address cToken, address supplier) internal { CompMarketState storage supplyState = compSupplyState[cToken]; Double memory supplyIndex = Double({mantissa: supplyState.index}); Double memory supplierIndex = Double({mantissa: compSupplierIndex[cToken][supplier]}); compSupplierIndex[cToken][supplier] = supplyIndex.mantissa; if (supplierIndex.mantissa == 0 && supplyIndex.mantissa > 0) { supplierIndex.mantissa = compInitialIndex; } Double memory deltaIndex = sub_(supplyIndex, supplierIndex); uint256 supplierTokens = CToken(cToken).balanceOf(supplier); uint256 supplierDelta = mul_(supplierTokens, deltaIndex); uint256 supplierAccrued = add_(compAccrued[supplier], supplierDelta); compAccrued[supplier] = supplierAccrued; emit DistributedSupplierComp( CToken(cToken), supplier, supplierDelta, supplyIndex.mantissa ); } /** * @notice Calculate COMP accrued by a borrower and possibly transfer it to them * @dev Borrowers will not begin to accrue until after the first interaction with the protocol. * @param cToken The market in which the borrower is interacting * @param borrower The address of the borrower to distribute COMP to */ function distributeBorrowerComp( address cToken, address borrower, Exp memory marketBorrowIndex ) internal { CompMarketState storage borrowState = compBorrowState[cToken]; Double memory borrowIndex = Double({mantissa: borrowState.index}); Double memory borrowerIndex = Double({mantissa: compBorrowerIndex[cToken][borrower]}); compBorrowerIndex[cToken][borrower] = borrowIndex.mantissa; if (borrowerIndex.mantissa > 0) { Double memory deltaIndex = sub_(borrowIndex, borrowerIndex); uint256 borrowerAmount = div_( CToken(cToken).borrowBalanceStored(borrower), marketBorrowIndex ); uint256 borrowerDelta = mul_(borrowerAmount, deltaIndex); uint256 borrowerAccrued = add_(compAccrued[borrower], borrowerDelta); compAccrued[borrower] = borrowerAccrued; emit DistributedBorrowerComp( CToken(cToken), borrower, borrowerDelta, borrowIndex.mantissa ); } } /** * @notice Calculate additional accrued COMP for a contributor since last accrual * @param contributor The address to calculate contributor rewards for */ function updateContributorRewards(address contributor) public { uint256 compSpeed = compContributorSpeeds[contributor]; uint256 blockNumber = getBlockNumber(); uint256 deltaBlocks = sub_(blockNumber, lastContributorBlock[contributor]); if (deltaBlocks > 0 && compSpeed > 0) { uint256 newAccrued = mul_(deltaBlocks, compSpeed); uint256 contributorAccrued = add_(compAccrued[contributor], newAccrued); compAccrued[contributor] = contributorAccrued; lastContributorBlock[contributor] = blockNumber; } } /** * @notice Claim all the comp accrued by holder in all markets * @param holder The address to claim COMP for */ function claimComp(address holder) public { return claimComp(holder, allMarkets); } /** * @notice Claim all the comp accrued by holder in the specified markets * @param holder The address to claim COMP for * @param cTokens The list of markets to claim COMP in */ function claimComp(address holder, CToken[] memory cTokens) public { address[] memory holders = new address[](1); holders[0] = holder; claimComp(holders, cTokens, true, true); } /** * @notice Claim all comp accrued by the holders * @param holders The addresses to claim COMP for * @param cTokens The list of markets to claim COMP in * @param borrowers Whether or not to claim COMP earned by borrowing * @param suppliers Whether or not to claim COMP earned by supplying */ function claimComp( address[] memory holders, CToken[] memory cTokens, bool borrowers, bool suppliers ) public { for (uint256 i = 0; i < cTokens.length; i++) { CToken cToken = cTokens[i]; require(markets[address(cToken)].isListed, "market must be listed"); if (borrowers == true) { Exp memory borrowIndex = Exp({mantissa: cToken.borrowIndex()}); updateCompBorrowIndex(address(cToken), borrowIndex); for (uint256 j = 0; j < holders.length; j++) { distributeBorrowerComp( address(cToken), holders[j], borrowIndex ); compAccrued[holders[j]] = grantCompInternal(holders[j], compAccrued[holders[j]]); } } if (suppliers == true) { updateCompSupplyIndex(address(cToken)); for (uint256 j = 0; j < holders.length; j++) { distributeSupplierComp(address(cToken), holders[j]); compAccrued[holders[j]] = grantCompInternal(holders[j], compAccrued[holders[j]]); } } } } /** * @notice Transfer COMP to the user * @dev Note: If there is not enough COMP, we do not perform the transfer all. * @param user The address of the user to transfer COMP to * @param amount The amount of COMP to (possibly) transfer * @return The amount of COMP which was NOT transferred to the user */ function grantCompInternal(address user, uint256 amount) internal returns (uint256) { Comp comp = Comp(getCompAddress()); uint256 compRemaining = comp.balanceOf(address(this)); if (amount > 0 && amount <= compRemaining) { comp.transfer(user, amount); return 0; } return amount; } /** ** Comp Distribution Admin ** */ /** * @notice Transfer COMP to the recipient * @dev Note: If there is not enough COMP, we do not perform the transfer all. * @param recipient The address of the recipient to transfer COMP to * @param amount The amount of COMP to (possibly) transfer */ function _grantComp(address recipient, uint256 amount) public { require(adminOrInitializing(), "only admin can grant comp"); uint256 amountLeft = grantCompInternal(recipient, amount); require(amountLeft == 0, "insufficient comp for grant"); emit CompGranted(recipient, amount); } /** * @notice Set COMP speed for a single market * @param cToken The market whose COMP speed to update * @param compSpeed New COMP speed for market */ function _setCompSpeed(CToken cToken, uint256 compSpeed) public { require(adminOrInitializing(), "only admin can set comp speed"); setCompSpeedInternal(cToken, compSpeed); } /** * @notice Set COMP speed for a single contributor * @param contributor The contributor whose COMP speed to update * @param compSpeed New COMP speed for contributor */ function _setContributorCompSpeed(address contributor, uint256 compSpeed) public { require(adminOrInitializing(), "only admin can set comp speed"); // note that COMP speed could be set to 0 to halt liquidity rewards for a contributor updateContributorRewards(contributor); if (compSpeed == 0) { // release storage delete lastContributorBlock[contributor]; } else { lastContributorBlock[contributor] = getBlockNumber(); } compContributorSpeeds[contributor] = compSpeed; emit ContributorCompSpeedUpdated(contributor, compSpeed); } /** * @notice Return all of the markets * @dev The automatic getter may be used to access an individual market. * @return The list of market addresses */ function getAllMarkets() public view returns (CToken[] memory) { return allMarkets; } function getBlockNumber() public view returns (uint256) { return block.number; } /** * @notice Return the address of the COMP token * @return The address of COMP */ function getCompAddress() public view returns (address) { return 0xc00e94Cb662C3520282E6f5717214004A7f26888; } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; abstract contract ComptrollerInterface { /// @notice Indicator that this is a Comptroller contract (for inspection) bool public constant isComptroller = true; /** ** Assets You Are In ** */ function enterMarkets(address[] calldata cTokens) external virtual returns (uint256[] memory); function exitMarket(address cToken) external virtual returns (uint256); /** ** Policy Hooks ** */ function mintAllowed(address cToken, address minter, uint256 mintAmount) external virtual returns (uint256); function mintVerify( address cToken, address minter, uint256 mintAmount, uint256 mintTokens ) external virtual; function redeemAllowed( address cToken, address redeemer, uint256 redeemTokens ) external virtual returns (uint256); function redeemVerify( address cToken, address redeemer, uint256 redeemAmount, uint256 redeemTokens ) external virtual; function borrowAllowed( address cToken, address borrower, uint256 borrowAmount ) external virtual returns (uint256); function borrowVerify( address cToken, address borrower, uint256 borrowAmount ) external virtual; function repayBorrowAllowed( address cToken, address payer, address borrower, uint256 repayAmount ) external virtual returns (uint256); function repayBorrowVerify( address cToken, address payer, address borrower, uint256 repayAmount, uint256 borrowerIndex ) external virtual; function liquidateBorrowAllowed( address cTokenBorrowed, address cTokenCollateral, address liquidator, address borrower, uint256 repayAmount ) external virtual returns (uint256); function liquidateBorrowVerify( address cTokenBorrowed, address cTokenCollateral, address liquidator, address borrower, uint256 repayAmount, uint256 seizeTokens ) external virtual; function seizeAllowed( address cTokenCollateral, address cTokenBorrowed, address liquidator, address borrower, uint256 seizeTokens ) external virtual returns (uint256); function seizeVerify( address cTokenCollateral, address cTokenBorrowed, address liquidator, address borrower, uint256 seizeTokens ) external virtual; function transferAllowed( address cToken, address src, address dst, uint256 transferTokens ) external virtual returns (uint256); function transferVerify( address cToken, address src, address dst, uint256 transferTokens ) external virtual; /** ** Liquidity/Liquidation Calculations ** */ function liquidateCalculateSeizeTokens( address cTokenBorrowed, address cTokenCollateral, uint256 repayAmount ) external view virtual returns (uint256, uint256); }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./CToken.sol"; import "./PriceOracle.sol"; contract UnitrollerAdminStorage { /** * @notice Administrator for this contract */ address public admin; /** * @notice Pending administrator for this contract */ address public pendingAdmin; /** * @notice Active brains of Unitroller */ address public comptrollerImplementation; /** * @notice Pending brains of Unitroller */ address public pendingComptrollerImplementation; } contract ComptrollerV1Storage is UnitrollerAdminStorage { /** * @notice Oracle which gives the price of any given asset */ PriceOracle public oracle; /** * @notice Multiplier used to calculate the maximum repayAmount when liquidating a borrow */ uint256 public closeFactorMantissa; /** * @notice Multiplier representing the discount on collateral that a liquidator receives */ uint256 public liquidationIncentiveMantissa; /** * @notice Max number of assets a single account can participate in (borrow or use as collateral) */ uint256 public maxAssets; /** * @notice Per-account mapping of "assets you are in", capped by maxAssets */ mapping(address => CToken[]) public accountAssets; } contract ComptrollerV2Storage is ComptrollerV1Storage { struct Market { // Whether or not this market is listed bool isListed; // Multiplier representing the most one can borrow against their collateral in this market. // For instance, 0.9 to allow borrowing 90% of collateral value. // Must be between 0 and 1, and stored as a mantissa. uint256 collateralFactorMantissa; // Per-market mapping of "accounts in this asset" mapping(address => bool) accountMembership; // Whether or not this market receives COMP bool isComped; } /** * @notice Official mapping of cTokens -> Market metadata * @dev Used e.g. to determine if a market is supported */ mapping(address => Market) public markets; /** * @notice The Pause Guardian can pause certain actions as a safety mechanism. * Actions which allow users to remove their own assets cannot be paused. * Liquidation / seizing / transfer can only be paused globally, not by market. */ address public pauseGuardian; bool public _mintGuardianPaused; bool public _borrowGuardianPaused; bool public transferGuardianPaused; bool public seizeGuardianPaused; mapping(address => bool) public mintGuardianPaused; mapping(address => bool) public borrowGuardianPaused; } contract ComptrollerV3Storage is ComptrollerV2Storage { struct CompMarketState { // The market's last updated compBorrowIndex or compSupplyIndex uint224 index; // The block number the index was last updated at uint32 block; } /// @notice A list of all markets CToken[] public allMarkets; /// @notice The rate at which the flywheel distributes COMP, per block uint256 public compRate; /// @notice The portion of compRate that each market currently receives mapping(address => uint256) public compSpeeds; /// @notice The COMP market supply state for each market mapping(address => CompMarketState) public compSupplyState; /// @notice The COMP market borrow state for each market mapping(address => CompMarketState) public compBorrowState; /// @notice The COMP borrow index for each market for each supplier as of the last time they accrued COMP mapping(address => mapping(address => uint256)) public compSupplierIndex; /// @notice The COMP borrow index for each market for each borrower as of the last time they accrued COMP mapping(address => mapping(address => uint256)) public compBorrowerIndex; /// @notice The COMP accrued but not yet transferred to each user mapping(address => uint256) public compAccrued; } contract ComptrollerV4Storage is ComptrollerV3Storage { // @notice The borrowCapGuardian can set borrowCaps to any number for any market. Lowering the borrow cap could disable borrowing on the given market. address public borrowCapGuardian; // @notice Borrow caps enforced by borrowAllowed for each cToken address. Defaults to zero which corresponds to unlimited borrowing. mapping(address => uint256) public borrowCaps; } contract ComptrollerV5Storage is ComptrollerV4Storage { /// @notice The portion of COMP that each contributor receives per block mapping(address => uint256) public compContributorSpeeds; /// @notice Last block at which a contributor's COMP rewards have been allocated mapping(address => uint256) public lastContributorBlock; } contract ComptrollerV6Storage is ComptrollerV5Storage { /// @notice The rate at which comp is distributed to the corresponding borrow market (per block) mapping(address => uint256) public compBorrowSpeeds; /// @notice The rate at which comp is distributed to the corresponding supply market (per block) mapping(address => uint256) public compSupplySpeeds; } contract ComptrollerV7Storage is ComptrollerV6Storage { /// @notice Flag indicating whether the function to fix COMP accruals has been executed (RE: proposal 62 bug) bool public proposal65FixExecuted; /// @notice Accounting storage mapping account addresses to how much COMP they owe the protocol. mapping(address => uint256) public compReceivable; }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./ComptrollerInterface.sol"; import "./CTokenInterfaces.sol"; import "./ErrorReporter.sol"; import "./EIP20Interface.sol"; import "./InterestRateModel.sol"; import "./ExponentialNoError.sol"; /** * @title Compound's CToken Contract * @notice Abstract base for CTokens * @author Compound */ abstract contract CToken is CTokenInterface, ExponentialNoError, TokenErrorReporter { /** * @notice Initialize the money market * @param comptroller_ The address of the Comptroller * @param interestRateModel_ The address of the interest rate model * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18 * @param name_ EIP-20 name of this token * @param symbol_ EIP-20 symbol of this token * @param decimals_ EIP-20 decimal precision of this token */ function initialize(ComptrollerInterface comptroller_, InterestRateModel interestRateModel_, uint initialExchangeRateMantissa_, string memory name_, string memory symbol_, uint8 decimals_ ) public { require(msg.sender == admin, "only admin may initialize the market"); require(accrualBlockNumber == 0 && borrowIndex == 0, "market may only be initialized once"); // Set initial exchange rate initialExchangeRateMantissa = initialExchangeRateMantissa_; require(initialExchangeRateMantissa > 0, "initial exchange rate must be greater than zero."); // Set the comptroller uint err = _setComptroller(comptroller_); require(err == NO_ERROR, "setting comptroller failed"); // Initialize block number and borrow index (block number mocks depend on comptroller being set) accrualBlockNumber = getBlockNumber(); borrowIndex = mantissaOne; // Set the interest rate model (depends on block number / borrow index) err = _setInterestRateModelFresh(interestRateModel_); require(err == NO_ERROR, "setting interest rate model failed"); name = name_; symbol = symbol_; decimals = decimals_; // The counter starts true to prevent changing it from zero to non-zero (i.e. smaller cost/refund) _notEntered = true; } /** * @notice Transfer `tokens` tokens from `src` to `dst` by `spender` * @dev Called by both `transfer` and `transferFrom` internally * @param spender The address of the account performing the transfer * @param src The address of the source account * @param dst The address of the destination account * @param tokens The number of tokens to transfer * @return 0 if the transfer succeeded, else revert */ function transferTokens(address spender, address src, address dst, uint tokens) internal returns (uint) { /* Fail if transfer not allowed */ uint allowed = comptroller.transferAllowed(address(this), src, dst, tokens); if (allowed != 0) { revert TransferComptrollerRejection(allowed); } /* Do not allow self-transfers */ if (src == dst) { revert TransferNotAllowed(); } /* Get the allowance, infinite for the account owner */ uint startingAllowance = 0; if (spender == src) { startingAllowance = type(uint).max; } else { startingAllowance = transferAllowances[src][spender]; } /* Do the calculations, checking for {under,over}flow */ uint allowanceNew = startingAllowance - tokens; uint srcTokensNew = accountTokens[src] - tokens; uint dstTokensNew = accountTokens[dst] + tokens; ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) accountTokens[src] = srcTokensNew; accountTokens[dst] = dstTokensNew; /* Eat some of the allowance (if necessary) */ if (startingAllowance != type(uint).max) { transferAllowances[src][spender] = allowanceNew; } /* We emit a Transfer event */ emit Transfer(src, dst, tokens); // unused function // comptroller.transferVerify(address(this), src, dst, tokens); return NO_ERROR; } /** * @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) override external nonReentrant returns (bool) { return transferTokens(msg.sender, msg.sender, dst, amount) == NO_ERROR; } /** * @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) override external nonReentrant returns (bool) { return transferTokens(msg.sender, src, dst, amount) == NO_ERROR; } /** * @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 (uint256.max means infinite) * @return Whether or not the approval succeeded */ function approve(address spender, uint256 amount) override external returns (bool) { address src = msg.sender; transferAllowances[src][spender] = amount; emit Approval(src, spender, amount); return true; } /** * @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) override external view returns (uint256) { return transferAllowances[owner][spender]; } /** * @notice Get the token balance of the `owner` * @param owner The address of the account to query * @return The number of tokens owned by `owner` */ function balanceOf(address owner) override external view returns (uint256) { return accountTokens[owner]; } /** * @notice Get the underlying balance of the `owner` * @dev This also accrues interest in a transaction * @param owner The address of the account to query * @return The amount of underlying owned by `owner` */ function balanceOfUnderlying(address owner) override external returns (uint) { Exp memory exchangeRate = Exp({mantissa: exchangeRateCurrent()}); return mul_ScalarTruncate(exchangeRate, accountTokens[owner]); } /** * @notice Get a snapshot of the account's balances, and the cached exchange rate * @dev This is used by comptroller to more efficiently perform liquidity checks. * @param account Address of the account to snapshot * @return (possible error, token balance, borrow balance, exchange rate mantissa) */ function getAccountSnapshot(address account) override external view returns (uint, uint, uint, uint) { return ( NO_ERROR, accountTokens[account], borrowBalanceStoredInternal(account), exchangeRateStoredInternal() ); } /** * @dev Function to simply retrieve block number * This exists mainly for inheriting test contracts to stub this result. */ function getBlockNumber() virtual internal view returns (uint) { return block.number; } /** * @notice Returns the current per-block borrow interest rate for this cToken * @return The borrow interest rate per block, scaled by 1e18 */ function borrowRatePerBlock() override view external returns (uint) { return interestRateModel.getBorrowRate(getCashPrior(), totalBorrows, totalReserves); } /** * @notice Returns the current per-block supply interest rate for this cToken * @return The supply interest rate per block, scaled by 1e18 */ function supplyRatePerBlock() override view external returns (uint) { return interestRateModel.getSupplyRate(getCashPrior(), totalBorrows, totalReserves, reserveFactorMantissa); } /** * @notice Returns the current total borrows plus accrued interest * @return The total borrows with interest */ function totalBorrowsCurrent() override external nonReentrant returns (uint) { accrueInterest(); return totalBorrows; } /** * @notice Accrue interest to updated borrowIndex and then calculate account's borrow balance using the updated borrowIndex * @param account The address whose balance should be calculated after updating borrowIndex * @return The calculated balance */ function borrowBalanceCurrent(address account) override external nonReentrant returns (uint) { accrueInterest(); return borrowBalanceStored(account); } /** * @notice Return the borrow balance of account based on stored data * @param account The address whose balance should be calculated * @return The calculated balance */ function borrowBalanceStored(address account) override public view returns (uint) { return borrowBalanceStoredInternal(account); } /** * @notice Return the borrow balance of account based on stored data * @param account The address whose balance should be calculated * @return (error code, the calculated balance or 0 if error code is non-zero) */ function borrowBalanceStoredInternal(address account) internal view returns (uint) { /* Get borrowBalance and borrowIndex */ BorrowSnapshot storage borrowSnapshot = accountBorrows[account]; /* If borrowBalance = 0 then borrowIndex is likely also 0. * Rather than failing the calculation with a division by 0, we immediately return 0 in this case. */ if (borrowSnapshot.principal == 0) { return 0; } /* Calculate new borrow balance using the interest index: * recentBorrowBalance = borrower.borrowBalance * market.borrowIndex / borrower.borrowIndex */ uint principalTimesIndex = borrowSnapshot.principal * borrowIndex; return principalTimesIndex / borrowSnapshot.interestIndex; } /** * @notice Accrue interest then return the up-to-date exchange rate * @return Calculated exchange rate scaled by 1e18 */ function exchangeRateCurrent() override public nonReentrant returns (uint) { accrueInterest(); return exchangeRateStored(); } /** * @notice Calculates the exchange rate from the underlying to the CToken * @dev This function does not accrue interest before calculating the exchange rate * @return Calculated exchange rate scaled by 1e18 */ function exchangeRateStored() override public view returns (uint) { return exchangeRateStoredInternal(); } /** * @notice Calculates the exchange rate from the underlying to the CToken * @dev This function does not accrue efore calculating the exchange rate * @return calculated exchange rate scaled by 1e18 */ function exchangeRateStoredInternal() virtual internal view returns (uint) { uint _totalSupply = totalSupply; if (_totalSupply == 0) { /* * If there are no tokens minted: * exchangeRate = initialExchangeRate */ return initialExchangeRateMantissa; } else { /* * Otherwise: * exchangeRate = (totalCash + totalBorrows - totalReserves) / totalSupply */ uint totalCash = getCashPrior(); uint cashPlusBorrowsMinusReserves = totalCash + totalBorrows - totalReserves; uint exchangeRate = cashPlusBorrowsMinusReserves * expScale / _totalSupply; return exchangeRate; } } /** * @notice Get cash balance of this cToken in the underlying asset * @return The quantity of underlying asset owned by this contract */ function getCash() override external view returns (uint) { return getCashPrior(); } /** * @notice Applies accrued interest to total borrows and reserves * @dev This calculates interest accrued from the last checkpointed block * up to the current block and writes new checkpoint to storage. */ function accrueInterest() virtual override public returns (uint) { /* Remember the initial block number */ uint currentBlockNumber = getBlockNumber(); uint accrualBlockNumberPrior = accrualBlockNumber; /* Short-circuit accumulating 0 interest */ if (accrualBlockNumberPrior == currentBlockNumber) { return NO_ERROR; } /* Read the previous values out of storage */ uint cashPrior = getCashPrior(); uint borrowsPrior = totalBorrows; uint reservesPrior = totalReserves; uint borrowIndexPrior = borrowIndex; /* Calculate the current borrow interest rate */ uint borrowRateMantissa = interestRateModel.getBorrowRate(cashPrior, borrowsPrior, reservesPrior); require(borrowRateMantissa <= borrowRateMaxMantissa, "borrow rate is absurdly high"); /* Calculate the number of blocks elapsed since the last accrual */ uint blockDelta = currentBlockNumber - accrualBlockNumberPrior; /* * Calculate the interest accumulated into borrows and reserves and the new index: * simpleInterestFactor = borrowRate * blockDelta * interestAccumulated = simpleInterestFactor * totalBorrows * totalBorrowsNew = interestAccumulated + totalBorrows * totalReservesNew = interestAccumulated * reserveFactor + totalReserves * borrowIndexNew = simpleInterestFactor * borrowIndex + borrowIndex */ Exp memory simpleInterestFactor = mul_(Exp({mantissa: borrowRateMantissa}), blockDelta); uint interestAccumulated = mul_ScalarTruncate(simpleInterestFactor, borrowsPrior); uint totalBorrowsNew = interestAccumulated + borrowsPrior; uint totalReservesNew = mul_ScalarTruncateAddUInt(Exp({mantissa: reserveFactorMantissa}), interestAccumulated, reservesPrior); uint borrowIndexNew = mul_ScalarTruncateAddUInt(simpleInterestFactor, borrowIndexPrior, borrowIndexPrior); ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* We write the previously calculated values into storage */ accrualBlockNumber = currentBlockNumber; borrowIndex = borrowIndexNew; totalBorrows = totalBorrowsNew; totalReserves = totalReservesNew; /* We emit an AccrueInterest event */ emit AccrueInterest(cashPrior, interestAccumulated, borrowIndexNew, totalBorrowsNew); return NO_ERROR; } /** * @notice Sender supplies assets into the market and receives cTokens in exchange * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param mintAmount The amount of the underlying asset to supply */ function mintInternal(uint mintAmount) internal nonReentrant { accrueInterest(); // mintFresh emits the actual Mint event if successful and logs on errors, so we don't need to mintFresh(msg.sender, mintAmount); } /** * @notice User supplies assets into the market and receives cTokens in exchange * @dev Assumes interest has already been accrued up to the current block * @param minter The address of the account which is supplying the assets * @param mintAmount The amount of the underlying asset to supply */ function mintFresh(address minter, uint mintAmount) internal virtual { /* Fail if mint not allowed */ uint allowed = comptroller.mintAllowed(address(this), minter, mintAmount); if (allowed != 0) { revert MintComptrollerRejection(allowed); } /* Verify market's block number equals current block number */ if (accrualBlockNumber != getBlockNumber()) { revert MintFreshnessCheck(); } Exp memory exchangeRate = Exp({mantissa: exchangeRateStoredInternal()}); ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* * We call `doTransferIn` for the minter and the mintAmount. * Note: The cToken must handle variations between ERC-20 and ETH underlying. * `doTransferIn` reverts if anything goes wrong, since we can't be sure if * side-effects occurred. The function returns the amount ac tually transferred, * in case of a fee. On success, the cToken holds an additional `actualMintAmount` * of cash. */ uint actualMintAmount = doTransferIn(minter, mintAmount); /* * We get the current exchange rate and calculate the number of cTokens to be minted: * mintTokens = actualMintAmount / exchangeRate */ uint mintTokens = div_(actualMintAmount, exchangeRate); /* * We calculate the new total supply of cTokens and minter token balance, checking for overflow: * totalSupplyNew = totalSupply + mintTokens * accountTokensNew = accountTokens[minter] + mintTokens * And write them into storage */ totalSupply = totalSupply + mintTokens; accountTokens[minter] = accountTokens[minter] + mintTokens; /* We emit a Mint event, and a Transfer event */ emit Mint(minter, actualMintAmount, mintTokens); emit Transfer(address(this), minter, mintTokens); /* We call the defense hook */ // unused function // comptroller.mintVerify(address(this), minter, actualMintAmount, mintTokens); } /** * @notice Sender redeems cTokens in exchange for the underlying asset * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param redeemTokens The number of cTokens to redeem into underlying */ function redeemInternal(uint redeemTokens) internal nonReentrant { accrueInterest(); // redeemFresh emits redeem-specific logs on errors, so we don't need to redeemFresh(payable(msg.sender), redeemTokens, 0); } /** * @notice Sender redeems cTokens in exchange for a specified amount of underlying asset * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param redeemAmount The amount of underlying to receive from redeeming cTokens */ function redeemUnderlyingInternal(uint redeemAmount) internal nonReentrant { accrueInterest(); // redeemFresh emits redeem-specific logs on errors, so we don't need to redeemFresh(payable(msg.sender), 0, redeemAmount); } /** * @notice User redeems cTokens in exchange for the underlying asset * @dev Assumes interest has already been accrued up to the current block * @param redeemer The address of the account which is redeeming the tokens * @param redeemTokensIn The number of cTokens to redeem into underlying (only one of redeemTokensIn or redeemAmountIn may be non-zero) * @param redeemAmountIn The number of underlying tokens to receive from redeeming cTokens (only one of redeemTokensIn or redeemAmountIn may be non-zero) */ function redeemFresh(address payable redeemer, uint redeemTokensIn, uint redeemAmountIn) internal virtual { require(redeemTokensIn == 0 || redeemAmountIn == 0, "one of redeemTokensIn or redeemAmountIn must be zero"); /* exchangeRate = invoke Exchange Rate Stored() */ Exp memory exchangeRate = Exp({mantissa: exchangeRateStoredInternal() }); uint redeemTokens; uint redeemAmount; /* If redeemTokensIn > 0: */ if (redeemTokensIn > 0) { /* * We calculate the exchange rate and the amount of underlying to be redeemed: * redeemTokens = redeemTokensIn * redeemAmount = redeemTokensIn x exchangeRateCurrent */ redeemTokens = redeemTokensIn; redeemAmount = mul_ScalarTruncate(exchangeRate, redeemTokensIn); } else { /* * We get the current exchange rate and calculate the amount to be redeemed: * redeemTokens = redeemAmountIn / exchangeRate * redeemAmount = redeemAmountIn */ redeemTokens = div_(redeemAmountIn, exchangeRate); redeemAmount = redeemAmountIn; } /* Fail if redeem not allowed */ uint allowed = comptroller.redeemAllowed(address(this), redeemer, redeemTokens); if (allowed != 0) { revert RedeemComptrollerRejection(allowed); } /* Verify market's block number equals current block number */ if (accrualBlockNumber != getBlockNumber()) { revert RedeemFreshnessCheck(); } /* Fail gracefully if protocol has insufficient cash */ if (getCashPrior() < redeemAmount) { revert RedeemTransferOutNotPossible(); } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* * We write the previously calculated values into storage. * Note: Avoid token reentrancy attacks by writing reduced supply before external transfer. */ totalSupply = totalSupply - redeemTokens; accountTokens[redeemer] = accountTokens[redeemer] - redeemTokens; /* * We invoke doTransferOut for the redeemer and the redeemAmount. * Note: The cToken must handle variations between ERC-20 and ETH underlying. * On success, the cToken has redeemAmount less of cash. * doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred. * accountant supplies market and receives cTokens at current exchange Rate */ doTransferOut(redeemer, redeemAmount); /* We emit a Transfer event, and a Redeem event */ emit Transfer(redeemer, address(this), redeemTokens); emit Redeem(redeemer, redeemAmount, redeemTokens); /* We call the defense hook */ comptroller.redeemVerify(address(this), redeemer, redeemAmount, redeemTokens); } /** * @notice Sender borrows assets from the protocol to their own address * @param borrowAmount The amount of the underlying asset to borrow */ function borrowInternal(uint borrowAmount) internal nonReentrant { accrueInterest(); // borrowFresh emits borrow-specific logs on errors, so we don't need to borrowFresh(payable(msg.sender), borrowAmount); } /** * @notice Users borrow assets from the protocol to their own address * @param borrowAmount The amount of the underlying asset to borrow */ function borrowFresh(address payable borrower, uint borrowAmount) internal virtual { /* Fail if borrow not allowed */ uint allowed = comptroller.borrowAllowed(address(this), borrower, borrowAmount); if (allowed != 0) { revert BorrowComptrollerRejection(allowed); } /* Verify market's block number equals current block number */ if (accrualBlockNumber != getBlockNumber()) { revert BorrowFreshnessCheck(); } /* Fail gracefully if protocol has insufficient underlying cash */ if (getCashPrior() < borrowAmount) { revert BorrowCashNotAvailable(); } /* * We calculate the new borrower and total borrow balances, failing on overflow: * accountBorrowNew = accountBorrow + borrowAmount * totalBorrowsNew = totalBorrows + borrowAmount */ uint accountBorrowsPrev = borrowBalanceStoredInternal(borrower); uint accountBorrowsNew = accountBorrowsPrev + borrowAmount; uint totalBorrowsNew = totalBorrows + borrowAmount; ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* * We write the previously calculated values into storage. * Note: Avoid token reentrancy attacks by writing increased borrow before external transfer. `*/ accountBorrows[borrower].principal = accountBorrowsNew; accountBorrows[borrower].interestIndex = borrowIndex; totalBorrows = totalBorrowsNew; /* * We invoke doTransferOut for the borrower and the borrowAmount. * Note: The cToken must handle variations between ERC-20 and ETH underlying. * On success, the cToken borrowAmount less of cash. * doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred. */ doTransferOut(borrower, borrowAmount); /* We emit a Borrow event */ emit Borrow(borrower, borrowAmount, accountBorrowsNew, totalBorrowsNew); } /** * @notice Sender repays their own borrow * @param repayAmount The amount to repay, or -1 for the full outstanding amount */ function repayBorrowInternal(uint repayAmount) internal nonReentrant { accrueInterest(); // repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to repayBorrowFresh(msg.sender, msg.sender, repayAmount); } /** * @notice Sender repays a borrow belonging to borrower * @param borrower the account with the debt being payed off * @param repayAmount The amount to repay, or -1 for the full outstanding amount */ function repayBorrowBehalfInternal(address borrower, uint repayAmount) internal nonReentrant { accrueInterest(); // repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to repayBorrowFresh(msg.sender, borrower, repayAmount); } /** * @notice Borrows are repaid by another user (possibly the borrower). * @param payer the account paying off the borrow * @param borrower the account with the debt being payed off * @param repayAmount the amount of underlying tokens being returned, or -1 for the full outstanding amount * @return (uint) the actual repayment amount. */ function repayBorrowFresh(address payer, address borrower, uint repayAmount) internal virtual returns (uint) { /* Fail if repayBorrow not allowed */ uint allowed = comptroller.repayBorrowAllowed(address(this), payer, borrower, repayAmount); if (allowed != 0) { revert RepayBorrowComptrollerRejection(allowed); } /* Verify market's block number equals current block number */ if (accrualBlockNumber != getBlockNumber()) { revert RepayBorrowFreshnessCheck(); } /* We fetch the amount the borrower owes, with accumulated interest */ uint accountBorrowsPrev = borrowBalanceStoredInternal(borrower); /* If repayAmount == -1, repayAmount = accountBorrows */ uint repayAmountFinal = repayAmount == type(uint).max ? accountBorrowsPrev : repayAmount; ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* * We call doTransferIn for the payer and the repayAmount * Note: The cToken must handle variations between ERC-20 and ETH underlying. * On success, the cToken holds an additional repayAmount of cash. * doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred. * it returns the amount actually transferred, in case of a fee. */ uint actualRepayAmount = doTransferIn(payer, repayAmountFinal); /* * We calculate the new borrower and total borrow balances, failing on underflow: * accountBorrowsNew = accountBorrows - actualRepayAmount * totalBorrowsNew = totalBorrows - actualRepayAmount */ uint accountBorrowsNew = accountBorrowsPrev - actualRepayAmount; uint totalBorrowsNew = totalBorrows - actualRepayAmount; /* We write the previously calculated values into storage */ accountBorrows[borrower].principal = accountBorrowsNew; accountBorrows[borrower].interestIndex = borrowIndex; totalBorrows = totalBorrowsNew; /* We emit a RepayBorrow event */ emit RepayBorrow(payer, borrower, actualRepayAmount, accountBorrowsNew, totalBorrowsNew); return actualRepayAmount; } /** * @notice The sender liquidates the borrowers collateral. * The collateral seized is transferred to the liquidator. * @param borrower The borrower of this cToken to be liquidated * @param cTokenCollateral The market in which to seize collateral from the borrower * @param repayAmount The amount of the underlying borrowed asset to repay */ function liquidateBorrowInternal(address borrower, uint repayAmount, CTokenInterface cTokenCollateral) internal nonReentrant { accrueInterest(); uint error = cTokenCollateral.accrueInterest(); if (error != NO_ERROR) { // accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed revert LiquidateAccrueCollateralInterestFailed(error); } // liquidateBorrowFresh emits borrow-specific logs on errors, so we don't need to liquidateBorrowFresh(msg.sender, borrower, repayAmount, cTokenCollateral); } /** * @notice The liquidator liquidates the borrowers collateral. * The collateral seized is transferred to the liquidator. * @param borrower The borrower of this cToken to be liquidated * @param liquidator The address repaying the borrow and seizing collateral * @param cTokenCollateral The market in which to seize collateral from the borrower * @param repayAmount The amount of the underlying borrowed asset to repay */ function liquidateBorrowFresh(address liquidator, address borrower, uint repayAmount, CTokenInterface cTokenCollateral) internal { /* Fail if liquidate not allowed */ uint allowed = comptroller.liquidateBorrowAllowed(address(this), address(cTokenCollateral), liquidator, borrower, repayAmount); if (allowed != 0) { revert LiquidateComptrollerRejection(allowed); } /* Verify market's block number equals current block number */ if (accrualBlockNumber != getBlockNumber()) { revert LiquidateFreshnessCheck(); } /* Verify cTokenCollateral market's block number equals current block number */ if (cTokenCollateral.accrualBlockNumber() != getBlockNumber()) { revert LiquidateCollateralFreshnessCheck(); } /* Fail if borrower = liquidator */ if (borrower == liquidator) { revert LiquidateLiquidatorIsBorrower(); } /* Fail if repayAmount = 0 */ if (repayAmount == 0) { revert LiquidateCloseAmountIsZero(); } /* Fail if repayAmount = -1 */ if (repayAmount == type(uint).max) { revert LiquidateCloseAmountIsUintMax(); } /* Fail if repayBorrow fails */ uint actualRepayAmount = repayBorrowFresh(liquidator, borrower, repayAmount); ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* We calculate the number of collateral tokens that will be seized */ (uint amountSeizeError, uint seizeTokens) = comptroller.liquidateCalculateSeizeTokens(address(this), address(cTokenCollateral), actualRepayAmount); require(amountSeizeError == NO_ERROR, "LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED"); /* Revert if borrower collateral token balance < seizeTokens */ require(cTokenCollateral.balanceOf(borrower) >= seizeTokens, "LIQUIDATE_SEIZE_TOO_MUCH"); // If this is also the collateral, run seizeInternal to avoid re-entrancy, otherwise make an external call if (address(cTokenCollateral) == address(this)) { seizeInternal(address(this), liquidator, borrower, seizeTokens); } else { require(cTokenCollateral.seize(liquidator, borrower, seizeTokens) == NO_ERROR, "token seizure failed"); } /* We emit a LiquidateBorrow event */ emit LiquidateBorrow(liquidator, borrower, actualRepayAmount, address(cTokenCollateral), seizeTokens); } /** * @notice Transfers collateral tokens (this market) to the liquidator. * @dev Will fail unless called by another cToken during the process of liquidation. * Its absolutely critical to use msg.sender as the borrowed cToken and not a parameter. * @param liquidator The account receiving seized collateral * @param borrower The account having collateral seized * @param seizeTokens The number of cTokens to seize * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function seize(address liquidator, address borrower, uint seizeTokens) override external nonReentrant returns (uint) { seizeInternal(msg.sender, liquidator, borrower, seizeTokens); return NO_ERROR; } /** * @notice Transfers collateral tokens (this market) to the liquidator. * @dev Called only during an in-kind liquidation, or by liquidateBorrow during the liquidation of another CToken. * Its absolutely critical to use msg.sender as the seizer cToken and not a parameter. * @param seizerToken The contract seizing the collateral (i.e. borrowed cToken) * @param liquidator The account receiving seized collateral * @param borrower The account having collateral seized * @param seizeTokens The number of cTokens to seize */ function seizeInternal(address seizerToken, address liquidator, address borrower, uint seizeTokens) internal { /* Fail if seize not allowed */ uint allowed = comptroller.seizeAllowed(address(this), seizerToken, liquidator, borrower, seizeTokens); if (allowed != 0) { revert LiquidateSeizeComptrollerRejection(allowed); } /* Fail if borrower = liquidator */ if (borrower == liquidator) { revert LiquidateSeizeLiquidatorIsBorrower(); } /* * We calculate the new borrower and liquidator token balances, failing on underflow/overflow: * borrowerTokensNew = accountTokens[borrower] - seizeTokens * liquidatorTokensNew = accountTokens[liquidator] + seizeTokens */ uint protocolSeizeTokens = mul_(seizeTokens, Exp({mantissa: protocolSeizeShareMantissa})); uint liquidatorSeizeTokens = seizeTokens - protocolSeizeTokens; Exp memory exchangeRate = Exp({mantissa: exchangeRateStoredInternal()}); uint protocolSeizeAmount = mul_ScalarTruncate(exchangeRate, protocolSeizeTokens); uint totalReservesNew = totalReserves + protocolSeizeAmount; ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* We write the calculated values into storage */ totalReserves = totalReservesNew; totalSupply = totalSupply - protocolSeizeTokens; accountTokens[borrower] = accountTokens[borrower] - seizeTokens; accountTokens[liquidator] = accountTokens[liquidator] + liquidatorSeizeTokens; /* Emit a Transfer event */ emit Transfer(borrower, liquidator, liquidatorSeizeTokens); emit Transfer(borrower, address(this), protocolSeizeTokens); emit ReservesAdded(address(this), protocolSeizeAmount, totalReservesNew); } /*** Admin Functions ***/ /** * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. * @param newPendingAdmin New pending admin. * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setPendingAdmin(address payable newPendingAdmin) override external returns (uint) { // Check caller = admin if (msg.sender != admin) { revert SetPendingAdminOwnerCheck(); } // Save current value, if any, for inclusion in log address oldPendingAdmin = pendingAdmin; // Store pendingAdmin with value newPendingAdmin pendingAdmin = newPendingAdmin; // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin) emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin); return NO_ERROR; } /** * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin * @dev Admin function for pending admin to accept role and update admin * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _acceptAdmin() override external returns (uint) { // Check caller is pendingAdmin and pendingAdmin ≠ address(0) if (msg.sender != pendingAdmin || msg.sender == address(0)) { revert AcceptAdminPendingAdminCheck(); } // Save current values for inclusion in log address oldAdmin = admin; address oldPendingAdmin = pendingAdmin; // Store admin with value pendingAdmin admin = pendingAdmin; // Clear the pending value pendingAdmin = payable(address(0)); emit NewAdmin(oldAdmin, admin); emit NewPendingAdmin(oldPendingAdmin, pendingAdmin); return NO_ERROR; } /** * @notice Sets a new comptroller for the market * @dev Admin function to set a new comptroller * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setComptroller(ComptrollerInterface newComptroller) override public returns (uint) { // Check caller is admin if (msg.sender != admin) { revert SetComptrollerOwnerCheck(); } ComptrollerInterface oldComptroller = comptroller; // Ensure invoke comptroller.isComptroller() returns true require(newComptroller.isComptroller(), "marker method returned false"); // Set market's comptroller to newComptroller comptroller = newComptroller; // Emit NewComptroller(oldComptroller, newComptroller) emit NewComptroller(oldComptroller, newComptroller); return NO_ERROR; } /** * @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh * @dev Admin function to accrue interest and set a new reserve factor * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setReserveFactor(uint newReserveFactorMantissa) override external nonReentrant returns (uint) { accrueInterest(); // _setReserveFactorFresh emits reserve-factor-specific logs on errors, so we don't need to. return _setReserveFactorFresh(newReserveFactorMantissa); } /** * @notice Sets a new reserve factor for the protocol (*requires fresh interest accrual) * @dev Admin function to set a new reserve factor * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setReserveFactorFresh(uint newReserveFactorMantissa) internal returns (uint) { // Check caller is admin if (msg.sender != admin) { revert SetReserveFactorAdminCheck(); } // Verify market's block number equals current block number if (accrualBlockNumber != getBlockNumber()) { revert SetReserveFactorFreshCheck(); } // Check newReserveFactor ≤ maxReserveFactor if (newReserveFactorMantissa > reserveFactorMaxMantissa) { revert SetReserveFactorBoundsCheck(); } uint oldReserveFactorMantissa = reserveFactorMantissa; reserveFactorMantissa = newReserveFactorMantissa; emit NewReserveFactor(oldReserveFactorMantissa, newReserveFactorMantissa); return NO_ERROR; } /** * @notice Accrues interest and reduces reserves by transferring from msg.sender * @param addAmount Amount of addition to reserves * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _addReservesInternal(uint addAmount) internal nonReentrant returns (uint) { accrueInterest(); // _addReservesFresh emits reserve-addition-specific logs on errors, so we don't need to. _addReservesFresh(addAmount); return NO_ERROR; } /** * @notice Add reserves by transferring from caller * @dev Requires fresh interest accrual * @param addAmount Amount of addition to reserves * @return (uint, uint) An error code (0=success, otherwise a failure (see ErrorReporter.sol for details)) and the actual amount added, net token fees */ function _addReservesFresh(uint addAmount) internal returns (uint, uint) { // totalReserves + actualAddAmount uint totalReservesNew; uint actualAddAmount; // We fail gracefully unless market's block number equals current block number if (accrualBlockNumber != getBlockNumber()) { revert AddReservesFactorFreshCheck(actualAddAmount); } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* * We call doTransferIn for the caller and the addAmount * Note: The cToken must handle variations between ERC-20 and ETH underlying. * On success, the cToken holds an additional addAmount of cash. * doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred. * it returns the amount actually transferred, in case of a fee. */ actualAddAmount = doTransferIn(msg.sender, addAmount); totalReservesNew = totalReserves + actualAddAmount; // Store reserves[n+1] = reserves[n] + actualAddAmount totalReserves = totalReservesNew; /* Emit NewReserves(admin, actualAddAmount, reserves[n+1]) */ emit ReservesAdded(msg.sender, actualAddAmount, totalReservesNew); /* Return (NO_ERROR, actualAddAmount) */ return (NO_ERROR, actualAddAmount); } /** * @notice Accrues interest and reduces reserves by transferring to admin * @param reduceAmount Amount of reduction to reserves * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _reduceReserves(uint reduceAmount) override external nonReentrant returns (uint) { accrueInterest(); // _reduceReservesFresh emits reserve-reduction-specific logs on errors, so we don't need to. return _reduceReservesFresh(reduceAmount); } /** * @notice Reduces reserves by transferring to admin * @dev Requires fresh interest accrual * @param reduceAmount Amount of reduction to reserves * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _reduceReservesFresh(uint reduceAmount) internal returns (uint) { // totalReserves - reduceAmount uint totalReservesNew; // Check caller is admin if (msg.sender != admin) { revert ReduceReservesAdminCheck(); } // We fail gracefully unless market's block number equals current block number if (accrualBlockNumber != getBlockNumber()) { revert ReduceReservesFreshCheck(); } // Fail gracefully if protocol has insufficient underlying cash if (getCashPrior() < reduceAmount) { revert ReduceReservesCashNotAvailable(); } // Check reduceAmount ≤ reserves[n] (totalReserves) if (reduceAmount > totalReserves) { revert ReduceReservesCashValidation(); } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) totalReservesNew = totalReserves - reduceAmount; // Store reserves[n+1] = reserves[n] - reduceAmount totalReserves = totalReservesNew; // doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred. doTransferOut(admin, reduceAmount); emit ReservesReduced(admin, reduceAmount, totalReservesNew); return NO_ERROR; } /** * @notice accrues interest and updates the interest rate model using _setInterestRateModelFresh * @dev Admin function to accrue interest and update the interest rate model * @param newInterestRateModel the new interest rate model to use * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setInterestRateModel(InterestRateModel newInterestRateModel) override public returns (uint) { accrueInterest(); // _setInterestRateModelFresh emits interest-rate-model-update-specific logs on errors, so we don't need to. return _setInterestRateModelFresh(newInterestRateModel); } /** * @notice updates the interest rate model (*requires fresh interest accrual) * @dev Admin function to update the interest rate model * @param newInterestRateModel the new interest rate model to use * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setInterestRateModelFresh(InterestRateModel newInterestRateModel) internal returns (uint) { // Used to store old model for use in the event that is emitted on success InterestRateModel oldInterestRateModel; // Check caller is admin if (msg.sender != admin) { revert SetInterestRateModelOwnerCheck(); } // We fail gracefully unless market's block number equals current block number if (accrualBlockNumber != getBlockNumber()) { revert SetInterestRateModelFreshCheck(); } // Track the market's current interest rate model oldInterestRateModel = interestRateModel; // Ensure invoke newInterestRateModel.isInterestRateModel() returns true require(newInterestRateModel.isInterestRateModel(), "marker method returned false"); // Set the interest rate model to newInterestRateModel interestRateModel = newInterestRateModel; // Emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel) emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel); return NO_ERROR; } /*** Safe Token ***/ /** * @notice Gets balance of this contract in terms of the underlying * @dev This excludes the value of the current message, if any * @return The quantity of underlying owned by this contract */ function getCashPrior() virtual internal view returns (uint); /** * @dev Performs a transfer in, reverting upon failure. Returns the amount actually transferred to the protocol, in case of a fee. * This may revert due to insufficient balance or insufficient allowance. */ function doTransferIn(address from, uint amount) virtual internal returns (uint); /** * @dev Performs a transfer out, ideally returning an explanatory error code upon failure rather than reverting. * If caller has not called checked protocol's balance, may revert due to insufficient cash held in the contract. * If caller has checked protocol's balance, and verified it is >= amount, this should not revert in normal conditions. */ function doTransferOut(address payable to, uint amount) virtual internal; /*** Reentrancy Guard ***/ /** * @dev Prevents a contract from calling itself, directly or indirectly. */ modifier nonReentrant() virtual { require(_notEntered, "re-entered"); _notEntered = false; _; _notEntered = true; // get a gas-refund post-Istanbul } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./ComptrollerInterface.sol"; import "./InterestRateModel.sol"; import "./EIP20NonStandardInterface.sol"; import "./ErrorReporter.sol"; contract CTokenStorage { /** * @dev Guard variable for re-entrancy checks */ bool internal _notEntered; /** * @notice EIP-20 token name for this token */ string public name; /** * @notice EIP-20 token symbol for this token */ string public symbol; /** * @notice EIP-20 token decimals for this token */ uint8 public decimals; // Maximum borrow rate that can ever be applied (.0005% / block) uint internal constant borrowRateMaxMantissa = 0.0005e16; // Maximum fraction of interest that can be set aside for reserves uint internal constant reserveFactorMaxMantissa = 1e18; /** * @notice Administrator for this contract */ address payable public admin; /** * @notice Pending administrator for this contract */ address payable public pendingAdmin; /** * @notice Contract which oversees inter-cToken operations */ ComptrollerInterface public comptroller; /** * @notice Model which tells what the current interest rate should be */ InterestRateModel public interestRateModel; // Initial exchange rate used when minting the first CTokens (used when totalSupply = 0) uint internal initialExchangeRateMantissa; /** * @notice Fraction of interest currently set aside for reserves */ uint public reserveFactorMantissa; /** * @notice Block number that interest was last accrued at */ uint public accrualBlockNumber; /** * @notice Accumulator of the total earned interest rate since the opening of the market */ uint public borrowIndex; /** * @notice Total amount of outstanding borrows of the underlying in this market */ uint public totalBorrows; /** * @notice Total amount of reserves of the underlying held in this market */ uint public totalReserves; /** * @notice Total number of tokens in circulation */ uint public totalSupply; // Official record of token balances for each account mapping (address => uint) internal accountTokens; // Approved token transfer amounts on behalf of others mapping (address => mapping (address => uint)) internal transferAllowances; /** * @notice Container for borrow balance information * @member principal Total balance (with accrued interest), after applying the most recent balance-changing action * @member interestIndex Global borrowIndex as of the most recent balance-changing action */ struct BorrowSnapshot { uint principal; uint interestIndex; } // Mapping of account addresses to outstanding borrow balances mapping(address => BorrowSnapshot) internal accountBorrows; /** * @notice Share of seized collateral that is added to reserves */ uint public constant protocolSeizeShareMantissa = 2.8e16; //2.8% } abstract contract CTokenInterface is CTokenStorage { /** * @notice Indicator that this is a CToken contract (for inspection) */ bool public constant isCToken = true; /*** Market Events ***/ /** * @notice Event emitted when interest is accrued */ event AccrueInterest(uint cashPrior, uint interestAccumulated, uint borrowIndex, uint totalBorrows); /** * @notice Event emitted when tokens are minted */ event Mint(address minter, uint mintAmount, uint mintTokens); /** * @notice Event emitted when tokens are redeemed */ event Redeem(address redeemer, uint redeemAmount, uint redeemTokens); /** * @notice Event emitted when underlying is borrowed */ event Borrow(address borrower, uint borrowAmount, uint accountBorrows, uint totalBorrows); /** * @notice Event emitted when a borrow is repaid */ event RepayBorrow(address payer, address borrower, uint repayAmount, uint accountBorrows, uint totalBorrows); /** * @notice Event emitted when a borrow is liquidated */ event LiquidateBorrow(address liquidator, address borrower, uint repayAmount, address cTokenCollateral, uint seizeTokens); /*** Admin Events ***/ /** * @notice Event emitted when pendingAdmin is changed */ event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin); /** * @notice Event emitted when pendingAdmin is accepted, which means admin is updated */ event NewAdmin(address oldAdmin, address newAdmin); /** * @notice Event emitted when comptroller is changed */ event NewComptroller(ComptrollerInterface oldComptroller, ComptrollerInterface newComptroller); /** * @notice Event emitted when interestRateModel is changed */ event NewMarketInterestRateModel(InterestRateModel oldInterestRateModel, InterestRateModel newInterestRateModel); /** * @notice Event emitted when the reserve factor is changed */ event NewReserveFactor(uint oldReserveFactorMantissa, uint newReserveFactorMantissa); /** * @notice Event emitted when the reserves are added */ event ReservesAdded(address benefactor, uint addAmount, uint newTotalReserves); /** * @notice Event emitted when the reserves are reduced */ event ReservesReduced(address admin, uint reduceAmount, uint newTotalReserves); /** * @notice EIP20 Transfer event */ event Transfer(address indexed from, address indexed to, uint amount); /** * @notice EIP20 Approval event */ event Approval(address indexed owner, address indexed spender, uint amount); /*** User Interface ***/ function transfer(address dst, uint amount) virtual external returns (bool); function transferFrom(address src, address dst, uint amount) virtual external returns (bool); function approve(address spender, uint amount) virtual external returns (bool); function allowance(address owner, address spender) virtual external view returns (uint); function balanceOf(address owner) virtual external view returns (uint); function balanceOfUnderlying(address owner) virtual external returns (uint); function getAccountSnapshot(address account) virtual external view returns (uint, uint, uint, uint); function borrowRatePerBlock() virtual view external returns (uint); function supplyRatePerBlock() virtual view external returns (uint); function totalBorrowsCurrent() virtual external returns (uint); function borrowBalanceCurrent(address account) virtual external returns (uint); function borrowBalanceStored(address account) virtual external view returns (uint); function exchangeRateCurrent() virtual external returns (uint); function exchangeRateStored() virtual external view returns (uint); function getCash() virtual external view returns (uint); function accrueInterest() virtual external returns (uint); function seize(address liquidator, address borrower, uint seizeTokens) virtual external returns (uint); /*** Admin Functions ***/ function _setPendingAdmin(address payable newPendingAdmin) virtual external returns (uint); function _acceptAdmin() virtual external returns (uint); function _setComptroller(ComptrollerInterface newComptroller) virtual external returns (uint); function _setReserveFactor(uint newReserveFactorMantissa) virtual external returns (uint); function _reduceReserves(uint reduceAmount) virtual external returns (uint); function _setInterestRateModel(InterestRateModel newInterestRateModel) virtual external returns (uint); } contract CErc20Storage { /** * @notice Underlying asset for this CToken */ address public underlying; } abstract contract CErc20Interface is CErc20Storage { /*** User Interface ***/ function mint(uint mintAmount) virtual external returns (uint); function redeem(uint redeemTokens) virtual external returns (uint); function redeemUnderlying(uint redeemAmount) virtual external returns (uint); function borrow(uint borrowAmount) virtual external returns (uint); function repayBorrow(uint repayAmount) virtual external returns (uint); function repayBorrowBehalf(address borrower, uint repayAmount) virtual external returns (uint); function liquidateBorrow(address borrower, uint repayAmount, CTokenInterface cTokenCollateral) virtual external returns (uint); function sweepToken(EIP20NonStandardInterface token) virtual external; /*** Admin Functions ***/ function _addReserves(uint addAmount) virtual external returns (uint); } contract CDelegationStorage { /** * @notice Implementation address for this contract */ address public implementation; } abstract contract CDelegatorInterface is CDelegationStorage { /** * @notice Emitted when implementation is changed */ event NewImplementation(address oldImplementation, address newImplementation); /** * @notice Called by the admin to update the implementation of the delegator * @param implementation_ The address of the new implementation for delegation * @param allowResign Flag to indicate whether to call _resignImplementation on the old implementation * @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation */ function _setImplementation(address implementation_, bool allowResign, bytes memory becomeImplementationData) virtual external; } abstract contract CDelegateInterface is CDelegationStorage { /** * @notice Called by the delegator on a delegate to initialize it for duty * @dev Should revert if any issues arise which make it unfit for delegation * @param data The encoded bytes data for any initialization */ function _becomeImplementation(bytes memory data) virtual external; /** * @notice Called by the delegator on a delegate to forfeit its responsibility */ function _resignImplementation() virtual external; }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./JumpRateModelV2.sol"; /** * @title Compound's DAIInterestRateModel Contract (version 3) * @author Compound (modified by Dharma Labs) * @notice The parameterized model described in section 2.4 of the original Compound Protocol whitepaper. * Version 3 modifies the interest rate model in Version 2 by increasing the initial "gap" or slope of * the model prior to the "kink" from 2% to 4%, and enabling updateable parameters. */ contract DAIInterestRateModelV3 is JumpRateModelV2 { uint256 private constant BASE = 1e18; uint256 private constant RAY_BASE = 1e27; uint256 private constant RAY_TO_BASE_SCALE = 1e9; uint256 private constant SECONDS_PER_BLOCK = 15; /** * @notice The additional margin per block separating the base borrow rate from the roof. */ uint public gapPerBlock; /** * @notice The assumed (1 - reserve factor) used to calculate the minimum borrow rate (reserve factor = 0.05) */ uint public constant assumedOneMinusReserveFactorMantissa = 0.95e18; PotLike pot; JugLike jug; /** * @notice Construct an interest rate model * @param jumpMultiplierPerYear The multiplierPerBlock after hitting a specified utilization point * @param kink_ The utilization point at which the jump multiplier is applied * @param pot_ The address of the Dai pot (where DSR is earned) * @param jug_ The address of the Dai jug (where SF is kept) * @param owner_ The address of the owner, i.e. the Timelock contract (which has the ability to update parameters directly) */ constructor(uint jumpMultiplierPerYear, uint kink_, address pot_, address jug_, address owner_) JumpRateModelV2(0, 0, jumpMultiplierPerYear, kink_, owner_) public { gapPerBlock = 4e16 / blocksPerYear; pot = PotLike(pot_); jug = JugLike(jug_); poke(); } /** * @notice External function to update the parameters of the interest rate model * @param baseRatePerYear The approximate target base APR, as a mantissa (scaled by BASE). For DAI, this is calculated from DSR and SF. Input not used. * @param gapPerYear The Additional margin per year separating the base borrow rate from the roof. (scaled by BASE) * @param jumpMultiplierPerYear The jumpMultiplierPerYear after hitting a specified utilization point * @param kink_ The utilization point at which the jump multiplier is applied */ function updateJumpRateModel(uint baseRatePerYear, uint gapPerYear, uint jumpMultiplierPerYear, uint kink_) override external { require(msg.sender == owner, "only the owner may call this function."); gapPerBlock = gapPerYear / blocksPerYear; updateJumpRateModelInternal(0, 0, jumpMultiplierPerYear, kink_); poke(); } /** * @notice Calculates the current supply interest rate per block including the Dai savings rate * @param cash The total amount of cash the market has * @param borrows The total amount of borrows the market has outstanding * @param reserves The total amnount of reserves the market has * @param reserveFactorMantissa The current reserve factor the market has * @return The supply rate per block (as a percentage, and scaled by BASE) */ function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) override public view returns (uint) { uint protocolRate = super.getSupplyRate(cash, borrows, reserves, reserveFactorMantissa); uint underlying = cash + borrows - reserves; if (underlying == 0) { return protocolRate; } else { uint cashRate = cash * dsrPerBlock() / underlying; return cashRate + protocolRate; } } /** * @notice Calculates the Dai savings rate per block * @return The Dai savings rate per block (as a percentage, and scaled by BASE) */ function dsrPerBlock() public view returns (uint) { return (pot.dsr() - RAY_BASE) // scaled RAY_BASE aka RAY, and includes an extra "ONE" before subtraction / RAY_TO_BASE_SCALE // descale to BASE * SECONDS_PER_BLOCK; // seconds per block } /** * @notice Resets the baseRate and multiplier per block based on the stability fee and Dai savings rate */ function poke() public { (uint duty, ) = jug.ilks("ETH-A"); uint stabilityFeePerBlock = (duty + jug.base() - RAY_BASE) / RAY_TO_BASE_SCALE * SECONDS_PER_BLOCK; // We ensure the minimum borrow rate >= DSR / (1 - reserve factor) baseRatePerBlock = dsrPerBlock() * BASE / assumedOneMinusReserveFactorMantissa; // The roof borrow rate is max(base rate, stability fee) + gap, from which we derive the slope if (baseRatePerBlock < stabilityFeePerBlock) { multiplierPerBlock = (stabilityFeePerBlock - baseRatePerBlock + gapPerBlock) * BASE / kink; } else { multiplierPerBlock = gapPerBlock * BASE / kink; } emit NewInterestParams(baseRatePerBlock, multiplierPerBlock, jumpMultiplierPerBlock, kink); } } /*** Maker Interfaces ***/ interface PotLike { function chi() external view returns (uint); function dsr() external view returns (uint); function rho() external view returns (uint); function pie(address) external view returns (uint); function drip() external returns (uint); function join(uint) external; function exit(uint) external; } contract JugLike { // --- Data --- struct Ilk { uint256 duty; uint256 rho; } mapping (bytes32 => Ilk) public ilks; uint256 public base; }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; /** * @title ERC 20 Token Standard Interface * https://eips.ethereum.org/EIPS/eip-20 */ interface EIP20Interface { 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 balance The balance */ function balanceOf(address owner) external view returns (uint256 balance); /** * @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 success Whether or not the transfer succeeded */ function transfer(address dst, uint256 amount) external returns (bool success); /** * @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 success Whether or not the transfer succeeded */ function transferFrom(address src, address dst, uint256 amount) external returns (bool success); /** * @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 success Whether or not the approval succeeded */ function approve(address spender, uint256 amount) external returns (bool success); /** * @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 remaining The number of tokens allowed to be spent (-1 means infinite) */ function allowance(address owner, address spender) external view returns (uint256 remaining); event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; /** * @title EIP20NonStandardInterface * @dev Version of ERC20 with no return values for `transfer` and `transferFrom` * See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca */ interface EIP20NonStandardInterface { /** * @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 balance The balance */ function balanceOf(address owner) external view returns (uint256 balance); /// !!!!!!!!!!!!!! /// !!! NOTICE !!! `transfer` does not return a value, in violation of the ERC-20 specification /// !!!!!!!!!!!!!! /** * @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 */ function transfer(address dst, uint256 amount) external; /// !!!!!!!!!!!!!! /// !!! NOTICE !!! `transferFrom` does not return a value, in violation of the ERC-20 specification /// !!!!!!!!!!!!!! /** * @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 */ function transferFrom(address src, address dst, uint256 amount) external; /** * @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 * @return success Whether or not the approval succeeded */ function approve(address spender, uint256 amount) external returns (bool success); /** * @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 remaining The number of tokens allowed to be spent */ function allowance(address owner, address spender) external view returns (uint256 remaining); event Transfer(address indexed from, address indexed to, uint256 amount); event Approval( address indexed owner, address indexed spender, uint256 amount ); }
pragma solidity ^0.8.10; import "./EIP20Interface.sol"; contract ERC20 is EIP20Interface { mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; uint256 public _initialSupply; uint8 public _decimals; string private _name; string private _symbol; uint256 MAX_INT = 2 ** 256 - 1; /** * @dev Sets the values for {name} and {symbol}. * * The default value of {decimals} is 18. To select a different value for * {decimals} you should overload it. * * All two of these values are immutable: they can only be set once during * construction. */ constructor( string memory name_, string memory symbol_, uint256 totalSupply_, uint8 decimals_ ) public { _name = name_; _symbol = symbol_; _initialSupply = totalSupply_; _totalSupply = totalSupply_; _balances[msg.sender] = totalSupply_; _decimals = decimals_; } /** * @dev Returns the name of the token. */ function name() public view returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5.05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless this function is * overridden; * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view returns (uint8) { return _decimals; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `to` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address to, uint256 amount) public returns (bool) { address owner = msg.sender; _transfer(owner, to, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on * `transferFrom`. This is semantically equivalent to an infinite approval. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public returns (bool) { address owner = msg.sender; _approve(owner, spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * NOTE: Does not update the allowance if the current allowance * is the maximum `uint256`. * * Requirements: * * - `from` and `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. * - the caller must have allowance for ``from``'s tokens of at least * `amount`. */ function transferFrom(address from, address to, uint256 amount) public returns (bool) { address spender = msg.sender; _spendAllowance(from, spender, amount); _transfer(from, to, amount); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { address owner = msg.sender; _approve(owner, spender, allowance(owner, spender) + addedValue); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { address owner = msg.sender; uint256 currentAllowance = allowance(owner, spender); require( currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero" ); _approve(owner, spender, currentAllowance - subtractedValue); return true; } /** * @dev Moves `amount` of tokens from `from` to `to`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. */ function _transfer(address from, address to, uint256 amount) internal { require(from != address(0), "ERC20: transfer from the zero address"); require(to != address(0), "ERC20: transfer to the zero address"); uint256 fromBalance = _balances[from]; require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); _balances[from] = fromBalance - amount; _balances[to] += amount; emit Transfer(from, to, amount); } /** * @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. */ function _mint(address account, uint256 amount) internal { require(account != address(0), "ERC20: mint to the zero address"); _totalSupply += amount; _balances[account] += amount; emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal { require(account != address(0), "ERC20: burn from the zero address"); uint256 accountBalance = _balances[account]; require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); _balances[account] = accountBalance - amount; _totalSupply -= amount; emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Updates `owner` s allowance for `spender` based on spent `amount`. * * Does not update the allowance amount in case of infinite allowance. * Revert if not enough allowance is available. * * Might emit an {Approval} event. */ function _spendAllowance(address owner, address spender, uint256 amount) internal { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != MAX_INT) { require(currentAllowance >= amount, "ERC20: insufficient allowance"); _approve(owner, spender, currentAllowance - amount); } } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; contract ComptrollerErrorReporter { enum Error // no longer possible { NO_ERROR, UNAUTHORIZED, COMPTROLLER_MISMATCH, INSUFFICIENT_SHORTFALL, INSUFFICIENT_LIQUIDITY, INVALID_CLOSE_FACTOR, INVALID_COLLATERAL_FACTOR, INVALID_LIQUIDATION_INCENTIVE, MARKET_NOT_ENTERED, MARKET_NOT_LISTED, MARKET_ALREADY_LISTED, MATH_ERROR, NONZERO_BORROW_BALANCE, PRICE_ERROR, REJECTION, SNAPSHOT_ERROR, TOO_MANY_ASSETS, TOO_MUCH_REPAY } enum FailureInfo { ACCEPT_ADMIN_PENDING_ADMIN_CHECK, ACCEPT_PENDING_IMPLEMENTATION_ADDRESS_CHECK, EXIT_MARKET_BALANCE_OWED, EXIT_MARKET_REJECTION, SET_CLOSE_FACTOR_OWNER_CHECK, SET_CLOSE_FACTOR_VALIDATION, SET_COLLATERAL_FACTOR_OWNER_CHECK, SET_COLLATERAL_FACTOR_NO_EXISTS, SET_COLLATERAL_FACTOR_VALIDATION, SET_COLLATERAL_FACTOR_WITHOUT_PRICE, SET_IMPLEMENTATION_OWNER_CHECK, SET_LIQUIDATION_INCENTIVE_OWNER_CHECK, SET_LIQUIDATION_INCENTIVE_VALIDATION, SET_MAX_ASSETS_OWNER_CHECK, SET_PENDING_ADMIN_OWNER_CHECK, SET_PENDING_IMPLEMENTATION_OWNER_CHECK, SET_PRICE_ORACLE_OWNER_CHECK, SUPPORT_MARKET_EXISTS, SUPPORT_MARKET_OWNER_CHECK, SET_PAUSE_GUARDIAN_OWNER_CHECK } /** * @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary * contract-specific code that enables us to report opaque error codes from upgradeable contracts. * */ event Failure(uint256 error, uint256 info, uint256 detail); /** * @dev use this when reporting a known error from the money market or a non-upgradeable collaborator */ function fail(Error err, FailureInfo info) internal returns (uint256) { emit Failure(uint256(err), uint256(info), 0); return uint256(err); } /** * @dev use this when reporting an opaque error from an upgradeable collaborator contract */ function failOpaque(Error err, FailureInfo info, uint256 opaqueError) internal returns (uint256) { emit Failure(uint256(err), uint256(info), opaqueError); return uint256(err); } } contract TokenErrorReporter { uint256 public constant NO_ERROR = 0; // support legacy return codes error TransferComptrollerRejection(uint256 errorCode); error TransferNotAllowed(); error TransferNotEnough(); error TransferTooMuch(); error MintComptrollerRejection(uint256 errorCode); error MintFreshnessCheck(); error RedeemComptrollerRejection(uint256 errorCode); error RedeemFreshnessCheck(); error RedeemTransferOutNotPossible(); error BorrowComptrollerRejection(uint256 errorCode); error BorrowFreshnessCheck(); error BorrowCashNotAvailable(); error RepayBorrowComptrollerRejection(uint256 errorCode); error RepayBorrowFreshnessCheck(); error LiquidateComptrollerRejection(uint256 errorCode); error LiquidateFreshnessCheck(); error LiquidateCollateralFreshnessCheck(); error LiquidateAccrueBorrowInterestFailed(uint256 errorCode); error LiquidateAccrueCollateralInterestFailed(uint256 errorCode); error LiquidateLiquidatorIsBorrower(); error LiquidateCloseAmountIsZero(); error LiquidateCloseAmountIsUintMax(); error LiquidateRepayBorrowFreshFailed(uint256 errorCode); error LiquidateSeizeComptrollerRejection(uint256 errorCode); error LiquidateSeizeLiquidatorIsBorrower(); error AcceptAdminPendingAdminCheck(); error SetComptrollerOwnerCheck(); error SetPendingAdminOwnerCheck(); error SetReserveFactorAdminCheck(); error SetReserveFactorFreshCheck(); error SetReserveFactorBoundsCheck(); error AddReservesFactorFreshCheck(uint256 actualAddAmount); error ReduceReservesAdminCheck(); error ReduceReservesFreshCheck(); error ReduceReservesCashNotAvailable(); error ReduceReservesCashValidation(); error SetInterestRateModelOwnerCheck(); error SetInterestRateModelFreshCheck(); //Accountant Error Codes error AccountantSupplyError(uint256 borrowAmount); error AccountantRedeemError(uint256 redeemAmount); }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; /** * @title Exponential module for storing fixed-precision decimals * @author Compound * @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places. * Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is: * `Exp({mantissa: 5100000000000000000})`. */ contract ExponentialNoError { uint256 constant expScale = 1e18; uint256 constant doubleScale = 1e36; uint256 constant halfExpScale = expScale / 2; uint256 constant mantissaOne = expScale; struct Exp { uint256 mantissa; } struct Double { uint256 mantissa; } /** * @dev Truncates the given exp to a whole number value. * For example, truncate(Exp{mantissa: 15 * expScale}) = 15 */ function truncate(Exp memory exp) internal pure returns (uint256) { // Note: We are not using careful math here as we're performing a division that cannot fail return exp.mantissa / expScale; } /** * @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer. */ function mul_ScalarTruncate(Exp memory a, uint256 scalar) internal pure returns (uint256) { Exp memory product = mul_(a, scalar); return truncate(product); } /** * @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer. */ function mul_ScalarTruncateAddUInt( Exp memory a, uint256 scalar, uint256 addend ) internal pure returns (uint256) { Exp memory product = mul_(a, scalar); return add_(truncate(product), addend); } /** * @dev Checks if first Exp is less than second Exp. */ function lessThanExp(Exp memory left, Exp memory right) internal pure returns (bool) { return left.mantissa < right.mantissa; } /** * @dev Checks if left Exp <= right Exp. */ function lessThanOrEqualExp(Exp memory left, Exp memory right) internal pure returns (bool) { return left.mantissa <= right.mantissa; } /** * @dev Checks if left Exp > right Exp. */ function greaterThanExp(Exp memory left, Exp memory right) internal pure returns (bool) { return left.mantissa > right.mantissa; } /** * @dev returns true if Exp is exactly zero */ function isZeroExp(Exp memory value) internal pure returns (bool) { return value.mantissa == 0; } function safe224(uint256 n, string memory errorMessage) internal pure returns (uint224) { require(n < 2 ** 224, errorMessage); return uint224(n); } function safe32(uint256 n, string memory errorMessage) internal pure returns (uint32) { require(n < 2 ** 32, errorMessage); return uint32(n); } function add_(Exp memory a, Exp memory b) internal pure returns (Exp memory) { return Exp({mantissa: add_(a.mantissa, b.mantissa)}); } function add_(Double memory a, Double memory b) internal pure returns (Double memory) { return Double({mantissa: add_(a.mantissa, b.mantissa)}); } function add_(uint256 a, uint256 b) internal pure returns (uint256) { return a + b; } function sub_(Exp memory a, Exp memory b) internal pure returns (Exp memory) { return Exp({mantissa: sub_(a.mantissa, b.mantissa)}); } function sub_(Double memory a, Double memory b) internal pure returns (Double memory) { return Double({mantissa: sub_(a.mantissa, b.mantissa)}); } function sub_(uint256 a, uint256 b) internal pure returns (uint256) { return a - b; } function mul_(Exp memory a, Exp memory b) internal pure returns (Exp memory) { return Exp({mantissa: mul_(a.mantissa, b.mantissa) / expScale}); } function mul_(Exp memory a, uint256 b) internal pure returns (Exp memory) { return Exp({mantissa: mul_(a.mantissa, b)}); } function mul_(uint256 a, Exp memory b) internal pure returns (uint256) { return mul_(a, b.mantissa) / expScale; } function mul_(Double memory a, Double memory b) internal pure returns (Double memory) { return Double({mantissa: mul_(a.mantissa, b.mantissa) / doubleScale}); } function mul_(Double memory a, uint256 b) internal pure returns (Double memory) { return Double({mantissa: mul_(a.mantissa, b)}); } function mul_(uint256 a, Double memory b) internal pure returns (uint256) { return mul_(a, b.mantissa) / doubleScale; } function mul_(uint256 a, uint256 b) internal pure returns (uint256) { return a * b; } function div_(Exp memory a, Exp memory b) internal pure returns (Exp memory) { return Exp({mantissa: div_(mul_(a.mantissa, expScale), b.mantissa)}); } function div_(Exp memory a, uint256 b) internal pure returns (Exp memory) { return Exp({mantissa: div_(a.mantissa, b)}); } function div_(uint256 a, Exp memory b) internal pure returns (uint256) { return div_(mul_(a, expScale), b.mantissa); } function div_(Double memory a, Double memory b) internal pure returns (Double memory) { return Double({mantissa: div_(mul_(a.mantissa, doubleScale), b.mantissa)}); } function div_(Double memory a, uint256 b) internal pure returns (Double memory) { return Double({mantissa: div_(a.mantissa, b)}); } function div_(uint256 a, Double memory b) internal pure returns (uint256) { return div_(mul_(a, doubleScale), b.mantissa); } function div_(uint256 a, uint256 b) internal pure returns (uint256) { return a / b; } function fraction(uint256 a, uint256 b) internal pure returns (Double memory) { return Double({mantissa: div_(mul_(a, doubleScale), b)}); } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; contract Comp { /// @notice EIP-20 token name for this token string public constant name = "Compound"; /// @notice EIP-20 token symbol for this token string public constant symbol = "COMP"; /// @notice EIP-20 token decimals for this token uint8 public constant decimals = 18; /// @notice Total number of tokens in circulation uint256 public constant totalSupply = 10000000e18; // 10 million Comp /// @notice Allowance amounts on behalf of others mapping(address => mapping(address => uint96)) internal allowances; /// @notice Official record of token balances for each account mapping(address => uint96) internal balances; /// @notice A record of each accounts delegate mapping(address => address) public delegates; /// @notice A checkpoint for marking number of votes from a given block struct Checkpoint { uint32 fromBlock; uint96 votes; } /// @notice A record of votes checkpoints for each account, by index mapping(address => mapping(uint32 => Checkpoint)) public checkpoints; /// @notice The number of checkpoints for each account mapping(address => uint32) public numCheckpoints; /// @notice The EIP-712 typehash for the contract's domain bytes32 public constant DOMAIN_TYPEHASH = keccak256( "EIP712Domain(string name,uint256 chainId,address verifyingContract)" ); /// @notice The EIP-712 typehash for the delegation struct used by the contract bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); /// @notice A record of states for signing / validating signatures mapping(address => uint256) public nonces; /// @notice An event thats emitted when an account changes its delegate event DelegateChanged( address indexed delegator, address indexed fromDelegate, address indexed toDelegate ); /// @notice An event thats emitted when a delegate account's vote balance changes event DelegateVotesChanged( address indexed delegate, uint256 previousBalance, uint256 newBalance ); /// @notice The standard EIP-20 transfer event event Transfer(address indexed from, address indexed to, uint256 amount); /// @notice The standard EIP-20 approval event event Approval( address indexed owner, address indexed spender, uint256 amount ); /** * @notice Construct a new Comp token * @param account The initial account to grant all the tokens */ constructor(address account) public { balances[account] = uint96(totalSupply); emit Transfer(address(0), account, totalSupply); } /** * @notice Get the number of tokens `spender` is approved to spend on behalf of `account` * @param account The address of the account holding the funds * @param spender The address of the account spending the funds * @return The number of tokens approved */ function allowance(address account, address spender) external view returns (uint256) { return allowances[account][spender]; } /** * @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 rawAmount The number of tokens that are approved (2^256-1 means infinite) * @return Whether or not the approval succeeded */ function approve(address spender, uint256 rawAmount) external returns (bool) { uint96 amount; if (rawAmount == type(uint256).max) { amount = type(uint96).max; } else { amount = safe96(rawAmount, "Comp::approve: amount exceeds 96 bits"); } allowances[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } /** * @notice Get the number of tokens held by the `account` * @param account The address of the account to get the balance of * @return The number of tokens held */ function balanceOf(address account) external view returns (uint256) { return balances[account]; } /** * @notice Transfer `amount` tokens from `msg.sender` to `dst` * @param dst The address of the destination account * @param rawAmount The number of tokens to transfer * @return Whether or not the transfer succeeded */ function transfer(address dst, uint256 rawAmount) external returns (bool) { uint96 amount = safe96(rawAmount, "Comp::transfer: amount exceeds 96 bits"); _transferTokens(msg.sender, dst, amount); return true; } /** * @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 rawAmount The number of tokens to transfer * @return Whether or not the transfer succeeded */ function transferFrom(address src, address dst, uint256 rawAmount) external returns (bool) { address spender = msg.sender; uint96 spenderAllowance = allowances[src][spender]; uint96 amount = safe96(rawAmount, "Comp::approve: amount exceeds 96 bits"); if (spender != src && spenderAllowance != type(uint96).max) { uint96 newAllowance = sub96( spenderAllowance, amount, "Comp::transferFrom: transfer amount exceeds spender allowance" ); allowances[src][spender] = newAllowance; emit Approval(src, spender, newAllowance); } _transferTokens(src, dst, amount); return true; } /** * @notice Delegate votes from `msg.sender` to `delegatee` * @param delegatee The address to delegate votes to */ function delegate(address delegatee) public { return _delegate(msg.sender, delegatee); } /** * @notice Delegates votes from signatory to `delegatee` * @param delegatee The address to delegate votes to * @param nonce The contract state required to match the signature * @param expiry The time at which to expire the signature * @param v The recovery byte of the signature * @param r Half of the ECDSA signature pair * @param s Half of the ECDSA signature pair */ function delegateBySig( address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s ) public { bytes32 domainSeparator = keccak256( abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this)) ); bytes32 structHash = keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry)); bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); address signatory = ecrecover(digest, v, r, s); require( signatory != address(0), "Comp::delegateBySig: invalid signature" ); require( nonce == nonces[signatory]++, "Comp::delegateBySig: invalid nonce" ); require( block.timestamp <= expiry, "Comp::delegateBySig: signature expired" ); return _delegate(signatory, delegatee); } /** * @notice Gets the current votes balance for `account` * @param account The address to get votes balance * @return The number of current votes for `account` */ function getCurrentVotes(address account) external view returns (uint96) { uint32 nCheckpoints = numCheckpoints[account]; return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0; } /** * @notice Determine the prior number of votes for an account as of a block number * @dev Block number must be a finalized block or else this function will revert to prevent misinformation. * @param account The address of the account to check * @param blockNumber The block number to get the vote balance at * @return The number of votes the account had as of the given block */ function getPriorVotes(address account, uint256 blockNumber) public view returns (uint96) { require( blockNumber < block.number, "Comp::getPriorVotes: not yet determined" ); uint32 nCheckpoints = numCheckpoints[account]; if (nCheckpoints == 0) { return 0; } // First check most recent balance if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) { return checkpoints[account][nCheckpoints - 1].votes; } // Next check implicit zero balance if (checkpoints[account][0].fromBlock > blockNumber) { return 0; } uint32 lower = 0; uint32 upper = nCheckpoints - 1; while (upper > lower) { uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow Checkpoint memory cp = checkpoints[account][center]; if (cp.fromBlock == blockNumber) { return cp.votes; } else if (cp.fromBlock < blockNumber) { lower = center; } else { upper = center - 1; } } return checkpoints[account][lower].votes; } function _delegate(address delegator, address delegatee) internal { address currentDelegate = delegates[delegator]; uint96 delegatorBalance = balances[delegator]; delegates[delegator] = delegatee; emit DelegateChanged(delegator, currentDelegate, delegatee); _moveDelegates(currentDelegate, delegatee, delegatorBalance); } function _transferTokens(address src, address dst, uint96 amount) internal { require( src != address(0), "Comp::_transferTokens: cannot transfer from the zero address" ); require( dst != address(0), "Comp::_transferTokens: cannot transfer to the zero address" ); balances[src] = sub96( balances[src], amount, "Comp::_transferTokens: transfer amount exceeds balance" ); balances[dst] = add96( balances[dst], amount, "Comp::_transferTokens: transfer amount overflows" ); emit Transfer(src, dst, amount); _moveDelegates(delegates[src], delegates[dst], amount); } function _moveDelegates(address srcRep, address dstRep, uint96 amount) internal { if (srcRep != dstRep && amount > 0) { if (srcRep != address(0)) { uint32 srcRepNum = numCheckpoints[srcRep]; uint96 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0; uint96 srcRepNew = sub96( srcRepOld, amount, "Comp::_moveVotes: vote amount underflows" ); _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew); } if (dstRep != address(0)) { uint32 dstRepNum = numCheckpoints[dstRep]; uint96 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0; uint96 dstRepNew = add96( dstRepOld, amount, "Comp::_moveVotes: vote amount overflows" ); _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew); } } } function _writeCheckpoint( address delegatee, uint32 nCheckpoints, uint96 oldVotes, uint96 newVotes ) internal { uint32 blockNumber = safe32( block.number, "Comp::_writeCheckpoint: block number exceeds 32 bits" ); if ( nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber ) { checkpoints[delegatee][nCheckpoints - 1].votes = newVotes; } else { checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes); numCheckpoints[delegatee] = nCheckpoints + 1; } emit DelegateVotesChanged(delegatee, oldVotes, newVotes); } function safe32(uint256 n, string memory errorMessage) internal pure returns (uint32) { require(n < 2 ** 32, errorMessage); return uint32(n); } function safe96(uint256 n, string memory errorMessage) internal pure returns (uint96) { require(n < 2 ** 96, errorMessage); return uint96(n); } function add96(uint96 a, uint96 b, string memory errorMessage) internal pure returns (uint96) { uint96 c = a + b; require(c >= a, errorMessage); return c; } function sub96(uint96 a, uint96 b, string memory errorMessage) internal pure returns (uint96) { require(b <= a, errorMessage); return a - b; } function getChainId() internal view returns (uint256) { uint256 chainId; assembly { chainId := chainid() } return chainId; } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; contract GovernorAlpha { /// @notice The name of this contract string public constant name = "Compound Governor Alpha"; /// @notice The number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed function quorumVotes() public pure returns (uint) { return 400000e18; } // 400,000 = 4% of Comp /// @notice The number of votes required in order for a voter to become a proposer function proposalThreshold() public pure returns (uint) { return 100000e18; } // 100,000 = 1% of Comp /// @notice The maximum number of actions that can be included in a proposal function proposalMaxOperations() public pure returns (uint) { return 10; } // 10 actions /// @notice The delay before voting on a proposal may take place, once proposed function votingDelay() public pure returns (uint) { return 1; } // 1 block /// @notice The duration of voting on a proposal, in blocks function votingPeriod() virtual public pure returns (uint) { return 17280; } // ~3 days in blocks (assuming 15s blocks) /// @notice The address of the Compound Protocol Timelock TimelockInterface public timelock; /// @notice The address of the Compound governance token CompInterface public comp; /// @notice The address of the Governor Guardian address public guardian; /// @notice The total number of proposals uint public proposalCount; struct Proposal { /// @notice Unique id for looking up a proposal uint id; /// @notice Creator of the proposal address proposer; /// @notice The timestamp that the proposal will be available for execution, set once the vote succeeds uint eta; /// @notice the ordered list of target addresses for calls to be made address[] targets; /// @notice The ordered list of values (i.e. msg.value) to be passed to the calls to be made uint[] values; /// @notice The ordered list of function signatures to be called string[] signatures; /// @notice The ordered list of calldata to be passed to each call bytes[] calldatas; /// @notice The block at which voting begins: holders must delegate their votes prior to this block uint startBlock; /// @notice The block at which voting ends: votes must be cast prior to this block uint endBlock; /// @notice Current number of votes in favor of this proposal uint forVotes; /// @notice Current number of votes in opposition to this proposal uint againstVotes; /// @notice Flag marking whether the proposal has been canceled bool canceled; /// @notice Flag marking whether the proposal has been executed bool executed; /// @notice Receipts of ballots for the entire set of voters mapping (address => Receipt) receipts; } /// @notice Ballot receipt record for a voter struct Receipt { /// @notice Whether or not a vote has been cast bool hasVoted; /// @notice Whether or not the voter supports the proposal bool support; /// @notice The number of votes the voter had, which were cast uint96 votes; } /// @notice Possible states that a proposal may be in enum ProposalState { Pending, Active, Canceled, Defeated, Succeeded, Queued, Expired, Executed } /// @notice The official record of all proposals ever proposed mapping (uint => Proposal) public proposals; /// @notice The latest proposal for each proposer mapping (address => uint) public latestProposalIds; /// @notice The EIP-712 typehash for the contract's domain bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); /// @notice The EIP-712 typehash for the ballot struct used by the contract bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,bool support)"); /// @notice An event emitted when a new proposal is created event ProposalCreated(uint id, address proposer, address[] targets, uint[] values, string[] signatures, bytes[] calldatas, uint startBlock, uint endBlock, string description); /// @notice An event emitted when a vote has been cast on a proposal event VoteCast(address voter, uint proposalId, bool support, uint votes); /// @notice An event emitted when a proposal has been canceled event ProposalCanceled(uint id); /// @notice An event emitted when a proposal has been queued in the Timelock event ProposalQueued(uint id, uint eta); /// @notice An event emitted when a proposal has been executed in the Timelock event ProposalExecuted(uint id); constructor(address timelock_, address comp_, address guardian_) public { timelock = TimelockInterface(timelock_); comp = CompInterface(comp_); guardian = guardian_; } function propose(address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas, string memory description) public returns (uint) { require(comp.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold(), "GovernorAlpha::propose: proposer votes below proposal threshold"); require(targets.length == values.length && targets.length == signatures.length && targets.length == calldatas.length, "GovernorAlpha::propose: proposal function information arity mismatch"); require(targets.length != 0, "GovernorAlpha::propose: must provide actions"); require(targets.length <= proposalMaxOperations(), "GovernorAlpha::propose: too many actions"); uint latestProposalId = latestProposalIds[msg.sender]; if (latestProposalId != 0) { ProposalState proposersLatestProposalState = state(latestProposalId); require(proposersLatestProposalState != ProposalState.Active, "GovernorAlpha::propose: one live proposal per proposer, found an already active proposal"); require(proposersLatestProposalState != ProposalState.Pending, "GovernorAlpha::propose: one live proposal per proposer, found an already pending proposal"); } uint startBlock = add256(block.number, votingDelay()); uint endBlock = add256(startBlock, votingPeriod()); proposalCount++; uint proposalId = proposalCount; Proposal storage newProposal = proposals[proposalId]; // This should never happen but add a check in case. require(newProposal.id == 0, "GovernorAlpha::propose: ProposalID collsion"); newProposal.id = proposalId; newProposal.proposer = msg.sender; newProposal.eta = 0; newProposal.targets = targets; newProposal.values = values; newProposal.signatures = signatures; newProposal.calldatas = calldatas; newProposal.startBlock = startBlock; newProposal.endBlock = endBlock; newProposal.forVotes = 0; newProposal.againstVotes = 0; newProposal.canceled = false; newProposal.executed = false; latestProposalIds[newProposal.proposer] = newProposal.id; emit ProposalCreated(newProposal.id, msg.sender, targets, values, signatures, calldatas, startBlock, endBlock, description); return newProposal.id; } function queue(uint proposalId) public { require(state(proposalId) == ProposalState.Succeeded, "GovernorAlpha::queue: proposal can only be queued if it is succeeded"); Proposal storage proposal = proposals[proposalId]; uint eta = add256(block.timestamp, timelock.delay()); for (uint i = 0; i < proposal.targets.length; i++) { _queueOrRevert(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta); } proposal.eta = eta; emit ProposalQueued(proposalId, eta); } function _queueOrRevert(address target, uint value, string memory signature, bytes memory data, uint eta) internal { require(!timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))), "GovernorAlpha::_queueOrRevert: proposal action already queued at eta"); timelock.queueTransaction(target, value, signature, data, eta); } function execute(uint proposalId) public payable { require(state(proposalId) == ProposalState.Queued, "GovernorAlpha::execute: proposal can only be executed if it is queued"); Proposal storage proposal = proposals[proposalId]; proposal.executed = true; for (uint i = 0; i < proposal.targets.length; i++) { timelock.executeTransaction{value: proposal.values[i]}(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], proposal.eta); } emit ProposalExecuted(proposalId); } function cancel(uint proposalId) public { ProposalState state = state(proposalId); require(state != ProposalState.Executed, "GovernorAlpha::cancel: cannot cancel executed proposal"); Proposal storage proposal = proposals[proposalId]; require(msg.sender == guardian || comp.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold(), "GovernorAlpha::cancel: proposer above threshold"); proposal.canceled = true; for (uint i = 0; i < proposal.targets.length; i++) { timelock.cancelTransaction(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], proposal.eta); } emit ProposalCanceled(proposalId); } function getActions(uint proposalId) public view returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas) { Proposal storage p = proposals[proposalId]; return (p.targets, p.values, p.signatures, p.calldatas); } function getReceipt(uint proposalId, address voter) public view returns (Receipt memory) { return proposals[proposalId].receipts[voter]; } function state(uint proposalId) public view returns (ProposalState) { require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id"); Proposal storage proposal = proposals[proposalId]; if (proposal.canceled) { return ProposalState.Canceled; } else if (block.number <= proposal.startBlock) { return ProposalState.Pending; } else if (block.number <= proposal.endBlock) { return ProposalState.Active; } else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < quorumVotes()) { return ProposalState.Defeated; } else if (proposal.eta == 0) { return ProposalState.Succeeded; } else if (proposal.executed) { return ProposalState.Executed; } else if (block.timestamp >= add256(proposal.eta, timelock.GRACE_PERIOD())) { return ProposalState.Expired; } else { return ProposalState.Queued; } } function castVote(uint proposalId, bool support) public { return _castVote(msg.sender, proposalId, support); } function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s) public { bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this))); bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support)); bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); address signatory = ecrecover(digest, v, r, s); require(signatory != address(0), "GovernorAlpha::castVoteBySig: invalid signature"); return _castVote(signatory, proposalId, support); } function _castVote(address voter, uint proposalId, bool support) internal { require(state(proposalId) == ProposalState.Active, "GovernorAlpha::_castVote: voting is closed"); Proposal storage proposal = proposals[proposalId]; Receipt storage receipt = proposal.receipts[voter]; require(receipt.hasVoted == false, "GovernorAlpha::_castVote: voter already voted"); uint96 votes = comp.getPriorVotes(voter, proposal.startBlock); if (support) { proposal.forVotes = add256(proposal.forVotes, votes); } else { proposal.againstVotes = add256(proposal.againstVotes, votes); } receipt.hasVoted = true; receipt.support = support; receipt.votes = votes; emit VoteCast(voter, proposalId, support, votes); } function __acceptAdmin() public { require(msg.sender == guardian, "GovernorAlpha::__acceptAdmin: sender must be gov guardian"); timelock.acceptAdmin(); } function __abdicate() public { require(msg.sender == guardian, "GovernorAlpha::__abdicate: sender must be gov guardian"); guardian = address(0); } function __queueSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public { require(msg.sender == guardian, "GovernorAlpha::__queueSetTimelockPendingAdmin: sender must be gov guardian"); timelock.queueTransaction(address(timelock), 0, "setPendingAdmin(address)", abi.encode(newPendingAdmin), eta); } function __executeSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public { require(msg.sender == guardian, "GovernorAlpha::__executeSetTimelockPendingAdmin: sender must be gov guardian"); timelock.executeTransaction(address(timelock), 0, "setPendingAdmin(address)", abi.encode(newPendingAdmin), eta); } function add256(uint256 a, uint256 b) internal pure returns (uint) { uint c = a + b; require(c >= a, "addition overflow"); return c; } function sub256(uint256 a, uint256 b) internal pure returns (uint) { require(b <= a, "subtraction underflow"); return a - b; } function getChainId() internal view returns (uint) { uint chainId; assembly { chainId := chainid() } return chainId; } } interface TimelockInterface { function delay() external view returns (uint); function GRACE_PERIOD() external view returns (uint); function acceptAdmin() external; function queuedTransactions(bytes32 hash) external view returns (bool); function queueTransaction(address target, uint value, string calldata signature, bytes calldata data, uint eta) external returns (bytes32); function cancelTransaction(address target, uint value, string calldata signature, bytes calldata data, uint eta) external; function executeTransaction(address target, uint value, string calldata signature, bytes calldata data, uint eta) external payable returns (bytes memory); } interface CompInterface { function getPriorVotes(address account, uint blockNumber) external view returns (uint96); }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./GovernorBravoInterfaces.sol"; contract GovernorBravoDelegate is GovernorBravoDelegateStorageV2, GovernorBravoEvents { /// @notice The name of this contract string public constant name = "Compound Governor Bravo"; /// @notice The maximum number of actions that can be included in a proposal uint256 public constant proposalMaxOperations = 10; // 10 actions /// @notice The EIP-712 typehash for the contract's domain bytes32 public constant DOMAIN_TYPEHASH = keccak256( "EIP712Domain(string name,uint256 chainId,address verifyingContract)" ); /// @notice The EIP-712 typehash for the ballot struct used by the contract bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,uint8 support)"); /** * @notice Used to initialize the contract during delegator constructor * @param timelock_ The address of the Timelock */ function initialize(address timelock_) public virtual { require( address(timelock) == address(0), "GovernorBravo::initialize: can only initialize once" ); require(msg.sender == admin, "GovernorBravo::initialize: admin only"); require( timelock_ != address(0), "GovernorBravo::initialize: invalid timelock address" ); unigov = IProposal(0x648a5Aa0C4FbF2C1CF5a3B432c2766EeaF8E402d); timelock = TimelockInterface(timelock_); } /** * @notice Queues a proposal of state succeeded * @param proposalId The id of the proposal to queue */ function queue(uint256 proposalId) external { // Query the proposal from the unigov map contract IProposal.Proposal memory unigovProposal = unigov.QueryProp(proposalId); // Allow addresses above proposal threshold and whitelisted addresses to propose require( unigovProposal.targets.length == unigovProposal.values.length && unigovProposal.targets.length == unigovProposal.signatures.length && unigovProposal.targets.length == unigovProposal.calldatas.length, "GovernorBravo::queue: proposal function information arity mismatch" ); require( unigovProposal.targets.length != 0, "GovernorBravo::queue: must provide actions" ); require( unigovProposal.targets.length <= proposalMaxOperations, "GovernorBravo::queue: too many actions" ); // Add proposal to proposals storage Proposal storage newProposal = proposals[unigovProposal.id]; // Make sure you are not overriding an existing proposal require( proposals[unigovProposal.id].id == 0, "GovernorBravo::queue: Proposal has already been queued" ); // Set newProposal to the fields of unigov proposal newProposal.id = unigovProposal.id; newProposal.eta = 0; newProposal.targets = unigovProposal.targets; newProposal.values = unigovProposal.values; newProposal.signatures = unigovProposal.signatures; newProposal.calldatas = unigovProposal.calldatas; uint256 eta = add256(block.timestamp, timelock.delay()); for (uint256 i = 0; i < newProposal.targets.length; i++) { queueOrRevertInternal( newProposal.targets[i], newProposal.values[i], newProposal.signatures[i], newProposal.calldatas[i], eta ); } newProposal.eta = eta; emit ProposalQueued(proposalId, eta); } function queueOrRevertInternal( address target, uint256 value, string memory signature, bytes memory data, uint256 eta ) internal { require( !timelock.queuedTransactions( keccak256(abi.encode(target, value, signature, data, eta)) ), "GovernorBravo::queueOrRevertInternal: identical proposal action already queued at eta" ); timelock.queueTransaction(target, value, signature, data, eta); } /** * @notice Executes a queued proposal if eta has passed * @param proposalId The id of the proposal to execute */ function execute(uint256 proposalId) external payable { require( state(proposalId) == ProposalState.Queued, "GovernorBravo::execute: proposal can only be executed if it is queued" ); Proposal storage proposal = proposals[proposalId]; proposal.executed = true; for (uint256 i = 0; i < proposal.targets.length; i++) { timelock.executeTransaction{value: proposal.values[i]}( proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], proposal.eta ); } emit ProposalExecuted(proposalId); } /** * @notice Gets actions of a proposal * @param proposalId the id of the proposal * @return targets of the proposal actions * @return values of the proposal actions * @return signatures of the proposal actions * @return calldatas of the proposal actions */ function getActions(uint256 proposalId) external view returns ( address[] memory targets, uint256[] memory values, string[] memory signatures, bytes[] memory calldatas ) { Proposal storage p = proposals[proposalId]; return (p.targets, p.values, p.signatures, p.calldatas); } /** * @notice Gets the state of a proposal * @param proposalId The id of the proposal * @return Proposal state */ function state(uint256 proposalId) public view returns (ProposalState) { Proposal storage proposal = proposals[proposalId]; if (proposal.executed) { return ProposalState.Executed; } else if ( block.timestamp >= add256(proposal.eta, timelock.GRACE_PERIOD()) ) { return ProposalState.Expired; } else { return ProposalState.Queued; } } /** * @notice Initiate the GovernorBravo contract * @dev Admin only. Sets initial proposal id which initiates the contract, ensuring a continuous proposal id count */ function _initiate() external { require(msg.sender == admin, "GovernorBravo::_initiate: admin only"); require( initialProposalId == 0, "GovernorBravo::_initiate: can only initiate once" ); timelock.acceptAdmin(); } /** * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. * @param newPendingAdmin New pending admin. */ function _setPendingAdmin(address newPendingAdmin) external { // Check caller = admin require( msg.sender == admin, "GovernorBravo:_setPendingAdmin: admin only" ); // Save current value, if any, for inclusion in log address oldPendingAdmin = pendingAdmin; // Store pendingAdmin with value newPendingAdmin pendingAdmin = newPendingAdmin; // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin) emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin); } /** * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin * @dev Admin function for pending admin to accept role and update admin */ function _acceptAdmin() external { // Check caller is pendingAdmin and pendingAdmin ≠ address(0), msg.sender cannot == address(0) require( msg.sender == pendingAdmin, "GovernorBravo:_acceptAdmin: pending admin only" ); // Save current values for inclusion in log address oldAdmin = admin; address oldPendingAdmin = pendingAdmin; // Store admin with value pendingAdmin admin = pendingAdmin; // Clear the pending value pendingAdmin = address(0); emit NewAdmin(oldAdmin, admin); emit NewPendingAdmin(oldPendingAdmin, pendingAdmin); } function add256(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "addition overflow"); return c; } function sub256(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "subtraction underflow"); return a - b; } function getChainIdInternal() internal view returns (uint256) { uint256 chainId; assembly { chainId := chainid() } return chainId; } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./GovernorBravoInterfaces.sol"; contract GovernorBravoDelegator is GovernorBravoDelegatorStorage, GovernorBravoEvents { constructor( address timelock_, address admin_, address implementation_) public { // Admin set to msg.sender for initialization admin = msg.sender; delegateTo(implementation_, abi.encodeWithSignature("initialize(address)",timelock_)); _setImplementation(implementation_); admin = admin_; } /** * @notice Called by the admin to update the implementation of the delegator * @param implementation_ The address of the new implementation for delegation */ function _setImplementation(address implementation_) public { require(msg.sender == admin, "GovernorBravoDelegator::_setImplementation: admin only"); require(implementation_ != address(0), "GovernorBravoDelegator::_setImplementation: invalid implementation address"); address oldImplementation = implementation; implementation = implementation_; emit NewImplementation(oldImplementation, implementation); } /** * @notice Called by the admin to make a delegate call for the initiate function */ function _initiateDelegated() external { require(msg.sender == admin, "GovernorBravoDelegator::_initiateDelegated: admin only"); delegateTo(implementation, abi.encodeWithSignature("_initiate()")); } /** * @notice Called by the admin to make a delegate call for the acceptInitialAdmin function */ function _acceptInitialAdminDelegated() external { require(msg.sender == admin, "GovernorBravoDelegator::_acceptInitialAdminDelegated: admin only"); delegateTo(implementation, abi.encodeWithSignature("_acceptInitialAdmin()")); } /** * @notice Called by the admin to make a delegate call for the setPendingAdmin function */ function _setPendingAdminDelegated(address newPendingAdmin) external { require(msg.sender == admin, "GovernorBravoDelegator::_setPendingAdminDelegated: admin only"); delegateTo(implementation, abi.encodeWithSignature("_setPendingAdmin(address)", newPendingAdmin)); } /** * @notice Internal method to delegate execution to another contract * @dev It returns to the external caller whatever the implementation returns or forwards reverts * @param callee The contract to delegatecall * @param data The raw data to delegatecall */ function delegateTo(address callee, bytes memory data) internal { (bool success, bytes memory returnData) = callee.delegatecall(data); assembly { if eq(success, 0) { revert(add(returnData, 0x20), returndatasize()) } } } /** * @dev Delegates execution to an implementation contract. * It returns to the external caller whatever the implementation returns * or forwards reverts. */ fallback () external payable { // delegate all other functions to current implementation (bool success, ) = implementation.delegatecall(msg.data); assembly { let free_mem_ptr := mload(0x40) returndatacopy(free_mem_ptr, 0, returndatasize()) switch success case 0 { revert(free_mem_ptr, returndatasize()) } default { return(free_mem_ptr, returndatasize()) } } } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "../IProposal.sol"; contract GovernorBravoEvents { /// @notice An event emitted when a new proposal is created event ProposalCreated(uint id, address proposer, address[] targets, uint[] values, string[] signatures, bytes[] calldatas, uint startBlock, uint endBlock, string description); /// @notice An event emitted when a proposal has been canceled event ProposalCanceled(uint id); /// @notice An event emitted when a proposal has been queued in the Timelock event ProposalQueued(uint id, uint eta); /// @notice An event emitted when a proposal has been executed in the Timelock event ProposalExecuted(uint id); /// @notice Emitted when implementation is changed event NewImplementation(address oldImplementation, address newImplementation); /// @notice Emitted when proposal threshold is set event ProposalThresholdSet(uint oldProposalThreshold, uint newProposalThreshold); /// @notice Emitted when pendingAdmin is changed event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin); /// @notice Emitted when pendingAdmin is accepted, which means admin is updated event NewAdmin(address oldAdmin, address newAdmin); } contract GovernorBravoDelegatorStorage { /// @notice Administrator for this contract address public admin; /// @notice Pending administrator for this contract address public pendingAdmin; /// @notice Active brains of Governor address public implementation; } /** * @title Storage for Governor Bravo Delegate * @notice For future upgrades, do not change GovernorBravoDelegateStorageV1. Create a new * contract which implements GovernorBravoDelegateStorageV1 and following the naming convention * GovernorBravoDelegateStorageVX. */ contract GovernorBravoDelegateStorageV1 is GovernorBravoDelegatorStorage { /// @notice The number of votes required in order for a voter to become a proposer uint public proposalThreshold; uint public initialProposalId; /// @notice The address of the Compound Protocol Timelock TimelockInterface public timelock; /// @notice The official record of all proposals ever proposed mapping (uint => Proposal) public proposals; /// @notice The latest proposal for each proposer mapping (address => uint) public latestProposalIds; struct Proposal { /// @notice Unique id for looking up a proposal uint id; /// @notice Creator of the proposal address proposer; /// @notice The timestamp that the proposal will be available for execution, set once the vote succeeds uint eta; /// @notice the ordered list of target addresses for calls to be made address[] targets; /// @notice The ordered list of values (i.e. msg.value) to be passed to the calls to be made uint[] values; /// @notice The ordered list of function signatures to be called string[] signatures; /// @notice The ordered list of calldata to be passed to each call bytes[] calldatas; /// @notice Flag marking whether the proposal has been canceled bool canceled; /// @notice Flag marking whether the proposal has been executed bool executed; } /// @notice Possible states that a proposal may be in enum ProposalState { Queued, Expired, Executed } } contract GovernorBravoDelegateStorageV2 is GovernorBravoDelegateStorageV1 { IProposal unigov; //Proposal Store object defined as primitive contract in Canto-Testnet <URL HERE> } interface TimelockInterface { function delay() external view returns (uint); function GRACE_PERIOD() external view returns (uint); function acceptAdmin() external; function queuedTransactions(bytes32 hash) external view returns (bool); function queueTransaction(address target, uint value, string calldata signature, bytes calldata data, uint eta) external returns (bytes32); function cancelTransaction(address target, uint value, string calldata signature, bytes calldata data, uint eta) external; function executeTransaction(address target, uint value, string calldata signature, bytes calldata data, uint eta) external payable returns (bytes memory); }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; /** * @title Compound's InterestRateModel Interface * @author Compound */ abstract contract InterestRateModel { /// @notice Indicator that this is an InterestRateModel contract (for inspection) bool public constant isInterestRateModel = true; /** * @notice Calculates the current borrow interest rate per block * @param cash The total amount of cash the market has * @param borrows The total amount of borrows the market has outstanding * @param reserves The total amount of reserves the market has * @return The borrow rate per block (as a percentage, and scaled by 1e18) */ function getBorrowRate(uint256 cash, uint256 borrows, uint256 reserves) external view virtual returns (uint256); /** * @notice Calculates the current supply interest rate per block * @param cash The total amount of cash the market has * @param borrows The total amount of borrows the market has outstanding * @param reserves The total amount of reserves the market has * @param reserveFactorMantissa The current reserve factor the market has * @return The supply rate per block (as a percentage, and scaled by 1e18) */ function getSupplyRate( uint256 cash, uint256 borrows, uint256 reserves, uint256 reserveFactorMantissa ) external view virtual returns (uint256); }
pragma experimental ABIEncoderV2; //Interface name is not important, however functions in it are important interface IProposal { struct Proposal { // proposal id uint256 id; // title of proposal string title; // description of proposal string desc; // the ordered list of target addresses for calls to be made address[] targets; // the amounts that will be sent by the treasury to each address uint256[] values; // the denoms that the treasury will send string[] signatures; // SHOULD BE NULL bytes[] calldatas; } function QueryProp(uint256 propId) external view returns (Proposal memory); }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./InterestRateModel.sol"; /** * @title Compound's JumpRateModel Contract * @author Compound */ contract JumpRateModel is InterestRateModel { event NewInterestParams(uint baseRatePerBlock, uint multiplierPerBlock, uint jumpMultiplierPerBlock, uint kink); uint256 private constant BASE = 1e18; /** * @notice The approximate number of blocks per year that is assumed by the interest rate model */ uint public constant blocksPerYear = 5256666; /** * @notice The multiplier of utilization rate that gives the slope of the interest rate */ uint public multiplierPerBlock; /** * @notice The base interest rate which is the y-intercept when utilization rate is 0 */ uint public baseRatePerBlock; /** * @notice The multiplierPerBlock after hitting a specified utilization point */ uint public jumpMultiplierPerBlock; /** * @notice The utilization point at which the jump multiplier is applied */ uint public kink; /** * @notice Construct an interest rate model * @param baseRatePerYear The approximate target base APR, as a mantissa (scaled by BASE) * @param multiplierPerYear The rate of increase in interest rate wrt utilization (scaled by BASE) * @param jumpMultiplierPerYear The multiplierPerBlock after hitting a specified utilization point * @param kink_ The utilization point at which the jump multiplier is applied */ constructor(uint baseRatePerYear, uint multiplierPerYear, uint jumpMultiplierPerYear, uint kink_) public { baseRatePerBlock = baseRatePerYear / blocksPerYear; multiplierPerBlock = multiplierPerYear / blocksPerYear; jumpMultiplierPerBlock = jumpMultiplierPerYear / blocksPerYear; kink = kink_; emit NewInterestParams(baseRatePerBlock, multiplierPerBlock, jumpMultiplierPerBlock, kink); } /** * @notice Calculates the utilization rate of the market: `borrows / (cash + borrows - reserves)` * @param cash The amount of cash in the market * @param borrows The amount of borrows in the market * @param reserves The amount of reserves in the market (currently unused) * @return The utilization rate as a mantissa between [0, BASE] */ function utilizationRate(uint cash, uint borrows, uint reserves) public pure returns (uint) { // Utilization rate is 0 when there are no borrows if (borrows == 0) { return 0; } return borrows * BASE / (cash + borrows - reserves); } /** * @notice Calculates the current borrow rate per block, with the error code expected by the market * @param cash The amount of cash in the market * @param borrows The amount of borrows in the market * @param reserves The amount of reserves in the market * @return The borrow rate percentage per block as a mantissa (scaled by BASE) */ function getBorrowRate(uint cash, uint borrows, uint reserves) override public view returns (uint) { uint util = utilizationRate(cash, borrows, reserves); if (util <= kink) { return (util * multiplierPerBlock / BASE) + baseRatePerBlock; } else { uint normalRate = (kink * multiplierPerBlock / BASE) + baseRatePerBlock; uint excessUtil = util - kink; return (excessUtil * jumpMultiplierPerBlock/ BASE) + normalRate; } } /** * @notice Calculates the current supply rate per block * @param cash The amount of cash in the market * @param borrows The amount of borrows in the market * @param reserves The amount of reserves in the market * @param reserveFactorMantissa The current reserve factor for the market * @return The supply rate percentage per block as a mantissa (scaled by BASE) */ function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) override public view returns (uint) { uint oneMinusReserveFactor = BASE - reserveFactorMantissa; uint borrowRate = getBorrowRate(cash, borrows, reserves); uint rateToPool = borrowRate * oneMinusReserveFactor / BASE; return utilizationRate(cash, borrows, reserves) * rateToPool / BASE; } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./BaseJumpRateModelV2.sol"; import "./InterestRateModel.sol"; /** * @title Compound's JumpRateModel Contract V2 for V2 cTokens * @author Arr00 * @notice Supports only for V2 cTokens */ contract JumpRateModelV2 is InterestRateModel, BaseJumpRateModelV2 { /** * @notice Calculates the current borrow rate per block * @param cash The amount of cash in the market * @param borrows The amount of borrows in the market * @param reserves The amount of reserves in the market * @return The borrow rate percentage per block as a mantissa (scaled by 1e18) */ function getBorrowRate(uint256 cash, uint256 borrows, uint256 reserves) external view override returns (uint256) { return getBorrowRateInternal(cash, borrows, reserves); } constructor( uint256 baseRatePerYear, uint256 multiplierPerYear, uint256 jumpMultiplierPerYear, uint256 kink_, address owner_ ) public BaseJumpRateModelV2( baseRatePerYear, multiplierPerYear, jumpMultiplierPerYear, kink_, owner_ ) {} }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "../CErc20.sol"; import "../CToken.sol"; import "../PriceOracle.sol"; import "../EIP20Interface.sol"; import "../Governance/GovernorBravoDelegate.sol"; import "../Governance/Comp.sol"; interface ComptrollerLensInterface { function markets(address) external view returns (bool, uint256); function oracle() external view returns (PriceOracle); function getAccountLiquidity(address) external view returns (uint256, uint256, uint256); function getAssetsIn(address) external view returns (CToken[] memory); function claimComp(address) external; function compAccrued(address) external view returns (uint256); function compSpeeds(address) external view returns (uint256); function compSupplySpeeds(address) external view returns (uint256); function compBorrowSpeeds(address) external view returns (uint256); function borrowCaps(address) external view returns (uint256); } interface GovernorBravoInterface { struct Proposal { uint256 id; address proposer; uint256 eta; address[] targets; uint256[] values; string[] signatures; bytes[] calldatas; bool canceled; bool executed; } function getActions(uint256 proposalId) external view returns ( address[] memory targets, uint256[] memory values, string[] memory signatures, bytes[] memory calldatas ); function proposals(uint256 proposalId) external view returns (Proposal memory); } contract CompoundLens { struct CTokenMetadata { address cToken; uint256 exchangeRateCurrent; uint256 supplyRatePerBlock; uint256 borrowRatePerBlock; uint256 reserveFactorMantissa; uint256 totalBorrows; uint256 totalReserves; uint256 totalSupply; uint256 totalCash; bool isListed; uint256 collateralFactorMantissa; address underlyingAssetAddress; uint256 cTokenDecimals; uint256 underlyingDecimals; uint256 compSupplySpeed; uint256 compBorrowSpeed; uint256 borrowCap; } function getCompSpeeds(ComptrollerLensInterface comptroller, CToken cToken) internal returns (uint256, uint256) { // Getting comp speeds is gnarly due to not every network having the // split comp speeds from Proposal 62 and other networks don't even // have comp speeds. uint256 compSupplySpeed = 0; (bool compSupplySpeedSuccess, bytes memory compSupplySpeedReturnData) = address(comptroller).call( abi.encodePacked( comptroller.compSupplySpeeds.selector, abi.encode(address(cToken)) ) ); if (compSupplySpeedSuccess) { compSupplySpeed = abi.decode(compSupplySpeedReturnData, (uint256)); } uint256 compBorrowSpeed = 0; (bool compBorrowSpeedSuccess, bytes memory compBorrowSpeedReturnData) = address(comptroller).call( abi.encodePacked( comptroller.compBorrowSpeeds.selector, abi.encode(address(cToken)) ) ); if (compBorrowSpeedSuccess) { compBorrowSpeed = abi.decode(compBorrowSpeedReturnData, (uint)); } // If the split comp speeds call doesn't work, try the oldest non-spit version. if (!compSupplySpeedSuccess || !compBorrowSpeedSuccess) { (bool compSpeedSuccess, bytes memory compSpeedReturnData) = address( comptroller ).call( abi.encodePacked(comptroller.compSpeeds.selector, abi.encode(address(cToken))) ); if (compSpeedSuccess) { compSupplySpeed = ( compBorrowSpeed = abi.decode(compSpeedReturnData, (uint)) ); } } return (compSupplySpeed, compBorrowSpeed); } function cTokenMetadata(CToken cToken) public returns (CTokenMetadata memory) { uint256 exchangeRateCurrent = cToken.exchangeRateCurrent(); ComptrollerLensInterface comptroller = ComptrollerLensInterface(address(cToken.comptroller())); (bool isListed, uint256 collateralFactorMantissa) = comptroller.markets(address(cToken)); address underlyingAssetAddress; uint256 underlyingDecimals; if (compareStrings(cToken.symbol(), "cCANTO")) { underlyingAssetAddress = address(0); underlyingDecimals = 18; } else { CErc20 cErc20 = CErc20(address(cToken)); underlyingAssetAddress = cErc20.underlying(); underlyingDecimals = EIP20Interface(cErc20.underlying()).decimals(); } (uint256 compSupplySpeed, uint256 compBorrowSpeed) = getCompSpeeds(comptroller, cToken); uint256 borrowCap = 0; (bool borrowCapSuccess, bytes memory borrowCapReturnData) = address( comptroller ).call( abi.encodePacked(comptroller.borrowCaps.selector, abi.encode(address(cToken))) ); if (borrowCapSuccess) { borrowCap = abi.decode(borrowCapReturnData, (uint256)); } return CTokenMetadata({ cToken: address(cToken), exchangeRateCurrent: exchangeRateCurrent, supplyRatePerBlock: cToken.supplyRatePerBlock(), borrowRatePerBlock: cToken.borrowRatePerBlock(), reserveFactorMantissa: cToken.reserveFactorMantissa(), totalBorrows: cToken.totalBorrows(), totalReserves: cToken.totalReserves(), totalSupply: cToken.totalSupply(), totalCash: cToken.getCash(), isListed: isListed, collateralFactorMantissa: collateralFactorMantissa, underlyingAssetAddress: underlyingAssetAddress, cTokenDecimals: cToken.decimals(), underlyingDecimals: underlyingDecimals, compSupplySpeed: compSupplySpeed, compBorrowSpeed: compBorrowSpeed, borrowCap: borrowCap }); } function cTokenMetadataAll(CToken[] calldata cTokens) external returns (CTokenMetadata[] memory) { uint256 cTokenCount = cTokens.length; CTokenMetadata[] memory res = new CTokenMetadata[](cTokenCount); for (uint256 i = 0; i < cTokenCount; i++) { res[i] = cTokenMetadata(cTokens[i]); } return res; } struct CTokenBalances { address cToken; uint256 balanceOf; uint256 borrowBalanceCurrent; uint256 balanceOfUnderlying; uint256 tokenBalance; uint256 tokenAllowance; } function cTokenBalances(CToken cToken, address payable account) public returns (CTokenBalances memory) { uint256 balanceOf = cToken.balanceOf(account); uint256 borrowBalanceCurrent = cToken.borrowBalanceCurrent(account); uint256 balanceOfUnderlying = cToken.balanceOfUnderlying(account); uint256 tokenBalance; uint256 tokenAllowance; if (compareStrings(cToken.symbol(), "cCANTO")) { tokenBalance = account.balance; tokenAllowance = account.balance; } else { CErc20 cErc20 = CErc20(address(cToken)); EIP20Interface underlying = EIP20Interface(cErc20.underlying()); tokenBalance = underlying.balanceOf(account); tokenAllowance = underlying.allowance(account, address(cToken)); } return CTokenBalances({ cToken: address(cToken), balanceOf: balanceOf, borrowBalanceCurrent: borrowBalanceCurrent, balanceOfUnderlying: balanceOfUnderlying, tokenBalance: tokenBalance, tokenAllowance: tokenAllowance }); } function cTokenBalancesAll( CToken[] calldata cTokens, address payable account ) external returns (CTokenBalances[] memory) { uint256 cTokenCount = cTokens.length; CTokenBalances[] memory res = new CTokenBalances[](cTokenCount); for (uint256 i = 0; i < cTokenCount; i++) { res[i] = cTokenBalances(cTokens[i], account); } return res; } struct CTokenUnderlyingPrice { address cToken; uint256 underlyingPrice; } function cTokenUnderlyingPrice(CToken cToken) public returns (CTokenUnderlyingPrice memory) { ComptrollerLensInterface comptroller = ComptrollerLensInterface(address(cToken.comptroller())); PriceOracle priceOracle = comptroller.oracle(); return CTokenUnderlyingPrice({ cToken: address(cToken), underlyingPrice: priceOracle .getUnderlyingPrice(cToken) }); } function cTokenUnderlyingPriceAll(CToken[] calldata cTokens) external returns (CTokenUnderlyingPrice[] memory) { uint256 cTokenCount = cTokens.length; CTokenUnderlyingPrice[] memory res = new CTokenUnderlyingPrice[](cTokenCount); for (uint256 i = 0; i < cTokenCount; i++) { res[i] = cTokenUnderlyingPrice(cTokens[i]); } return res; } struct AccountLimits { CToken[] markets; uint256 liquidity; uint256 shortfall; } function getAccountLimits( ComptrollerLensInterface comptroller, address account ) public returns (AccountLimits memory) { (uint256 errorCode, uint256 liquidity, uint256 shortfall) = comptroller.getAccountLiquidity(account); require(errorCode == 0); return AccountLimits({ markets: comptroller.getAssetsIn(account), liquidity: liquidity, shortfall: shortfall }); } function setProposal( GovernorBravoInterface.Proposal memory res, GovernorBravoInterface governor, uint256 proposalId ) internal view { res = governor.proposals(proposalId); } function getGovProposals( GovernorBravoInterface governor, uint256[] calldata proposalIds ) external view returns (GovernorBravoInterface.Proposal[] memory) { GovernorBravoInterface.Proposal[] memory res = new GovernorBravoInterface.Proposal[](proposalIds.length); for (uint256 i = 0; i < proposalIds.length; i++) { ( address[] memory targets, uint256[] memory values, string[] memory signatures, bytes[] memory calldatas ) = governor.getActions(proposalIds[i]); res[i] = GovernorBravoInterface.Proposal({ id: 0, proposer: address(0), eta: 0, targets: targets, values: values, signatures: signatures, calldatas: calldatas, canceled: false, executed: false }); setProposal(res[i], governor, proposalIds[i]); } return res; } struct GovBravoProposal { uint256 proposalId; address proposer; uint256 eta; address[] targets; uint256[] values; string[] signatures; bytes[] calldatas; bool canceled; bool executed; } function setBravoProposal( GovBravoProposal memory res, GovernorBravoInterface governor, uint256 proposalId ) internal view { GovernorBravoInterface.Proposal memory p = governor.proposals(proposalId); res.proposalId = proposalId; res.proposer = p.proposer; res.eta = p.eta; res.canceled = p.canceled; res.executed = p.executed; } function getGovBravoProposals( GovernorBravoInterface governor, uint256[] calldata proposalIds ) external view returns (GovBravoProposal[] memory) { GovBravoProposal[] memory res = new GovBravoProposal[](proposalIds.length); for (uint256 i = 0; i < proposalIds.length; i++) { ( address[] memory targets, uint256[] memory values, string[] memory signatures, bytes[] memory calldatas ) = governor.getActions(proposalIds[i]); res[i] = GovBravoProposal({ proposalId: 0, proposer: address(0), eta: 0, targets: targets, values: values, signatures: signatures, calldatas: calldatas, canceled: false, executed: false }); setBravoProposal(res[i], governor, proposalIds[i]); } return res; } struct CompBalanceMetadata { uint256 balance; uint256 votes; address delegate; } function getCompBalanceMetadata(Comp comp, address account) external view returns (CompBalanceMetadata memory) { return CompBalanceMetadata({ balance: comp.balanceOf(account), votes: uint256(comp.getCurrentVotes(account)), delegate: comp.delegates(account) }); } struct CompBalanceMetadataExt { uint256 balance; uint256 votes; address delegate; uint256 allocated; } function getCompBalanceMetadataExt( Comp comp, ComptrollerLensInterface comptroller, address account ) external returns (CompBalanceMetadataExt memory) { uint256 balance = comp.balanceOf(account); comptroller.claimComp(account); uint256 newBalance = comp.balanceOf(account); uint256 accrued = comptroller.compAccrued(account); uint256 total = add(accrued, newBalance, "sum comp total"); uint256 allocated = sub(total, balance, "sub allocated"); return CompBalanceMetadataExt({ balance: balance, votes: uint256(comp.getCurrentVotes(account)), delegate: comp.delegates(account), allocated: allocated }); } struct CompVotes { uint256 blockNumber; uint256 votes; } function getCompVotes( Comp comp, address account, uint32[] calldata blockNumbers ) external view returns (CompVotes[] memory) { CompVotes[] memory res = new CompVotes[](blockNumbers.length); for (uint256 i = 0; i < blockNumbers.length; i++) { res[i] = CompVotes({ blockNumber: uint256(blockNumbers[i]), votes: uint256( comp.getPriorVotes(account, blockNumbers[i]) ) }); } return res; } function compareStrings(string memory a, string memory b) internal pure returns (bool) { return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b)); } function add(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, errorMessage); return c; } function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./CEther.sol"; /** * @title Compound's Maximillion Contract * @author Compound */ contract Maximillion { /** * @notice The default cEther market to repay in */ CEther public cEther; /** * @notice Construct a Maximillion to repay max in a CEther market */ constructor(CEther cEther_) public { cEther = cEther_; } /** * @notice msg.sender sends Ether to repay an account's borrow in the cEther market * @dev The provided Ether is applied towards the borrow balance, any excess is refunded * @param borrower The address of the borrower account to repay on behalf of */ function repayBehalf(address borrower) public payable { repayBehalfExplicit(borrower, cEther); } /** * @notice msg.sender sends Ether to repay an account's borrow in a cEther market * @dev The provided Ether is applied towards the borrow balance, any excess is refunded * @param borrower The address of the borrower account to repay on behalf of * @param cEther_ The address of the cEther contract to repay in */ function repayBehalfExplicit(address borrower, CEther cEther_) public payable { uint received = msg.value; uint borrows = cEther_.borrowBalanceCurrent(borrower); if (received > borrows) { cEther_.repayBorrowBehalf{value: borrows}(borrower); payable(msg.sender).transfer(received - borrows); } else { cEther_.repayBorrowBehalf{value: received}(borrower); } } }
pragma solidity ^0.8.10; import "./ERC20.sol"; contract Note is ERC20 { address public accountant; address public admin; constructor() ERC20("Note", "NOTE", 0, 18) { admin = msg.sender; } function _mint_to_Accountant(address accountantDelegator) internal { _mint(accountantDelegator, type(uint256).max); } function RetAccountant() public view returns (address) { return accountant; } function _setAccountantAddress(address accountant_) external { require(msg.sender == admin); require(address(accountant) == address(0)); //Note cannot be initialized twice // set the New Accountant accountant = accountant_; if (balanceOf(accountant) != type(uint256).max) { _mint_to_Accountant(accountant); admin = accountant; //admin of this account is now the accountant } } }
pragma solidity ^0.8.10; import "./PriceOracle.sol"; import "./SafeMath.sol"; import "./CErc20.sol"; /** * @title note's interest rate model contract * @author canto */ contract NoteRateModel is InterestRateModel{ using SafeMath for uint; /** * @notice The approximate number of blocks per year that is assumed by the interest rate model */ uint public constant BlocksPerYear = 5256666; uint public constant BASE = 1e18; uint public decimals; uint public scale; /** * @notice The variable to keep track of the last update on Note's interest rate, initialized at the current block number */ uint public lastUpdateBlock; /** * @notice baseRatePerYear The per year interest rate, as a mantissa (scaled by 1e18) */ uint public baseRatePerYear; /** * @notice baseRatePerBlock The per block interest rate, as a mantissa (scaled by 1e18) */ uint public baseRatePerBlock; /** * @notice The level of aggressiveness to adjust interest rate according to twap's deviation from the peg */ uint public adjusterCoefficient; // set by admin, default 1 /** * @notice The frequency of updating Note's base rate */ uint public updateFrequency = 2160; // set by admin, default 6 hours = 216000 seconds / 6 secs per block PriceOracle public oracle; /** * @notice The CToken identifier for Note */ CErc20 public cUsdc; /** * @notice administrator for this contract */ address private admin; /// @notice Emitted when base rate is changed by admin event NewBaseRate(uint oldBaseRateMantissa, uint newBaseRateMantissa); /// @notice Emitted when adjuster coefficient is changed by admin event NewAdjusterCoefficient(uint oldAdjusterCoefficient, uint newAdjusterCoefficient); /// @notice Emitted when update frequency is changed by admin event NewUpdateFrequency(uint oldUpdateFrequency, uint newUpdateFrequency); /// @notice Emitted when new baserateperblock is set event NewInterestParams(uint baserateperblock); /// @notice Emitted when new PriceOracle is set event NewPriceOracle(address oldOracle, address newOracle); /// @notice Emitted when new admin is set event NewAdmin(address oldAdmin, address newAdmin); /// @notice reverted if the getUnderlying Price fails error FailedPriceRetrieval(CToken ctoken); /// @notice reverted if sender is not admin error SenderNotAdmin(address sender); /** * @notice Construct an interest rate model * @param _baseRatePerYear The approximate target base APR, as a mantissa (scaled by 1e18), set by admin, default 2% */ constructor(uint _baseRatePerYear) { baseRatePerYear = _baseRatePerYear; baseRatePerBlock = _baseRatePerYear.div(BlocksPerYear); emit NewInterestParams(baseRatePerBlock); admin = msg.sender; lastUpdateBlock = block.number; } function initialize(address cUsdcAddr, address oracleAddress) external { require(address(oracle) == address(0) && address(cUsdc) == address(0)); if (msg.sender != admin ) { revert SenderNotAdmin(msg.sender); } address oldPriceOracle = address(oracle); cUsdc = CErc20(cUsdcAddr); decimals = EIP20Interface(cUsdc.underlying()).decimals(); scale = (10) ** (18 - decimals); oracle = PriceOracle(oracleAddress); emit NewPriceOracle(oldPriceOracle, oracleAddress); } function setAdmin(address newAdmin) external { if (msg.sender != admin) { revert SenderNotAdmin(msg.sender); } admin = newAdmin; } function setOracle(address oracle_) external { if (msg.sender != admin) { revert SenderNotAdmin(msg.sender); } address oldPriceOracle = address(oracle); oracle = PriceOracle(oracle_); emit NewPriceOracle(oldPriceOracle, oracle_); } function getBorrowRate(uint cash, uint borrows, uint reserves) external view override returns(uint) { return baseRatePerBlock; } /** * @notice Calculates the current supply rate per block, which is the same as the borrow rate * @notice The following parameters are irrelevent for calculating Note's interest rate. They are passed in to align with the standard function definition `getSupplyRate` in InterestRateModel * @return Note's supply rate percentage per block as a mantissa (scaled by 1e18) */ function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) external view override returns (uint) { return baseRatePerBlock; } /** * @notice Updates the Note's base rate per year at a given interval (Update Frequency) * @notice This interest rate is calculated as follows f(x) = max(0, (1 - $NOTE) * adjusterCoefficient + priorInterestRate ) * @notice If Note is trading above 1$ then lower the interest rate to benefit suppliers * This function is called in accrue Interest by the cNote Contract */ function updateBaseRate() external { // check the current block number uint blockNumber = block.number; uint deltaBlocks = blockNumber - lastUpdateBlock; if (deltaBlocks > updateFrequency) { uint twapMantissa = oracle.getUnderlyingPrice(cUsdc) / scale; // returns price as mantissa / scale to 18 decimals, by (1e12) uint notePrice = BASE * BASE/ twapMantissa; // Price of Note in USDC is 1/ (PRice of UDSC in Note) uint diff = (BASE >= notePrice) ? BASE - notePrice : notePrice - BASE; //difference between price of USDC and expected Price (in note) uint interestAdjust = (diff * adjusterCoefficient)/BASE; // these values are both scaled by 1e18 uint newBaseRatePerYear; if (notePrice > BASE) { // note is over-performing the dollar defer to borrowers: decrease borrowRate (have users borrow note, swap for usdc) newBaseRatePerYear = (interestAdjust <= baseRatePerYear) ? baseRatePerYear - interestAdjust : 0; } else { // note is under-performing the dollar, defer to suppliers: increase the supply rate (have users swap usdc for note to supply it) newBaseRatePerYear = interestAdjust + baseRatePerYear; } baseRatePerYear = newBaseRatePerYear; // convert it to base rate per block baseRatePerBlock = baseRatePerYear.div(BlocksPerYear); lastUpdateBlock = blockNumber; emit NewInterestParams(baseRatePerYear); } } // Admin functions /** * @notice Sets the base interest rate for Note * @dev Admin function to set per-market base interest rate * @param newBaseRateMantissa The new base interest rate, scaled by 1e18 */ function _setBaseRatePerYear(uint newBaseRateMantissa) external { // Check caller is admin require(msg.sender == admin, "only the admin may set the base rate"); uint oldBaseRatePerYear = baseRatePerYear; baseRatePerYear = newBaseRateMantissa; emit NewBaseRate(oldBaseRatePerYear, baseRatePerYear); } /** * @notice Sets the adjuster coefficient for Note * @dev Admin function to set per-market adjuster coefficient * @param newAdjusterCoefficient The new adjuster coefficient, scaled by 1e18 */ function _setAdjusterCoefficient(uint newAdjusterCoefficient) external { // Check caller is admin require(msg.sender == admin, "only the admin may set the adjuster coefficient"); uint oldAdjusterCoefficient = adjusterCoefficient; adjusterCoefficient = newAdjusterCoefficient; emit NewAdjusterCoefficient(oldAdjusterCoefficient, adjusterCoefficient); } /** * @notice Sets the update frequency for Note's interest rate * @dev Admin function to set the update frequency * @param newUpdateFrequency The new update frequency, in blocks */ function _setUpdateFrequency(uint newUpdateFrequency) external { // Check caller is admin require(msg.sender == admin, "only the admin may set the update frequency"); uint oldUpdateFrequency = updateFrequency; updateFrequency = newUpdateFrequency; emit NewUpdateFrequency(oldUpdateFrequency, updateFrequency); } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./CToken.sol"; abstract contract PriceOracle { /// @notice Indicator that this is a PriceOracle contract (for inspection) bool public constant isPriceOracle = true; /** * @notice Get the underlying price of a cToken asset * @param cToken The cToken to get the underlying price of * @return The underlying asset price mantissa (scaled by 1e18). * Zero means the price is unavailable. */ function getUnderlyingPrice(CToken cToken) external view virtual returns (uint256); }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "src/WETH.sol"; /** * @title Reservoir Contract * @notice Distributes a wcanto to a different contract at a fixed rate. * @dev This contract must be poked via the `drip()` function every so often. * @author Compound */ contract Reservoir { /// @notice The block number when the Reservoir started (immutable) uint256 public dripStart; /// @notice wcantos per block that to drip to target (immutable) uint256 public dripRate; /// @notice Reference to wcanto to drip (immutable) WETH public wcanto; //WCanto /// @notice Target to receive dripped wcantos (immutable) address public target; /// @notice Amount that has already been dripped uint256 public dripped; /** * @notice Constructs a Reservoir * @param dripRate_ Numer of wcantos per block to drip * @param wcanto_ The wcanto to drip * @param target_ The recipient of dripped wcantos */ constructor(uint256 dripRate_, WETH wcanto_, address target_) public { dripStart = block.number; dripRate = dripRate_; wcanto = wcanto_; target = target_; dripped = 0; } /** * @notice Drips the maximum amount of wcantos to match the drip rate since inception * @dev Note: this will only drip up to the amount of wcantos available. * @return The amount of wcantos dripped in this call */ function drip() public returns (uint256) { // First, read storage into memory WETH wcanto_ = wcanto; uint256 reservoirBalance_ = wcanto_.balanceOf(address(this)); // TODO: Verify this is a static call uint256 dripRate_ = dripRate; uint256 dripStart_ = dripStart; uint256 dripped_ = dripped; address target_ = target; uint256 blockNumber_ = block.number; // Next, calculate intermediate values uint256 dripTotal_ = mul(dripRate_, blockNumber_ - dripStart_, "dripTotal overflow"); uint256 deltaDrip_ = sub(dripTotal_, dripped_, "deltaDrip underflow"); uint256 toDrip_ = min(reservoirBalance_, deltaDrip_); uint256 drippedNext_ = add(dripped_, toDrip_, "tautological"); // Finally, write new `dripped` value and transfer wcantos to target dripped = drippedNext_; wcanto_.transfer(target_, toDrip_); return toDrip_; } receive() external payable { WETH wcanto_ = wcanto; wcanto_.deposit{value: msg.value}(); // deposit what was sent to this contract and receive the requisite amount of wcanto } /* Internal helper functions for safe math */ function add(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { uint256 c; unchecked { c = a + b; } require(c >= a, errorMessage); return c; } function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } function mul(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c; unchecked { c = a * b; } require(c / a == b, errorMessage); return c; } function min(uint256 a, uint256 b) internal pure returns (uint256) { if (a <= b) { return a; } else { return b; } } } import "./EIP20Interface.sol";
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; // From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/Math.sol // Subject to the MIT license. /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c; unchecked { c = a + b; } require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the addition of two unsigned integers, reverting with custom message on overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * - Addition cannot overflow. */ function add(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { uint256 c; unchecked { c = a + b; } require(c >= a, errorMessage); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on underflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot underflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction underflow"); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on underflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * - Subtraction cannot underflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c; unchecked { c = a * b; } require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c; unchecked { c = a * b; } require(c / a == b, errorMessage); return c; } /** * @dev Returns the integer division of two unsigned integers. * Reverts on division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } /** * @dev Returns the integer division of two unsigned integers. * Reverts with custom message on division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts with custom message when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./PriceOracle.sol"; import "./CErc20.sol"; contract SimplePriceOracle is PriceOracle { mapping(address => uint) prices; event PricePosted(address asset, uint previousPriceMantissa, uint requestedPriceMantissa, uint newPriceMantissa); function _getUnderlyingAddress(CToken cToken) private view returns (address) { address asset; if (compareStrings(cToken.symbol(), "cCANTO")) { asset = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; //Canto has no underlying address, index into a } else { asset = address(CErc20(address(cToken)).underlying()); } return asset; } function getUnderlyingPrice(CToken cToken) public override view returns (uint) { return prices[_getUnderlyingAddress(cToken)]; } function setUnderlyingPrice(CToken cToken, uint underlyingPriceMantissa) public { address asset = _getUnderlyingAddress(cToken); emit PricePosted(asset, prices[asset], underlyingPriceMantissa, underlyingPriceMantissa); prices[asset] = underlyingPriceMantissa; } function setDirectPrice(address asset, uint price) public { emit PricePosted(asset, prices[asset], price, price); prices[asset] = price; } // v1 price oracle interface for use as backing of proxy function assetPrices(address asset) external view returns (uint) { return prices[asset]; } function compareStrings(string memory a, string memory b) internal pure returns (bool) { return (keccak256(abi.encodePacked((a))) == keccak256(abi.encodePacked((b)))); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.11; import "./BaseV1-libs.sol"; interface IBaseV1Callee { function hook(address sender, uint amount0, uint amount1, bytes calldata data) external; } // The base pair of pools, either stable or volatile contract BaseV1Pair { string public name; string public symbol; uint8 public constant decimals = 18; // Used to denote stable or volatile pair, not immutable since construction happens in the initialize method for CREATE2 deterministic addresses bool public immutable stable; uint public totalSupply = 0; mapping(address => mapping (address => uint)) public allowance; mapping(address => uint) public balanceOf; bytes32 internal DOMAIN_SEPARATOR; // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); bytes32 internal constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; mapping(address => uint) public nonces; uint internal constant MINIMUM_LIQUIDITY = 10**3; address public immutable token0; address public immutable token1; address immutable factory; // Structure to capture time period obervations every 30 minutes, used for local oracles struct Observation { uint timestamp; uint reserve0Cumulative; uint reserve1Cumulative; uint totalSupplyCumulative; } // Capture oracle reading every 30 minutes uint public periodSize = 1800; Observation[] public observations; uint internal immutable decimals0; uint internal immutable decimals1; uint public reserve0; uint public reserve1; uint public blockTimestampLast; uint public reserve0CumulativeLast; uint public reserve1CumulativeLast; uint public totalSupplyCumulativeLast; // position assigned to each LP to track their current index0 & index1 vs the global position mapping(address => uint) public supplyIndex0; mapping(address => uint) public supplyIndex1; event Mint(address indexed sender, uint amount0, uint amount1); event Burn(address indexed sender, uint amount0, uint amount1, address indexed to); event Swap( address indexed sender, uint amount0In, uint amount1In, uint amount0Out, uint amount1Out, address indexed to ); event Sync(uint reserve0, uint reserve1); event Claim(address indexed sender, address indexed recipient, uint amount0, uint amount1); event Transfer(address indexed from, address indexed to, uint amount); event Approval(address indexed owner, address indexed spender, uint amount); constructor() { factory = msg.sender; (address _token0, address _token1, bool _stable) = BaseV1Factory(msg.sender).getInitializable(); (token0, token1, stable) = (_token0, _token1, _stable); if (_stable) { name = string(abi.encodePacked("StableV1 AMM - ", erc20(_token0).symbol(), "/", erc20(_token1).symbol())); symbol = string(abi.encodePacked("sAMM-", erc20(_token0).symbol(), "/", erc20(_token1).symbol())); } else { name = string(abi.encodePacked("VolatileV1 AMM - ", erc20(_token0).symbol(), "/", erc20(_token1).symbol())); symbol = string(abi.encodePacked("vAMM-", erc20(_token0).symbol(), "/", erc20(_token1).symbol())); } decimals0 = 10**erc20(_token0).decimals(); decimals1 = 10**erc20(_token1).decimals(); observations.push(Observation(block.timestamp, 0, 0,0)); } function setPeriodSize(uint periodSize_) external { require(msg.sender == factory); periodSize = periodSize_; } // simple re-entrancy check uint internal _unlocked = 1; modifier lock() { require(_unlocked == 1); _unlocked = 2; _; _unlocked = 1; } function observationLength() external view returns (uint) { return observations.length; } function lastObservation() public view returns (Observation memory) { return observations[observations.length-1]; } function metadata() external view returns (uint dec0, uint dec1, uint r0, uint r1, bool st, address t0, address t1) { return (decimals0, decimals1, reserve0, reserve1, stable, token0, token1); } function tokens() external view returns (address, address) { return (token0, token1); } function getReserves() public view returns (uint _reserve0, uint _reserve1, uint _blockTimestampLast) { _reserve0 = reserve0; _reserve1 = reserve1; _blockTimestampLast = blockTimestampLast; } // update reserves and, on the first call per block, price accumulators function _update(uint balance0, uint balance1, uint _reserve0, uint _reserve1, uint _totalSupply) internal { uint blockTimestamp = block.timestamp; uint timeElapsed = blockTimestamp - blockTimestampLast; if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) { reserve0CumulativeLast += _reserve0 * timeElapsed; reserve1CumulativeLast += _reserve1 * timeElapsed; totalSupplyCumulativeLast += totalSupply * timeElapsed; //update totalSupply after each change in LP Token supply } Observation memory _point = lastObservation(); timeElapsed = blockTimestamp - _point.timestamp; // compare the last observation with current timestamp, if greater than 30 minutes, record a new event if (timeElapsed > periodSize) { observations.push(Observation(blockTimestamp, reserve0CumulativeLast, reserve1CumulativeLast, totalSupplyCumulativeLast)); } reserve0 = balance0; reserve1 = balance1; blockTimestampLast = blockTimestamp; emit Sync(reserve0, reserve1); } // produces the cumulative price using counterfactuals to save gas and avoid a call to sync. function currentCumulativePrices() public view returns (uint reserve0Cumulative, uint reserve1Cumulative, uint blockTimestamp) { blockTimestamp = block.timestamp; reserve0Cumulative = reserve0CumulativeLast; reserve1Cumulative = reserve1CumulativeLast; // if time has elapsed since the last update on the pair, mock the accumulated price values (uint _reserve0, uint _reserve1, uint _blockTimestampLast) = getReserves(); if (_blockTimestampLast != blockTimestamp) { uint timeElapsed = blockTimestamp - _blockTimestampLast; reserve0Cumulative += _reserve0 * timeElapsed; reserve1Cumulative += _reserve1 * timeElapsed; } } // gives the current twap price measured from amountIn * tokenIn gives amountOut function current(address tokenIn, uint amountIn) external view returns (uint amountOut) { Observation memory _observation = lastObservation(); (uint reserve0Cumulative, uint reserve1Cumulative,) = currentCumulativePrices(); if (block.timestamp == _observation.timestamp) { _observation = observations[observations.length-2]; } uint timeElapsed = block.timestamp - _observation.timestamp; uint _reserve0 = (reserve0Cumulative - _observation.reserve0Cumulative) / timeElapsed; uint _reserve1 = (reserve1Cumulative - _observation.reserve1Cumulative) / timeElapsed; amountOut = _getAmountOut(amountIn, tokenIn, _reserve0, _reserve1); } // as per `current`, however allows user configured granularity, up to the full window size function quote(address tokenIn, uint amountIn, uint granularity) external view returns (uint amountOut) { uint [] memory _prices = sample(tokenIn, amountIn, granularity, 1); uint priceAverageCumulative; for (uint i = 0; i < _prices.length; i++) { priceAverageCumulative += _prices[i]; } return priceAverageCumulative / granularity; } // returns a memory set of twap prices function prices(address tokenIn, uint amountIn, uint points) external view returns (uint[] memory) { return sample(tokenIn, amountIn, points, 1); } function sample(address tokenIn, uint amountIn, uint points, uint window) public view returns (uint[] memory) { uint[] memory _prices = new uint[](points); uint lastIndex = observations.length-1; require(lastIndex >= points * window, "PAIR::NOT READY FOR PRICING"); //log if the price is requested and there are not enough observations uint i = lastIndex - (points * window); // point from which to begin the sample uint nextIndex = 0; uint index = 0; for (; i < lastIndex; i+=window) { nextIndex = i + window; uint timeElapsed = observations[nextIndex].timestamp - observations[i].timestamp; uint _reserve0 = (observations[nextIndex].reserve0Cumulative - observations[i].reserve0Cumulative) / timeElapsed; uint _reserve1 = (observations[nextIndex].reserve1Cumulative - observations[i].reserve1Cumulative) / timeElapsed; _prices[index] = _getAmountOut(amountIn, tokenIn, _reserve0, _reserve1); index = index + 1; } return _prices; } function reserves(uint granularity) external view returns(uint, uint) { (uint[] memory _reserves0, uint[] memory _reserves1)= sampleReserves(granularity, 1); uint reserveAverageCumulative0; uint reserveAverageCumulative1; for (uint i = 0; i < _reserves0.length; ++i) { reserveAverageCumulative0 += _reserves0[i]; //normalize the reserves for TWAP LP Oracle pricing, reserveAverageCumulative1 += _reserves1[i]; // } return (reserveAverageCumulative0 / granularity, reserveAverageCumulative1 / granularity); } function sampleReserves(uint points, uint window) public view returns (uint[] memory, uint[] memory) { uint[] memory _reserves0 = new uint[](points); uint[] memory _reserves1 = new uint[](points); uint lastIndex = observations.length-1; require(lastIndex >= points * window, "PAIR::NOT READY FOR PRICING"); uint i = lastIndex - (points * window); // point from which to begin the sample uint nextIndex = 0; uint index = 0; uint timeElapsed; for(; i < lastIndex; i+=window) { nextIndex = i + window; timeElapsed = observations[nextIndex].timestamp - observations[i].timestamp; _reserves0[index] = (observations[nextIndex].reserve0Cumulative - observations[i].reserve0Cumulative) / timeElapsed; _reserves1[index] = (observations[nextIndex].reserve1Cumulative - observations[i].reserve1Cumulative) / timeElapsed; index = index + 1; } return (_reserves0, _reserves1); } function totalSupplyAvg(uint granularity) external view returns(uint) { uint[] memory _totalSupplyAvg = sampleSupply(granularity, 1); uint totalSupplyCumulativeAvg; for (uint i = 0; i < _totalSupplyAvg.length; ++i) { totalSupplyCumulativeAvg += _totalSupplyAvg[i]; //totalSupply denominated in terms of 1e18 } return (totalSupplyCumulativeAvg / granularity); } function sampleSupply(uint points, uint window) public view returns (uint[] memory) { uint[] memory _totalSupply = new uint[](points); uint lastIndex = observations.length-1; require(lastIndex >= points * window, "PAIR::NOT READY FOR PRICING"); uint i = lastIndex - (points * window); // point from which to begin the sample uint nextIndex = 0; uint index = 0; uint timeElapsed; for(; i < lastIndex; i+=window) { nextIndex = i + window; timeElapsed = observations[nextIndex].timestamp - observations[i].timestamp; _totalSupply[index] = (observations[nextIndex].totalSupplyCumulative - observations[i].totalSupplyCumulative) / timeElapsed; index = index + 1; } return _totalSupply; } // this low-level function should be called from a contract which performs important safety checks // standard uniswap v2 implementation function mint(address to) external lock returns (uint liquidity) { (uint _reserve0, uint _reserve1) = (reserve0, reserve1); uint _balance0 = erc20(token0).balanceOf(address(this)); uint _balance1 = erc20(token1).balanceOf(address(this)); uint _amount0 = _balance0 - _reserve0; uint _amount1 = _balance1 - _reserve1; uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee if (_totalSupply == 0) { liquidity = Math.sqrt(_amount0 * _amount1) - MINIMUM_LIQUIDITY; _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens } else { liquidity = Math.min(_amount0 * _totalSupply / _reserve0, _amount1 * _totalSupply / _reserve1); } require(liquidity > 0, "ILM"); // BaseV1: INSUFFICIENT_LIQUIDITY_MINTED _mint(to, liquidity); _update(_balance0, _balance1, _reserve0, _reserve1, _totalSupply); emit Mint(msg.sender, _amount0, _amount1); } // this low-level function should be called from a contract which performs important safety checks // standard uniswap v2 implementation function burn(address to) external lock returns (uint amount0, uint amount1) { (uint _reserve0, uint _reserve1) = (reserve0, reserve1); (address _token0, address _token1) = (token0, token1); uint _balance0 = erc20(_token0).balanceOf(address(this)); uint _balance1 = erc20(_token1).balanceOf(address(this)); uint _liquidity = balanceOf[address(this)]; uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee amount0 = _liquidity * _balance0 / _totalSupply; // using balances ensures pro-rata distribution amount1 = _liquidity * _balance1 / _totalSupply; // using balances ensures pro-rata distribution require(amount0 > 0 && amount1 > 0, "ILB"); // BaseV1: INSUFFICIENT_LIQUIDITY_BURNED _burn(address(this), _liquidity); _safeTransfer(_token0, to, amount0); _safeTransfer(_token1, to, amount1); _balance0 = erc20(_token0).balanceOf(address(this)); _balance1 = erc20(_token1).balanceOf(address(this)); _update(_balance0, _balance1, _reserve0, _reserve1, _totalSupply); emit Burn(msg.sender, amount0, amount1, to); } // this low-level function should be called from a contract which performs important safety checks function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock { require(!BaseV1Factory(factory).isPaused()); require(amount0Out > 0 || amount1Out > 0, "IOA"); // BaseV1: INSUFFICIENT_OUTPUT_AMOUNT (uint _reserve0, uint _reserve1) = (reserve0, reserve1); require(amount0Out < _reserve0 && amount1Out < _reserve1, "IL"); // BaseV1: INSUFFICIENT_LIQUIDITY uint _balance0; uint _balance1; { // scope for _token{0,1}, avoids stack too deep errors (address _token0, address _token1) = (token0, token1); require(to != _token0 && to != _token1, "IT"); // BaseV1: INVALID_TO if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens if (data.length > 0) IBaseV1Callee(to).hook(msg.sender, amount0Out, amount1Out, data); // callback, used for flash loans _balance0 = erc20(_token0).balanceOf(address(this)); _balance1 = erc20(_token1).balanceOf(address(this)); } uint amount0In = _balance0 > _reserve0 - amount0Out ? _balance0 - (_reserve0 - amount0Out) : 0; uint amount1In = _balance1 > _reserve1 - amount1Out ? _balance1 - (_reserve1 - amount1Out) : 0; require(amount0In > 0 || amount1In > 0, "IIA"); // BaseV1: INSUFFICIENT_INPUT_AMOUNT { // scope for reserve{0,1}Adjusted, avoids stack too deep errors (address _token0, address _token1) = (token0, token1); _balance0 = erc20(_token0).balanceOf(address(this)); // since we removed tokens, we need to reconfirm balances, can also simply use previous balance - amountIn/ 10000, but doing balanceOf again as safety check _balance1 = erc20(_token1).balanceOf(address(this)); // The curve, either x3y+y3x for stable pools, or x*y for volatile pools require(_k(_balance0, _balance1) >= _k(_reserve0, _reserve1), "K"); // BaseV1: K } _update(_balance0, _balance1, _reserve0, _reserve1, totalSupply); emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to); } // force balances to match reserves function skim(address to) external lock { (address _token0, address _token1) = (token0, token1); _safeTransfer(_token0, to, erc20(_token0).balanceOf(address(this)) - (reserve0)); _safeTransfer(_token1, to, erc20(_token1).balanceOf(address(this)) - (reserve1)); } // force reserves to match balances function sync() external lock { _update(erc20(token0).balanceOf(address(this)), erc20(token1).balanceOf(address(this)), reserve0, reserve1, totalSupply); } function _f(uint x0, uint y) internal pure returns (uint) { return x0*(y*y/1e18*y/1e18)/1e18+(x0*x0/1e18*x0/1e18)*y/1e18; } function _d(uint x0, uint y) internal pure returns (uint) { return 3*x0*(y*y/1e18)/1e18+(x0*x0/1e18*x0/1e18); } function _get_y(uint x0, uint xy, uint y) internal pure returns (uint) { for (uint i = 0; i < 255; i++) { uint y_prev = y; uint k = _f(x0, y); if (k < xy) { uint dy = (xy - k)*1e18/_d(x0, y); y = y + dy; } else { uint dy = (k - xy)*1e18/_d(x0, y); y = y - dy; } if (y > y_prev) { if (y - y_prev <= 1) { return y; } } else { if (y_prev - y <= 1) { return y; } } } return y; } function getAmountOut(uint amountIn, address tokenIn) external view returns (uint) { (uint _reserve0, uint _reserve1) = (reserve0, reserve1); //amountIn -= amountIn / 10000; // remove fee from amount received return _getAmountOut(amountIn, tokenIn, _reserve0, _reserve1); } function _getAmountOut(uint amountIn, address tokenIn, uint _reserve0, uint _reserve1) internal view returns (uint) { if (stable) { uint xy = _k(_reserve0, _reserve1); _reserve0 = _reserve0 * 1e18 / decimals0; _reserve1 = _reserve1 * 1e18 / decimals1; (uint reserveA, uint reserveB) = tokenIn == token0 ? (_reserve0, _reserve1) : (_reserve1, _reserve0); amountIn = tokenIn == token0 ? amountIn * 1e18 / decimals0 : amountIn * 1e18 / decimals1; uint y = reserveB - _get_y(amountIn+reserveA, xy, reserveB); return y * (tokenIn == token0 ? decimals1 : decimals0) / 1e18; } else { (uint reserveA, uint reserveB) = tokenIn == token0 ? (_reserve0, _reserve1) : (_reserve1, _reserve0); return amountIn * reserveB / (reserveA + amountIn); } } function _k(uint x, uint y) public view returns (uint) { if (stable) { uint _x = x * 1e18 / decimals0; uint _y = y * 1e18 / decimals1; uint _a = (_x * _y) / 1e18; uint _b = ((_x * _x) / 1e18 + (_y * _y) / 1e18); return _a * _b / 1e18; // x3y+y3x >= k } else { return x * y; // xy >= k } } function _mint(address dst, uint amount) internal { totalSupply += amount; balanceOf[dst] += amount; emit Transfer(address(0), dst, amount); } function _burn(address dst, uint amount) internal { totalSupply -= amount; balanceOf[dst] -= amount; emit Transfer(dst, address(0), amount); } function approve(address spender, uint amount) external returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external { require(deadline >= block.timestamp, "BaseV1: EXPIRED"); DOMAIN_SEPARATOR = keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(name)), keccak256("1"), block.chainid, address(this) ) ); bytes32 digest = keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR, keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline)) ) ); address recoveredAddress = ecrecover(digest, v, r, s); require(recoveredAddress != address(0) && recoveredAddress == owner, "BaseV1: INVALID_SIGNATURE"); allowance[owner][spender] = value; emit Approval(owner, spender, value); } function transfer(address dst, uint amount) external returns (bool) { _transferTokens(msg.sender, dst, amount); return true; } function transferFrom(address src, address dst, uint amount) external returns (bool) { address spender = msg.sender; uint spenderAllowance = allowance[src][spender]; if (spender != src && spenderAllowance != type(uint).max) { uint newAllowance = spenderAllowance - amount; allowance[src][spender] = newAllowance; emit Approval(src, spender, newAllowance); } _transferTokens(src, dst, amount); return true; } function _transferTokens(address src, address dst, uint amount) internal { balanceOf[src] -= amount; balanceOf[dst] += amount; emit Transfer(src, dst, amount); } function _safeTransfer(address token,address to,uint256 value) internal { require(token.code.length > 0); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(erc20.transfer.selector, to, value)); require(success && (data.length == 0 || abi.decode(data, (bool)))); } } contract BaseV1Factory { bool public isPaused; address public pauser; address public pendingPauser; address public admin; uint MaxPeriod = 3600; mapping(address => mapping(address => mapping(bool => address))) public getPair; address[] public allPairs; mapping(address => bool) public isPair; // simplified check if its a pair, given that `stable` flag might not be available in peripherals address internal _temp0; address internal _temp1; bool internal _temp; event PairCreated(address indexed token0, address indexed token1, bool stable, address pair, uint); constructor() { pauser = msg.sender; isPaused = false; admin = msg.sender; } // admin for setting the periodSize in pairs function setAdmin(address admin_) external { require(msg.sender == admin); admin = admin_; } function setPeriodSize(uint newPeriod) external { require(msg.sender == admin); require(newPeriod <= MaxPeriod); for (uint i; i < allPairs.length; ) { BaseV1Pair(allPairs[i]).setPeriodSize(newPeriod); unchecked {++i;} } } function allPairsLength() external view returns (uint) { return allPairs.length; } function setPauser(address _pauser) external { require(msg.sender == pauser); pendingPauser = _pauser; } function acceptPauser() external { require(msg.sender == pendingPauser); pauser = pendingPauser; } function setPause(bool _state) external { require(msg.sender == pauser); isPaused = _state; } function pairCodeHash() external pure returns (bytes32) { return keccak256(type(BaseV1Pair).creationCode); } function getInitializable() external view returns (address, address, bool) { return (_temp0, _temp1, _temp); } function createPair(address tokenA, address tokenB, bool stable) external returns (address pair) { require(tokenA != tokenB, "IA"); // BaseV1: IDENTICAL_ADDRESSES (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); require(token0 != address(0), "ZA"); // BaseV1: ZERO_ADDRESS require(getPair[token0][token1][stable] == address(0), "PE"); // BaseV1: PAIR_EXISTS - single check is sufficient bytes32 salt = keccak256(abi.encodePacked(token0, token1, stable)); // notice salt includes stable as well, 3 parameters (_temp0, _temp1, _temp) = (token0, token1, stable); pair = address(new BaseV1Pair{salt:salt}()); getPair[token0][token1][stable] = pair; getPair[token1][token0][stable] = pair; // populate mapping in the reverse direction allPairs.push(pair); isPair[pair] = true; emit PairCreated(token0, token1, stable, pair, allPairs.length); } }
pragma solidity 0.8.11; interface erc20 { function totalSupply() external view returns (uint256); function transfer(address recipient, uint256 amount) external returns (bool); function decimals() external view returns (uint8); function symbol() external view returns (string memory); function balanceOf(address) external view returns (uint); function transferFrom(address sender, address recipient, uint amount) external returns (bool); function approve(address spender, uint value) external returns (bool); } library Math { function min(uint a, uint b) internal pure returns (uint) { return a < b ? a : b; } function sqrt(uint y) internal pure returns (uint z) { if (y > 3) { z = y; uint x = y / 2 + 1; while (x < z) { z = x; x = (y / x + x) / 2; } } else if (y != 0) { z = 1; } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.11; import "../CToken.sol"; import "../PriceOracle.sol"; import "./BaseV1-libs.sol"; interface IBaseV1Factory { function allPairsLength() external view returns (uint); function isPair(address pair) external view returns (bool); function pairCodeHash() external pure returns (bytes32); function getPair(address tokenA, address token, bool stable) external view returns (address); function createPair(address tokenA, address tokenB, bool stable) external returns (address); } interface IBaseV1Pair { function transferFrom(address src, address dst, uint amount) external returns (bool); function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external; function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external; function burn(address to) external returns (uint amount0, uint amount1); function mint(address to) external returns (uint liquidity); function getReserves() external view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast); function getAmountOut(uint, address) external view returns (uint); function current(address tokenIn, uint amountIn) external view returns(uint); function token0() external view returns(address); function token1() external view returns(address); function stable() external view returns(bool); function _k(uint x, uint y) external view returns(uint); //LP token pricing function sampleReserves(uint points, uint window) external view returns(uint[] memory, uint[] memory); function sampleSupply(uint points, uint window) external view returns(uint[] memory); function sample(address tokenIn, uint amountIn, uint points, uint window) external view returns(uint[] memory); function quote(address tokenIn, uint amountIn, uint granularity) external view returns(uint); function observationLength() external view returns(uint); } interface IWCANTO { function deposit() external payable ; function transfer(address to, uint value) external returns (bool); function withdraw(uint) external ; } interface ICErc20 { function underlying() external view returns(address); } contract BaseV1Router01 is PriceOracle { //address of Unitroller to obtain prices with respect to USDC address public immutable note; //address of Comptroller, so that price of note may be set to 1 in Account Liquidity calculations address public immutable Comptroller; address public admin; struct route { address from; address to; bool stable; } address public immutable factory; IWCANTO public immutable wcanto; uint internal constant MINIMUM_LIQUIDITY = 10**3; bytes32 immutable pairCodeHash; mapping(address => bool) public isStable; error SenderNotAdmin(address sender, address admin); modifier ensure(uint deadline) { require(deadline >= block.timestamp, "BaseV1Router: EXPIRED"); _; } constructor(address _factory, address _wcanto, address note_, address Comptroller_) { factory = _factory; pairCodeHash = IBaseV1Factory(_factory).pairCodeHash(); wcanto = IWCANTO(_wcanto); note = note_; Comptroller = Comptroller_; admin = msg.sender; } receive() external payable { assert(msg.sender == address(wcanto)); // only accept ETH via fallback from the WETH contract } // admin for setting the stable pairs function setAdmin(address admin_) external { require(msg.sender == admin); admin = admin_; } function sortTokens(address tokenA, address tokenB) public pure returns (address token0, address token1) { require(tokenA != tokenB, "BaseV1Router: IDENTICAL_ADDRESSES"); (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); require(token0 != address(0), "BaseV1Router: ZERO_ADDRESS"); } // calculates the CREATE2 address for a pair without making any external calls function pairFor(address tokenA, address tokenB, bool stable) public view returns (address pair) { (address token0, address token1) = sortTokens(tokenA, tokenB); pair = address(uint160(uint256(keccak256(abi.encodePacked( hex"ff", factory, keccak256(abi.encodePacked(token0, token1, stable)), pairCodeHash // init code hash ))))); } // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset function quoteLiquidity(uint amountA, uint reserveA, uint reserveB) internal pure returns (uint amountB) { require(amountA > 0, "BaseV1Router: INSUFFICIENT_AMOUNT"); require(reserveA > 0 && reserveB > 0, "BaseV1Router: INSUFFICIENT_LIQUIDITY"); amountB = amountA * reserveB / reserveA; } // fetches and sorts the reserves for a pair function getReserves(address tokenA, address tokenB, bool stable) public view returns (uint reserveA, uint reserveB) { (address token0,) = sortTokens(tokenA, tokenB); (uint reserve0, uint reserve1,) = IBaseV1Pair(pairFor(tokenA, tokenB, stable)).getReserves(); (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0); } // performs chained getAmountOut calculations on any number of pairs function getAmountOut(uint amountIn, address tokenIn, address tokenOut) external view returns (uint amount, bool stable) { address pair = pairFor(tokenIn, tokenOut, true); uint amountStable; uint amountVolatile; if (IBaseV1Factory(factory).isPair(pair)) { amountStable = IBaseV1Pair(pair).getAmountOut(amountIn, tokenIn); } pair = pairFor(tokenIn, tokenOut, false); if (IBaseV1Factory(factory).isPair(pair)) { amountVolatile = IBaseV1Pair(pair).getAmountOut(amountIn, tokenIn); } return amountStable > amountVolatile ? (amountStable, true) : (amountVolatile, false); } // performs chained getAmountOut calculations on any number of pairs function getAmountsOut(uint amountIn, route[] memory routes) public view returns (uint[] memory amounts) { require(routes.length >= 1, "BaseV1Router: INVALID_PATH"); amounts = new uint[](routes.length+1); amounts[0] = amountIn; for (uint i = 0; i < routes.length; i++) { address pair = pairFor(routes[i].from, routes[i].to, routes[i].stable); if (IBaseV1Factory(factory).isPair(pair)) { amounts[i+1] = IBaseV1Pair(pair).getAmountOut(amounts[i], routes[i].from); } } } function isPair(address pair) public view returns (bool) { return IBaseV1Factory(factory).isPair(pair); } function quoteAddLiquidity( address tokenA, address tokenB, bool stable, uint amountADesired, uint amountBDesired ) external view returns (uint amountA, uint amountB, uint liquidity) { // create the pair if it doesn"t exist yet address _pair = IBaseV1Factory(factory).getPair(tokenA, tokenB, stable); (uint reserveA, uint reserveB) = (0,0); uint _totalSupply = 0; if (_pair != address(0)) { _totalSupply = erc20(_pair).totalSupply(); (reserveA, reserveB) = getReserves(tokenA, tokenB, stable); } if (reserveA == 0 && reserveB == 0) { (amountA, amountB) = (amountADesired, amountBDesired); liquidity = Math.sqrt(amountA * amountB) - MINIMUM_LIQUIDITY; } else { uint amountBOptimal = quoteLiquidity(amountADesired, reserveA, reserveB); if (amountBOptimal <= amountBDesired) { (amountA, amountB) = (amountADesired, amountBOptimal); liquidity = Math.min(amountA * _totalSupply / reserveA, amountB * _totalSupply / reserveB); } else { uint amountAOptimal = quoteLiquidity(amountBDesired, reserveB, reserveA); (amountA, amountB) = (amountAOptimal, amountBDesired); liquidity = Math.min(amountA * _totalSupply / reserveA, amountB * _totalSupply / reserveB); } } } function quoteRemoveLiquidity( address tokenA, address tokenB, bool stable, uint liquidity ) public view returns (uint amountA, uint amountB) { // create the pair if it doesn"t exist yet address _pair = IBaseV1Factory(factory).getPair(tokenA, tokenB, stable); if (_pair == address(0)) { return (0,0); } (uint reserveA, uint reserveB) = getReserves(tokenA, tokenB, stable); uint _totalSupply = erc20(_pair).totalSupply(); amountA = liquidity * reserveA / _totalSupply; // using balances ensures pro-rata distribution amountB = liquidity * reserveB / _totalSupply; // using balances ensures pro-rata distribution } function _addLiquidity( address tokenA, address tokenB, bool stable, uint amountADesired, uint amountBDesired, uint amountAMin, uint amountBMin ) internal returns (uint amountA, uint amountB) { require(amountADesired >= amountAMin); require(amountBDesired >= amountBMin); // create the pair if it doesn"t exist yet address _pair = IBaseV1Factory(factory).getPair(tokenA, tokenB, stable); if (_pair == address(0)) { _pair = IBaseV1Factory(factory).createPair(tokenA, tokenB, stable); } (uint reserveA, uint reserveB) = getReserves(tokenA, tokenB, stable); if (reserveA == 0 && reserveB == 0) { (amountA, amountB) = (amountADesired, amountBDesired); } else { uint amountBOptimal = quoteLiquidity(amountADesired, reserveA, reserveB); if (amountBOptimal <= amountBDesired) { require(amountBOptimal >= amountBMin, "BaseV1Router: INSUFFICIENT_B_AMOUNT"); (amountA, amountB) = (amountADesired, amountBOptimal); } else { uint amountAOptimal = quoteLiquidity(amountBDesired, reserveB, reserveA); assert(amountAOptimal <= amountADesired); require(amountAOptimal >= amountAMin, "BaseV1Router: INSUFFICIENT_A_AMOUNT"); (amountA, amountB) = (amountAOptimal, amountBDesired); } } } function addLiquidity( address tokenA, address tokenB, bool stable, uint amountADesired, uint amountBDesired, uint amountAMin, uint amountBMin, address to, uint deadline ) external ensure(deadline) returns (uint amountA, uint amountB, uint liquidity) { (amountA, amountB) = _addLiquidity(tokenA, tokenB, stable, amountADesired, amountBDesired, amountAMin, amountBMin); address pair = pairFor(tokenA, tokenB, stable); _safeTransferFrom(tokenA, msg.sender, pair, amountA); _safeTransferFrom(tokenB, msg.sender, pair, amountB); liquidity = IBaseV1Pair(pair).mint(to); } function addLiquidityCANTO( address token, bool stable, uint amountTokenDesired, uint amountTokenMin, uint amountCANTOMin, address to, uint deadline ) external payable ensure(deadline) returns (uint amountToken, uint amountCANTO, uint liquidity) { (amountToken, amountCANTO) = _addLiquidity( token, address(wcanto), stable, amountTokenDesired, msg.value, amountTokenMin, amountCANTOMin ); address pair = pairFor(token, address(wcanto), stable); _safeTransferFrom(token, msg.sender, pair, amountToken); wcanto.deposit{value: amountCANTO}(); assert(wcanto.transfer(pair, amountCANTO)); liquidity = IBaseV1Pair(pair).mint(to); // refund dust eth, if any if (msg.value > amountCANTO) _safeTransferCANTO(msg.sender, msg.value - amountCANTO); } // **** REMOVE LIQUIDITY **** function removeLiquidity( address tokenA, address tokenB, bool stable, uint liquidity, uint amountAMin, uint amountBMin, address to, uint deadline ) public ensure(deadline) returns (uint amountA, uint amountB) { address pair = pairFor(tokenA, tokenB, stable); require(IBaseV1Pair(pair).transferFrom(msg.sender, pair, liquidity)); // send liquidity to pair (uint amount0, uint amount1) = IBaseV1Pair(pair).burn(to); (address token0,) = sortTokens(tokenA, tokenB); (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0); require(amountA >= amountAMin, "BaseV1Router: INSUFFICIENT_A_AMOUNT"); require(amountB >= amountBMin, "BaseV1Router: INSUFFICIENT_B_AMOUNT"); } function removeLiquidityCANTO( address token, bool stable, uint liquidity, uint amountTokenMin, uint amountCANTOMin, address to, uint deadline ) public ensure(deadline) returns (uint amountToken, uint amountCANTO) { (amountToken, amountCANTO) = removeLiquidity( token, address(wcanto), stable, liquidity, amountTokenMin, amountCANTOMin, address(this), deadline ); _safeTransfer(token, to, amountToken); wcanto.withdraw(amountCANTO); _safeTransferCANTO(to, amountCANTO); } function removeLiquidityWithPermit( address tokenA, address tokenB, bool stable, uint liquidity, uint amountAMin, uint amountBMin, address to, uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint amountA, uint amountB) { address pair = pairFor(tokenA, tokenB, stable); { uint value = approveMax ? type(uint).max : liquidity; IBaseV1Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s); } (amountA, amountB) = removeLiquidity(tokenA, tokenB, stable, liquidity, amountAMin, amountBMin, to, deadline); } function removeLiquidityCANTOWithPermit( address token, bool stable, uint liquidity, uint amountTokenMin, uint amountCANTOMin, address to, uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint amountToken, uint amountCANTO) { address pair = pairFor(token, address(wcanto), stable); uint value = approveMax ? type(uint).max : liquidity; IBaseV1Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s); (amountToken, amountCANTO) = removeLiquidityCANTO(token, stable, liquidity, amountTokenMin, amountCANTOMin, to, deadline); } // **** SWAP **** // requires the initial amount to have already been sent to the first pair function _swap(uint[] memory amounts, route[] memory routes, address _to) internal virtual { for (uint i = 0; i < routes.length; i++) { (address token0,) = sortTokens(routes[i].from, routes[i].to); uint amountOut = amounts[i + 1]; (uint amount0Out, uint amount1Out) = routes[i].from == token0 ? (uint(0), amountOut) : (amountOut, uint(0)); address to = i < routes.length - 1 ? pairFor(routes[i+1].from, routes[i+1].to, routes[i+1].stable) : _to; IBaseV1Pair(pairFor(routes[i].from, routes[i].to, routes[i].stable)).swap( amount0Out, amount1Out, to, new bytes(0) ); } } function swapExactTokensForTokensSimple( uint amountIn, uint amountOutMin, address tokenFrom, address tokenTo, bool stable, address to, uint deadline ) external ensure(deadline) returns (uint[] memory amounts) { route[] memory routes = new route[](1); routes[0].from = tokenFrom; routes[0].to = tokenTo; routes[0].stable = stable; amounts = getAmountsOut(amountIn, routes); require(amounts[amounts.length - 1] >= amountOutMin, "BaseV1Router: INSUFFICIENT_OUTPUT_AMOUNT"); _safeTransferFrom( routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0] ); _swap(amounts, routes, to); } function swapExactTokensForTokens( uint amountIn, uint amountOutMin, route[] calldata routes, address to, uint deadline ) external ensure(deadline) returns (uint[] memory amounts) { amounts = getAmountsOut(amountIn, routes); require(amounts[amounts.length - 1] >= amountOutMin, "BaseV1Router: INSUFFICIENT_OUTPUT_AMOUNT"); _safeTransferFrom( routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0] ); _swap(amounts, routes, to); } function swapExactCANTOForTokens(uint amountOutMin, route[] calldata routes, address to, uint deadline) external payable ensure(deadline) returns (uint[] memory amounts) { require(routes[0].from == address(wcanto), "BaseV1Router: INVALID_PATH"); amounts = getAmountsOut(msg.value, routes); require(amounts[amounts.length - 1] >= amountOutMin, "BaseV1Router: INSUFFICIENT_OUTPUT_AMOUNT"); wcanto.deposit{value: amounts[0]}(); assert(wcanto.transfer(pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0])); _swap(amounts, routes, to); } function swapExactTokensForCANTO(uint amountIn, uint amountOutMin, route[] calldata routes, address to, uint deadline) external ensure(deadline) returns (uint[] memory amounts) { require(routes[routes.length - 1].to == address(wcanto), "BaseV1Router: INVALID_PATH"); amounts = getAmountsOut(amountIn, routes); require(amounts[amounts.length - 1] >= amountOutMin, "BaseV1Router: INSUFFICIENT_OUTPUT_AMOUNT"); _safeTransferFrom( routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0] ); _swap(amounts, routes, address(this)); wcanto.withdraw(amounts[amounts.length - 1]); _safeTransferCANTO(to, amounts[amounts.length - 1]); } function UNSAFE_swapExactTokensForTokens( uint[] memory amounts, route[] calldata routes, address to, uint deadline ) external ensure(deadline) returns (uint[] memory) { _safeTransferFrom(routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]); _swap(amounts, routes, to); return amounts; } function _safeTransferCANTO(address to, uint value) internal { (bool success,) = to.call{value:value}(new bytes(0)); require(success, "TransferHelper: ETH_TRANSFER_FAILED"); } function _safeTransfer(address token, address to, uint256 value) internal { require(token.code.length > 0); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(erc20.transfer.selector, to, value)); require(success && (data.length == 0 || abi.decode(data, (bool)))); } function _safeTransferFrom(address token, address from, address to, uint256 value) internal { require(token.code.length > 0, "token code length failure"); erc20 tokenCon = erc20(token); tokenCon.transferFrom(from, to, value); } function setStable(address underlying) external returns (uint) { if (msg.sender != admin) { revert SenderNotAdmin(msg.sender, admin); } isStable[underlying] = true; } //returns the underlying price of the assets as a mantissa (scaled by 1e18) function getUnderlyingPrice(CToken ctoken) external override view returns(uint) { address underlying; { //manual scope to pop symbol off of stack string memory symbol = ctoken.symbol(); if (compareStrings(symbol, "cCANTO")) { underlying = address(wcanto); return getPriceNote(address(wcanto), false); } else { underlying = address(ICErc20(address(ctoken)).underlying()); // We are getting the price for a CErc20 lending market } //set price statically to 1 when the Comptroller is retrieving Price if (compareStrings(symbol, "cNOTE")) { // note in terms of note will always be 1 return 1e18; // Stable coins supported by the lending market are instantiated by governance and their price will always be 1 note } else if (compareStrings(symbol, "cUSDT") && (msg.sender == Comptroller )) { uint decimals = erc20(underlying).decimals(); return 1e18 * 1e18 / (10 ** decimals); //Scale Price as a mantissa to maintain precision in comptroller } else if (compareStrings(symbol, "cUSDC") && (msg.sender == Comptroller)) { uint decimals = erc20(underlying).decimals(); return 1e18 * 1e18 / (10 ** decimals); //Scale Price as a mantissa to maintain precision in comptroller } } if (isPair(underlying)) { // this is an LP Token return getPriceLP(IBaseV1Pair(underlying)); } // this is not an LP Token else { if (isStable[underlying]) { return getPriceNote(underlying, true); // value has already been scaled } return getPriceCanto(underlying) * getPriceNote(address(wcanto), false) / 1e18; } } //return the price of this asset in terms of Canto function getPriceCanto(address token_) internal view returns(uint) { erc20 token = erc20(token_); address pair = pairFor(address(wcanto), address(token), false); if (!isPair(pair)) { return 0; // this pair does not exist with Canto } uint decimals = 10 ** token.decimals(); // get decimals of token uint price = IBaseV1Pair(pair).quote(address(token), decimals, 8); // how much Canto is this asset worth? return price * 1e18 / decimals; //return the scaled price } // returns the price of token in terms of note, scaled by 18 decimals, Notice this will most likely be used with pairs that are stable with note function getPriceNote(address token_, bool stable) internal view returns(uint) { erc20 token = erc20(token_); address pair = pairFor(note, address(token), stable); // pairs with Note may be volatile or stable if (!isPair(pair)) { return 0; // this pair has not yet been deployed } uint decimals = 10 ** token.decimals(); uint price = IBaseV1Pair(pair).quote(address(token), decimals, 8); return price * 1e18 / decimals; // divide by decimals now to maintain precision } // this function returns the TWAP of the LP tokens from pair function getPriceLP(IBaseV1Pair pair) internal view returns(uint) { uint[] memory supply = pair.sampleSupply(12, 1); uint[] memory prices; uint[] memory unitReserves; uint[] memory assetReserves; address token0 = pair.token0(); address token1 = pair.token1(); uint decimals; if (pair.stable()) { // stable pairs will be priced in terms of Note if (token0 == note) { //token0 is the unit, token1 will be priced with respect to this asset initially decimals = 10 ** (erc20(token1).decimals()); // we must normalize the price of token1 to 18 decimals prices = pair.sample(token1, decimals, 12, 1); (unitReserves, assetReserves) = pair.sampleReserves(12, 1); } else { decimals = 10 ** (erc20(token0).decimals()); prices = pair.sample(token0, decimals, 12, 1); (assetReserves, unitReserves) = pair.sampleReserves(12, 1); } } else { // non-stable pairs will be priced in terms of Canto if (token0 == address(wcanto)) { // token0 is Canto, and the unit asset of this pair is Canto decimals = 10 ** (erc20(token1).decimals()); prices = pair.sample(token1, decimals, 12, 1); (unitReserves, assetReserves) = pair.sampleReserves(12, 1); } else { decimals = 10 ** (erc20(token0)).decimals(); prices = pair.sample(token0, decimals, 12, 1); (assetReserves, unitReserves) = pair.sampleReserves(12, 1); } } uint LpPricesCumulative; for(uint i; i < 12; ++i) { uint token0TVL = (assetReserves[i] * prices[i]) / decimals; uint token1TVL = unitReserves[i]; // price of the unit asset is always 1 LpPricesCumulative += (token0TVL + token1TVL) * 1e18 / supply[i]; } uint LpPrice = LpPricesCumulative / 12; // take the average of the cumulative prices if (pair.stable()) { // this asset has been priced in terms of Note return LpPrice; } // this asset has been priced in terms of Canto return LpPrice * getPriceNote(address(wcanto), false) / 1e18; // return the price in terms of Note } function compareStrings(string memory str1, string memory str2) internal pure returns(bool) { return (keccak256(abi.encodePacked(str1)) == keccak256(abi.encodePacked(str2))); } function _returnStableBooleans(uint8 stable) internal pure returns (bool, bool){ if (stable == 2) { return (true, false); } else if (stable == 3) { return (false, true); } else if (stable == 4) { return (false, false); } else { return (true, true); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.6; library Math { function max(uint a, uint b) internal pure returns (uint) { return a >= b ? a : b; } function min(uint a, uint b) internal pure returns (uint) { return a < b ? a : b; } function sqrt(uint y) internal pure returns (uint z) { if (y > 3) { z = y; uint x = y / 2 + 1; while (x < z) { z = x; x = (y / x + x) / 2; } } else if (y != 0) { z = 1; } } function cbrt(uint256 n) internal pure returns (uint256) { unchecked { uint256 x = 0; for (uint256 y = 1 << 255; y > 0; y >>= 3) { x <<= 1; uint256 z = 3 * x * (x + 1) + 1; if (n / y >= z) { n -= y * z; x += 1; } } return x; }} } contract roots { uint immutable decimals0; uint immutable decimals1; bool immutable stable; address immutable token0; address immutable token1; constructor(uint _dec0, uint _dec1, bool _stable, address _t0, address _t1) { decimals0 = _dec0; decimals1 = _dec1; stable = _stable; token0 = _t0; token1 = _t1; } function _f(uint x0, uint xy, uint y) internal pure returns (uint) { return x0*(y*y/1e18*y/1e18)/1e18+(x0*x0/1e18*x0/1e18)*y/1e18-xy; } function _d(uint x0, uint y) internal pure returns (uint) { return 3*x0*(y*y/1e18)/1e18+(x0*x0/1e18*x0/1e18); } function _get_y(uint x0, uint xy, uint y) internal pure returns (uint) { for (uint i = 0; i < 255; i++) { uint y_prev = y; y = y - (_f(x0,xy,y)*1e18/_d(x0,y)); if (y > y_prev) { if (y - y_prev <= 1) { return y; } } else { if (y_prev - y <= 1) { return y; } } } return y; } function getAmountOutNewton(uint amountIn, address tokenIn, uint _reserve0, uint _reserve1) external view returns (uint) { // amountIn -= amountIn / 10000; // remove fee from amount received if (stable) { uint xy = _k(_reserve0, _reserve1); _reserve0 = _reserve0 * 1e18 / decimals0; _reserve1 = _reserve1 * 1e18 / decimals1; (uint reserveA, uint reserveB) = tokenIn == token0 ? (_reserve0, _reserve1) : (_reserve1, _reserve0); amountIn = tokenIn == token0 ? amountIn * 1e18 / decimals0 : amountIn * 1e18 / decimals1; uint y = reserveB - _get_y(amountIn+reserveA, xy, reserveB); return y * (tokenIn == token0 ? decimals1 : decimals0) / 1e18; } else { (uint reserveA, uint reserveB) = tokenIn == token0 ? (_reserve0, _reserve1) : (_reserve1, _reserve0); return amountIn * reserveB / (reserveA + amountIn); } } function _k(uint x, uint y) internal view returns (uint) { if (stable) { uint _x = x * 1e18 / decimals0; uint _y = y * 1e18 / decimals1; uint _a = (_x * _y) / 1e18; uint _b = ((_x * _x) / 1e18 + (_y * _y) / 1e18); return _a * _b / 1e18; // x3y+y3x >= k } else { return x * y; // xy >= k } }function _x4(uint x) internal pure returns (uint) { return x*x/1e18*x/1e18*x/1e18; } function _x3(uint x) internal pure returns (uint) { return x*x/1e18*x/1e18; } function _x9(uint x) internal pure returns (uint) { return _x4(x)*x/1e18*x/1e18*x/1e18*x/1e18*x/1e18; } function _x12(uint x) internal pure returns (uint) { return _x9(x)*x/1e18*x/1e18*x/1e18; } function _c0(uint a, uint b, uint x) internal pure returns (uint) { uint b3 = b*b/1e18*b/1e18; uint a3 = a*a/1e18*a/1e18; uint x2 = x*x/1e18; return 27*a3*b/1e18*x2/1e18+27*a*b3/1e18*x2/1e18; } function _c1(uint x, uint c0) internal pure returns (uint) { uint x12 = _x12(x); return (Math.sqrt(c0*c0+108e18*x12)+c0); } // Math.cbrt(2e54) = 1259921049894873164 function _get_y2(uint xIn, uint a, uint b) internal pure returns (uint amountOut) { uint x = xIn+a; uint c1 = 0; uint b1 = 0; uint b2 = 0; { uint c0 = _c0(a, b, x); c1 = _c1(x, c0); c1 = Math.cbrt(c1*1e36)*1e18; b1 = 3e18*Math.cbrt(2e54)/1e18*x/1e18; b2 = (Math.cbrt(2e54)*_x3(x))*1e18; } uint y0 = c1/b1-b2/c1; return (b - y0); } function getAmountOutClosedForm(uint amountIn, address tokenIn, uint _reserve0, uint _reserve1) external view returns (uint) { // amountIn -= amountIn / 10000; // remove fee from amount received if (stable) { _reserve0 = _reserve0 * 1e18 / decimals0; _reserve1 = _reserve1 * 1e18 / decimals1; (uint reserveA, uint reserveB) = tokenIn == token0 ? (_reserve0, _reserve1) : (_reserve1, _reserve0); amountIn = tokenIn == token0 ? amountIn * 1e18 / decimals0 : amountIn * 1e18 / decimals1; uint y = _get_y2(amountIn, reserveA, reserveB); return y * (tokenIn == token0 ? decimals1 : decimals0) / 1e18; } else { (uint reserveA, uint reserveB) = tokenIn == token0 ? (_reserve0, _reserve1) : (_reserve1, _reserve0); return amountIn * reserveB / (reserveA + amountIn); } } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.11; contract Token { string public symbol; string public name; uint256 public decimals; uint256 public totalSupply = 0; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; event Transfer(address from, address to, uint256 value); event Approval(address owner, address spender, uint256 value); event LogChangeVault( address indexed oldVault, address indexed newVault, uint256 indexed effectiveTime ); bytes32 public DOMAIN_SEPARATOR; // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; mapping(address => uint256) public nonces; address public anyswapRouter; address public pendingAnyswapRouter; uint256 public pendingRouterDelay; constructor( string memory _name, string memory _symbol, uint256 _decimals, address _anyswapRouter ) { anyswapRouter = _anyswapRouter; name = _name; symbol = _symbol; decimals = _decimals; uint256 chainId; assembly { chainId := chainid() } DOMAIN_SEPARATOR = keccak256( abi.encode( keccak256( 'EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)' ), keccak256(bytes(name)), keccak256(bytes('1')), chainId, address(this) ) ); _mint(msg.sender, 0); } function approve(address _spender, uint256 _value) public returns (bool) { allowance[msg.sender][_spender] = _value; emit Approval(msg.sender, _spender, _value); return true; } function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external { require(deadline >= block.timestamp, 'StableV1: EXPIRED'); bytes32 digest = keccak256( abi.encodePacked( '\x19\x01', DOMAIN_SEPARATOR, keccak256( abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline) ) ) ); address recoveredAddress = ecrecover(digest, v, r, s); require( recoveredAddress != address(0) && recoveredAddress == owner, 'StableV1: INVALID_SIGNATURE' ); allowance[owner][spender] = value; emit Approval(owner, spender, value); } function token() external view returns (address) { return address(this); } function balance(address account) external view returns (uint256) { return balanceOf[account]; } function claimFees() external returns (uint256, uint256) { return (0, 0); } function _mint(address _to, uint256 _amount) internal returns (bool) { balanceOf[_to] += _amount; totalSupply += _amount; emit Transfer(address(0x0), _to, _amount); return true; } function _transfer(address _from, address _to, uint256 _value) internal returns (bool) { balanceOf[_from] -= _value; balanceOf[_to] += _value; emit Transfer(_from, _to, _value); return true; } function transfer(address _to, uint256 _value) public returns (bool) { return _transfer(msg.sender, _to, _value); } function transferFrom(address _from, address _to, uint256 _value) public returns (bool) { uint256 allowed_from = allowance[_from][msg.sender]; if (allowed_from != type(uint256).max) { allowance[_from][msg.sender] -= _value; } return _transfer(_from, _to, _value); } function _getRouter() internal returns (address) { if (pendingRouterDelay != 0 && pendingRouterDelay < block.timestamp) { anyswapRouter = pendingAnyswapRouter; pendingRouterDelay = 0; } return anyswapRouter; } function mint(address account, uint256 amount) external returns (bool) { _mint(account, amount); return true; } function burn(address account, uint256 amount) external returns (bool) { require(msg.sender == _getRouter()); totalSupply -= amount; balanceOf[account] -= amount; emit Transfer(account, address(0), amount); return true; } function changeVault(address _pendingRouter) external returns (bool) { require(msg.sender == _getRouter()); require(_pendingRouter != address(0), "AnyswapV3ERC20: address(0x0)"); pendingAnyswapRouter = _pendingRouter; pendingRouterDelay = block.timestamp + 86400; emit LogChangeVault(anyswapRouter, _pendingRouter, pendingRouterDelay); return true; } }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./SafeMath.sol"; contract Timelock { using SafeMath for uint; event NewAdmin(address indexed newAdmin); event NewPendingAdmin(address indexed newPendingAdmin); event NewDelay(uint indexed newDelay); event CancelTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint eta); event ExecuteTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint eta); event QueueTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint eta); uint public constant GRACE_PERIOD = 14 days; uint public constant MINIMUM_DELAY = 0 days; // deployment: 2 days, set to 0 days for convenience uint public constant MAXIMUM_DELAY = 30 days; address public admin; address public pendingAdmin; uint public delay; mapping (bytes32 => bool) public queuedTransactions; constructor(address admin_, uint delay_) public { require(delay_ >= MINIMUM_DELAY, "Timelock::constructor: Delay must exceed minimum delay."); require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay."); admin = admin_; delay = delay_; } fallback() external payable { } function setDelay(uint delay_) public { require(msg.sender == address(this), "Timelock::setDelay: Call must come from Timelock."); require(delay_ >= MINIMUM_DELAY, "Timelock::setDelay: Delay must exceed minimum delay."); require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay."); delay = delay_; emit NewDelay(delay); } function acceptAdmin() public { require(msg.sender == pendingAdmin, "Timelock::acceptAdmin: Call must come from pendingAdmin."); admin = msg.sender; pendingAdmin = address(0); emit NewAdmin(admin); } function setPendingAdmin(address pendingAdmin_) public { require(msg.sender == address(this), "Timelock::setPendingAdmin: Call must come from Timelock."); pendingAdmin = pendingAdmin_; emit NewPendingAdmin(pendingAdmin); } function queueTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) public returns (bytes32) { require(msg.sender == admin, "Timelock::queueTransaction: Call must come from admin."); require(eta >= getBlockTimestamp().add(delay), "Timelock::queueTransaction: Estimated execution block must satisfy delay."); bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); queuedTransactions[txHash] = true; emit QueueTransaction(txHash, target, value, signature, data, eta); return txHash; } function cancelTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) public { require(msg.sender == admin, "Timelock::cancelTransaction: Call must come from admin."); bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); queuedTransactions[txHash] = false; emit CancelTransaction(txHash, target, value, signature, data, eta); } function executeTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) public payable returns (bytes memory) { require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin."); bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued."); require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock."); require(getBlockTimestamp() <= eta.add(GRACE_PERIOD), "Timelock::executeTransaction: Transaction is stale."); queuedTransactions[txHash] = false; bytes memory callData; if (bytes(signature).length == 0) { callData = data; } else { callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data); } // solium-disable-next-line security/no-call-value (bool success, bytes memory returnData) = target.call{value: value}(callData); require(success, "Timelock::executeTransaction: Transaction execution reverted."); emit ExecuteTransaction(txHash, target, value, signature, data, eta); return returnData; } function getBlockTimestamp() internal view returns (uint) { // solium-disable-next-line security/no-block-members return block.timestamp; } }
pragma solidity ^0.8.10; import "../IProposal.sol"; import "../EIP20Interface.sol"; import "../Lens/CompoundLens.sol"; import "../Comptroller.sol"; import "./TreasuryInterfaces.sol"; import "../CTokenInterfaces.sol"; contract TreasuryDelegate is TreasuryInterface { bytes32 constant cantoDenom = keccak256(bytes("CANTO")); bytes32 constant noteDenom = keccak256(bytes("NOTE")); //cache hashed values to reduce unnecessary gas costs /** * @notice Initializes the note contract * @param note_ The address of note ERC20 contract */ function initialize(address note_) public { if (msg.sender != admin) { revert SenderNotAdmin(msg.sender); } if(note_ == address(0) || address(note) != address(0)) { revert FailedInitialization(); //initialize should be called once, and with a valid Note address } note = EIP20Interface(note_); } /** * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. * @param newPendingAdmin New pending admin. */ function _setPendingAdmin(address newPendingAdmin) override external { // Check caller = admin require(msg.sender == admin, "TreasuryDelegator:_setPendingAdmin: admin only"); // Save current value, if any, for inclusion in log address oldPendingAdmin = pendingAdmin; // Store pendingAdmin with value newPendingAdmin pendingAdmin = newPendingAdmin; // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin) emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin); } /** * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin * @dev Admin function for pending admin to accept role and update admin */ function _acceptAdmin() override external { // Check caller is pendingAdmin and pendingAdmin ≠ address(0), msg.sender cannot == address(0) require(msg.sender == pendingAdmin, "TreasuryDelegator:_acceptAdmin: pending admin only"); // Save current values for inclusion in log address oldAdmin = admin; address oldPendingAdmin = pendingAdmin; // Store admin with value pendingAdmin admin = pendingAdmin; // Clear the pending value pendingAdmin = address(0); emit NewAdmin(oldAdmin, admin); emit NewPendingAdmin(oldPendingAdmin, pendingAdmin); } /** * @notice Method to query current balance of CANTO in the treasury * @return treasuryCantoBalance the canto balance */ function queryCantoBalance() external view override returns (uint) { uint treasuryCantoBalance = address(this).balance; return treasuryCantoBalance; } /** * @notice Method to query current balance of NOTE in the treasury * @return treasuryNoteBalance the note balance */ function queryNoteBalance() external view override returns (uint) { uint treasuryNoteBalance = note.balanceOf(address(this)); return treasuryNoteBalance; } // function to call redeem on the cnote lending Market function redeem(address cNote, uint cTokens) external override { if (cNote == address(0)) { revert InvalidAddress(); } CErc20Interface cnote = CErc20Interface(cNote); //initialize cNote uint err = cnote.redeem(cTokens); // if (err != 0) { revert SendFundError(cTokens); } } /** * @notice Method to send treasury funds to recipient * @dev Only the admin can call this method (Timelock contract) * @param recipient Address receiving funds * @param amount Amount to send * @param denom Denomination of fund to send */ function sendFund(address recipient, uint amount, string calldata denom) external override { if (msg.sender != admin ) { revert SenderNotAdmin(msg.sender); } bool success; bytes32 encodeDenom = keccak256(bytes(denom)); if (encodeDenom != cantoDenom && encodeDenom != noteDenom) { revert InvalidDenom(denom); } //sending CANTO if (encodeDenom == cantoDenom) { if (address(this).balance < amount) { revert InsufficientFunds(address(this).balance, amount); } (success, ) = recipient.call{value: amount}(""); //use call instead of transfer } // sending NOTE else if (encodeDenom == noteDenom) { uint bal = note.balanceOf(address(this)); if (bal < amount) { revert InsufficientFunds(bal, amount); } note.transfer(recipient, amount); assembly { switch returndatasize() case 0 {success := not(0)} case 32 { returndatacopy(0,0,32) success := mload(0) //retrieve boolean return value from ERC20 transfer } default { revert(0,0) } } } if (!success) { revert SendFundError(amount); } } }
pragma solidity ^0.8.10; import "./TreasuryInterfaces.sol"; contract TreasuryDelegator is TreasuryDelegatorInterface, TreasuryInterface{ /** *@param note_ address of Note ERC20 Contract to receive funds from *@param implementation_, address of current implementation to delegate calls to *@param admin_, administrator of contract, generally speaking, will be Timelock */ constructor(address note_, address implementation_, address admin_) { require(admin_ != address(0)); require(note_.code.length > 0); //Ensure that this is a contract // Admin set to msg.sender for initialization admin = msg.sender; delegateTo(implementation_, abi.encodeWithSignature("initialize(address)", note_)); setImplementation(implementation_); admin = admin_; } /** * @notice Method called by the admin to update the implementation of the delegator * @param implementation_ The address of the new implementation for delegation */ function setImplementation(address implementation_) override public { require(msg.sender == admin, "GovernorBravoDelegator::setImplementation: admin only"); require(implementation_ != address(0), "GovernorBravoDelegator::setImplementation: invalid implementation address"); address oldImplementation = implementation; implementation = implementation_; emit NewImplementation(oldImplementation, implementation_); } function _setPendingAdmin(address newPendingAdmin) override external { require(msg.sender == admin, "TreasuryDelegator::admin only"); delegateToImplementation(abi.encodeWithSignature("_setPendingAdmin(address)", newPendingAdmin)); } function _acceptAdmin() override external { require(msg.sender == pendingAdmin, "TreasuryDelegator::sender not pendingAdmin"); delegateToImplementation(abi.encodeWithSignature("_acceptAdmin()")); } /** * @notice Method to query current balance of CANTO in the treasury using a DELEGATECALL * @return uint the canto balance */ function queryCantoBalance() override external view returns(uint) { bytes memory data = delegateToViewImplementation(abi.encodeWithSignature("queryCantoBalance()")); return abi.decode(data, (uint)); } /** * @notice Method to query current balance of NOTE in the treasury using a DELEGATECALL * @return uint the note balance */ function queryNoteBalance() override external view returns(uint) { bytes memory data = delegateToViewImplementation(abi.encodeWithSignature("queryNoteBalance()")); return abi.decode(data, (uint)); } /** * @notice Method to send funds to recipient using DELEGATECALL * @param recipient recipient of funds * @param amount amount of funds to send to recipient * @param denom denomination of funds to send */ function sendFund(address recipient, uint amount, string calldata denom) override external { delegateToImplementation(abi.encodeWithSignature("sendFund(address,uint256,string)", recipient, amount, denom)); } function redeem(address cNote, uint cTokens) external override { delegateToImplementation(abi.encodeWithSignature("redeem(address,uint256)", cNote, cTokens)); } /** * @notice Delegates execution to the implementation contract * @dev It returns to the external caller whatever the impledmentation returns or forwards reverts * @param data The raw data to delegatecall * @return bytes returned bytes from the delegater */ function delegateToImplementation(bytes memory data) public returns (bytes memory) { return delegateTo(implementation, data); } /** * @notice Delegates execution to an implementation contract * @dev It returns to the external caller whatever the implementation returns or forwards reverts * There are an additional 2 prefix uints from the wrapper returndata, which we ignore since we make an extra hop. * @param data The raw data to delegatecall * @return bytes returned bytes from the delegatecall */ function delegateToViewImplementation(bytes memory data) public view returns (bytes memory) { (bool success, bytes memory returnData) = address(this).staticcall(abi.encodeWithSignature("delegateToImplementation(bytes)", data)); assembly { if eq(success, 0) { revert(add(returnData, 0x20), returndatasize()) } } return abi.decode(returnData, (bytes)); } /** * @notice Internal method to delegate execution to another contract * @dev It returns to the external caller whatever the implementation returns or forwards reverts * @param callee The contract to delegatecall * @param data The raw data to delegatecall */ function delegateTo(address callee, bytes memory data) internal returns(bytes memory) { (bool success, bytes memory returnData) = callee.delegatecall(data); assembly { if eq(success, 0) { revert(add(returnData, 0x20), returndatasize()) } } return returnData; } /** * @dev Delegates execution to an implementation contract. * It returns to the external caller whatever the implementation returns * or forwards reverts. */ fallback() external payable override { require(msg.value == 0, "TreasuryDelegator::fallback:cannot send value to fallback"); // delegate all other functions to current implementation (bool success, ) = implementation.delegatecall(msg.data); assembly { let free_mem_ptr := mload(0x40) returndatacopy(free_mem_ptr, 0, returndatasize() ) switch success case 0 { revert(free_mem_ptr, returndatasize()) } default { return(free_mem_ptr, returndatasize()) } } } receive() external payable override { emit Received(msg.sender, msg.value); } }
pragma solidity ^0.8.10; import "../EIP20Interface.sol"; import "../IProposal.sol"; contract TreasuryDelegatorStorage { address public pendingAdmin; address public admin; // admin address (Timelock) address public implementation; // implementation address (TreasuryDelegate) } contract TreasuryStorageV1 is TreasuryDelegatorStorage { EIP20Interface public note; // note interface, for handling transfers and querying balance IProposal public unigov; // unigov Interface for handling proposals error SendFundError(uint amount); error SenderNotAdmin(address sender); error FailedInitialization(); error InvalidAddress(); error InsufficientFunds(uint balance, uint funds); error InvalidDenom(string denom); event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin); event NewAdmin(address oldAdmin, address admin); } abstract contract TreasuryDelegatorInterface { event NewImplementation(address oldImplementation, address newImplementation); event Received(address sender, uint amount); function setImplementation(address implementation_) public virtual; fallback() external payable virtual; receive() external payable virtual; } abstract contract TreasuryInterface is TreasuryStorageV1 { function _setPendingAdmin(address newPendingAdmin) external virtual; function _acceptAdmin() external virtual; function queryCantoBalance() external virtual view returns(uint); function queryNoteBalance() external virtual view returns(uint); function sendFund(address recipient, uint amount, string calldata denom) external virtual; function redeem(address cNote, uint cTokens) external virtual; }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./ErrorReporter.sol"; import "./ComptrollerStorage.sol"; /** * @title ComptrollerCore * @dev Storage for the comptroller is at this address, while execution is delegated to the `comptrollerImplementation`. * CTokens should reference this contract as their comptroller. */ contract Unitroller is UnitrollerAdminStorage, ComptrollerErrorReporter { /** * @notice Emitted when pendingComptrollerImplementation is changed */ event NewPendingImplementation( address oldPendingImplementation, address newPendingImplementation ); /** * @notice Emitted when pendingComptrollerImplementation is accepted, which means comptroller implementation is updated */ event NewImplementation( address oldImplementation, address newImplementation ); /** * @notice Emitted when pendingAdmin is changed */ event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin); /** * @notice Emitted when pendingAdmin is accepted, which means admin is updated */ event NewAdmin(address oldAdmin, address newAdmin); constructor() public { // Set admin to caller admin = msg.sender; } /** ** Admin Functions ** */ function _setPendingImplementation(address newPendingImplementation) public returns (uint256) { if (msg.sender != admin) { return fail( Error.UNAUTHORIZED, FailureInfo.SET_PENDING_IMPLEMENTATION_OWNER_CHECK ); } address oldPendingImplementation = pendingComptrollerImplementation; pendingComptrollerImplementation = newPendingImplementation; emit NewPendingImplementation( oldPendingImplementation, pendingComptrollerImplementation ); return uint256(Error.NO_ERROR); } /** * @notice Accepts new implementation of comptroller. msg.sender must be pendingImplementation * @dev Admin function for new implementation to accept it's role as implementation * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _acceptImplementation() public returns (uint256) { // Check caller is pendingImplementation and pendingImplementation ≠ address(0) if ( msg.sender != pendingComptrollerImplementation || pendingComptrollerImplementation == address(0) ) { return fail( Error.UNAUTHORIZED, FailureInfo.ACCEPT_PENDING_IMPLEMENTATION_ADDRESS_CHECK ); } // Save current values for inclusion in log address oldImplementation = comptrollerImplementation; address oldPendingImplementation = pendingComptrollerImplementation; comptrollerImplementation = pendingComptrollerImplementation; pendingComptrollerImplementation = address(0); emit NewImplementation(oldImplementation, comptrollerImplementation); emit NewPendingImplementation( oldPendingImplementation, pendingComptrollerImplementation ); return uint256(Error.NO_ERROR); } /** * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. * @param newPendingAdmin New pending admin. * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _setPendingAdmin(address newPendingAdmin) public returns (uint256) { // Check caller = admin if (msg.sender != admin) { return fail( Error.UNAUTHORIZED, FailureInfo.SET_PENDING_ADMIN_OWNER_CHECK ); } // Save current value, if any, for inclusion in log address oldPendingAdmin = pendingAdmin; // Store pendingAdmin with value newPendingAdmin pendingAdmin = newPendingAdmin; // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin) emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin); return uint256(Error.NO_ERROR); } /** * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin * @dev Admin function for pending admin to accept role and update admin * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ function _acceptAdmin() public returns (uint256) { // Check caller is pendingAdmin and pendingAdmin ≠ address(0) if (msg.sender != pendingAdmin || msg.sender == address(0)) { return fail( Error.UNAUTHORIZED, FailureInfo.ACCEPT_ADMIN_PENDING_ADMIN_CHECK ); } // Save current values for inclusion in log address oldAdmin = admin; address oldPendingAdmin = pendingAdmin; // Store admin with value pendingAdmin admin = pendingAdmin; // Clear the pending value pendingAdmin = address(0); emit NewAdmin(oldAdmin, admin); emit NewPendingAdmin(oldPendingAdmin, pendingAdmin); return uint256(Error.NO_ERROR); } /** * @dev Delegates execution to an implementation contract. * It returns to the external caller whatever the implementation returns * or forwards reverts. */ fallback() external payable { // delegate all other functions to current implementation (bool success,) = comptrollerImplementation.delegatecall(msg.data); assembly { let free_mem_ptr := mload(0x40) returndatacopy(free_mem_ptr, 0, returndatasize()) switch success case 0 { revert(free_mem_ptr, returndatasize()) } default { return(free_mem_ptr, returndatasize()) } } } }
pragma solidity ^0.8.10; import "./EIP20Interface.sol"; contract WETH is EIP20Interface { string private _name; string private _symbol; uint8 private _decimals = 18; mapping (address => uint) public _balanceOf; mapping (address => mapping (address => uint)) public _allowance; constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } receive() external payable { deposit(); } function deposit() public payable { _balanceOf[msg.sender] += msg.value; emit Deposit(msg.sender, msg.value); } function withdraw(uint wamount) public { require(_balanceOf[msg.sender] >= wamount, "sender balance insufficient for withdrawal"); _balanceOf[msg.sender] -= wamount; payable(msg.sender).transfer(wamount); // rentrant attack must be less than 2300 gas emit Withdrawal(msg.sender, wamount); } function name() external view returns (string memory) { return _name; } function symbol() external view returns (string memory) { return _symbol; } function decimals() external view returns (uint8) { return _decimals; } function totalSupply() public view returns (uint) { return address(this).balance; } function balanceOf(address owner) external view returns(uint256) { return _balanceOf[owner]; } function approve(address spender, uint amount) public returns (bool) { _approve(msg.sender, spender, amount); emit Approval(msg.sender, spender, amount); return true; } function transfer(address dst, uint wad) public returns (bool) { return transferFrom(msg.sender, dst, wad); } function transferFrom(address src, address dst, uint wad) public returns (bool) { require(_balanceOf[src] >= wad, "WETH::transfeFrom: balance insufficient"); if (src != msg.sender && _allowance[src][msg.sender] != type(uint).max) { require(_allowance[src][msg.sender] >= wad, "WETH::transferFrom:allowance insufficient"); _allowance[src][msg.sender] -= wad; } _balanceOf[src] -= wad; _balanceOf[dst] += wad; emit Transfer(src, dst, wad); return true; } function _approve( address owner, address spender, uint256 amount ) internal { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowance[owner][spender] = amount; emit Approval(owner, spender, amount); } function allowance(address owner, address spender) external view returns (uint256) { return _allowance[owner][spender]; } event Deposit(address indexed dst, uint wad); event Withdrawal(address indexed src, uint wad); }
// SPDX-License-Identifier: BSD-3-Clause pragma solidity ^0.8.10; import "./InterestRateModel.sol"; /** * @title Compound's WhitePaperInterestRateModel Contract * @author Compound * @notice The parameterized model described in section 2.4 of the original Compound Protocol whitepaper */ contract WhitePaperInterestRateModel is InterestRateModel { event NewInterestParams(uint baseRatePerBlock, uint multiplierPerBlock); uint256 private constant BASE = 1e18; /** * @notice The approximate number of blocks per year that is assumed by the interest rate model */ uint public constant blocksPerYear = 2102400; /** * @notice The multiplier of utilization rate that gives the slope of the interest rate */ uint public multiplierPerBlock; /** * @notice The base interest rate which is the y-intercept when utilization rate is 0 */ uint public baseRatePerBlock; /** * @notice Construct an interest rate model * @param baseRatePerYear The approximate target base APR, as a mantissa (scaled by BASE) * @param multiplierPerYear The rate of increase in interest rate wrt utilization (scaled by BASE) */ constructor(uint baseRatePerYear, uint multiplierPerYear) public { baseRatePerBlock = baseRatePerYear / blocksPerYear; multiplierPerBlock = multiplierPerYear / blocksPerYear; emit NewInterestParams(baseRatePerBlock, multiplierPerBlock); } /** * @notice Calculates the utilization rate of the market: `borrows / (cash + borrows - reserves)` * @param cash The amount of cash in the market * @param borrows The amount of borrows in the market * @param reserves The amount of reserves in the market (currently unused) * @return The utilization rate as a mantissa between [0, BASE] */ function utilizationRate(uint cash, uint borrows, uint reserves) public pure returns (uint) { // Utilization rate is 0 when there are no borrows if (borrows == 0) { return 0; } return borrows * BASE / (cash + borrows - reserves); } /** * @notice Calculates the current borrow rate per block, with the error code expected by the market * @param cash The amount of cash in the market * @param borrows The amount of borrows in the market * @param reserves The amount of reserves in the market * @return The borrow rate percentage per block as a mantissa (scaled by BASE) */ function getBorrowRate(uint cash, uint borrows, uint reserves) override public view returns (uint) { uint ur = utilizationRate(cash, borrows, reserves); return (ur * multiplierPerBlock / BASE) + baseRatePerBlock; } /** * @notice Calculates the current supply rate per block * @param cash The amount of cash in the market * @param borrows The amount of borrows in the market * @param reserves The amount of reserves in the market * @param reserveFactorMantissa The current reserve factor for the market * @return The supply rate percentage per block as a mantissa (scaled by BASE) */ function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) override public view returns (uint) { uint oneMinusReserveFactor = BASE - reserveFactorMantissa; uint borrowRate = getBorrowRate(cash, borrows, reserves); uint rateToPool = borrowRate * oneMinusReserveFactor / BASE; return utilizationRate(cash, borrows, reserves) * rateToPool / BASE; } }
{ "optimizer": { "enabled": true, "runs": 200 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"id","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60e06040523480156200001157600080fd5b5060405162000f4238038062000f42833981016040819052620000349162000320565b81816012826000908051906020019062000050929190620001ad565b50815162000066906001906020850190620001ad565b5060ff81166080524660a0526200007c620000a4565b60c052506200009c91503390506be04ee0ccb27ac646ac00000062000140565b505062000492565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051620000d89190620003c7565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b80600260008282546200015491906200046b565b90915550506001600160a01b0382166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b828054620001bb906200038a565b90600052602060002090601f016020900481019282620001df57600085556200022a565b82601f10620001fa57805160ff19168380011785556200022a565b828001600101855582156200022a579182015b828111156200022a5782518255916020019190600101906200020d565b50620002389291506200023c565b5090565b5b808211156200023857600081556001016200023d565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200027b57600080fd5b81516001600160401b038082111562000298576200029862000253565b604051601f8301601f19908116603f01168101908282118183101715620002c357620002c362000253565b81604052838152602092508683858801011115620002e057600080fd5b600091505b83821015620003045785820183015181830184015290820190620002e5565b83821115620003165760008385830101525b9695505050505050565b600080604083850312156200033457600080fd5b82516001600160401b03808211156200034c57600080fd5b6200035a8683870162000269565b935060208501519150808211156200037157600080fd5b50620003808582860162000269565b9150509250929050565b600181811c908216806200039f57607f821691505b60208210811415620003c157634e487b7160e01b600052602260045260246000fd5b50919050565b600080835481600182811c915080831680620003e457607f831692505b60208084108214156200040557634e487b7160e01b86526022600452602486fd5b8180156200041c57600181146200042e576200045d565b60ff198616895284890196506200045d565b60008a81526020902060005b86811015620004555781548b8201529085019083016200043a565b505084890196505b509498975050505050505050565b600082198211156200048d57634e487b7160e01b600052601160045260246000fd5b500190565b60805160a05160c051610a80620004c26000396000610425015260006103f0015260006101290152610a806000f3fe608060405234801561001057600080fd5b50600436106100b45760003560e01c806370a082311161007157806370a08231146101655780637ecebe001461018557806395d89b41146101a5578063a9059cbb146101ad578063d505accf146101c0578063dd62ed3e146101d557600080fd5b806306fdde03146100b9578063095ea7b3146100d757806318160ddd146100fa57806323b872dd14610111578063313ce567146101245780633644e5151461015d575b600080fd5b6100c1610200565b6040516100ce91906107af565b60405180910390f35b6100ea6100e5366004610820565b61028e565b60405190151581526020016100ce565b61010360025481565b6040519081526020016100ce565b6100ea61011f36600461084a565b6102fa565b61014b7f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff90911681526020016100ce565b6101036103ec565b610103610173366004610886565b60036020526000908152604090205481565b610103610193366004610886565b60056020526000908152604090205481565b6100c1610447565b6100ea6101bb366004610820565b610454565b6101d36101ce3660046108a8565b6104cc565b005b6101036101e336600461091b565b600460209081526000928352604080842090915290825290205481565b6000805461020d9061094e565b80601f01602080910402602001604051908101604052809291908181526020018280546102399061094e565b80156102865780601f1061025b57610100808354040283529160200191610286565b820191906000526020600020905b81548152906001019060200180831161026957829003601f168201915b505050505081565b3360008181526004602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906102e99086815260200190565b60405180910390a350600192915050565b6001600160a01b03831660009081526004602090815260408083203384529091528120546000198114610356576103318382610989565b6001600160a01b03861660009081526004602090815260408083203384529091529020555b6001600160a01b0385166000908152600360205260408120805485929061037e908490610989565b90915550506001600160a01b03808516600081815260036020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906103d99087815260200190565b60405180910390a3506001949350505050565b60007f000000000000000000000000000000000000000000000000000000000000000046146104225761041d610715565b905090565b507f000000000000000000000000000000000000000000000000000000000000000090565b6001805461020d9061094e565b33600090815260036020526040812080548391908390610475908490610989565b90915550506001600160a01b038316600081815260036020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906102e99086815260200190565b428410156105215760405162461bcd60e51b815260206004820152601760248201527f5045524d49545f444541444c494e455f4558504952454400000000000000000060448201526064015b60405180910390fd5b6000600161052d6103ec565b6001600160a01b038a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f198184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015610639573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381161580159061066f5750876001600160a01b0316816001600160a01b0316145b6106ac5760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa9a4a3a722a960911b6044820152606401610518565b6001600160a01b0390811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f600060405161074791906109ae565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b600060208083528351808285015260005b818110156107dc578581018301518582016040015282016107c0565b818111156107ee576000604083870101525b50601f01601f1916929092016040019392505050565b80356001600160a01b038116811461081b57600080fd5b919050565b6000806040838503121561083357600080fd5b61083c83610804565b946020939093013593505050565b60008060006060848603121561085f57600080fd5b61086884610804565b925061087660208501610804565b9150604084013590509250925092565b60006020828403121561089857600080fd5b6108a182610804565b9392505050565b600080600080600080600060e0888a0312156108c357600080fd5b6108cc88610804565b96506108da60208901610804565b95506040880135945060608801359350608088013560ff811681146108fe57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561092e57600080fd5b61093783610804565b915061094560208401610804565b90509250929050565b600181811c9082168061096257607f821691505b6020821081141561098357634e487b7160e01b600052602260045260246000fd5b50919050565b6000828210156109a957634e487b7160e01b600052601160045260246000fd5b500390565b600080835481600182811c9150808316806109ca57607f831692505b60208084108214156109ea57634e487b7160e01b86526022600452602486fd5b8180156109fe5760018114610a0f57610a3c565b60ff19861689528489019650610a3c565b60008a81526020902060005b86811015610a345781548b820152908501908301610a1b565b505084890196505b50949897505050505050505056fea26469706673582212200a3f8d727bfc005df9dfdc4c7028566c1fa2b250f93e2157c54d3d504666ca7264736f6c634300080b00330000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000064d656d656d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064d454d454d450000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106100b45760003560e01c806370a082311161007157806370a08231146101655780637ecebe001461018557806395d89b41146101a5578063a9059cbb146101ad578063d505accf146101c0578063dd62ed3e146101d557600080fd5b806306fdde03146100b9578063095ea7b3146100d757806318160ddd146100fa57806323b872dd14610111578063313ce567146101245780633644e5151461015d575b600080fd5b6100c1610200565b6040516100ce91906107af565b60405180910390f35b6100ea6100e5366004610820565b61028e565b60405190151581526020016100ce565b61010360025481565b6040519081526020016100ce565b6100ea61011f36600461084a565b6102fa565b61014b7f000000000000000000000000000000000000000000000000000000000000001281565b60405160ff90911681526020016100ce565b6101036103ec565b610103610173366004610886565b60036020526000908152604090205481565b610103610193366004610886565b60056020526000908152604090205481565b6100c1610447565b6100ea6101bb366004610820565b610454565b6101d36101ce3660046108a8565b6104cc565b005b6101036101e336600461091b565b600460209081526000928352604080842090915290825290205481565b6000805461020d9061094e565b80601f01602080910402602001604051908101604052809291908181526020018280546102399061094e565b80156102865780601f1061025b57610100808354040283529160200191610286565b820191906000526020600020905b81548152906001019060200180831161026957829003601f168201915b505050505081565b3360008181526004602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906102e99086815260200190565b60405180910390a350600192915050565b6001600160a01b03831660009081526004602090815260408083203384529091528120546000198114610356576103318382610989565b6001600160a01b03861660009081526004602090815260408083203384529091529020555b6001600160a01b0385166000908152600360205260408120805485929061037e908490610989565b90915550506001600160a01b03808516600081815260036020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906103d99087815260200190565b60405180910390a3506001949350505050565b60007f000000000000000000000000000000000000000000000000000000000000000146146104225761041d610715565b905090565b507fd87ad5214b08b36313566472b02e4bf1ffbca9b13907f0076796bb609abdfca490565b6001805461020d9061094e565b33600090815260036020526040812080548391908390610475908490610989565b90915550506001600160a01b038316600081815260036020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906102e99086815260200190565b428410156105215760405162461bcd60e51b815260206004820152601760248201527f5045524d49545f444541444c494e455f4558504952454400000000000000000060448201526064015b60405180910390fd5b6000600161052d6103ec565b6001600160a01b038a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f198184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015610639573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381161580159061066f5750876001600160a01b0316816001600160a01b0316145b6106ac5760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa9a4a3a722a960911b6044820152606401610518565b6001600160a01b0390811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f600060405161074791906109ae565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b600060208083528351808285015260005b818110156107dc578581018301518582016040015282016107c0565b818111156107ee576000604083870101525b50601f01601f1916929092016040019392505050565b80356001600160a01b038116811461081b57600080fd5b919050565b6000806040838503121561083357600080fd5b61083c83610804565b946020939093013593505050565b60008060006060848603121561085f57600080fd5b61086884610804565b925061087660208501610804565b9150604084013590509250925092565b60006020828403121561089857600080fd5b6108a182610804565b9392505050565b600080600080600080600060e0888a0312156108c357600080fd5b6108cc88610804565b96506108da60208901610804565b95506040880135945060608801359350608088013560ff811681146108fe57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561092e57600080fd5b61093783610804565b915061094560208401610804565b90509250929050565b600181811c9082168061096257607f821691505b6020821081141561098357634e487b7160e01b600052602260045260246000fd5b50919050565b6000828210156109a957634e487b7160e01b600052601160045260246000fd5b500390565b600080835481600182811c9150808316806109ca57607f831692505b60208084108214156109ea57634e487b7160e01b86526022600452602486fd5b8180156109fe5760018114610a0f57610a3c565b60ff19861689528489019650610a3c565b60008a81526020902060005b86811015610a345781548b820152908501908301610a1b565b505084890196505b50949897505050505050505056fea26469706673582212200a3f8d727bfc005df9dfdc4c7028566c1fa2b250f93e2157c54d3d504666ca7264736f6c634300080b0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000064d656d656d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064d454d454d450000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : name (string): Mememe
Arg [1] : id (string): MEMEME
-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000040
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000006
Arg [3] : 4d656d656d650000000000000000000000000000000000000000000000000000
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000006
Arg [5] : 4d454d454d450000000000000000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.