/ contracts / finance / credit / UnderlyingCreditProvider.sol
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  }