-
-
Save brianairb/5154d4612276366fd8bbf1e7e7d3bf13 to your computer and use it in GitHub Desktop.
donkey.fund
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.5.6; | |
import "./DToken.sol"; | |
import "./ControllerInterface.sol"; | |
import "./ControllerStorage.sol"; | |
import "./Donkey.sol"; | |
import "./Exponential.sol"; | |
import "./Error.sol"; | |
import "./upgradeable/Initializable.sol"; | |
contract Controller is Initializable, ControllerInterface, ControllerStorage, Exponential, ControllerErrorReport { | |
event MarketListed(DToken dToken); | |
event MarketEntered(DToken dToken, address account); | |
event MarketExited(DToken dToken, address account); | |
event ActionPaused(DToken dToken, string action, bool pauseState); | |
event NewGuardian(address pauseGuardian); | |
event NewBorrowCap(DToken indexed dToken, uint newBorrowCap); | |
event NewCollateralFactor(DToken dToken, uint oldCollateralFactorMantissa, uint newCollateralFactorMantissa); | |
event NewCloseFactor(uint oldCloseFactorMantissa, uint newCloseFactorMantissa); | |
event NewPriceOracle(PriceOracle newPriceOracle); | |
event DistributeSupplierDonkey(DToken indexed dToken, address indexed supplier, uint donkeyDelta, uint donkeySupplyIndex); | |
event DistributeBorrowerDonkey(DToken indexed dToken, address indexed borrower, uint donkeyDelta, uint donkeyBorrowIndex); | |
uint224 public constant donkeyInitialIndex = 1e36; | |
uint internal constant closeFactorMinMantissa = 0.05e18; // 0.05 | |
uint internal constant closeFactorMaxMantissa = 0.9e18; // 0.9 | |
uint internal constant collateralFactorMaxMantissa = 0.9e18; // 0.9 | |
function initialize(address admin_, address donkeyAddress_) public initializer { | |
admin = admin_; | |
DONKEY_ADDRESS = donkeyAddress_; | |
} | |
function enterMarkets(address dToken, address account) internal returns (uint) { | |
DToken asset = DToken(dToken); | |
uint result = uint(addToMarketInternal(asset, account)); | |
return result; | |
} | |
function exitMarket(address dTokenAddress, address account) external returns (uint) { | |
require(DToken(dTokenAddress).isDToken() == true, "only DToken contract can exitMarket"); | |
DToken dToken = DToken(dTokenAddress); | |
(uint oErr, uint tokensHeld, uint amountOwed, ) = dToken.getAccountSnapshot(account); | |
require(oErr == 0, "exitMarket: getAccountSnapshot failed"); | |
if (amountOwed != 0) { | |
return fail(Error.NONZERO_BORROW_BALANCE, FailureInfo.EXIT_MARKET_BALANCE_OWED); | |
} | |
uint allowed = redeemAllowedInternal(dTokenAddress, account, tokensHeld); | |
if (allowed != 0) { | |
return failOpaque(Error.REJECTION, FailureInfo.EXIT_MARKET_REJECTION, allowed); | |
} | |
Market storage marketToExit = markets[address(dToken)]; | |
if (!marketToExit.accountMembership[account]) { | |
return uint(Error.NO_ERROR); | |
} | |
delete marketToExit.accountMembership[account]; | |
DToken[] memory userAssetList = accountAssets[account]; | |
uint len = userAssetList.length; | |
uint assetIndex = len; | |
for (uint i = 0; i < len; i++) { | |
if (userAssetList[i] == dToken) { | |
assetIndex = i; | |
break; | |
} | |
} | |
assert(assetIndex < len); | |
DToken[] storage storedList = accountAssets[account]; | |
storedList[assetIndex] = storedList[storedList.length - 1]; | |
storedList.length--; | |
emit MarketExited(dToken, account); | |
return uint(Error.NO_ERROR); | |
} | |
function getAccountMembership(DToken dToken, address account) view external returns (bool) { | |
return markets[address(dToken)].accountMembership[account]; | |
} | |
function addToMarketInternal(DToken dToken, address borrower) internal returns (Error) { | |
Market storage marketToJoin = markets[address(dToken)]; | |
if (!marketToJoin.isListed) { | |
return Error.MARKET_NOT_LISTED; | |
} | |
if (marketToJoin.accountMembership[borrower]) { | |
return Error.NO_ERROR; | |
} | |
marketToJoin.accountMembership[borrower] = true; | |
accountAssets[borrower].push(dToken); | |
emit MarketEntered(dToken, borrower); | |
return Error.NO_ERROR; | |
} | |
function _addMarketInternal(address dToken) internal { | |
for (uint i = 0; i < allMarkets.length; i ++) { | |
require(allMarkets[i] != DToken(dToken), "market already added"); | |
} | |
allMarkets.push(DToken(dToken)); | |
} | |
function _setMarketBorrowCaps(DToken[] calldata dTokens, uint[] calldata newBorrowCaps) external { | |
require(msg.sender == admin, "only admin or borrow cap guardian can set borrow caps"); | |
uint numMarkets = dTokens.length; | |
uint numBorrowCaps = newBorrowCaps.length; | |
require(numMarkets != 0 && numMarkets == numBorrowCaps, "invalid input"); | |
for(uint i = 0; i < numMarkets; i++) { | |
borrowCaps[address(dTokens[i])] = newBorrowCaps[i]; | |
emit NewBorrowCap(dTokens[i], newBorrowCaps[i]); | |
} | |
} | |
function redeemAllowed(address dToken, address redeemer, uint redeemAmount) external returns (uint) { | |
uint allowed = redeemAllowedInternal(dToken, redeemer, redeemAmount); | |
require(allowed == uint(Error.NO_ERROR), "REDEEM_ALLOWED_INTERNAL_FAILED"); | |
updateDonkeySupplyIndex(dToken); | |
distributeSupplierDonkey(dToken, redeemer); | |
return uint(Error.NO_ERROR); | |
} | |
function redeemAllowedInternal(address dToken, address redeemer, uint redeemAmount) internal view returns (uint) { | |
require(markets[dToken].isListed, "MARKET_NOT_LISTED"); | |
if (!markets[dToken].accountMembership[redeemer]) { | |
return uint(Error.NO_ERROR); | |
} | |
(Error err, , uint shortfall) = getHypotheticalAccountLiquidityInternal(redeemer, DToken(dToken), redeemAmount, 0, true); | |
if (err != Error.NO_ERROR) { | |
return uint(err); | |
} | |
require(shortfall <= 0, "INSUFFICIENT_LIQUIDITIY"); | |
return uint(Error.NO_ERROR); | |
} | |
function transferAllowed(address dToken, address src, address dst, uint transferTokens) external returns (uint) { | |
require(!transferGuardianPaused, "transfer is paused"); | |
uint allowed = redeemAllowedInternal(dToken, src, transferTokens); | |
if (allowed != uint(Error.NO_ERROR)) { | |
return allowed; | |
} | |
updateDonkeySupplyIndex(dToken); | |
distributeSupplierDonkey(dToken, src); | |
distributeSupplierDonkey(dToken, dst); | |
return uint(Error.NO_ERROR); | |
} | |
function mintAllowed(address dToken, address minter) external returns (uint) { | |
require(!isMintPaused[dToken], "TOKEN_PAUSED__MINT_ALLOWED_FAILED"); | |
require(markets[dToken].isListed, "MARKET_NOT_LISTED"); | |
uint enteredMarket = enterMarkets(dToken, minter); | |
require(enteredMarket == uint(Error.NO_ERROR), "ENTERMARKET_FAILED"); | |
updateDonkeySupplyIndex(dToken); | |
distributeSupplierDonkey(dToken, minter); | |
return uint(Error.NO_ERROR); | |
} | |
function setMintPaused(DToken dToken, bool state) external returns (bool) { | |
require(msg.sender == admin || msg.sender == pauseGuardian, "only pause guardian and admin can pause"); | |
require(markets[address(dToken)].isListed, "cannot pause a market that is not listed"); | |
isMintPaused[address(dToken)] = state; | |
emit ActionPaused(dToken, "Mint", state); | |
return state; | |
} | |
function _supportMarket(DToken dToken) external returns (uint) { | |
if (msg.sender != admin) { | |
return fail(Error.UNAUTHORIZED, FailureInfo.SUPPORT_MARKET_OWNER_CHECK); | |
} | |
if (markets[address(dToken)].isListed) { | |
return fail(Error.MARKET_ALREADY_LISTED, FailureInfo.SUPPORT_MARKET_EXISTS); | |
} | |
dToken.isDToken(); | |
markets[address(dToken)] = Market({isListed: true, isDonkeyed: false, collateralFactorMantissa: 0}); | |
_addMarketInternal(address(dToken)); | |
emit MarketListed(dToken); | |
return uint(Error.NO_ERROR); | |
} | |
function setProtectedAssets(address dToken, bool state) external { | |
require(msg.sender == admin || msg.sender == pauseGuardian, "only admin and pauseGuardian can set protectedAssets"); | |
protectedAssets[dToken] = state; | |
} | |
function borrowAllowed(address dToken, address payable borrower, uint borrowAmount) external returns (uint) { | |
require(!isBorrowPaused[dToken], "TOKEN_PAUSED__BORROW_ALLOWED_FAILED"); | |
require(markets[dToken].isListed, "MARKET_NOT_LISTED"); | |
if (!markets[dToken].accountMembership[borrower]) { | |
require(msg.sender == dToken, "SENDER_ERROR__SENDER_MUST_BE_DTOKEN_CONTRACT"); | |
Error err = addToMarketInternal(DToken(dToken), borrower); | |
if (err != Error.NO_ERROR) { | |
return uint(err); | |
} | |
require(markets[dToken].accountMembership[borrower], "ENTERMARKET_FAILED"); | |
} | |
{ | |
uint borrowCap = borrowCaps[dToken]; | |
if (borrowCap != 0) { | |
uint totalBorrows = DToken(dToken).totalBorrows(); | |
uint nextTotalBorrows = add_(totalBorrows, borrowAmount); | |
require(nextTotalBorrows < borrowCap, "MARKET_BORROW_CAP_REACHED"); | |
} | |
} | |
(Error err, , uint shortfall) = getHypotheticalAccountLiquidityInternal(borrower, DToken(dToken), 0, borrowAmount, true); | |
if (err != Error.NO_ERROR) { | |
return uint(err); | |
} | |
require(shortfall <= 0, "INSUFFICIENT_LIQUIDITIY"); | |
Exp memory borrowIndex = Exp({mantissa: DToken(dToken).borrowIndex()}); | |
updateDonkeyBorrowIndex(dToken, borrowIndex); | |
distributeBorrowerDonkey(dToken, borrower, borrowIndex); | |
return uint(Error.NO_ERROR); | |
} | |
function repayBorrowAllowed(address dToken, address borrower) external returns (uint) { | |
require(markets[dToken].isListed, "MARKET_NOT_LISTED"); | |
Exp memory borrowIndex = Exp({mantissa : DToken(dToken).borrowIndex()}); | |
updateDonkeyBorrowIndex(dToken, borrowIndex); | |
distributeBorrowerDonkey(dToken, borrower, borrowIndex); | |
return uint(Error.NO_ERROR); | |
} | |
function setPauseGuardian(address newGuardianAddress) external returns (uint) { | |
require(msg.sender == admin, "only admin can set guardian"); | |
pauseGuardian = newGuardianAddress; | |
emit NewGuardian(pauseGuardian); | |
return uint(Error.NO_ERROR); | |
} | |
function setBorrowPaused(DToken dToken, bool state) external returns (bool) { | |
require(markets[address(dToken)].isListed, "cannot pause a market that is not listed"); | |
require(msg.sender == admin || msg.sender == pauseGuardian, "only pause guadian and admin can pause"); | |
isBorrowPaused[address(dToken)] = state; | |
emit ActionPaused(dToken, "Borrow", state); | |
return state; | |
} | |
struct AccountLiquidityLocalVars { | |
uint sumCollateral; | |
uint sumBorrowPlusEffects; | |
uint dTokenBalance; | |
uint borrowBalance; | |
uint exchangeRateMantissa; | |
uint oraclePriceMantissa; | |
Exp collateralFactor; | |
Exp exchangeRate; | |
Exp oraclePrice; | |
Exp tokensToDenom; | |
} | |
function _setSeizePaused(address dToken, bool state) external returns (bool) { | |
require(msg.sender == pauseGuardian || msg.sender == admin, "only pause guardian and admin can pause"); | |
seizeGuardianPaused[dToken] = state; | |
emit ActionPaused(DToken(dToken), "Seize", state); | |
return state; | |
} | |
function seizeAllowed( | |
address dTokenCollateral, | |
address dTokenBorrowed, | |
address liquidator, | |
address borrower, | |
uint seizeTokens) external returns (uint) { | |
seizeTokens; | |
require(markets[dTokenCollateral].isListed, "MARKET_NOT_LISTED__COLLATERAL_ASSET_IS_NOT_LISTED"); | |
require(markets[dTokenBorrowed].isListed, "MARKET_NOT_LISTED__BORROWED_ASSET_IS_NOT_LISTED"); | |
require(DToken(dTokenCollateral).controller() == DToken(dTokenBorrowed).controller(), "CONTROLLER_MISMATCH"); | |
updateDonkeySupplyIndex(dTokenCollateral); | |
distributeSupplierDonkey(dTokenCollateral, borrower); | |
distributeSupplierDonkey(dTokenCollateral, liquidator); | |
return uint(Error.NO_ERROR); | |
} | |
function liquidateCalculateSeizeTokens(address dTokenBorrowed, address dTokenCollateral, uint actualRepayAmount) external view returns (uint, uint) { | |
uint priceBorrowedMantissa = priceOracle.getUnderlyingPrice(DToken(dTokenBorrowed).underlyingSymbol()); | |
uint priceCollateralMantissa = priceOracle.getUnderlyingPrice(DToken(dTokenCollateral).underlyingSymbol()); | |
if (priceBorrowedMantissa == 0 || priceCollateralMantissa == 0) { | |
return (uint(Error.PRICE_ERROR), 0); | |
} | |
uint exchangeRateMantissa = DToken(dTokenCollateral).exchangeRateStored(); // Note: reverts on error | |
uint seizeTokens; | |
Exp memory numerator; | |
Exp memory denominator; | |
Exp memory ratio; | |
numerator = mul_(Exp({mantissa: liquidationIncentiveMantissa[dTokenCollateral]}), Exp({mantissa: priceBorrowedMantissa})); | |
denominator = mul_(Exp({mantissa: priceCollateralMantissa}), Exp({mantissa: exchangeRateMantissa})); | |
ratio = div_(numerator, denominator); | |
seizeTokens = mul_ScalarTruncate(ratio, actualRepayAmount); | |
return (uint(Error.NO_ERROR), seizeTokens); | |
} | |
function getAccountLiquidity(address account, bool isProtectedCall) external view returns (uint, uint, uint) { | |
(Error err, uint liquidity, uint shortfall) = getHypotheticalAccountLiquidityInternal(account, DToken(0), 0, 0, isProtectedCall); | |
return (uint(err), liquidity, shortfall); | |
} | |
function redeemVerify(uint redeemAmount, uint redeemTokens) external { | |
if (redeemTokens == 0 && redeemAmount > 0) { | |
revert("redeemTokens zero"); | |
} | |
} | |
function getHypotheticalAccountLiquidityInternal( | |
address account, | |
DToken dTokenModify, | |
uint redeemTokens, | |
uint borrowAmount, | |
bool isProtectedCall | |
) internal view returns (Error, uint, uint) { | |
AccountLiquidityLocalVars memory vars; | |
uint oErr; | |
DToken[] memory assets = accountAssets[account]; | |
for (uint i = 0; i < assets.length; i++) { | |
DToken asset = assets[i]; | |
if (isProtectedCall && protectedAssets[address(asset)]) { | |
continue; | |
} | |
(oErr, vars.dTokenBalance, vars.borrowBalance, vars.exchangeRateMantissa) = asset.getAccountSnapshot(account); | |
require(oErr == 0, "snap shot error"); | |
vars.collateralFactor = Exp({mantissa: markets[address(asset)].collateralFactorMantissa}); | |
vars.exchangeRate = Exp({mantissa: vars.exchangeRateMantissa}); | |
vars.oraclePriceMantissa = priceOracle.getUnderlyingPrice(asset.underlyingSymbol()); | |
require(vars.oraclePriceMantissa != 0, "price error"); | |
vars.oraclePrice = Exp({mantissa: vars.oraclePriceMantissa}); | |
vars.tokensToDenom = mul_(mul_(vars.collateralFactor, vars.exchangeRate), vars.oraclePrice); | |
vars.sumCollateral = mul_ScalarTruncateAddUInt(vars.tokensToDenom, vars.dTokenBalance, vars.sumCollateral); | |
vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt(vars.oraclePrice, vars.borrowBalance, vars.sumBorrowPlusEffects); | |
if (asset == dTokenModify) { | |
vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt(vars.tokensToDenom, redeemTokens, vars.sumBorrowPlusEffects); | |
vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt(vars.oraclePrice, borrowAmount, vars.sumBorrowPlusEffects); | |
} | |
} | |
if (vars.sumCollateral > vars.sumBorrowPlusEffects) { | |
return (Error.NO_ERROR, vars.sumCollateral - vars.sumBorrowPlusEffects, 0); | |
} else { | |
return (Error.NO_ERROR, 0, vars.sumBorrowPlusEffects - vars.sumCollateral); | |
} | |
} | |
function _setCloseFactor(uint newCloseFactorMantissa) external returns (uint) { | |
require(msg.sender == admin, "only admin can set close factor"); | |
uint oldCloseFactorMantissa = closeFactorMantissa; | |
closeFactorMantissa = newCloseFactorMantissa; | |
emit NewCloseFactor(oldCloseFactorMantissa, closeFactorMantissa); | |
return uint(Error.NO_ERROR); | |
} | |
function _setCollateralFactor(DToken dToken, uint newCollateralFactorMantissa) external returns (uint) { | |
require(msg.sender == admin || msg.sender == pauseGuardian, "only pause guadian and admin can pause"); | |
Market storage market = markets[address(dToken)]; | |
if (!market.isListed) { | |
return fail(Error.MARKET_NOT_LISTED, FailureInfo.SET_COLLATERAL_FACTOR_NO_EXISTS); | |
} | |
Exp memory newCollateralFactorExp = Exp({mantissa: newCollateralFactorMantissa}); | |
Exp memory highLimit = Exp({mantissa: collateralFactorMaxMantissa}); | |
if (lessThanExp(highLimit, newCollateralFactorExp)) { | |
return fail(Error.INVALID_COLLATERAL_FACTOR, FailureInfo.SET_COLLATERAL_FACTOR_VALIDATION); | |
} | |
if (newCollateralFactorMantissa != 0 && priceOracle.getUnderlyingPrice(dToken.underlyingSymbol()) == 0) { | |
return fail(Error.PRICE_ERROR, FailureInfo.SET_COLLATERAL_FACTOR_WITHOUT_PRICE); | |
} | |
uint oldCollateralFactorMantissa = market.collateralFactorMantissa; | |
market.collateralFactorMantissa = newCollateralFactorMantissa; | |
emit NewCollateralFactor(dToken, oldCollateralFactorMantissa, newCollateralFactorMantissa); | |
return uint(Error.NO_ERROR); | |
} | |
// DONKEY TOKEN $_$ // | |
function setDonkeySpeedInternal(DToken dToken, uint donkeySpeed) internal { | |
uint currentDonkeySpeed = donkeySpeeds[address(dToken)]; | |
if (currentDonkeySpeed != 0) { | |
Exp memory borrowIndex = Exp({mantissa: dToken.borrowIndex()}); | |
updateDonkeySupplyIndex(address(dToken)); | |
updateDonkeyBorrowIndex(address(dToken), borrowIndex); | |
} else if (donkeySpeed != 0) { | |
Market storage market = markets[address(dToken)]; | |
require(market.isListed, "donkey market is not listed"); | |
if (donkeySupplyState[address(dToken)].index == 0 && donkeySupplyState[address(dToken)].block == 0) { | |
donkeySupplyState[address(dToken)] = DonkeyMarketState({ | |
index: donkeyInitialIndex, | |
block: safe32(getBlockNumber(), "block number exceeds 32 bits") | |
}); | |
} | |
if (donkeyBorrowState[address(dToken)].index == 0 && donkeyBorrowState[address(dToken)].block == 0) { | |
donkeyBorrowState[address(dToken)] = DonkeyMarketState({ | |
index: donkeyInitialIndex, | |
block: safe32(getBlockNumber(), "block number exceeds 32 bits") | |
}); | |
} | |
} | |
if (currentDonkeySpeed != donkeySpeed) { | |
donkeySpeeds[address(dToken)] = donkeySpeed; | |
} | |
} | |
function updateDonkeySupplyIndex(address dToken) internal { | |
DonkeyMarketState storage supplyState = donkeySupplyState[dToken]; | |
uint supplySpeed = donkeySpeeds[dToken]; | |
uint blockNumber = getBlockNumber(); | |
uint deltaBlocks = sub_(blockNumber, uint(supplyState.block)); | |
if (deltaBlocks > 0 && supplySpeed > 0) { | |
uint supplyTokens = DToken(dToken).totalSupply(); | |
uint donkeyAccrued = mul_(deltaBlocks, supplySpeed); | |
Double memory ratio = supplyTokens > 0 ? fraction(donkeyAccrued, supplyTokens) : Double({mantissa: 0}); | |
Double memory index = add_(Double({mantissa: supplyState.index}), ratio); | |
donkeySupplyState[dToken] = DonkeyMarketState({ | |
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"); | |
} | |
} | |
function updateDonkeyBorrowIndex(address dToken, Exp memory marketBorrowIndex) internal { | |
DonkeyMarketState storage borrowState = donkeyBorrowState[dToken]; | |
uint borrowSpeed = donkeySpeeds[dToken]; | |
uint blockNumber = getBlockNumber(); | |
uint deltaBlocks = sub_(blockNumber, uint(borrowState.block)); | |
if (deltaBlocks > 0 && borrowSpeed > 0) { | |
uint borrowAmount = div_(DToken(dToken).totalBorrows(), marketBorrowIndex); | |
uint donkeyAccrued = mul_(deltaBlocks, borrowSpeed); | |
Double memory ratio = borrowAmount > 0 ? fraction(donkeyAccrued, borrowAmount) : Double({mantissa: 0}); | |
Double memory index = add_(Double({mantissa: borrowState.index}), ratio); | |
donkeyBorrowState[dToken] = DonkeyMarketState({ | |
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"); | |
} | |
} | |
function distributeSupplierDonkey(address dToken, address supplier) internal { | |
DonkeyMarketState storage supplyState = donkeySupplyState[dToken]; | |
Double memory supplyIndex = Double({mantissa: supplyState.index}); | |
Double memory supplierIndex = Double({mantissa: donkeySupplierIndex[dToken][supplier]}); | |
donkeySupplierIndex[dToken][supplier] = supplyIndex.mantissa; | |
if (supplierIndex.mantissa == 0 && supplyIndex.mantissa > 0) { | |
supplierIndex.mantissa = donkeyInitialIndex; | |
} | |
Double memory deltaIndex = sub_(supplyIndex, supplierIndex); | |
uint supplierTokens = DToken(dToken).balanceOf(supplier); | |
uint supplierDelta = mul_(supplierTokens, deltaIndex); | |
uint supplierAccrued = add_(donkeyAccrued[supplier], supplierDelta); | |
donkeyAccrued[supplier] = supplierAccrued; | |
emit DistributeSupplierDonkey(DToken(dToken), supplier, supplierDelta, supplyIndex.mantissa); | |
} | |
function setDonkeyLockUp(uint lockupBlockNum) external returns (uint) { | |
require(msg.sender == admin, "only admin can set Lock up block number of donkey"); | |
donkeyLockUpBlock = lockupBlockNum; | |
return donkeyLockUpBlock; | |
} | |
function claimDonkey(address holder) public { | |
require(block.number > donkeyLockUpBlock, "LOCKUP__DONKEY_IS_LOCKED_UP"); | |
return claimDonkey(holder, allMarkets); | |
} | |
function claimDonkey(address holder, DToken[] memory dTokens) public { | |
require(block.number > donkeyLockUpBlock, "LOCKUP__DONKEY_IS_LOCKED_UP"); | |
address[] memory holders = new address[](1); | |
holders[0] = holder; | |
claimDonkey(holders, dTokens, true, true); | |
} | |
function claimDonkey(address[] memory holders, DToken[] memory dTokens, bool borrowers, bool suppliers) public { | |
require(block.number > donkeyLockUpBlock, "LOCKUP__DONKEY_IS_LOCKED_UP"); | |
for (uint i = 0; i < dTokens.length; i++) { | |
DToken dToken = dTokens[i]; | |
require(markets[address(dToken)].isListed, "MARKET_NOT_LISTED"); | |
if (borrowers) { | |
Exp memory borrowIndex = Exp({mantissa: dToken.borrowIndex()}); | |
updateDonkeyBorrowIndex(address(dToken), borrowIndex); | |
for (uint j = 0; j < holders.length; j++) { | |
distributeBorrowerDonkey(address(dToken), holders[j], borrowIndex); | |
donkeyAccrued[holders[j]] = grantDonkeyInternal(holders[j], donkeyAccrued[holders[j]]); | |
} | |
} | |
if (suppliers) { | |
updateDonkeySupplyIndex(address(dToken)); | |
for (uint j = 0; j < holders.length; j++) { | |
distributeSupplierDonkey(address(dToken), holders[j]); | |
donkeyAccrued[holders[j]] = grantDonkeyInternal(holders[j], donkeyAccrued[holders[j]]); | |
} | |
} | |
} | |
} | |
function distributeBorrowerDonkey(address dToken, address borrower, Exp memory marketBorrowIndex) internal { | |
DonkeyMarketState storage borrowState = donkeyBorrowState[dToken]; | |
Double memory borrowIndex = Double({mantissa: borrowState.index}); | |
Double memory borrowerIndex = Double({mantissa: donkeyBorrowerIndex[dToken][borrower]}); | |
donkeyBorrowerIndex[dToken][borrower] = borrowIndex.mantissa; | |
if (borrowerIndex.mantissa > 0) { | |
Double memory deltaIndex = sub_(borrowIndex, borrowerIndex); | |
uint borrowerAmount = div_(DToken(dToken).borrowBalanceStored(borrower), marketBorrowIndex); | |
uint borrowerDelta = mul_(borrowerAmount, deltaIndex); | |
uint borrowerAccrued = add_(donkeyAccrued[borrower], borrowerDelta); | |
donkeyAccrued[borrower] = borrowerAccrued; | |
emit DistributeBorrowerDonkey(DToken(dToken), borrower, borrowerDelta, borrowIndex.mantissa); | |
} | |
} | |
function grantDonkeyInternal(address user, uint amount) internal returns (uint) { | |
Donkey donkey = Donkey(getDonkeyAddress()); | |
uint donkeyRemaining = donkey.balanceOf(address(this)); | |
if (amount > 0 && amount <= donkeyRemaining) { | |
donkey.transfer(user, amount); | |
return 0; | |
} | |
return amount; | |
} | |
// Donkey Admin $_$ // | |
function _grantDonkey(address recipient, uint amount) public { | |
require(msg.sender == admin, "only admin can grant donkey"); | |
uint amountLeft = grantDonkeyInternal(recipient, amount); | |
require(amountLeft == 0, "insufficient donkey for grant"); | |
} | |
function _setDonkeySpeed(DToken dToken, uint donkeySpeed) public { | |
require(msg.sender == admin, "only admin can set donkey speed"); | |
setDonkeySpeedInternal(dToken, donkeySpeed); | |
} | |
function getDonkeyAddress() view public returns (address) { | |
return DONKEY_ADDRESS; | |
} | |
function getBlockNumber() public view returns (uint) { | |
return block.number; | |
} | |
function getAccountAssets(address owner) external view returns (DToken[] memory) { | |
return accountAssets[owner]; | |
} | |
function getAccountLiquidityInternal(address account, bool isProtectedCall) internal view returns (Error, uint, uint) { | |
return getHypotheticalAccountLiquidityInternal(account, DToken(0), 0, 0, isProtectedCall); | |
} | |
function getSumCollateralLiquidity(address account) external view returns(uint) { | |
AccountLiquidityLocalVars memory vars; | |
DToken[] memory assets = accountAssets[account]; | |
uint oErr; | |
for (uint i = 0; i < assets.length; i++) { | |
DToken asset = DToken(assets[i]); | |
(oErr, vars.dTokenBalance, vars.borrowBalance, vars.exchangeRateMantissa) = asset.getAccountSnapshot(account); | |
vars.collateralFactor = Exp({mantissa: markets[address(asset)].collateralFactorMantissa}); | |
vars.exchangeRate = Exp({mantissa: vars.exchangeRateMantissa}); | |
vars.oraclePriceMantissa = priceOracle.getUnderlyingPrice(asset.underlyingSymbol()); | |
vars.oraclePrice = Exp({mantissa: vars.oraclePriceMantissa}); | |
vars.tokensToDenom = mul_(mul_(vars.collateralFactor, vars.exchangeRate), vars.oraclePrice); | |
vars.sumCollateral = mul_ScalarTruncateAddUInt(vars.tokensToDenom, vars.dTokenBalance, vars.sumCollateral); | |
} | |
return vars.sumCollateral; | |
} | |
function liquidateBorrowAllowed( | |
address dTokenBorrowed, | |
address dTokenCollateral, | |
address borrower, | |
uint repayAmount | |
) external returns (uint) { | |
if (!markets[dTokenBorrowed].isListed || !markets[dTokenCollateral].isListed) { | |
return uint(Error.MARKET_NOT_LISTED); | |
} | |
{ | |
DToken[] memory assets = accountAssets[borrower]; | |
uint len = assets.length; | |
for (uint i = 0; i < len; i+=1) { | |
require(!seizeGuardianPaused[address(assets[i])], "PROTECTED__CURRENT_ACCOUNT_CAN_NOT_BE_LIQUDATED"); | |
} | |
} | |
(Error err, , uint shortfall) = getAccountLiquidityInternal(borrower, false); | |
if (err != Error.NO_ERROR) { | |
return uint(err); | |
} | |
if (shortfall == 0) { | |
return uint(Error.INSUFFICIENT_SHORTFALL); | |
} | |
uint borrowBalance = DToken(dTokenBorrowed).borrowBalanceStored(borrower); | |
uint maxClose = mul_ScalarTruncate(Exp({mantissa: closeFactorMantissa}), borrowBalance); | |
if (repayAmount > maxClose) { | |
return uint(Error.TOO_MUCH_REPAY); | |
} | |
return uint(Error.NO_ERROR); | |
} | |
function _setLiquidationIncentive(address dToken, uint newLiquidationIncentiveMantissa) external returns (uint) { | |
require(msg.sender == admin, "only admin can set liquidationIncentiveMantissa"); | |
liquidationIncentiveMantissa[dToken] = newLiquidationIncentiveMantissa; | |
return uint(Error.NO_ERROR); | |
} | |
function _setPriceOracle(PriceOracle newOracle) public returns (uint) { | |
require(msg.sender == admin, "only admin can set price oracle"); | |
priceOracle = newOracle; | |
emit NewPriceOracle(newOracle); | |
return uint(Error.NO_ERROR); | |
} | |
function getAllMarkets() external view returns (DToken[] memory) { | |
return allMarkets; | |
} | |
function getCurrentMyDonkey(address account) external view returns (uint) { | |
DToken[] memory assets = accountAssets[account]; | |
uint len = assets.length; | |
uint total = donkeyAccrued[account]; | |
for (uint i=0; i < len; i+=1) { | |
{ | |
DonkeyMarketState memory supplyState = donkeySupplyState[address(assets[i])]; | |
Double memory supplyIndex = Double({mantissa: supplyState.index}); | |
Double memory supplierIndex = Double({mantissa: donkeySupplierIndex[address(assets[i])][account]}); | |
if (supplierIndex.mantissa == 0 && supplyIndex.mantissa > 0) { | |
supplierIndex.mantissa = donkeyInitialIndex; | |
} | |
Double memory deltaSupplyIndex = sub_(supplyIndex, supplierIndex); | |
uint supplierTokens = DToken(assets[i]).balanceOf(account); | |
uint supplierDelta = mul_(supplierTokens, deltaSupplyIndex); | |
uint supplierAccrued = add_(total, supplierDelta); | |
total = supplierAccrued; | |
} | |
{ | |
DonkeyMarketState memory borrowState = donkeyBorrowState[address(assets[i])]; | |
Double memory borrowIndex = Double({mantissa: borrowState.index}); | |
Double memory borrowerIndex = Double({mantissa: donkeyBorrowerIndex[address(assets[i])][account]}); | |
Double memory deltaBorrowIndex = sub_(borrowIndex, borrowerIndex); | |
Exp memory marketBorrowIndex = Exp({mantissa: assets[i].borrowIndex()}); | |
uint borrowerAmount = div_(DToken(assets[i]).borrowBalanceStored(account), marketBorrowIndex); | |
uint borrowerDelta = mul_(borrowerAmount, deltaBorrowIndex); | |
uint borrowerAccrued = add_(total, borrowerDelta); | |
total = borrowerAccrued; | |
} | |
} | |
return total; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.5.6; | |
import "./DToken.sol"; | |
import "./DErc20Interface.sol"; | |
import "./InterestRateModel.sol"; | |
import "./ControllerInterface.sol"; | |
import "./IERC20.sol"; | |
import "./upgradeable/Initializable.sol"; | |
contract DErc20 is Initializable, DToken, DErc20Interface { | |
function initialize(ControllerInterface controller_, | |
InterestRateModel interestRateModel_, | |
uint initialExchangeRateMantissa_, | |
address underlying_, | |
string memory name_, | |
string memory symbol_, | |
bytes32 underlyingSymbol_, | |
uint8 decimals_) public initializer { | |
admin = msg.sender; | |
underlying = underlying_; | |
DToken.initialize(controller_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, underlyingSymbol_, decimals_); | |
} | |
function mint(uint mintAmount) external returns (uint actualMintAmount) { | |
return mintFresh(msg.sender, mintAmount); | |
} | |
function liquidateBorrow(address borrower, uint repayAmount, DTokenInterface dTokenCollateral) external returns (uint) { | |
(uint err,) = liquidateBorrowInternal(borrower, repayAmount, dTokenCollateral); | |
return err; | |
} | |
function borrow(uint borrowAmount) external returns (uint) { | |
return borrowInternal(borrowAmount); | |
} | |
function repayBorrow(uint repayAmount) external payable returns (uint, uint) { | |
require(accrueInterest() == uint(Error.NO_ERROR), "accrue interest failed"); | |
require(accrualBlockNumber == getBlockNumber(), "market is not fresh"); | |
return repayBorrowInternal(repayAmount); | |
} | |
function doTransferIn(address sender, uint amount) internal returns (uint) { | |
IERC20 token = IERC20(underlying); | |
uint balanceBeforeTransfer = IERC20(underlying).balanceOf(address(this)); | |
token.transferFrom(sender, 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 external call | |
} | |
default { // This is an excessively non-compliant ERC-20, revert. | |
revert(0, 0) | |
} | |
} | |
require(success, "TOKEN_TRANSFER_IN_FAILED"); | |
uint balanceAfterTransfer = IERC20(underlying).balanceOf(address(this)); | |
require(balanceAfterTransfer >= balanceBeforeTransfer , "Token TransferIn overflow"); | |
return balanceAfterTransfer - balanceBeforeTransfer; | |
} | |
function doTransferOut(address payable recipient, uint amount) internal returns (uint) { | |
IERC20 token = IERC20(underlying); | |
token.transfer(recipient, 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 complaint ERC-20 | |
returndatacopy(0, 0, 32) | |
success := mload(0) // Set `success = returndata` of external call | |
} | |
default { // This is an excessively non-compliant ERC-20, revert. | |
revert(0, 0) | |
} | |
} | |
require(success, "TOKEN_TRANSFER_OUT_FAILED"); | |
return amount; | |
} | |
function getCashPrior() internal view returns (uint) { | |
IERC20 token = IERC20(underlying); | |
return token.balanceOf(address(this)); | |
} | |
function _addReserves(uint addAmount) external returns (uint) { | |
return _addReservesInternal(addAmount); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment