UnderlyingCreditProvider.sol
1 pragma solidity >=0.6.0; 2 pragma experimental ABIEncoderV2; 3 4 import "../../deployment/Deployer.sol"; 5 import "../../governance/ProtocolSettings.sol"; 6 import "../../interfaces/IOptionsExchange.sol"; 7 import "../../interfaces/ICreditToken.sol"; 8 import "../../interfaces/UnderlyingFeed.sol"; 9 import "../../interfaces/IUnderlyingVault.sol"; 10 import "../../interfaces/IUniswapV2Router01.sol"; 11 import "../../utils/MoreMath.sol"; 12 import "../../utils/Convert.sol"; 13 import "../../utils/SafeERC20.sol"; 14 import "../../utils/SafeMath.sol"; 15 import "../../utils/SignedSafeMath.sol"; 16 17 18 contract UnderlyingCreditProvider { 19 20 using SafeERC20 for IERC20_2; 21 using SafeMath for uint; 22 using SignedSafeMath for int; 23 24 ProtocolSettings private settings; 25 ICreditToken private creditToken; 26 27 mapping(address => uint) private debts; 28 mapping(address => uint) private balances; 29 mapping(address => uint) private debtsDate; 30 mapping(address => uint) private primeCallers; 31 32 address private vaultAddr; 33 address private exchangeAddr; 34 address private exchangeCreditProviderAddr; 35 address private udlAssetAddr; 36 37 uint private _totalDebt; 38 uint private _totalOwners; 39 uint private _totalBalance; 40 uint private _totalAccruedFees; 41 42 43 event DepositTokens(address indexed to, address indexed token, uint value); 44 45 event WithdrawTokens(address indexed from, address indexed token, uint value); 46 47 event TransferBalance(address indexed from, address indexed to, uint value); 48 49 event AccumulateDebt(address indexed to, uint value); 50 51 event BurnDebt(address indexed from, uint value); 52 53 event AccrueFees(address indexed from, uint value); 54 55 constructor(address _deployAddr, address _udlFeedAddr) public { 56 Deployer deployer = Deployer(_deployAddr); 57 settings = ProtocolSettings(deployer.getContractAddress("ProtocolSettings")); 58 exchangeAddr = deployer.getContractAddress("OptionsExchange"); 59 vaultAddr = deployer.getContractAddress("UnderlyingVault"); 60 address collateralManagerAddr = deployer.getContractAddress("CollateralManager"); 61 exchangeCreditProviderAddr = deployer.getContractAddress("CreditProvider"); 62 63 udlAssetAddr = UnderlyingFeed(_udlFeedAddr).getUnderlyingAddr(); 64 primeCallers[exchangeAddr] = 1; 65 primeCallers[address(settings)] = 1; 66 primeCallers[vaultAddr] = 1; 67 primeCallers[collateralManagerAddr] = 1; 68 } 69 70 function initialize(address underlyingCreditToken) external { 71 ensurePrimeCaller(); 72 creditToken = ICreditToken(underlyingCreditToken); 73 primeCallers[underlyingCreditToken] = 1; 74 } 75 76 function getUnderlyingCreditToken() external view returns (address) { 77 return address(creditToken); 78 } 79 80 function totalTokenStock() external view returns (uint v) { 81 uint value = IERC20_2(udlAssetAddr).balanceOf(address(this)); 82 v = Convert.to18DecimalsBase(udlAssetAddr, value); 83 } 84 85 function totalAccruedFees() external view returns (uint) { 86 87 return _totalAccruedFees; 88 } 89 90 function totalDebt() external view returns (uint) { 91 92 return _totalDebt; 93 } 94 95 function getTotalOwners() external view returns (uint) { 96 return _totalOwners; 97 } 98 99 function getTotalBalance() external view returns (uint) { 100 return _totalBalance; 101 } 102 103 function issueCredit(address to, uint value) external { 104 ensureRehypothicationManagerCaller(); 105 106 //TODO: protocol settings cannot execute this currently, needs to be a proposal? 107 require(msg.sender == address(settings) || msg.sender == to, "not allowed issuer"); 108 issueCreditTokens(to, value); 109 } 110 111 function balanceOf(address owner) external view returns (uint) { 112 113 return balances[owner]; 114 } 115 116 function addBalance(address to, address token, uint value) external { 117 118 addBalance(to, token, value, false); 119 } 120 121 function addBalance(uint value) external { 122 require(creditToken.balanceOf(msg.sender) >= value, "not enought redeemable debt"); 123 addBalance(msg.sender, value); 124 } 125 126 function transferBalance(address from, address to, uint value) external { 127 ensurePrimeCaller(); 128 transferBalanceInternal(from, to, value); 129 } 130 131 function depositTokens(address to, address token, uint value) external { 132 require(token == udlAssetAddr, "not udlAssetAddr"); 133 IERC20_2(token).safeTransferFrom(msg.sender, address(this), value); 134 addBalance(to, token, value, true); 135 emit DepositTokens(to, token, value); 136 } 137 138 139 function swapTokenForCredit(address to, address token, uint value) external { 140 require(token == udlAssetAddr, "not udlAssetAddr"); 141 IERC20_2(token).safeTransferFrom(msg.sender, address(this), value); 142 emit DepositTokens(to, token, value); 143 issueCreditTokens(to, value); 144 } 145 146 function withdrawTokens(address owner, uint value) external { 147 148 ensureRehypothicationManagerCaller(); 149 removeBalance(owner, value); 150 burnDebtAndTransferTokens(owner, value); 151 } 152 153 function swapBalanceForCreditTokens(address owner, uint value) external { 154 155 ensureRehypothicationManagerCaller(); 156 removeBalance(owner, value); 157 issueCreditTokens(owner, value); 158 } 159 160 function grantTokens(address to, uint value) external { 161 162 ensurePrimeCaller(); 163 burnDebtAndTransferTokens(to, value); 164 } 165 166 function calcDebt(address addr) public view returns (uint debt) { 167 168 debt = debts[addr]; 169 if (debt > 0) { 170 debt = settings.applyUnderlyingDebtInterestRate(debt, debtsDate[addr], udlAssetAddr); 171 } 172 } 173 174 function processPayment(address from, address to, uint value) external { 175 ensurePrimeCaller(); 176 177 require(from != to); 178 179 if (value > 0) { 180 181 (uint v, uint b) = settings.getProcessingFee(); 182 if (v > 0) { 183 uint fee = MoreMath.min(value.mul(v).div(b), balances[from]); 184 value = value.sub(fee); 185 addBalance(address(settings), fee); 186 emit AccrueFees(from, fee); 187 } 188 189 uint credit; 190 if (balances[from] < value) { 191 credit = value.sub(balances[from]); 192 value = balances[from]; 193 } 194 195 transferBalanceInternal(from, to, value); 196 197 if (credit > 0) { 198 applyDebtInterestRate(from); 199 setDebt(from, debts[from].add(credit)); 200 addBalance(to, credit); 201 emit AccumulateDebt(to, credit); 202 } 203 } 204 } 205 206 function transferBalanceInternal(address from, address to, uint value) private { 207 208 ensurePrimeCaller(); 209 210 removeBalance(from, value); 211 addBalance(to, value); 212 emit TransferBalance(from, to, value); 213 } 214 215 function addBalance(address to, address token, uint value, bool trusted) private { 216 217 if (value > 0) { 218 219 if (!trusted) { 220 ensurePrimeCaller(); 221 } 222 223 require(token != address(creditToken) || token != udlAssetAddr, "token not allowed"); 224 value = Convert.to18DecimalsBase(token, value); 225 addBalance(to, value); 226 emit TransferBalance(address(0), to, value); 227 } 228 } 229 230 function addBalance(address owner, uint value) private { 231 232 if (value > 0) { 233 234 uint burnt = burnDebt(owner, value); 235 uint v = value.sub(burnt); 236 237 if (balances[owner] == 0) { 238 _totalOwners = _totalOwners.add(1); 239 } 240 241 balances[owner] = balances[owner].add(v); 242 _totalBalance = _totalBalance.add(v); 243 } 244 } 245 246 247 function removeBalance(address owner, uint value) private { 248 249 require(balances[owner] >= value, "insufficient balance"); 250 balances[owner] = balances[owner].sub(value); 251 252 if (value > 0) { 253 _totalBalance = _totalBalance.sub(value); 254 } 255 256 if (_totalOwners > 0 && balances[owner] == 0) { 257 _totalOwners = _totalOwners.sub(1); 258 } 259 } 260 261 function burnDebtAndTransferTokens(address to, uint value) private { 262 263 if (debts[to] > 0) { 264 uint burnt = burnDebt(to, value); 265 value = value.sub(burnt); 266 } 267 268 transferTokens(to, value); 269 } 270 271 function burnDebt(address from, uint value) private returns (uint burnt) { 272 273 uint d = applyDebtInterestRate(from); 274 if (d > 0) { 275 burnt = MoreMath.min(value, d); 276 setDebt(from, d.sub(burnt)); 277 emit BurnDebt(from, burnt); 278 } 279 } 280 281 function applyDebtInterestRate(address owner) private returns (uint debt) { 282 283 uint d = debts[owner]; 284 if (d > 0) { 285 286 debt = calcDebt(owner); 287 288 if (debt > 0 && debt != d) { 289 setDebt(owner, debt); 290 uint diff = debt.sub(d); 291 emit AccumulateDebt(owner, diff); 292 } 293 } 294 } 295 296 function setDebt(address owner, uint value) private { 297 298 if (debts[owner] >= value) { 299 // less debt being set 300 _totalDebt = _totalDebt.sub(debts[owner].sub(value)); 301 } else { 302 // more debt being set 303 _totalDebt = _totalDebt.add(value.sub(debts[owner])); 304 } 305 306 debts[owner] = value; 307 debtsDate[owner] = settings.exchangeTime(); 308 } 309 310 function transferTokens(address to, uint value) private { 311 require(to != address(this) && to != address(creditToken), "invalid token transfer address"); 312 313 IERC20_2 t = IERC20_2(udlAssetAddr); 314 315 uint v = MoreMath.min( 316 value, 317 Convert.to18DecimalsBase(udlAssetAddr, t.balanceOf(address(this))) 318 ); 319 t.safeTransfer( 320 to, 321 Convert.from18DecimalsBase(udlAssetAddr, v) 322 ); 323 emit WithdrawTokens(to, udlAssetAddr, Convert.from18DecimalsBase(udlAssetAddr, v)); 324 value = value.sub(v); 325 326 if (value > 0) { 327 issueCreditTokens(to, value); 328 } 329 } 330 331 function issueCreditTokens(address to, uint value) private { 332 333 (uint r, uint b) = settings.getTokenRate(address(creditToken)); 334 if (b != 0) { 335 value = value.mul(r).div(b); 336 } 337 creditToken.issue(to, value); 338 emit WithdrawTokens(to, address(creditToken), value); 339 } 340 341 function swapStablecoinForUnderlying( 342 address udlCdtp, 343 address[] calldata path, 344 int price, 345 uint balance, 346 uint amountOut 347 ) external { 348 (address _router, address _stablecoin) = settings.getSwapRouterInfo(); 349 require( 350 _router != address(0) && _stablecoin != address(0) && path.length >= 2, 351 "invalid swap router settings/ or path" 352 ); 353 354 (uint amountOut, uint amountInMax) = filterSwapVals(balance, price, path); 355 356 IERC20_2 tk = IERC20_2(path[0]); 357 if (tk.allowance(address(this), _router) > 0) { 358 tk.safeApprove(_router, 0); 359 } 360 tk.safeApprove(_router, amountInMax); 361 362 IUniswapV2Router01(_router).swapTokensForExactTokens( 363 amountOut, 364 amountInMax, 365 path, 366 address(this), 367 settings.exchangeTime() 368 ); 369 370 //send residual stables back to owner 371 uint stableBal = tk.balanceOf(address(this)); 372 if (stableBal > 0) { 373 IERC20_2(path[0]).safeTransfer(exchangeCreditProviderAddr, stableBal); 374 } 375 } 376 377 function filterSwapVals(uint balance, int price, address[] memory path) private view returns (uint amountOut, uint amountInMax) { 378 (uint r, uint b) = settings.getTokenRate(path[0]); 379 uint stableBalance = Convert.from18DecimalsBase(path[0], balance); 380 amountInMax = IUnderlyingVault(vaultAddr).getAmountInMaxInv( 381 price, 382 amountOut, 383 path 384 ); 385 386 if (amountInMax > stableBalance) { 387 amountOut = amountOut.mul(stableBalance).div(amountInMax); 388 amountInMax = stableBalance; 389 } 390 391 amountOut = amountOut.mul(r).div(b); 392 } 393 394 function ensureCaller(address addr) external view { 395 require(primeCallers[addr] == 1, "unauthorized caller (ex)"); 396 } 397 398 function ensureRehypothicationManagerCaller() private view { 399 require(primeCallers[msg.sender] == 1 || settings.isAllowedRehypothicationManager(msg.sender) == true, "unauthorized caller (ex)"); 400 } 401 402 function ensurePrimeCaller() private view { 403 require(primeCallers[msg.sender] == 1, "unauthorized caller (prime)"); 404 } 405 }