Transaction Hash:
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 | ||
---|---|---|---|---|---|
0x3A8315E2...6de52C0Ff |
83.735877435032154781 Eth
Nonce: 1465
|
83.735598981032154781 Eth
Nonce: 1466
| 0.000278454 | ||
0xbD8b5a61...677872A6f | |||||
0xEA674fdD...16B898ec8
Miner
| (Ethermine) | 2,325.192588110775288495 Eth | 2,325.192866564775288495 Eth | 0.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; } }