Skip to content

Instantly share code, notes, and snippets.

@cNoveron
Created September 3, 2025 07:02
Show Gist options
  • Select an option

  • Save cNoveron/a76be781eef541607b7688fa1cf4f45d to your computer and use it in GitHub Desktop.

Select an option

Save cNoveron/a76be781eef541607b7688fa1cf4f45d to your computer and use it in GitHub Desktop.
pragma solidity ^0.5.16;
pragma experimental ABIEncoderV2;
import "./XSDInterface.sol";
import "./UltimateLoan.sol";
import "../PriceOracle.sol";
import "../Comptroller.sol";
import "../XErc20.sol";
import "../ExponentialNoError.sol";
import "../SwapTools/ISwapRouter.sol";
import "../SwapTools/Path.sol";
import "../EIP20Interface.sol";
import "../EIP20NonStandardInterface.sol";
contract XSDStabilizer is ExponentialNoError {
/// @notice These represent the fee percentage of Lexe taken when supplying and burning scaled as a mantissa
uint256 public lowFee = 0.001e18;
uint256 public highFee = 0.05e18;
/// @notice These represent the treshholds for different fee levels scaled as a mantissa
uint256 public feeTreshhold = 1e18;
uint256 public mintHighFeeTreshhold = 0.998e18;
uint256 public burnHighFeeTreshhold = 1.002e18;
/// @notice Array containing all xTokens that can be supplied
XToken[] public supplyTokens;
/// @notice xWBTC token (used if all suppliable stablecoins need to be liquidated)
XErc20 public xWBTC;
/// @notice bool for Reentry
bool private _notEntered = true;
/// @notice Mapping containing the status of each token
mapping(address => bool) blacklist;
uint256 public maxSlippage = 0.99e18;
using Path for bytes;
struct LiquidationParams {
bool isActive;
/// @notice Amount of liquidated token that will be swapped mapped to each output token
mapping(address => uint256) inputAmounts;
/// @notice Amount of output tokens that still need to be liquidated
uint256 remainingOutputTokens;
}
bool public isLiquidating;
mapping(address => LiquidationParams) public liquidationParams;
struct PoolShare {
XErc20 token;
uint256 share;
}
PoolShare[] public poolShares;
ISwapRouter public swapRouter;
XToken public xLEXE;
PriceOracle public priceOracle;
/// @notice Interface towards the core XSD Contract
XSDInterface public xsdInterface;
Comptroller comptroller;
address public admin;
address public ultimateLoanAddress;
/// @notice Keeps track of protocol and stabilizer liquidity
address public liquidityOracle;
uint256 public packedLiquidity;
/// @notice Emitted when token are minted
event Mint(
address minter,
uint256 amount,
address token,
uint256 suppliedAmount,
uint256 lexeFee
);
event MintShares(
address minter,
uint256 amount,
UltimateLoan.supplyToken[] tokens,
uint256 lexeFee
);
/// @notice Emitted when tokens are burned
event Burn(
address burner,
uint256 amount,
address token,
uint256 distributedAmount,
uint256 lexeFee
);
event BurnShares(
address burner,
uint256 amount,
UltimateLoan.supplyToken[] tokens,
uint256[] distributedAmount,
uint256 lexeFee
);
/// @notice Emitted when token is banned
event Banned(address token);
/// @notice Emitted when token is unbanned
event Unbanned(address token);
/// @notice Emitted when all suppliable stablecoins have been liquidated
event Deactivated();
/// @notice Emitted when UL address is changed by admin
event UltimateLoanAddressChanged(
address oldUltimateLoanAddress,
address newUltimateLoanAddress
);
/// @notice Emitted when price oracle is changed by admin
event PriceOracleChanged(address oldPriceOracle, address newPriceOracle);
/// @notice Emitted when liquidity oracle is changed by admin
event LiquidityOracleChanged(
address oldLiquidityOracle,
address newLiquidityOracle
);
/// @notice Emitted when comptroller is changed by admin
event ComptrollerChanged(address oldComptroller, address newComptroller);
struct ConstructorParams {
address[] _supplyTokens;
address xLexeAddress;
address priceOracleAddress;
address xsdAddress;
address xwbtcAddress;
address comptrollerAddress;
PoolShare[] _poolShares;
address _swapRouter;
address _liquidityOracle;
}
constructor(ConstructorParams memory params) public {
for (uint256 i = 0; i < params._supplyTokens.length; i++) {
supplyTokens.push(XToken(params._supplyTokens[i]));
}
xLEXE = XToken(params.xLexeAddress);
priceOracle = PriceOracle(params.priceOracleAddress);
xsdInterface = XSDInterface(params.xsdAddress);
xWBTC = XErc20(params.xwbtcAddress);
comptroller = Comptroller(params.comptrollerAddress);
swapRouter = ISwapRouter(params._swapRouter);
liquidityOracle = params._liquidityOracle;
uint256 totalAmount = 0;
for (uint256 i = 0; i < params._poolShares.length; i++) {
totalAmount += params._poolShares[i].share;
poolShares.push(params._poolShares[i]);
}
require(totalAmount == 1e18);
admin = msg.sender;
}
function validateToken(XToken token) public view returns (bool) {
bool validToken = false;
uint256 supplyLength = supplyTokens.length;
for (uint256 i = 0; i < supplyLength; i++) {
if (supplyTokens[i] == token) {
validToken = true;
break;
}
}
return validToken;
}
/**
* @notice Mints XSD
* @param amount Amount of XSD that will be minted
* @param token Token that will be used for supplying
*/
function mint(
uint256 amount,
XToken token
) external nonReentrant returns (uint256, uint256) {
require(amount >= 1e18, "Amount must be at least one!");
bool validToken = validateToken(token);
require(validToken, "Invalid Token!");
uint256 XSDPrice = getXSDPrice();
require(!blacklist[address(token)], "Token is blacklisted or banned!");
require(
getHypotheticalLendexeShare(amount) < 0.6e18,
"Market share of XSD is too high!"
);
uint256 lexeFee = getLexeFeeMint(amount, XSDPrice);
require(
lexeFee != uint256(-1),
"Minting not allowed due to low price!"
);
if (lexeFee > 0) {
bool transferStatusLexe = xLEXE.transferFrom(
msg.sender,
address(xsdInterface),
lexeFee
);
require(
transferStatusLexe,
"No allowance granted for Lexe or insufficient balance!"
);
}
uint256 supplyAmount = div_(
Exp({mantissa: amount}),
Exp({mantissa: token.exchangeRateCurrent()})
).mantissa;
bool transferStatusToken = token.transferFrom(
msg.sender,
address(xsdInterface),
supplyAmount
);
require(
transferStatusToken,
"No allowance granted or insufficient balance!"
);
xsdInterface.mint(msg.sender, amount);
emit Mint(msg.sender, amount, address(token), supplyAmount, lexeFee);
return (amount, lexeFee);
}
/**
* @notice Mints XSD with multiple supplyTokens
* @param amount Amount of XSD that will be minted
* @param supplyTokens_ Tokens that will be used for supplying with the share they make up of the total amount
*/
function mintShares(
uint256 amount,
UltimateLoan.supplyToken[] calldata supplyTokens_
) external nonReentrant returns (uint256, uint256) {
require(amount >= 1e18, "Amount must be at least one!");
Exp memory totalShare = Exp({mantissa: 0});
for (uint256 i = 0; i < supplyTokens_.length; i++) {
totalShare = add_(totalShare, supplyTokens_[i].share);
require(validateToken(supplyTokens_[i].token), "Invalid Token!");
require(
!blacklist[address(supplyTokens_[i].token)],
"Token is blacklisted or banned!"
);
uint256 supplyAmount = div_(
mul_(Exp({mantissa: amount}), supplyTokens_[i].share),
Exp({mantissa: supplyTokens_[i].token.exchangeRateCurrent()})
).mantissa;
bool transferStatusToken = supplyTokens_[i].token.transferFrom(
msg.sender,
address(xsdInterface),
supplyAmount
);
require(
transferStatusToken,
"No allowance granted or insufficient balance!"
);
}
require(totalShare.mantissa == 1e18, "Sum of shares has to equal 1e18");
if (msg.sender == ultimateLoanAddress) {
amount = mul_(amount, 2); //UL can mint double since it holds an equivalent amount of Lexe
}
uint256 XSDPrice = getXSDPrice();
require(
getHypotheticalLendexeShare(amount) < 0.6e18,
"Market share of XSD is too high!"
);
uint256 lexeFee = getLexeFeeMint(amount, XSDPrice);
require(
lexeFee != uint256(-1),
"Minting not allowed due to low price!"
);
if (lexeFee > 0) {
bool transferStatusLexe = xLEXE.transferFrom(
msg.sender,
address(xsdInterface),
lexeFee
);
require(
transferStatusLexe,
"No allowance granted for Lexe or insufficient balance!"
);
}
xsdInterface.mint(msg.sender, amount);
emit MintShares(msg.sender, amount, supplyTokens_, lexeFee);
return (amount, lexeFee);
}
/**
* @notice Get the amount of xLEXE that needs to be paid when minting
* @param amount Amount of XSD that will be minted
* @param xsdPrice Price of XSD
* @return uint Pmount of xLEXE that needs to be paid
*/
function getLexeFeeMint(
uint256 amount,
uint256 xsdPrice
) public returns (uint256) {
uint256 feePercentage;
if (xsdPrice >= feeTreshhold) {
return 0;
} else if (xsdPrice < mintHighFeeTreshhold) {
feePercentage = add_(
lowFee,
div_(
mul_((sub_(feeTreshhold, xsdPrice)), sub_(highFee, lowFee)),
sub_(feeTreshhold, mintHighFeeTreshhold)
)
);
} else {
// Minting disabled if price too low
return uint256(-1);
}
uint256 LexePrice = priceOracle.getUnderlyingPrice(xLEXE);
uint256 LexeExchangeRate = xLEXE.exchangeRateCurrent();
// feePercentage * amount * xsdPrice / (LexePrice * LexeExchangeRate) = lexeFee
return
div_(
mul_(Exp({mantissa: feePercentage}), Exp({mantissa: amount})),
mul_(
Exp({mantissa: LexePrice}),
Exp({mantissa: LexeExchangeRate})
)
).mantissa;
}
/**
* @notice Burns XSD
* @param amount Amount of XSD that will be burned
* @param token Token that will be exchanged for the burned xsd
*/
function burn(
uint256 amount,
XToken token
) external nonReentrant returns (uint256) {
require(amount >= 1e18, "Amount must be at least one!");
bool validToken = validateToken(token);
require(validToken, "Invalid Token!");
uint256 XSDPrice = getXSDPrice();
require(!blacklist[address(token)], "Token is blacklisted or banned!");
uint256 lexeFee = getLexeFeeBurn(amount, XSDPrice);
require(
lexeFee != uint256(-1),
"Burning not allowed due to high price!"
);
if (lexeFee > 0) {
bool transferStatusLexe = xLEXE.transferFrom(
msg.sender,
address(xsdInterface),
lexeFee
);
require(
transferStatusLexe,
"No allowance granted for Lexe or insufficient balance!"
);
}
uint256 burnAmount = div_(
Exp({mantissa: amount}),
Exp({mantissa: token.exchangeRateCurrent()})
).mantissa;
require(
token.balanceOf(address(xsdInterface)) >= burnAmount,
"Insufficient Supply of requested Token!"
);
xsdInterface.burn(msg.sender, amount); //reverts if XSD balance of msg.sender is less than amount
bool transferStatusToken = token.transferFrom(
address(xsdInterface),
msg.sender,
burnAmount
);
require(
transferStatusToken,
"No allowance granted for token or insufficient balance"
);
emit Burn(msg.sender, amount, address(token), burnAmount, lexeFee);
}
/**
* @notice Burns XSD
* @param amount Amount of XSD that will be burned
* @param supplyTokens_ Tokens that will be exchanged for the burned xsd
*/
function burnShares(
uint256 amount,
UltimateLoan.supplyToken[] calldata supplyTokens_
) external nonReentrant returns (uint256) {
require(amount >= 1e18, "Amount must be at least one!");
Exp memory totalShare = Exp({mantissa: 0});
uint256[] memory distributedAmounts = new uint256[](
supplyTokens_.length
);
for (uint256 i = 0; i < supplyTokens_.length; i++) {
totalShare = add_(totalShare, supplyTokens_[i].share);
bool validToken = validateToken(supplyTokens_[i].token);
require(validToken, "Invalid Token!");
require(
!blacklist[address(supplyTokens_[i].token)],
"Token is blacklisted or banned!"
);
distributedAmounts[i] = mul_(
div_(
Exp({mantissa: amount}),
Exp({
mantissa: supplyTokens_[i].token.exchangeRateCurrent()
})
),
supplyTokens_[i].share
).mantissa;
require(
supplyTokens_[i].token.balanceOf(address(xsdInterface)) >=
distributedAmounts[i],
"Insufficient Supply of requested Token!"
);
bool transferStatus = supplyTokens_[i].token.transferFrom(
address(xsdInterface),
msg.sender,
distributedAmounts[i]
);
require(
transferStatus,
"No allowance granted for token or insufficient balance"
);
}
require(totalShare.mantissa == 1e18, "Sum of shares has to equal 1e18");
uint256 XSDPrice = getXSDPrice();
uint256 lexeFee = getLexeFeeBurn(amount, XSDPrice);
require(
lexeFee != uint256(-1),
"Burning not allowed due to high price!"
);
if (msg.sender != ultimateLoanAddress && lexeFee > 0) {
//UL only burns when somebody repays their loan, no fee will be charged
bool transferStatusLexe = xLEXE.transferFrom(
msg.sender,
address(xsdInterface),
lexeFee
);
require(
transferStatusLexe,
"No allowance granted for Lexe or insufficient balance!"
);
}
xsdInterface.burn(msg.sender, amount); //reverts if XSD balance of msg.sender is less than amount
emit BurnShares(
msg.sender,
amount,
supplyTokens_,
distributedAmounts,
msg.sender != ultimateLoanAddress ? lexeFee : 0
);
}
/**
* @notice Burns XSD directly without handing out stablecoins. Can only be called by UL
* @param amount Amount of XSD that will be burned
*/
function burnDirectly(uint256 amount) external {
address ultimateLoanAddress_ = ultimateLoanAddress;
require(
msg.sender == ultimateLoanAddress_,
"Only UL can burn directly"
);
xsdInterface.burn(ultimateLoanAddress_, amount);
}
/**
* @notice Get the amount of xLEXE that needs to be paid when burning
* @param amount Amount of XSD that will be burned
* @param xsdPrice Price of XSD
* @return uint Amount of xLEXE that needs to be paid
*/
function getLexeFeeBurn(
uint256 amount,
uint256 xsdPrice
) public returns (uint256) {
uint256 feePercentage;
if (xsdPrice <= feeTreshhold) {
return 0;
} else if (xsdPrice < burnHighFeeTreshhold) {
feePercentage = add_(
lowFee,
div_(
mul_((sub_(xsdPrice, feeTreshhold)), sub_(highFee, lowFee)),
sub_(burnHighFeeTreshhold, feeTreshhold)
)
);
} else {
// Burning disabled
return uint256(-1);
}
uint256 LexePrice = priceOracle.getUnderlyingPrice(xLEXE);
uint256 LexeExchangeRate = xLEXE.exchangeRateCurrent();
// feePercentage * amount * xsdPrice / (LexePrice * LexeExchangeRate) = lexeFee
return
div_(
mul_(Exp({mantissa: feePercentage}), Exp({mantissa: amount})),
mul_(
Exp({mantissa: LexePrice}),
Exp({mantissa: LexeExchangeRate})
)
).mantissa;
}
/**
* @notice Get the current price of XSD
* @return uint XSD price
*/
function getXSDPrice() public returns (uint256) {
if (xsdInterface.totalSupply() == 0) {
return 1e18;
}
return priceOracle.assetPrices(address(xsdInterface));
}
function getSupplyValue() public returns (uint256) {
XToken[] memory _supplyTokens = supplyTokens;
uint256[] memory prices = new uint256[](supplyTokens.length);
// fetch prices of suppliable tokens
for (uint256 i = 0; i < _supplyTokens.length; i++) {
uint256 price = priceOracle.getUnderlyingPrice(_supplyTokens[i]);
require(price != 0, "Oracle is disabled!");
prices[i] = price;
}
Exp memory totalValue = Exp({mantissa: 0});
//calculates the total value of each suppliable token adds it
for (uint256 i = 0; i < _supplyTokens.length; i++) {
totalValue = add_(
totalValue,
mul_(
Exp({mantissa: prices[i]}),
Exp({
mantissa: _supplyTokens[i].balanceOfUnderlying(
address(xsdInterface)
)
})
)
);
}
//add value of supplied xLEXE
uint256 LEXEPrice = priceOracle.getUnderlyingPrice(xLEXE);
return
add_(
totalValue,
mul_(
Exp({mantissa: LEXEPrice}),
Exp({
mantissa: xLEXE.balanceOfUnderlying(
address(xsdInterface)
)
})
)
).mantissa;
}
/**
* @notice Get the hypothetical XSD supply share percentage of the Lendexe market capital scaled as mantissa
* @param amount amount of xsd that would be minted
* @return uint XSD supply share percentage
*/
function getHypotheticalLendexeShare(
uint256 amount
) public view returns (uint256) {
uint256 _packedLiquidity = packedLiquidity;
return
div_(
add_(
Exp(uint256(uint192(_packedLiquidity) >> 96)),
Exp(amount)
),
Exp(_packedLiquidity >> 192)
).mantissa;
}
/**
* @notice Updates the blacklist
*/
function updateBlacklist() public nonReentrant {
require(!isLiquidating, "Already liquidating!");
XToken[] memory _supplyTokens = supplyTokens;
for (uint i = 0; i < _supplyTokens.length; i++) {
XToken token = _supplyTokens[i];
bool status = blacklist[address(token)];
uint256 tokenPrice = priceOracle.getUnderlyingPrice(token);
if (
!blacklist[address(token)] && tokenPrice < mintHighFeeTreshhold
) {
require(
uint256(uint96(packedLiquidity)) + 10 minutes >
block.timestamp,
"Liquidity isn't fresh!"
);
blacklist[address(token)] = true;
liquidate(XErc20(address(token)));
emit Banned(address(token));
return; // max one liquidation per tx
}
}
}
function liquidate(XErc20 token) internal {
uint balance = token.balanceOf(address(xsdInterface));
uint xTokenCash = div_(
Exp(token.getCash()),
Exp(token.exchangeRateCurrent())
).mantissa;
uint maxRedeemableAmount = balance < xTokenCash ? balance : xTokenCash;
bool transferStatus = token.transferFrom(
address(xsdInterface),
address(this),
maxRedeemableAmount
);
require(transferStatus, "Stabilizer: Not enough balance on XSD Core"); //this should never be hit
require(token.redeem(maxRedeemableAmount) == 0, "Redeem failed"); // 0 is the enum for no error
EIP20NonStandardInterface underlyingToken = EIP20NonStandardInterface(
token.underlying()
);
uint underlyingBalance = underlyingToken.balanceOf(address(this));
underlyingToken.approve((address(swapRouter)), underlyingBalance);
PoolShare[] memory _poolShares = poolShares;
if (_poolShares.length == 1) {
// if this is the last token, liquidate to WBTC instead
liquidationParams[address(token)].isActive = true;
liquidationParams[address(token)].inputAmounts[
xWBTC.underlying()
] = underlyingBalance;
liquidationParams[address(token)].remainingOutputTokens = 1;
emit Deactivated(); // dead man
return;
}
uint256 liquidateShare = 0;
uint256 liquidateIndex = 0;
for (; liquidateIndex < _poolShares.length; liquidateIndex++) {
if (_poolShares[liquidateIndex].token == token) {
liquidateShare = _poolShares[liquidateIndex].share;
break;
}
}
Exp memory remainder = sub_(
Exp({mantissa: 1e18}),
Exp({mantissa: liquidateShare})
);
delete poolShares;
liquidationParams[address(token)].isActive = true;
liquidationParams[address(token)].remainingOutputTokens =
_poolShares.length -
1;
mapping(address => uint256) storage inputAmounts = liquidationParams[
address(token)
].inputAmounts;
for (uint256 i = 0; i < _poolShares.length; i++) {
if (i == liquidateIndex) {
continue;
}
uint newShare = div_(Exp(_poolShares[i].share), remainder).mantissa;
poolShares.push(PoolShare(_poolShares[i].token, newShare));
inputAmounts[address(_poolShares[i].token)] = mul_(
Exp(newShare),
Exp(underlyingBalance)
).mantissa;
}
isLiquidating = true;
}
function submitSwapRoute(
bytes memory path,
uint amount
) public nonReentrant {
(address inputToken, address outputToken) = path.getFirstAndLastPool();
require(liquidationParams[inputToken].isActive, "Token not active!");
uint maxAmount = liquidationParams[inputToken].inputAmounts[
outputToken
];
require(amount <= maxAmount && maxAmount > 0, "Amount too high!");
uint8 decimalsIn = EIP20Interface(inputToken).decimals();
uint8 decimalsOut = EIP20Interface(outputToken).decimals();
uint amountOutMinimum = scaleToDecimal(
mul_(
div_(
mul_(
Exp({mantissa: amount}),
Exp({mantissa: priceOracle.assetPrices(inputToken)})
),
Exp({mantissa: priceOracle.assetPrices(outputToken)})
),
Exp({mantissa: maxSlippage})
).mantissa,
decimalsIn,
decimalsOut
);
uint amountOut = swapRouter.exactInput(
ISwapRouter.ExactInputParams(
path,
address(this),
block.timestamp,
amount,
amountOutMinimum
)
);
uint reward = div_(sub_(Exp(amountOut), Exp(amountOutMinimum)), 4)
.mantissa;
if (reward > 0) {
EIP20NonStandardInterface(outputToken).transfer(msg.sender, reward);
}
liquidationParams[inputToken].inputAmounts[outputToken] = sub_(
Exp(liquidationParams[inputToken].inputAmounts[outputToken]),
Exp(amount)
).mantissa;
if (liquidationParams[inputToken].inputAmounts[outputToken] == 0) {
liquidationParams[inputToken].remainingOutputTokens--;
if (liquidationParams[inputToken].remainingOutputTokens == 0) {
liquidationParams[inputToken].isActive = false;
isLiquidating = false;
PoolShare[] memory _poolShares = poolShares;
for (uint i = 0; i < _poolShares.length; i++) {
if (outputToken == address(_poolShares[i].token)) {
mintXTokens(_poolShares[i].token);
return;
}
}
}
}
}
function scaleToDecimal(
uint256 amount,
uint8 decimalsIn,
uint8 decimalsOut
) internal pure returns (uint256) {
if (decimalsIn > decimalsOut) {
return
div_(
Exp(amount),
10 **
sub_(
Exp(uint256(decimalsIn)),
Exp(uint256(decimalsOut))
).mantissa
).mantissa;
} else {
return
mul_(
Exp(amount),
10 **
sub_(
Exp(uint256(decimalsOut)),
Exp(uint256(decimalsIn))
).mantissa
).mantissa;
}
}
function mintXTokens(XErc20 supplyToken) internal {
EIP20NonStandardInterface underlyingSupplyToken = EIP20NonStandardInterface(
supplyToken.underlying()
);
uint underlyingSupplyTokenBalance = underlyingSupplyToken.balanceOf(
address(this)
);
underlyingSupplyToken.approve(
(address(supplyToken)),
underlyingSupplyTokenBalance
);
supplyToken.mint(underlyingSupplyTokenBalance);
supplyToken.transfer(
address(xsdInterface),
supplyToken.balanceOf(address(this))
);
}
/**
* @notice Unbans a previously banned token
* @dev Needs to be called by admin
* @param token Token that should be enabled again
*/
function _unbanToken(XToken token) external {
require(msg.sender == admin, "Only admin can enable banned Tokens!");
blacklist[address(token)] = false;
emit Unbanned(address(token));
}
// admin setter functions
function _setUltimateLoanAddress(address ultimateLoanAddress_) external {
require(msg.sender == admin, "Only admin can enable banned Tokens!");
address oldUltimateLoanAddress = ultimateLoanAddress;
ultimateLoanAddress = ultimateLoanAddress_;
emit UltimateLoanAddressChanged(
oldUltimateLoanAddress,
ultimateLoanAddress_
);
}
function _setPriceOracle(address priceOracle_) external {
require(msg.sender == admin, "Only admin can set the price oracle!");
address oldPriceOracle = address(priceOracle);
priceOracle = PriceOracle(priceOracle_);
emit PriceOracleChanged(oldPriceOracle, priceOracle_);
}
function _setLiquidityOracle(address liquidityOracle_) external {
require(
msg.sender == admin,
"Only admin can set the liquidity oracle!"
);
address oldLiquidityOracle = liquidityOracle;
liquidityOracle = liquidityOracle_;
emit LiquidityOracleChanged(oldLiquidityOracle, liquidityOracle_);
}
function _setComptroller(address comptroller_) external {
require(msg.sender == admin, "Only admin can enable banned Tokens!");
address oldComptroller = address(comptroller);
comptroller = Comptroller(comptroller_);
emit ComptrollerChanged(oldComptroller, comptroller_);
}
/*** Reentrancy Guard ***/
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
*/
modifier nonReentrant() {
require(_notEntered, "re-entered");
_notEntered = false;
_;
_notEntered = true; // get a gas-refund post-Istanbul
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment