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