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 }