Skip to content

Instantly share code, notes, and snippets.

@brianairb
Created September 19, 2021 11:59
Show Gist options
  • Save brianairb/5154d4612276366fd8bbf1e7e7d3bf13 to your computer and use it in GitHub Desktop.
Save brianairb/5154d4612276366fd8bbf1e7e7d3bf13 to your computer and use it in GitHub Desktop.
donkey.fund
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;
}
}
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