Skip to content

Instantly share code, notes, and snippets.

@andrecronje
Last active February 1, 2022 16:11
Show Gist options
  • Save andrecronje/4df5ad24de83bd18a893efb2524b3536 to your computer and use it in GitHub Desktop.
Save andrecronje/4df5ad24de83bd18a893efb2524b3536 to your computer and use it in GitHub Desktop.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.6;
library Math {
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
}
interface IERC20 {
function transfer(address recipient, uint256 amount) external returns (bool);
function decimals() external view returns (uint256);
function symbol() external view returns (string memory);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
}
interface Oracle {
struct ReferenceData {
uint256 rate; // base/quote exchange rate, multiplied by 1e18.
uint256 lastUpdatedBase; // UNIX epoch of the last time when base price gets updated.
uint256 lastUpdatedQuote; // UNIX epoch of the last time when quote price gets updated.
}
function getReferenceData(string memory _base, string memory _quote)
external
view
returns (ReferenceData memory);
}
interface cToken {
function underlying() external view returns (address);
}
interface comptroller {
function getAllMarkets() external view returns (address[] memory);
function markets(address _market) external view returns (bool isListed, uint256 collateralFactorMantissa, bool isComped);
}
interface ibtroller {
function getAllMarkets() external view returns (address[] memory);
function markets(address _market) external view returns (bool isListed, uint256 collateralFactorMantissa);
}
interface aavecore {
struct ReserveConfigurationMap {
//bit 0-15: LTV
//bit 16-31: Liq. threshold
//bit 32-47: Liq. bonus
//bit 48-55: Decimals
//bit 56: Reserve is active
//bit 57: reserve is frozen
//bit 58: borrowing is enabled
//bit 59: stable rate borrowing enabled
//bit 60-63: reserved
//bit 64-79: reserve factor
uint256 data;
}
function getReserveConfiguration(address _market) external view returns (uint, uint, uint, bool);
function getConfiguration(address _market) external view returns (ReserveConfigurationMap memory);
}
interface vaultparams {
function initialCollateralRatio(address _token) external view returns (uint);
}
interface SushiswapV2Router02 {
function getAmountsOut(uint amountIn, address[] memory path) external view returns (uint[] memory amounts);
}
contract FixedUSD {
string public constant name = "Fixed USD";
string public constant symbol = "USDF";
uint8 public constant decimals = 18;
address constant _oracle = 0xDA7a001b254CD22e46d3eAB04d937489c93174C3;
address constant _stable = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address constant _router = 0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F;
string constant _quote = "USD";
uint constant _BASE = 100;
uint constant _LIQUIDITY_THRESHOLD = 5;
uint constant _LIQUIDATION_VALUE = 90;
uint constant _CACHE = 1 days;
uint constant _minLiquidity = 500000e18;
/// @notice Total number of tokens in circulation
uint public totalSupply = 0;
mapping(address => mapping (address => uint)) internal allowances;
mapping(address => uint) internal balances;
event Transfer(address indexed from, address indexed to, uint amount);
event Approval(address indexed owner, address indexed spender, uint amount);
mapping(address => mapping(address => uint)) public credit;
mapping(address => mapping(address => uint)) public collateral;
mapping(address => uint) public credits;
mapping(address => uint) public collaterals;
mapping(address => uint) public ltvs;
mapping(address => uint) _ltvCaches;
mapping(address => uint) public liquidities;
mapping(address => uint) _liquidityCaches;
mapping(address => uint) public fallbackQuote;
uint public arb;
event Mint(address indexed from, address indexed asset, address indexed to, uint amount);
event Burn(address indexed from, address indexed asset, address indexed to, uint amount);
event Liquidate(address indexed from, address indexed asset, address indexed to, uint amount);
function validateLTV(uint __ltv) public pure returns (bool) {
if ((__ltv == 60) || (__ltv == 65) || (__ltv == 70) || (__ltv == 75)) {
return true;
} else if ((__ltv == 80) || (__ltv == 85) || __ltv >= 90) {
return true;
} else {
return false;
}
}
function gentleRepaymentCalculator(uint __ltv, uint debt, uint value) public pure returns (uint repayment) {
if (__ltv == 60) {
return Math.min((debt - value) * 310 / _BASE, debt);
} else if (__ltv == 65) {
return Math.min((debt - value) * 370 / _BASE, debt);
} else if (__ltv == 70) {
return Math.min((debt - value) * 460 / _BASE, debt);
} else if (__ltv == 75) {
return Math.min((debt - value) * 610 / _BASE, debt);
} else if (__ltv == 80) {
return Math.min((debt - value) * 910 / _BASE, debt);
} else if (__ltv == 85) {
return Math.min((debt - value) * 1810 / _BASE, debt);
} else if (__ltv >= 90) {
return debt;
}
}
function _lookup(address quoted, uint amount) internal returns (uint) {
uint _quoted = Oracle(_oracle).getReferenceData(IERC20(quoted).symbol(), _quote).rate;
if (_quoted == 0 && fallbackQuote[quoted] != 0) {
_quoted = fallbackQuote[quoted];
} else {
fallbackQuote[quoted] = _quoted;
}
return _quoted * (amount * _ltv(quoted) / _BASE) / 10 ** IERC20(quoted).decimals();
}
function lookup(address quoted, uint amount) public view returns (uint) {
uint _quoted = Oracle(_oracle).getReferenceData(IERC20(quoted).symbol(), _quote).rate;
if (_quoted == 0 && fallbackQuote[quoted] != 0) {
_quoted = fallbackQuote[quoted];
}
return _quoted * (amount * ltvs[quoted] / _BASE) / 10 ** IERC20(quoted).decimals();
}
function lookup(address quoted) external view returns (uint) {
return Oracle(_oracle).getReferenceData(IERC20(quoted).symbol(), _quote).rate;
}
function lookupLiq(address quoted, uint amount) public view returns (uint) {
return Oracle(_oracle).getReferenceData(IERC20(quoted).symbol(), _quote).rate * (amount * _LIQUIDATION_VALUE / _BASE) / 10 ** IERC20(quoted).decimals();
}
function mintArb(uint amount) external {
_mintArb(amount, msg.sender);
}
function mintArb(uint amount, address recipient) external {
_mintArb(amount, recipient);
}
function _mintArb(uint amount, address recipient) internal {
_safeTransferFrom(_stable, msg.sender, address(this), amount);
_mint(recipient, amount);
arb += amount;
emit Mint(msg.sender, _stable, recipient, amount);
}
function burnArb(uint amount) external {
_burnArb(amount, msg.sender);
}
function burnArb(uint amount, address recipient) external {
_burnArb(amount, recipient);
}
function _burnArb(uint amount, address recipient) internal {
_burn(msg.sender, amount);
_safeTransfer(_stable, recipient, amount);
arb -= amount;
emit Burn(msg.sender, _stable, recipient, amount);
}
function mint(address asset, uint amount, uint minted) external {
_mint(asset, amount, minted, msg.sender);
}
function mint(address asset, uint amount, uint minted, address recipient) external {
_mint(asset, amount, minted, recipient);
}
function _mint(address asset, uint amount, uint minted, address recipient) internal {
if (amount > 0) {
_safeTransferFrom(asset, msg.sender, address(this), amount);
}
collateral[msg.sender][asset] += amount;
collaterals[asset] += amount;
credit[msg.sender][asset] += minted;
credits[asset] += minted;
require(_liquidity(asset, collaterals[asset]) >= credits[asset]);
require(_lookup(asset, collateral[msg.sender][asset]) >= credit[msg.sender][asset]);
_mint(recipient, minted);
emit Mint(msg.sender, asset, recipient, amount);
}
function burn(address asset, uint amount, uint burned) external {
_burn(asset, amount, burned, msg.sender);
}
function burn(address asset, uint amount, uint burned, address recipient) external {
_burn(asset, amount, burned, recipient);
}
function _burn(address asset, uint amount, uint burned, address recipient) internal {
_burn(msg.sender, burned);
credit[msg.sender][asset] -= burned;
credits[asset] -= burned;
collateral[msg.sender][asset] -= amount;
collaterals[asset] -= amount;
require(lookup(asset, collateral[msg.sender][asset]) >= credit[msg.sender][asset]);
if (amount > 0) {
_safeTransfer(asset, recipient, amount);
}
emit Burn(msg.sender, asset, recipient, amount);
}
function repaymentCalculator(address owner, address asset) external view returns (uint) {
uint _nominal = collateral[owner][asset];
uint _backed = lookup(asset, _nominal);
uint _debt = credit[owner][asset];
if (_backed < _debt) {
return gentleRepaymentCalculator(ltvs[asset], _debt, _backed);
} else {
return 0;
}
}
function paymentCalculator(address owner, address asset) external view returns (uint) {
uint _nominal = collateral[owner][asset];
uint _backed = lookup(asset, _nominal);
uint _debt = credit[owner][asset];
if (_backed < _debt) {
uint _repayment = gentleRepaymentCalculator(ltvs[asset], _debt, _backed);
return Math.min(_nominal * _repayment / lookupLiq(asset, _nominal), _nominal);
} else {
return 0;
}
}
function liquidate(address owner, address asset, uint max) external {
uint _nominal = collateral[owner][asset];
uint _backed = _lookup(asset, _nominal);
uint _debt = credit[owner][asset];
require(_backed < _debt);
uint _repayment = gentleRepaymentCalculator(_ltv(asset), _debt, _backed);
require(_repayment <= max);
uint _payment = Math.min(_nominal * _repayment / lookupLiq(asset, _nominal), _nominal);
_burn(msg.sender, _repayment);
credit[owner][asset] -= _repayment;
credits[asset] -= _repayment;
collateral[owner][asset] -= _payment;
collaterals[asset] -= _payment;
require(_lookup(asset, collateral[owner][asset]) >= credit[owner][asset]);
_safeTransfer(asset, msg.sender, _payment);
emit Liquidate(msg.sender, asset, owner, _repayment);
}
function _mint(address dst, uint amount) internal {
// mint the amount
totalSupply += amount;
// transfer the amount to the recipient
balances[dst] += amount;
emit Transfer(address(0), dst, amount);
}
function _burn(address dst, uint amount) internal {
// burn the amount
totalSupply -= amount;
// transfer the amount from the recipient
balances[dst] -= amount;
emit Transfer(dst, address(0), amount);
}
function allowance(address account, address spender) external view returns (uint) {
return allowances[account][spender];
}
function approve(address spender, uint amount) external returns (bool) {
allowances[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function balanceOf(address account) external view returns (uint) {
return balances[account];
}
function transfer(address dst, uint amount) external returns (bool) {
_transferTokens(msg.sender, dst, amount);
return true;
}
function transferFrom(address src, address dst, uint amount) external returns (bool) {
address spender = msg.sender;
uint spenderAllowance = allowances[src][spender];
if (spender != src && spenderAllowance != type(uint).max) {
uint newAllowance = spenderAllowance - amount;
allowances[src][spender] = newAllowance;
emit Approval(src, spender, newAllowance);
}
_transferTokens(src, dst, amount);
return true;
}
function _transferTokens(address src, address dst, uint amount) internal {
balances[src] -= amount;
balances[dst] += amount;
emit Transfer(src, dst, amount);
}
function _safeTransfer(
address token,
address to,
uint256 value
) internal {
(bool success, bytes memory data) =
token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))));
}
function _safeTransferFrom(
address token,
address from,
address to,
uint256 value
) internal {
(bool success, bytes memory data) =
token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))));
}
address constant _aavev2 = address(0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9);
address constant _ib = address(0xAB1c342C7bf5Ec5F02ADEA1c2270670bCa144CbB);
address constant _unit = address(0x203153522B9EAef4aE17c6e99851EE7b2F7D312E);
address constant _weth = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
uint256 constant _LTV_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000;
function _getParamsMemory(aavecore.ReserveConfigurationMap memory self) internal pure returns (uint256) {
return (self.data & ~_LTV_MASK);
}
function _lookupMarket(address _core, address _token) internal view returns (address) {
address[] memory _list = comptroller(_core).getAllMarkets();
for (uint i = 0; i < _list.length; i++) {
if (_list[i] != address(0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5) && _list[i] != address(0xD06527D5e56A3495252A528C4987003b712860eE)) {
if (cToken(_list[i]).underlying() == _token) {
return _list[i];
}
}
}
return address(0x0);
}
function _liquidityV(address token, uint amount) internal view returns (uint, bool) {
if (block.timestamp > _liquidityCaches[token]) {
if (token == _weth) {
return (_liquidityVWETH(amount), true);
} else {
address[] memory _path = new address[](3);
_path[0] = token;
_path[1] = _weth;
_path[2] = _stable;
uint _liq = SushiswapV2Router02(_router).getAmountsOut(amount, _path)[2];
uint _liquid = liquidities[token];
if (_liq > _liquid) {
_liquid += _liquid * _LIQUIDITY_THRESHOLD / _BASE;
}
return (_liquid, true);
}
} else {
return (liquidities[token], false);
}
}
function _liquidityVWETH(uint amount) internal view returns (uint) {
address[] memory _path = new address[](2);
_path[0] = _weth;
_path[1] = _stable;
uint _liq = SushiswapV2Router02(_router).getAmountsOut(amount, _path)[1];
uint _liquid = liquidities[_weth];
if (_liq > _liquid) {
_liquid += _liquid * _LIQUIDITY_THRESHOLD / _BASE;
}
return _liquid;
}
function _liquidity(address token, uint amount) internal returns (uint) {
if (_liquidityCaches[token] == 0) {
liquidities[token] = _minLiquidity;
}
(uint _val, bool _updated) = _liquidityV(token, amount);
if (_updated) {
_liquidityCaches[token] = block.timestamp + _CACHE;
liquidities[token] = _val;
}
return _val;
}
function liquidity(address token, uint amount) external view returns (uint val) {
(val,) = _liquidityV(token, amount);
}
function _getLTVIB(address token) internal view returns (uint ib) {
(,ib) = ibtroller(_ib).markets(_lookupMarket(_ib, token));
ib = ib / 1e16;
}
function _getLTVUnit(address _token) internal view returns (uint unit) {
unit = vaultparams(_unit).initialCollateralRatio(_token);
}
function _getLTVAaveV2(address token) internal view returns (uint aavev2) {
(aavev2) = _getParamsMemory(aavecore(_aavev2).getConfiguration(token));
aavev2 = aavev2 / 1e2;
}
function _ltv(address token) internal returns (uint) {
(uint _val, bool _updated) = _ltvV(token);
if (_updated) {
_ltvCaches[token] = block.timestamp + _CACHE;
ltvs[token] = _val;
}
return _val;
}
function ltv(address token) external view returns (uint val) {
(val,) = _ltvV(token);
}
function _ltvV(address token) internal view returns (uint, bool) {
if (block.timestamp > _ltvCaches[token]) {
uint _max = 0;
uint _tmp = _getLTVIB(token);
if (_tmp > _max) {
_max = _tmp;
}
_tmp = _getLTVAaveV2(token);
if (_tmp > _max) {
_max = _tmp;
}
_tmp = _getLTVUnit(token);
if (_tmp > _max) {
_max = _tmp;
}
_max = _max / 5 * 5;
if (_max < 60) {
_max = 0;
}
return (_max, true);
} else {
return (ltvs[token], false);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment