ETH Price: $2,295.14 (-4.66%)

Transaction Decoder

Block:
12757254 at Jul-03-2021 10:14:08 PM +UTC
Transaction Fee:
0.000278454 ETH $0.64
Gas Used:
46,409 Gas / 6 Gwei

Emitted Events:

295 Borrowable.Approval( owner=[Sender] 0x3a8315e25378ce9fc0d3279b26ec0576de52c0ff, spender=0x5e169082...8c543127D, value=115792089237316195423570985008687907853269984665640564039457584007913129639935 )

Account State Difference:

  Address   Before After State Difference Code
0x3A8315E2...6de52C0Ff
83.735877435032154781 Eth
Nonce: 1465
83.735598981032154781 Eth
Nonce: 1466
0.000278454
0xbD8b5a61...677872A6f
(Ethermine)
2,325.192588110775288495 Eth2,325.192866564775288495 Eth0.000278454

Execution Trace

Borrowable.approve( spender=0x5e169082fFf23cEE6766062B96051A78c543127D, value=115792089237316195423570985008687907853269984665640564039457584007913129639935 ) => ( True )
pragma solidity =0.5.16;
import "./BStorage.sol";
import "./PoolToken.sol";
contract BAllowance is PoolToken, BStorage {\t
\tevent BorrowApproval(address indexed owner, address indexed spender, uint256 value);
\tfunction _borrowApprove(address owner, address spender, uint256 value) private {
\t\tborrowAllowance[owner][spender] = value;
\t\temit BorrowApproval(owner, spender, value);
\t}
\t
\tfunction borrowApprove(address spender, uint256 value) external returns (bool) {
\t\t_borrowApprove(msg.sender, spender, value);
\t\treturn true;
\t}
\t
\tfunction _checkBorrowAllowance(address owner, address spender, uint256 value) internal {
\t\tuint _borrowAllowance = borrowAllowance[owner][spender];
\t\tif (spender != owner && _borrowAllowance != uint256(-1)) {
\t\t\trequire(_borrowAllowance >= value, "Impermax: BORROW_NOT_ALLOWED");
\t\t\tborrowAllowance[owner][spender] = _borrowAllowance - value;
\t\t}\t
\t}
\t// keccak256("BorrowPermit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
\tbytes32 public constant BORROW_PERMIT_TYPEHASH = 0xf6d86ed606f871fa1a557ac0ba607adce07767acf53f492fb215a1a4db4aea6f;
\tfunction borrowPermit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
\t\t_checkSignature(owner, spender, value, deadline, v, r, s, BORROW_PERMIT_TYPEHASH);
\t\t_borrowApprove(owner, spender, value);
\t}
}pragma solidity =0.5.16;
import "./BStorage.sol";
import "./PoolToken.sol";
contract BInterestRateModel is PoolToken, BStorage {
\t// When utilization is 100% borrowRate is kinkBorrowRate * KINK_MULTIPLIER
\t// kinkBorrowRate relative adjustment per second belongs to [1-adjustSpeed, 1+adjustSpeed*(KINK_MULTIPLIER-1)]
\tuint public constant KINK_MULTIPLIER = 5;
\tuint public constant KINK_BORROW_RATE_MAX = 31.7097920e9; //100% per year
\tuint public constant KINK_BORROW_RATE_MIN = 0.31709792e9; //1% per year
\tevent AccrueInterest(uint interestAccumulated, uint borrowIndex, uint totalBorrows);
\tevent CalculateKinkBorrowRate(uint kinkBorrowRate);
\tevent CalculateBorrowRate(uint borrowRate);
\t\t
\tfunction _calculateBorrowRate() internal {
\t\tuint _kinkUtilizationRate = kinkUtilizationRate;\t\t
\t\tuint _adjustSpeed = adjustSpeed;
\t\tuint _borrowRate = borrowRate;\t
\t\tuint _kinkBorrowRate = kinkBorrowRate;
\t\tuint32 _rateUpdateTimestamp = rateUpdateTimestamp;\t\t
\t
\t\t// update kinkBorrowRate using previous borrowRate
\t\tuint32 timeElapsed = getBlockTimestamp() - _rateUpdateTimestamp; // underflow is desired
\t\tif(timeElapsed > 0) {
\t\t\trateUpdateTimestamp = getBlockTimestamp();
\t\t\tuint adjustFactor;
\t\t\t
\t\t\tif (_borrowRate < _kinkBorrowRate) {
\t\t\t\t// never overflows, _kinkBorrowRate is never 0
\t\t\t\tuint tmp = (_kinkBorrowRate - _borrowRate) * 1e18 / _kinkBorrowRate * _adjustSpeed * timeElapsed / 1e18;
\t\t\t\tadjustFactor = tmp > 1e18 ? 0 : 1e18 - tmp;
\t\t\t} else {
\t\t\t\t// never overflows, _kinkBorrowRate is never 0
\t\t\t\tuint tmp = (_borrowRate - _kinkBorrowRate) * 1e18 / _kinkBorrowRate * _adjustSpeed * timeElapsed / 1e18;
\t\t\t\tadjustFactor = tmp + 1e18;
\t\t\t}
\t\t\t
\t\t\t// never overflows
\t\t\t_kinkBorrowRate = _kinkBorrowRate * adjustFactor / 1e18;
\t\t\tif(_kinkBorrowRate > KINK_BORROW_RATE_MAX) _kinkBorrowRate = KINK_BORROW_RATE_MAX;
\t\t\tif(_kinkBorrowRate < KINK_BORROW_RATE_MIN) _kinkBorrowRate = KINK_BORROW_RATE_MIN;
\t\t\tkinkBorrowRate = uint48(_kinkBorrowRate);
\t\t\temit CalculateKinkBorrowRate(_kinkBorrowRate);
\t\t}
\t\t
\t\tuint _utilizationRate;
\t\t{ // avoid stack to deep
\t\tuint _totalBorrows = totalBorrows; // gas savings
\t\tuint _actualBalance = totalBalance.add(_totalBorrows);
\t\t_utilizationRate = (_actualBalance == 0) ? 0 : _totalBorrows * 1e18 / _actualBalance;
\t\t}
\t\t
\t\t// update borrowRate using the new kinkBorrowRate\t
\t\tif(_utilizationRate <= _kinkUtilizationRate) {
\t\t\t// never overflows, _kinkUtilizationRate is never 0
\t\t\t_borrowRate = _kinkBorrowRate * _utilizationRate / _kinkUtilizationRate;
\t\t} else {
\t\t\t// never overflows, _kinkUtilizationRate is always < 1e18
\t\t\tuint overUtilization = (_utilizationRate - _kinkUtilizationRate) * 1e18 / (1e18 - _kinkUtilizationRate);
\t\t\t// never overflows
\t\t\t_borrowRate = ((KINK_MULTIPLIER - 1) * overUtilization + 1e18) * _kinkBorrowRate / 1e18;
\t\t}
\t\tborrowRate = uint48(_borrowRate);
\t\temit CalculateBorrowRate(_borrowRate);
\t}
\t
\t// applies accrued interest to total borrows and reserves
\tfunction accrueInterest() public {
\t\tuint _borrowIndex = borrowIndex;
\t\tuint _totalBorrows = totalBorrows;
\t\tuint32 _accrualTimestamp = accrualTimestamp;
\t\t
\t\tuint32 blockTimestamp = getBlockTimestamp();
\t\tif (_accrualTimestamp == blockTimestamp) return;
\t\tuint32 timeElapsed = blockTimestamp - _accrualTimestamp; // underflow is desired
\t\taccrualTimestamp = blockTimestamp;
\t\t
\t\tuint interestFactor = uint(borrowRate).mul(timeElapsed);\t
\t\tuint interestAccumulated = interestFactor.mul(_totalBorrows).div(1e18);
\t\t_totalBorrows = _totalBorrows.add( interestAccumulated );
\t\t_borrowIndex = _borrowIndex.add( interestFactor.mul(_borrowIndex).div(1e18) );
\t
\t\tborrowIndex = safe112(_borrowIndex);
\t\ttotalBorrows = safe112(_totalBorrows);
\t\temit AccrueInterest(interestAccumulated, _borrowIndex, _totalBorrows);
\t}
\t\t
\tfunction getBlockTimestamp() public view returns (uint32) {
\t\treturn uint32(block.timestamp % 2**32);
\t}
}pragma solidity =0.5.16;
import "./BStorage.sol";
import "./PoolToken.sol";
import "./interfaces/IFactory.sol";
contract BSetter is PoolToken, BStorage {
\tuint public constant RESERVE_FACTOR_MAX = 0.20e18; //20%
\tuint public constant KINK_UR_MIN = 0.50e18; //50%
\tuint public constant KINK_UR_MAX = 0.99e18; //99%
\tuint public constant ADJUST_SPEED_MIN = 0.05787037e12; //0.5% per day
\tuint public constant ADJUST_SPEED_MAX = 5.787037e12; //50% per day
\tevent NewReserveFactor(uint newReserveFactor);
\tevent NewKinkUtilizationRate(uint newKinkUtilizationRate);
\tevent NewAdjustSpeed(uint newAdjustSpeed);
\tevent NewBorrowTracker(address newBorrowTracker);
\t
\t// called once by the factory at time of deployment
\tfunction _initialize (
\t\tstring calldata _name, 
\t\tstring calldata _symbol,
\t\taddress _underlying, 
\t\taddress _collateral
\t) external {
\t\trequire(msg.sender == factory, "Impermax: UNAUTHORIZED"); // sufficient check
\t\t_setName(_name, _symbol);
\t\tunderlying = _underlying;
\t\tcollateral = _collateral;
\t\texchangeRateLast = initialExchangeRate;
\t}
\t
\tfunction _setReserveFactor(uint newReserveFactor) external nonReentrant {
\t\t_checkSetting(newReserveFactor, 0, RESERVE_FACTOR_MAX);
\t\treserveFactor = newReserveFactor;
\t\temit NewReserveFactor(newReserveFactor);
\t}
\tfunction _setKinkUtilizationRate(uint newKinkUtilizationRate) external nonReentrant {
\t\t_checkSetting(newKinkUtilizationRate, KINK_UR_MIN, KINK_UR_MAX);
\t\tkinkUtilizationRate = newKinkUtilizationRate;
\t\temit NewKinkUtilizationRate(newKinkUtilizationRate);
\t}
\tfunction _setAdjustSpeed(uint newAdjustSpeed) external nonReentrant {
\t\t_checkSetting(newAdjustSpeed, ADJUST_SPEED_MIN, ADJUST_SPEED_MAX);
\t\tadjustSpeed = newAdjustSpeed;
\t\temit NewAdjustSpeed(newAdjustSpeed);
\t}
\tfunction _setBorrowTracker(address newBorrowTracker) external nonReentrant {
\t\t_checkAdmin();
\t\tborrowTracker = newBorrowTracker;
\t\temit NewBorrowTracker(newBorrowTracker);
\t}
\t
\tfunction _checkSetting(uint parameter, uint min, uint max) internal view {
\t\t_checkAdmin();
\t\trequire(parameter >= min, "Impermax: INVALID_SETTING");
\t\trequire(parameter <= max, "Impermax: INVALID_SETTING");
\t}
\t
\tfunction _checkAdmin() internal view {
\t\trequire(msg.sender == IFactory(factory).admin(), "Impermax: UNAUTHORIZED");
\t}
}pragma solidity =0.5.16;
contract BStorage {
\taddress public collateral;
\tmapping (address => mapping (address => uint256)) public borrowAllowance;
\t
\tstruct BorrowSnapshot {
\t\tuint112 principal;\t\t// amount in underlying when the borrow was last updated
\t\tuint112 interestIndex;\t// borrow index when borrow was last updated
\t}
\tmapping(address => BorrowSnapshot) internal borrowBalances;\t
\t// use one memory slot
\tuint112 public borrowIndex = 1e18;
\tuint112 public totalBorrows;
\tuint32 public accrualTimestamp = uint32(block.timestamp % 2**32);\t
\tuint public exchangeRateLast;
\t\t
\t// use one memory slot
\tuint48 public borrowRate;
\tuint48 public kinkBorrowRate = 3.1709792e9; //10% per year
\tuint32 public rateUpdateTimestamp = uint32(block.timestamp % 2**32);
\tuint public reserveFactor = 0.10e18; //10%
\tuint public kinkUtilizationRate = 0.70e18; //70%
\tuint public adjustSpeed = 0.5787037e12; //5% per day
\taddress public borrowTracker;
    function safe112(uint n) internal pure returns (uint112) {
        require(n < 2**112, "Impermax: SAFE112");
        return uint112(n);
    }
}pragma solidity =0.5.16;
import "./PoolToken.sol";
import "./BAllowance.sol";
import "./BInterestRateModel.sol";
import "./BSetter.sol";
import "./BStorage.sol";
import "./interfaces/IBorrowable.sol";
import "./interfaces/ICollateral.sol";
import "./interfaces/IImpermaxCallee.sol";
import "./interfaces/IERC20.sol";
import "./interfaces/IFactory.sol";
import "./interfaces/IBorrowTracker.sol";
import "./libraries/Math.sol";
contract Borrowable is IBorrowable, PoolToken, BStorage, BSetter, BInterestRateModel, BAllowance {
\tuint public constant BORROW_FEE = 0.001e18; //0.1%
\tevent Borrow(address indexed sender, address indexed borrower, address indexed receiver, uint borrowAmount, uint repayAmount, uint accountBorrowsPrior, uint accountBorrows, uint totalBorrows);
\tevent Liquidate(address indexed sender, address indexed borrower, address indexed liquidator, uint seizeTokens, uint repayAmount, uint accountBorrowsPrior, uint accountBorrows, uint totalBorrows);
\t\t
\tconstructor() public {}
\t/*** PoolToken ***/
\t
\tfunction _update() internal {
\t\tsuper._update();
\t\t_calculateBorrowRate();
\t}
\t
\tfunction _mintReserves(uint _exchangeRate, uint _totalSupply) internal returns (uint) {
\t\tuint _exchangeRateLast = exchangeRateLast;
\t\tif (_exchangeRate > _exchangeRateLast) {
\t\t\tuint _exchangeRateNew = _exchangeRate.sub( _exchangeRate.sub(_exchangeRateLast).mul(reserveFactor).div(1e18) );
\t\t\tuint liquidity = _totalSupply.mul(_exchangeRate).div(_exchangeRateNew).sub(_totalSupply);
\t\t\tif (liquidity == 0) return _exchangeRate;
\t\t\taddress reservesManager = IFactory(factory).reservesManager();
\t\t\t_mint(reservesManager, liquidity);
\t\t\texchangeRateLast = _exchangeRateNew;
\t\t\treturn _exchangeRateNew;
\t\t}
\t\telse return _exchangeRate;
\t}
\t
\tfunction exchangeRate() public accrue returns (uint)\t{
\t\tuint _totalSupply = totalSupply;
\t\tuint _actualBalance = totalBalance.add(totalBorrows);
\t\tif (_totalSupply == 0 || _actualBalance == 0) return initialExchangeRate;
\t\tuint _exchangeRate = _actualBalance.mul(1e18).div(_totalSupply);
\t\treturn _mintReserves(_exchangeRate, _totalSupply);
\t}
\t
\t// force totalBalance to match real balance
\tfunction sync() external nonReentrant update accrue {}
\t
\t/*** Borrowable ***/
\t// this is the stored borrow balance; the current borrow balance may be slightly higher
\tfunction borrowBalance(address borrower) public view returns (uint) {
\t\tBorrowSnapshot memory borrowSnapshot = borrowBalances[borrower];
\t\tif (borrowSnapshot.interestIndex == 0) return 0; // not initialized
\t\treturn uint(borrowSnapshot.principal).mul(borrowIndex).div(borrowSnapshot.interestIndex);
\t}
\t
\tfunction _trackBorrow(address borrower, uint accountBorrows, uint _borrowIndex) internal {
\t\taddress _borrowTracker = borrowTracker;
\t\tif (_borrowTracker == address(0)) return;
\t\tIBorrowTracker(_borrowTracker).trackBorrow(borrower, accountBorrows, _borrowIndex);
\t}
\t
\tfunction _updateBorrow(address borrower, uint borrowAmount, uint repayAmount) private returns (uint accountBorrowsPrior, uint accountBorrows, uint _totalBorrows) {
\t\taccountBorrowsPrior = borrowBalance(borrower);
\t\tif (borrowAmount == repayAmount) return (accountBorrowsPrior, accountBorrowsPrior, totalBorrows);
\t\tuint112 _borrowIndex = borrowIndex;
\t\tif (borrowAmount > repayAmount) {
\t\t\tBorrowSnapshot storage borrowSnapshot = borrowBalances[borrower];
\t\t\tuint increaseAmount = borrowAmount - repayAmount;
\t\t\taccountBorrows = accountBorrowsPrior.add(increaseAmount);
\t\t\tborrowSnapshot.principal = safe112(accountBorrows);
\t\t\tborrowSnapshot.interestIndex = _borrowIndex;
\t\t\t_totalBorrows = uint(totalBorrows).add(increaseAmount);\t
\t\t\ttotalBorrows = safe112(_totalBorrows);
\t\t}
\t\telse {
\t\t\tBorrowSnapshot storage borrowSnapshot = borrowBalances[borrower];
\t\t\tuint decreaseAmount = repayAmount - borrowAmount;\t\t
\t\t\taccountBorrows = accountBorrowsPrior > decreaseAmount ? accountBorrowsPrior - decreaseAmount : 0;
\t\t\tborrowSnapshot.principal = safe112(accountBorrows);
\t\t\tif(accountBorrows == 0) {
\t\t\t\tborrowSnapshot.interestIndex = 0;
\t\t\t} else {
\t\t\t\tborrowSnapshot.interestIndex = _borrowIndex;
\t\t\t}
\t\t\tuint actualDecreaseAmount = accountBorrowsPrior.sub(accountBorrows);
\t\t\t_totalBorrows = totalBorrows; // gas savings
\t\t\t_totalBorrows = _totalBorrows > actualDecreaseAmount ? _totalBorrows - actualDecreaseAmount : 0;
\t\t\ttotalBorrows = safe112(_totalBorrows);\t\t\t
\t\t}
\t\t_trackBorrow(borrower, accountBorrows, _borrowIndex);
\t}
\t
\t// this low-level function should be called from another contract
\tfunction borrow(address borrower, address receiver, uint borrowAmount, bytes calldata data) external nonReentrant update accrue {\t\t
\t\tuint _totalBalance = totalBalance;
\t\trequire(borrowAmount <= _totalBalance, "Impermax: INSUFFICIENT_CASH");
\t\t_checkBorrowAllowance(borrower, msg.sender, borrowAmount);
\t\t
\t\t// optimistically transfer funds
\t\tif (borrowAmount > 0) _safeTransfer(receiver, borrowAmount);
\t\tif (data.length > 0) IImpermaxCallee(receiver).impermaxBorrow(msg.sender, borrower, borrowAmount, data);
\t\tuint balance = IERC20(underlying).balanceOf(address(this));
\t\t
\t\tuint borrowFee = borrowAmount.mul(BORROW_FEE).div(1e18);
\t\tuint adjustedBorrowAmount = borrowAmount.add(borrowFee);
\t\tuint repayAmount = balance.add(borrowAmount).sub(_totalBalance);
\t\t(uint accountBorrowsPrior, uint accountBorrows, uint _totalBorrows) = _updateBorrow(borrower, adjustedBorrowAmount, repayAmount);
\t\t
\t\tif(adjustedBorrowAmount > repayAmount) require(
\t\t\tICollateral(collateral).canBorrow(borrower, address(this), accountBorrows),
\t\t\t"Impermax: INSUFFICIENT_LIQUIDITY"
\t\t);
\t\t
\t\temit Borrow(msg.sender, borrower, receiver, borrowAmount, repayAmount, accountBorrowsPrior, accountBorrows, _totalBorrows);
\t}
\t// this low-level function should be called from another contract
\tfunction liquidate(address borrower, address liquidator) external nonReentrant update accrue returns (uint seizeTokens) {
\t\tuint balance = IERC20(underlying).balanceOf(address(this));
\t\tuint repayAmount = balance.sub(totalBalance);\t\t
\t\t
\t\tuint actualRepayAmount = Math.min(borrowBalance(borrower), repayAmount);
\t\tseizeTokens = ICollateral(collateral).seize(liquidator, borrower, actualRepayAmount);\t
\t\t(uint accountBorrowsPrior, uint accountBorrows, uint _totalBorrows) = _updateBorrow(borrower, 0, repayAmount);
\t\t
\t\temit Liquidate(msg.sender, borrower, liquidator, seizeTokens, repayAmount, accountBorrowsPrior, accountBorrows, _totalBorrows);
\t}
\t
\tfunction trackBorrow(address borrower) external {
\t\t_trackBorrow(borrower, borrowBalance(borrower), borrowIndex);
\t}
\t
\tmodifier accrue() {
\t\taccrueInterest();
\t\t_;
\t}
}pragma solidity =0.5.16;
import "./libraries/SafeMath.sol";
// This contract is basically UniswapV2ERC20 with small modifications
// src: https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol
contract ImpermaxERC20 {
\tusing SafeMath for uint;
\t
\tstring public name;
\tstring public symbol;
\tuint8 public decimals = 18;
\tuint public totalSupply;
\tmapping(address => uint) public balanceOf;
\tmapping(address => mapping(address => uint)) public allowance;
\t
\tbytes32 public DOMAIN_SEPARATOR;
\tmapping(address => uint) public nonces;
\t
\tevent Transfer(address indexed from, address indexed to, uint value);
\tevent Approval(address indexed owner, address indexed spender, uint value);
\tconstructor() public {}\t
\t
\tfunction _setName(string memory _name, string memory _symbol) internal {
\t\tname = _name;
\t\tsymbol = _symbol;
\t\tuint chainId;
\t\tassembly {
\t\t\tchainId := chainid
\t\t}
\t\tDOMAIN_SEPARATOR = keccak256(
\t\t\tabi.encode(
\t\t\t\tkeccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
\t\t\t\tkeccak256(bytes(_name)),
\t\t\t\tkeccak256(bytes("1")),
\t\t\t\tchainId,
\t\t\t\taddress(this)
\t\t\t)
\t\t);
\t}
\tfunction _mint(address to, uint value) internal {
\t\ttotalSupply = totalSupply.add(value);
\t\tbalanceOf[to] = balanceOf[to].add(value);
\t\temit Transfer(address(0), to, value);
\t}
\tfunction _burn(address from, uint value) internal {
\t\tbalanceOf[from] = balanceOf[from].sub(value);
\t\ttotalSupply = totalSupply.sub(value);
\t\temit Transfer(from, address(0), value);
\t}
\tfunction _approve(address owner, address spender, uint value) private {
\t\tallowance[owner][spender] = value;
\t\temit Approval(owner, spender, value);
\t}
\tfunction _transfer(address from, address to, uint value) internal {
\t\tbalanceOf[from] = balanceOf[from].sub(value, "Impermax: TRANSFER_TOO_HIGH");
\t\tbalanceOf[to] = balanceOf[to].add(value);
\t\temit Transfer(from, to, value);
\t}
\tfunction approve(address spender, uint value) external returns (bool) {
\t\t_approve(msg.sender, spender, value);
\t\treturn true;
\t}
\tfunction transfer(address to, uint value) external returns (bool) {
\t\t_transfer(msg.sender, to, value);
\t\treturn true;
\t}
\tfunction transferFrom(address from, address to, uint value) external returns (bool) {
\t\tif (allowance[from][msg.sender] != uint(-1)) {
\t\t\tallowance[from][msg.sender] = allowance[from][msg.sender].sub(value, "Impermax: TRANSFER_NOT_ALLOWED");
\t\t}
\t\t_transfer(from, to, value);
\t\treturn true;
\t}
\t
\tfunction _checkSignature(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s, bytes32 typehash) internal {
\t\trequire(deadline >= block.timestamp, "Impermax: EXPIRED");
\t\tbytes32 digest = keccak256(
\t\t\tabi.encodePacked(
\t\t\t\t'\\x19\\x01',
\t\t\t\tDOMAIN_SEPARATOR,
\t\t\t\tkeccak256(abi.encode(typehash, owner, spender, value, nonces[owner]++, deadline))
\t\t\t)
\t\t);
\t\taddress recoveredAddress = ecrecover(digest, v, r, s);
\t\trequire(recoveredAddress != address(0) && recoveredAddress == owner, "Impermax: INVALID_SIGNATURE");\t
\t}
\t// keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
\tbytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
\tfunction permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
\t\t_checkSignature(owner, spender, value, deadline, v, r, s, PERMIT_TYPEHASH);
\t\t_approve(owner, spender, value);
\t}
}pragma solidity =0.5.16;
import "./ImpermaxERC20.sol";
import "./interfaces/IERC20.sol";
import "./interfaces/IPoolToken.sol";
import "./libraries/SafeMath.sol";
contract PoolToken is IPoolToken, ImpermaxERC20 {
   \tuint internal constant initialExchangeRate = 1e18;
\taddress public underlying;
\taddress public factory;
\tuint public totalBalance;
\tuint public constant MINIMUM_LIQUIDITY = 1000;
\t
\tevent Mint(address indexed sender, address indexed minter, uint mintAmount, uint mintTokens);
\tevent Redeem(address indexed sender, address indexed redeemer, uint redeemAmount, uint redeemTokens);
\tevent Sync(uint totalBalance);
\t
\t/*** Initialize ***/
\t
\t// called once by the factory
\tfunction _setFactory() external {
\t\trequire(factory == address(0), "Impermax: FACTORY_ALREADY_SET");
\t\tfactory = msg.sender;
\t}
\t
\t/*** PoolToken ***/
\t
\tfunction _update() internal {
\t\ttotalBalance = IERC20(underlying).balanceOf(address(this));
\t\temit Sync(totalBalance);
\t}
\tfunction exchangeRate() public returns (uint) 
\t{
\t\tuint _totalSupply = totalSupply; // gas savings
\t\tuint _totalBalance = totalBalance; // gas savings
\t\tif (_totalSupply == 0 || _totalBalance == 0) return initialExchangeRate;
\t\treturn _totalBalance.mul(1e18).div(_totalSupply);
\t}
\t
\t// this low-level function should be called from another contract
\tfunction mint(address minter) external nonReentrant update returns (uint mintTokens) {
\t\tuint balance = IERC20(underlying).balanceOf(address(this));
\t\tuint mintAmount = balance.sub(totalBalance);
\t\tmintTokens = mintAmount.mul(1e18).div(exchangeRate());
\t\tif(totalSupply == 0) {
\t\t\t// permanently lock the first MINIMUM_LIQUIDITY tokens
\t\t\tmintTokens = mintTokens.sub(MINIMUM_LIQUIDITY);
\t\t\t_mint(address(0), MINIMUM_LIQUIDITY);
\t\t}
\t\trequire(mintTokens > 0, "Impermax: MINT_AMOUNT_ZERO");
\t\t_mint(minter, mintTokens);
\t\temit Mint(msg.sender, minter, mintAmount, mintTokens);
\t}
\t// this low-level function should be called from another contract
\tfunction redeem(address redeemer) external nonReentrant update returns (uint redeemAmount) {
\t\tuint redeemTokens = balanceOf[address(this)];
\t\tredeemAmount = redeemTokens.mul(exchangeRate()).div(1e18);
\t\trequire(redeemAmount > 0, "Impermax: REDEEM_AMOUNT_ZERO");
\t\trequire(redeemAmount <= totalBalance, "Impermax: INSUFFICIENT_CASH");
\t\t_burn(address(this), redeemTokens);
\t\t_safeTransfer(redeemer, redeemAmount);
\t\temit Redeem(msg.sender, redeemer, redeemAmount, redeemTokens);\t\t
\t}
\t// force real balance to match totalBalance
\tfunction skim(address to) external nonReentrant {
\t\t_safeTransfer(to, IERC20(underlying).balanceOf(address(this)).sub(totalBalance));
\t}
\t// force totalBalance to match real balance
\tfunction sync() external nonReentrant update {}
\t
\t/*** Utilities ***/
\t
\t// same safe transfer function used by UniSwapV2 (with fixed underlying)
\tbytes4 private constant SELECTOR = bytes4(keccak256(bytes("transfer(address,uint256)")));
\tfunction _safeTransfer(address to, uint amount) internal {
\t\t(bool success, bytes memory data) = underlying.call(abi.encodeWithSelector(SELECTOR, to, amount));
\t\trequire(success && (data.length == 0 || abi.decode(data, (bool))), "Impermax: TRANSFER_FAILED");
\t}
\t
\t// prevents a contract from calling itself, directly or indirectly.
\tbool internal _notEntered = true;
\tmodifier nonReentrant() {
\t\trequire(_notEntered, "Impermax: REENTERED");
\t\t_notEntered = false;
\t\t_;
\t\t_notEntered = true;
\t}
\t
\t// update totalBalance with current balance
\tmodifier update() {
\t\t_;
\t\t_update();
\t}
}pragma solidity >=0.5.0;
interface IBorrowTracker {
\tfunction trackBorrow(address borrower, uint borrowBalance, uint borrowIndex) external;
}pragma solidity >=0.5.0;
interface IBorrowable {
\t/*** Impermax ERC20 ***/
\t
\tevent Transfer(address indexed from, address indexed to, uint value);
\tevent Approval(address indexed owner, address indexed spender, uint value);
\t
\tfunction name() external pure returns (string memory);
\tfunction symbol() external pure returns (string memory);
\tfunction decimals() external pure returns (uint8);
\tfunction totalSupply() external view returns (uint);
\tfunction balanceOf(address owner) external view returns (uint);
\tfunction allowance(address owner, address spender) external view returns (uint);
\tfunction approve(address spender, uint value) external returns (bool);
\tfunction transfer(address to, uint value) external returns (bool);
\tfunction transferFrom(address from, address to, uint value) external returns (bool);
\t
\tfunction DOMAIN_SEPARATOR() external view returns (bytes32);
\tfunction PERMIT_TYPEHASH() external pure returns (bytes32);
\tfunction nonces(address owner) external view returns (uint);
\tfunction permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
\t
\t/*** Pool Token ***/
\t
\tevent Mint(address indexed sender, address indexed minter, uint mintAmount, uint mintTokens);
\tevent Redeem(address indexed sender, address indexed redeemer, uint redeemAmount, uint redeemTokens);
\tevent Sync(uint totalBalance);
\t
\tfunction underlying() external view returns (address);
\tfunction factory() external view returns (address);
\tfunction totalBalance() external view returns (uint);
\tfunction MINIMUM_LIQUIDITY() external pure returns (uint);
\tfunction exchangeRate() external returns (uint);
\tfunction mint(address minter) external returns (uint mintTokens);
\tfunction redeem(address redeemer) external returns (uint redeemAmount);
\tfunction skim(address to) external;
\tfunction sync() external;
\t
\tfunction _setFactory() external;
\t
\t/*** Borrowable ***/
\tevent BorrowApproval(address indexed owner, address indexed spender, uint value);
\tevent Borrow(address indexed sender, address indexed borrower, address indexed receiver, uint borrowAmount, uint repayAmount, uint accountBorrowsPrior, uint accountBorrows, uint totalBorrows);
\tevent Liquidate(address indexed sender, address indexed borrower, address indexed liquidator, uint seizeTokens, uint repayAmount, uint accountBorrowsPrior, uint accountBorrows, uint totalBorrows);
\t
\tfunction BORROW_FEE() external pure returns (uint);
\tfunction collateral() external view returns (address);
\tfunction reserveFactor() external view returns (uint);
\tfunction exchangeRateLast() external view returns (uint);
\tfunction borrowIndex() external view returns (uint);
\tfunction totalBorrows() external view returns (uint);
\tfunction borrowAllowance(address owner, address spender) external view returns (uint);
\tfunction borrowBalance(address borrower) external view returns (uint);\t
\tfunction borrowTracker() external view returns (address);
\t
\tfunction BORROW_PERMIT_TYPEHASH() external pure returns (bytes32);
\tfunction borrowApprove(address spender, uint256 value) external returns (bool);
\tfunction borrowPermit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
\tfunction borrow(address borrower, address receiver, uint borrowAmount, bytes calldata data) external;
\tfunction liquidate(address borrower, address liquidator) external returns (uint seizeTokens);
\tfunction trackBorrow(address borrower) external;
\t
\t/*** Borrowable Interest Rate Model ***/
\tevent AccrueInterest(uint interestAccumulated, uint borrowIndex, uint totalBorrows);
\tevent CalculateKink(uint kinkRate);
\tevent CalculateBorrowRate(uint borrowRate);
\t
\tfunction KINK_BORROW_RATE_MAX() external pure returns (uint);
\tfunction KINK_BORROW_RATE_MIN() external pure returns (uint);
\tfunction KINK_MULTIPLIER() external pure returns (uint);
\tfunction borrowRate() external view returns (uint);
\tfunction kinkBorrowRate() external view returns (uint);
\tfunction kinkUtilizationRate() external view returns (uint);
\tfunction adjustSpeed() external view returns (uint);
\tfunction rateUpdateTimestamp() external view returns (uint32);
\tfunction accrualTimestamp() external view returns (uint32);
\t
\tfunction accrueInterest() external;
\t
\t/*** Borrowable Setter ***/
\tevent NewReserveFactor(uint newReserveFactor);
\tevent NewKinkUtilizationRate(uint newKinkUtilizationRate);
\tevent NewAdjustSpeed(uint newAdjustSpeed);
\tevent NewBorrowTracker(address newBorrowTracker);
\tfunction RESERVE_FACTOR_MAX() external pure returns (uint);
\tfunction KINK_UR_MIN() external pure returns (uint);
\tfunction KINK_UR_MAX() external pure returns (uint);
\tfunction ADJUST_SPEED_MIN() external pure returns (uint);
\tfunction ADJUST_SPEED_MAX() external pure returns (uint);
\t
\tfunction _initialize (
\t\tstring calldata _name, 
\t\tstring calldata _symbol,
\t\taddress _underlying, 
\t\taddress _collateral
\t) external;
\tfunction _setReserveFactor(uint newReserveFactor) external;
\tfunction _setKinkUtilizationRate(uint newKinkUtilizationRate) external;
\tfunction _setAdjustSpeed(uint newAdjustSpeed) external;
\tfunction _setBorrowTracker(address newBorrowTracker) external;
}pragma solidity >=0.5.0;
interface ICollateral {
\t/*** Impermax ERC20 ***/
\t
\tevent Transfer(address indexed from, address indexed to, uint value);
\tevent Approval(address indexed owner, address indexed spender, uint value);
\t
\tfunction name() external pure returns (string memory);
\tfunction symbol() external pure returns (string memory);
\tfunction decimals() external pure returns (uint8);
\tfunction totalSupply() external view returns (uint);
\tfunction balanceOf(address owner) external view returns (uint);
\tfunction allowance(address owner, address spender) external view returns (uint);
\tfunction approve(address spender, uint value) external returns (bool);
\tfunction transfer(address to, uint value) external returns (bool);
\tfunction transferFrom(address from, address to, uint value) external returns (bool);
\t
\tfunction DOMAIN_SEPARATOR() external view returns (bytes32);
\tfunction PERMIT_TYPEHASH() external pure returns (bytes32);
\tfunction nonces(address owner) external view returns (uint);
\tfunction permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
\t
\t/*** Pool Token ***/
\t
\tevent Mint(address indexed sender, address indexed minter, uint mintAmount, uint mintTokens);
\tevent Redeem(address indexed sender, address indexed redeemer, uint redeemAmount, uint redeemTokens);
\tevent Sync(uint totalBalance);
\t
\tfunction underlying() external view returns (address);
\tfunction factory() external view returns (address);
\tfunction totalBalance() external view returns (uint);
\tfunction MINIMUM_LIQUIDITY() external pure returns (uint);
\tfunction exchangeRate() external returns (uint);
\tfunction mint(address minter) external returns (uint mintTokens);
\tfunction redeem(address redeemer) external returns (uint redeemAmount);
\tfunction skim(address to) external;
\tfunction sync() external;
\t
\tfunction _setFactory() external;
\t
\t/*** Collateral ***/
\t
\tfunction borrowable0() external view returns (address);
\tfunction borrowable1() external view returns (address);
\tfunction simpleUniswapOracle() external view returns (address);
\tfunction safetyMarginSqrt() external view returns (uint);
\tfunction liquidationIncentive() external view returns (uint);
\t
\tfunction getPrices() external returns (uint price0, uint price1);
\tfunction tokensUnlocked(address from, uint value) external returns (bool);
\tfunction accountLiquidityAmounts(address account, uint amount0, uint amount1) external returns (uint liquidity, uint shortfall);
\tfunction accountLiquidity(address account) external returns (uint liquidity, uint shortfall);
\tfunction canBorrow(address account, address borrowable, uint accountBorrows) external returns (bool);
\tfunction seize(address liquidator, address borrower, uint repayAmount) external returns (uint seizeTokens);
\tfunction flashRedeem(address redeemer, uint redeemAmount, bytes calldata data) external;
\t
\t/*** Collateral Setter ***/
\t
\tevent NewSafetyMargin(uint newSafetyMarginSqrt);
\tevent NewLiquidationIncentive(uint newLiquidationIncentive);
\tfunction SAFETY_MARGIN_SQRT_MIN() external pure returns (uint);
\tfunction SAFETY_MARGIN_SQRT_MAX() external pure returns (uint);
\tfunction LIQUIDATION_INCENTIVE_MIN() external pure returns (uint);
\tfunction LIQUIDATION_INCENTIVE_MAX() external pure returns (uint);
\t
\tfunction _initialize (
\t\tstring calldata _name, 
\t\tstring calldata _symbol,
\t\taddress _underlying, 
\t\taddress _borrowable0, 
\t\taddress _borrowable1
\t) external;
\tfunction _setSafetyMarginSqrt(uint newSafetyMarginSqrt) external;
\tfunction _setLiquidationIncentive(uint newLiquidationIncentive) external;
}pragma solidity >=0.5.0;
interface IERC20 {
    event Approval(address indexed owner, address indexed spender, uint value);
    event Transfer(address indexed from, address indexed to, uint value);
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function decimals() external view returns (uint8);
    function totalSupply() external view returns (uint);
    function balanceOf(address owner) external view returns (uint);
    function allowance(address owner, address spender) external view returns (uint);
    function approve(address spender, uint value) external returns (bool);
    function transfer(address to, uint value) external returns (bool);
    function transferFrom(address from, address to, uint value) external returns (bool);
}
pragma solidity >=0.5.0;
interface IFactory {
\tevent LendingPoolInitialized(address indexed uniswapV2Pair, address indexed token0, address indexed token1,
\t\taddress collateral, address borrowable0, address borrowable1, uint lendingPoolId);
\tevent NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);
\tevent NewAdmin(address oldAdmin, address newAdmin);
\tevent NewReservesPendingAdmin(address oldReservesPendingAdmin, address newReservesPendingAdmin);
\tevent NewReservesAdmin(address oldReservesAdmin, address newReservesAdmin);
\tevent NewReservesManager(address oldReservesManager, address newReservesManager);
\t
\tfunction admin() external view returns (address);
\tfunction pendingAdmin() external view returns (address);
\tfunction reservesAdmin() external view returns (address);
\tfunction reservesPendingAdmin() external view returns (address);
\tfunction reservesManager() external view returns (address);
\tfunction getLendingPool(address uniswapV2Pair) external view returns (
\t\tbool initialized, 
\t\tuint24 lendingPoolId, 
\t\taddress collateral, 
\t\taddress borrowable0, 
\t\taddress borrowable1
\t);
\tfunction allLendingPools(uint) external view returns (address uniswapV2Pair);
\tfunction allLendingPoolsLength() external view returns (uint);
\t
\tfunction bDeployer() external view returns (address);
\tfunction cDeployer() external view returns (address);
\tfunction uniswapV2Factory() external view returns (address);
\tfunction simpleUniswapOracle() external view returns (address);
\tfunction createCollateral(address uniswapV2Pair) external returns (address collateral);
\tfunction createBorrowable0(address uniswapV2Pair) external returns (address borrowable0);
\tfunction createBorrowable1(address uniswapV2Pair) external returns (address borrowable1);
\tfunction initializeLendingPool(address uniswapV2Pair) external;
\tfunction _setPendingAdmin(address newPendingAdmin) external;
\tfunction _acceptAdmin() external;
\tfunction _setReservesPendingAdmin(address newPendingAdmin) external;
\tfunction _acceptReservesAdmin() external;
\tfunction _setReservesManager(address newReservesManager) external;
}
pragma solidity >=0.5.0;
interface IImpermaxCallee {
    function impermaxBorrow(address sender, address borrower, uint borrowAmount, bytes calldata data) external;
    function impermaxRedeem(address sender, uint redeemAmount, bytes calldata data) external;
}pragma solidity >=0.5.0;
interface IPoolToken {
\t/*** Impermax ERC20 ***/
\t
\tevent Transfer(address indexed from, address indexed to, uint value);
\tevent Approval(address indexed owner, address indexed spender, uint value);
\t
\tfunction name() external pure returns (string memory);
\tfunction symbol() external pure returns (string memory);
\tfunction decimals() external pure returns (uint8);
\tfunction totalSupply() external view returns (uint);
\tfunction balanceOf(address owner) external view returns (uint);
\tfunction allowance(address owner, address spender) external view returns (uint);
\tfunction approve(address spender, uint value) external returns (bool);
\tfunction transfer(address to, uint value) external returns (bool);
\tfunction transferFrom(address from, address to, uint value) external returns (bool);
\t
\tfunction DOMAIN_SEPARATOR() external view returns (bytes32);
\tfunction PERMIT_TYPEHASH() external pure returns (bytes32);
\tfunction nonces(address owner) external view returns (uint);
\tfunction permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
\t
\t/*** Pool Token ***/
\t
\tevent Mint(address indexed sender, address indexed minter, uint mintAmount, uint mintTokens);
\tevent Redeem(address indexed sender, address indexed redeemer, uint redeemAmount, uint redeemTokens);
\tevent Sync(uint totalBalance);
\t
\tfunction underlying() external view returns (address);
\tfunction factory() external view returns (address);
\tfunction totalBalance() external view returns (uint);
\tfunction MINIMUM_LIQUIDITY() external pure returns (uint);
\tfunction exchangeRate() external returns (uint);
\tfunction mint(address minter) external returns (uint mintTokens);
\tfunction redeem(address redeemer) external returns (uint redeemAmount);
\tfunction skim(address to) external;
\tfunction sync() external;
\t
\tfunction _setFactory() external;
}pragma solidity =0.5.16;
// a library for performing various math operations
// forked from: https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/libraries/Math.sol
library Math {
    function min(uint x, uint y) internal pure returns (uint z) {
        z = x < y ? x : y;
    }
    // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
    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;
        }
    }
}
pragma solidity =0.5.16;
// 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 = 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 = 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 = 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 = 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;
    }
}