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 }