/ contracts / finance / credit / UnderlyingCreditToken.sol
UnderlyingCreditToken.sol
  1  pragma solidity >=0.6.0;
  2  pragma experimental ABIEncoderV2;
  3  
  4  import "../../deployment/Deployer.sol";
  5  import "../../utils/ERC20.sol";
  6  import "../../utils/MoreMath.sol";
  7  import "../../utils/Decimal.sol";
  8  import "../../interfaces/IProtocolSettings.sol";
  9  import "../../interfaces/IUnderlyingCreditProvider.sol";
 10  import "../../interfaces/IProposal.sol";
 11  
 12  
 13  contract UnderlyingCreditToken is ERC20 {
 14  
 15      using SafeMath for uint;
 16      using Decimal for Decimal.D256;
 17  
 18      IProtocolSettings private settings;
 19      IUnderlyingCreditProvider private creditProvider;
 20  
 21      mapping(address => uint) private creditDates;
 22  
 23      string private constant _name_prefix = "DeFi Options DAO Credit Token: ";
 24      string private constant _symbol_prefix = "DODv2-CDTK-";
 25  
 26      string private _name;
 27      string private _symbol;
 28  
 29      address private issuer;
 30      address private udlAsset;
 31  
 32      uint private serial;
 33  
 34      constructor(address _deployAddr, address _udlAsset, string memory _nm, string memory _sm) ERC20(string(abi.encodePacked(_name_prefix, _nm))) public {
 35          //DOMAIN_SEPARATOR = ERC20(address(this)).DOMAIN_SEPARATOR();
 36          Deployer deployer = Deployer(_deployAddr);
 37  
 38          settings = IProtocolSettings(deployer.getContractAddress("ProtocolSettings"));
 39          _name = _nm;
 40          _symbol = _sm;
 41          udlAsset = _udlAsset;
 42      }
 43  
 44      function initialize(address underlyingCreditProvider) external {
 45          require(msg.sender == address(settings), "init not allowed");
 46          creditProvider = IUnderlyingCreditProvider(underlyingCreditProvider);
 47          issuer = underlyingCreditProvider;
 48      }
 49  
 50      function name() override external view returns (string memory) {
 51          return string(abi.encodePacked(_name_prefix, _name));
 52      }
 53  
 54      function symbol() override external view returns (string memory) {
 55          return string(abi.encodePacked(_symbol_prefix, _symbol));
 56      }
 57  
 58      function getUdlAsset() external view returns (address) {
 59          return udlAsset;
 60      }
 61  
 62      function issue(address to, uint value) public {
 63  
 64          require(msg.sender == issuer, "issuance unallowed");
 65          addBalance(to, value);
 66          _totalSupply = _totalSupply.add(value);
 67          emitTransfer(address(0), to, value);
 68      }
 69  
 70      function balanceOf(address owner) override public view returns (uint bal) {
 71  
 72          bal = 0;
 73          if (balances[owner] > 0) {
 74              bal = settings.applyUnderlyingCreditInterestRate(balances[owner], creditDates[owner], udlAsset);
 75          }
 76      }
 77  
 78      function requestWithdraw() external {
 79          uint b = creditProvider.totalTokenStock();
 80          uint bC = creditProvider.getTotalBalance();
 81  
 82          require(b > 0, "CDTK: please wait to redeem");
 83          /*
 84              this is to avoid looping over credit dates and indivual balances, may need to use earliest credit date?
 85                  - may need a linked listed storing credit date reference?
 86          */
 87          uint theoreticalMaxBal = settings.applyUnderlyingCreditInterestRate(_totalSupply, creditDates[msg.sender], udlAsset);
 88  
 89          if (b > bC.add(theoreticalMaxBal)) {
 90              withdrawTokens(msg.sender, balanceOf(msg.sender));
 91          } else {
 92              uint diffCreditTime = settings.exchangeTime().sub(creditDates[msg.sender]);
 93              require(diffCreditTime > settings.getCreditWithdrawlTimeLock() && creditDates[msg.sender] != 0, "CDTK: Must wait until time lock has passed");
 94              Decimal.D256 memory withdrawalPct = Decimal.ratio(balanceOf(msg.sender), b);
 95              Decimal.D256 memory udlTokenPct = Decimal.ratio(b, bC.add(theoreticalMaxBal));
 96              uint currWitdrawalLimit = withdrawalPct.mul(udlTokenPct).mul(b).asUint256();
 97              require(currWitdrawalLimit > 0, "CDTK: please wait to redeem");
 98              withdrawTokens(msg.sender, currWitdrawalLimit);
 99          }
100      }
101  
102      function swapForExchangeBalance(uint value) external {
103          value = MoreMath.min(balanceOf(msg.sender), value);
104          removeBalance(msg.sender, value);
105          creditProvider.addBalance(value);
106          emitTransfer(msg.sender, address(0), value);
107      }
108  
109      function addBalance(address owner, uint value) override internal {
110  
111          updateBalance(owner);
112          balances[owner] = balances[owner].add(value);
113      }
114  
115  
116      function burnBalance(uint value) external {
117          updateBalance(msg.sender);
118          balances[msg.sender] = balances[msg.sender].sub(value);
119      }
120  
121      function removeBalance(address owner, uint value) override internal {
122  
123          updateBalance(owner);
124          balances[owner] = balances[owner].sub(value);
125      }
126  
127      function updateBalance(address owner) private {
128  
129          uint nb = balanceOf(owner);
130          uint accrued = nb.sub(balances[owner]);
131          _totalSupply = _totalSupply.add(accrued);
132          balances[owner] = nb;
133          creditDates[owner] = settings.exchangeTime();
134  
135          if (accrued > 0) {
136              emitTransfer(address(0), owner, accrued);
137          }
138      }
139  
140      function withdrawTokens(address owner, uint value) private returns(uint sent, bool dequeue) {
141  
142          if (value > 0) {
143  
144              value = MoreMath.min(balanceOf(owner), value);
145              uint b = creditProvider.totalTokenStock();
146  
147              if (b >= value) {
148                  sent = value;
149                  dequeue = true;
150              } else {
151                  sent = b;
152              }
153  
154              if (sent > 0) {
155                  removeBalance(owner, sent);
156                  creditProvider.grantTokens(owner, sent);
157                  emitTransfer(owner, address(0), value);
158              }
159          }
160      }
161  }