mememe: MEMEME Token
Source Code
Token Contract
More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 25 from a total of 19,059 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Approve | 24579684 | 3 days ago | IN | 0 ETH | 0.00000936 | ||||
| Approve | 24570776 | 4 days ago | IN | 0 ETH | 0.00004532 | ||||
| Approve | 24566288 | 5 days ago | IN | 0 ETH | 0.00009897 | ||||
| Approve | 24536102 | 9 days ago | IN | 0 ETH | 0.00005275 | ||||
| Transfer | 24532095 | 9 days ago | IN | 0 ETH | 0.00000155 | ||||
| Approve | 24499228 | 14 days ago | IN | 0 ETH | 0.00003109 | ||||
| Transfer | 24428692 | 24 days ago | IN | 0 ETH | 0.00000755 | ||||
| Approve | 24372215 | 32 days ago | IN | 0 ETH | 0.00000507 | ||||
| Approve | 24361745 | 33 days ago | IN | 0 ETH | 0.00011373 | ||||
| Approve | 24358598 | 33 days ago | IN | 0 ETH | 0.00010182 | ||||
| Approve | 24323546 | 38 days ago | IN | 0 ETH | 0.00000605 | ||||
| Approve | 24314419 | 40 days ago | IN | 0 ETH | 0.00000517 | ||||
| Approve | 24313377 | 40 days ago | IN | 0 ETH | 0.00009543 | ||||
| Approve | 24289628 | 43 days ago | IN | 0 ETH | 0.00009475 | ||||
| Approve | 24263274 | 47 days ago | IN | 0 ETH | 0.00000503 | ||||
| Approve | 24238676 | 50 days ago | IN | 0 ETH | 0.00006052 | ||||
| Approve | 24224409 | 52 days ago | IN | 0 ETH | 0.00000241 | ||||
| Approve | 24224407 | 52 days ago | IN | 0 ETH | 0.0000015 | ||||
| Approve | 24206662 | 55 days ago | IN | 0 ETH | 0.00000157 | ||||
| Approve | 24190657 | 57 days ago | IN | 0 ETH | 0.00010585 | ||||
| Transfer | 24189725 | 57 days ago | IN | 0 ETH | 0.00001609 | ||||
| Transfer | 24189044 | 57 days ago | IN | 0 ETH | 0.0000253 | ||||
| Approve | 24157822 | 62 days ago | IN | 0 ETH | 0.00000178 | ||||
| Approve | 24149195 | 63 days ago | IN | 0 ETH | 0.00000465 | ||||
| Approve | 24109781 | 68 days ago | IN | 0 ETH | 0.00000253 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
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
Contract ABI
API[{"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
Net Worth in USD
$1,647.13
Net Worth in ETH
0.831305
Token Allocations
USDT
33.18%
BSC-USD
30.35%
SWP
27.56%
Others
8.90%
Multichain Portfolio | 33 Chains
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.