TellerRehypothecationManager.sol
1 pragma solidity >=0.6.0; 2 pragma experimental ABIEncoderV2; 3 4 5 import "./BaseRehypothecationManager.sol"; 6 import "../../interfaces/UnderlyingFeed.sol"; 7 import "../../interfaces/IUnderlyingCreditToken.sol"; 8 import "../../interfaces/IBaseHedgingManager.sol"; 9 import "../../interfaces/IUnderlyingCreditProvider.sol"; 10 import "../../interfaces/external/teller/ITellerInterface.sol"; 11 12 contract TellerRehypothecationManager is BaseRehypothecationManager { 13 uint constant _volumeBase = 1e18; 14 15 mapping(address => mapping(address => mapping(address => uint256))) lenderCommitmentIdMap; 16 mapping(address => mapping(address => mapping(address => uint256))) borrowerBidIdMap; 17 mapping(address => mapping(address => mapping(address => uint256))) notionalExposureMap; 18 mapping(address => mapping(address => mapping(address => uint256))) notionalExposureInExchangeBalMap; 19 mapping(address => mapping(address => mapping(address => uint256))) collateralAmountMap; 20 21 //TODO: need to set to propper addr 22 address tellerInterfaceAddr = address(0); 23 24 function notionalExposure(address account, address asset, address collateral) override external view returns (uint256) { 25 return notionalExposureInExchangeBalMap[account][asset][collateral]; 26 } 27 28 function borrowExposure(address account, address asset, address collateral) override external view returns (uint256) { 29 return notionalExposureMap[account][asset][collateral]; 30 } 31 32 function lend(address asset, address collateral, uint assetAmount, uint collateralAmount, address udlFeed) override external { 33 34 require( 35 settings.isAllowedHedgingManager(msg.sender) == true, 36 "not allowed hedging manager" 37 ); 38 39 //https://docs.teller.org/teller-v2-protocol/l96ARgEDQcTgx4muwINt/personas/lenders/create-commitment 40 41 require(lenderCommitmentIdMap[msg.sender][asset][collateral] == 0, "already lending"); 42 uint notional; 43 44 if (collateral == address(exchange)) { 45 //non stable lending (lev short), can only hedge against udl credit (collateral == exchange balance, asset == udl credit) 46 require( 47 UnderlyingFeed(udlFeed).getUnderlyingAddr() == IUnderlyingCreditToken(asset).getUdlAsset(), 48 "bad udlFeed" 49 ); 50 uint256 udlAssetBal = IUnderlyingCreditProvider(asset).balanceOf(address(this)); 51 52 (,int udlPrice) = UnderlyingFeed(udlFeed).getLatestPrice(); 53 uint256 udlNotionalAmount = assetAmount.mul(_volumeBase).div(uint256(udlPrice)); 54 55 //assetAmount / price = collateralAmount * leverage 56 57 //before loan request 58 if (udlAssetBal >= udlNotionalAmount){ 59 IUnderlyingCreditProvider(asset).swapBalanceForCreditTokens(address(this), udlNotionalAmount); 60 } else { 61 if (udlAssetBal > 0) { 62 IUnderlyingCreditProvider(asset).swapBalanceForCreditTokens(address(this), udlAssetBal); 63 } 64 IUnderlyingCreditProvider(asset).issueCredit(address(this), udlNotionalAmount.sub(udlAssetBal)); 65 66 } 67 68 notional = udlNotionalAmount; 69 } else { 70 //stable lending (lev long), can only hedge against exchange balance (collateral == udl credit, asset == exchange balance) 71 72 uint256 assetBal = IERC20_2(asset).balanceOf(address(this)); 73 //before loan request 74 if (assetBal < assetAmount){ 75 creditProvider.issueCredit(address(this), assetAmount.sub(assetBal)); 76 creditToken.swapForExchangeBalance(assetAmount.sub(assetBal)); 77 } 78 79 notional = assetAmount; 80 } 81 82 //handle approval for commitment 83 IERC20_2 tk = IERC20_2(asset); 84 if (tk.allowance(address(this), tellerInterfaceAddr) > 0) { 85 tk.safeApprove(tellerInterfaceAddr, 0); 86 } 87 tk.safeApprove(tellerInterfaceAddr, notional); 88 89 /** 90 * @notice Creates a loan commitment from a lender for a market. 91 * @param _commitment The new commitment data expressed as a struct 92 * @param _borrowerAddressList The array of borrowers that are allowed to accept loans using this commitment 93 * @return commitmentId_ returns the commitmentId for the created commitment 94 */ 95 96 ITellerInterface.Commitment memory _commitment; 97 98 _commitment.maxPrincipal = assetAmount;//uint256 maxPrincipal; 99 _commitment.expiration = 7 days;//uint32 expiration; 100 _commitment.maxDuration = 7 days;//uint32 maxDuration;7 days on polygon with marketID 33 101 _commitment.minInterestRate = 0;//uint16 minInterestRate; 102 _commitment.collateralTokenAddress = collateral;//address collateralTokenAddress; 103 _commitment.collateralTokenId = 0;//uint256 collateralTokenId; 104 _commitment.maxPrincipalPerCollateralAmount = assetAmount.div(collateralAmount);//uint256 maxPrincipalPerCollateralAmount; 105 _commitment.collateralTokenType = ITellerInterface.CommitmentCollateralType.ERC20;//CommitmentCollateralType collateralTokenType; 106 _commitment.lender = address(this);//address lender; 107 _commitment.marketId = 33;//uint256 marketId;33 on polygon 108 _commitment.principalTokenAddress = asset;//address principalTokenAddress; 109 110 address[] memory _borrowerAddressList = new address[](2); 111 _borrowerAddressList[0] = address(this); 112 _borrowerAddressList[1] = msg.sender; 113 114 uint256 commitmentId_ = ITellerInterface(tellerInterfaceAddr).createCommitment( 115 _commitment, 116 _borrowerAddressList 117 ); 118 119 lenderCommitmentIdMap[msg.sender][asset][collateral] = commitmentId_; 120 collateralAmountMap[msg.sender][asset][collateral] = collateralAmount; 121 } 122 123 function withdraw(address asset, address collateral, uint amount) override external {} 124 125 function borrow(address asset, address collateral, uint assetAmount, uint collateralAmount, address udlFeed) override external { 126 require( 127 settings.isAllowedHedgingManager(msg.sender) == true, 128 "not allowed hedging manager" 129 ); 130 131 //https://docs.teller.org/teller-v2-protocol/l96ARgEDQcTgx4muwINt/personas/borrowers/accept-commitment 132 /** 133 * @notice Accept the commitment to submitBid and acceptBid using the funds 134 * @dev LoanDuration must be longer than the market payment cycle 135 * @param _commitmentId The id of the commitment being accepted. 136 * @param _principalAmount The amount of currency to borrow for the loan. 137 * @param _collateralAmount The amount of collateral to use for the loan. 138 * @param _collateralTokenId The tokenId of collateral to use for the loan if ERC721 or ERC1155. 139 * @param _collateralTokenAddress The contract address to use for the loan collateral token.s 140 * @param _interestRate The interest rate APY to use for the loan in basis points. 141 * @param _loanDuration The overall duratiion for the loan. Must be longer than market payment cycle duration. 142 * @return bidId The ID of the loan that was created on TellerV2 143 */ 144 require(lenderCommitmentIdMap[msg.sender][asset][collateral] > 0, "no outstanding loan"); 145 require(borrowerBidIdMap[msg.sender][asset][collateral] == 0, "already borrowing"); 146 147 require( 148 UnderlyingFeed(udlFeed).getUnderlyingAddr() == IUnderlyingCreditToken(asset).getUdlAsset(), 149 "bad udlFeed" 150 ); 151 152 (,int udlPrice) = UnderlyingFeed(udlFeed).getLatestPrice(); 153 154 if (collateral == address(exchange)) { 155 //(collateral == exchange balance, asset == udl credit) 156 IERC20_2(collateral).safeTransferFrom( 157 msg.sender, 158 address(this), 159 collateralAmount 160 ); 161 } else { 162 //(collateral == udl credit, asset == exchange balance) 163 uint256 collateralAmountInAsset = collateralAmount.mul(uint256(udlPrice)).div(_volumeBase); 164 //assetAmount / leverage = collateralAmount * price 165 166 IERC20_2(asset).safeTransferFrom( 167 msg.sender, 168 address(this), 169 collateralAmountInAsset 170 ); 171 uint256 udlAssetBal = IUnderlyingCreditProvider(collateral).balanceOf(address(this)); 172 //before borrow request, mint or credit the difference differing in collateral to rehypo manager 173 if (udlAssetBal >= collateralAmount){ 174 IUnderlyingCreditProvider(asset).swapBalanceForCreditTokens(address(this), collateralAmount); 175 } else { 176 if (udlAssetBal > 0) { 177 IUnderlyingCreditProvider(asset).swapBalanceForCreditTokens(address(this), udlAssetBal); 178 } 179 IUnderlyingCreditProvider(asset).issueCredit(address(this), collateralAmount.sub(udlAssetBal)); 180 } 181 } 182 183 uint256 _collateralTokenId = 0;//0 for erc20's 184 uint32 _loanDuration = 7 days;// 185 186 uint256 _bidId = ITellerInterface(tellerInterfaceAddr).acceptCommitment( 187 lenderCommitmentIdMap[msg.sender][asset][collateral], 188 assetAmount, 189 collateralAmount, 190 _collateralTokenId,//0 for erc20's 191 collateral, 192 0, 193 _loanDuration 194 ); 195 196 if (collateral == address(exchange)) { 197 //(collateral == exchange balance, asset == udl credit) 198 /* 199 200 - after borrow request 201 - swap udl credit borrowed for exchange balance at orace rate interally with agaisnt rehypo manager 202 - mint exchange balance to rehypo manager -> transfer to pool hedging manager 203 - rehypo manage keeps udl credit token 204 205 */ 206 uint256 collateralBal = IERC20_2(collateral).balanceOf(address(this)); 207 uint256 assetAmountInCollateral = assetAmount.mul(uint256(udlPrice)).div(_volumeBase); 208 if (collateralBal < assetAmountInCollateral){ 209 creditProvider.issueCredit(address(this), assetAmountInCollateral.sub(collateralBal)); 210 creditToken.swapForExchangeBalance(assetAmountInCollateral.sub(collateralBal)); 211 } 212 213 IERC20_2(collateral).safeTransfer(msg.sender, assetAmountInCollateral); 214 notionalExposureInExchangeBalMap[msg.sender][asset][collateral] = assetAmountInCollateral; 215 } else { 216 //(collateral == udl credit, asset == exchange balance) 217 /* 218 - after borrow request 219 - swap exchange balance borrowed for udl credit at orace rate interally with agaisnt rehypo manager 220 - withdraw (or mint the amount short) udl credit to rehypo manager -> transfer to pool hedging manager 221 - rehypo manage keeps exchange balance 222 */ 223 224 uint256 udlAssetBal = IERC20_2(collateral).balanceOf(address(this)); 225 uint256 collateralAmountInAsset = assetAmount.mul(_volumeBase).div(uint256(udlPrice)); 226 if (udlAssetBal >= collateralAmountInAsset){ 227 IUnderlyingCreditProvider(asset).swapBalanceForCreditTokens(address(this), collateralAmountInAsset); 228 } else { 229 if (udlAssetBal > 0) { 230 IUnderlyingCreditProvider(collateral).swapBalanceForCreditTokens(address(this), udlAssetBal); 231 } 232 IUnderlyingCreditProvider(collateral).issueCredit(address(this), collateralAmountInAsset.sub(udlAssetBal)); 233 } 234 235 IERC20_2(collateral).safeTransfer(msg.sender, collateralAmountInAsset); 236 notionalExposureInExchangeBalMap[msg.sender][asset][collateral] = assetAmount; 237 } 238 239 240 241 borrowerBidIdMap[msg.sender][asset][collateral] = _bidId; 242 notionalExposureMap[msg.sender][asset][collateral] = assetAmount; 243 } 244 245 function repay(address asset, address collateral, address udlFeed) override external { 246 //https://docs.teller.org/teller-v2-protocol/l96ARgEDQcTgx4muwINt/personas/borrowers/repay-loan 247 /** 248 * @notice Function for users to repay an active loan in full. 249 * @param _bidId The id of the loan to make the payment towards. 250 */ 251 252 require(lenderCommitmentIdMap[msg.sender][asset][collateral] > 0, "no outstanding loan"); 253 require(borrowerBidIdMap[msg.sender][asset][collateral] > 0, "no outstanding borrow"); 254 255 (,int udlPrice) = UnderlyingFeed(udlFeed).getLatestPrice(); 256 257 ITellerInterface.Bid memory bid = ITellerInterface(tellerInterfaceAddr).bids(borrowerBidIdMap[msg.sender][asset][collateral]); 258 259 if (collateral == address(exchange)) { 260 //(collateral == exchange balance, asset == udl credit) 261 uint256 transferAmountInCollateral = bid.loanDetails.principal.mul(uint(udlPrice)).div(_volumeBase); 262 IERC20_2(collateral).safeTransferFrom( 263 msg.sender, 264 address(this), 265 transferAmountInCollateral 266 ); 267 } else { 268 //(collateral == udl credit, asset == exchange balance) 269 270 uint256 transferAmountInAsset = notionalExposureMap[msg.sender][asset][collateral].mul(uint(udlPrice)).div(_volumeBase); 271 uint256 udlCreditBal = IERC20_2(collateral).balanceOf(msg.sender); 272 uint256 udlCreditBalInAsset = udlCreditBal.mul(uint(udlPrice)).div(_volumeBase); 273 uint256 assetBal = IERC20_2(asset).balanceOf(msg.sender); 274 IERC20_2(collateral).safeTransferFrom( 275 msg.sender, 276 address(this), 277 udlCreditBal 278 ); 279 280 uint256 diffAmountInExchangeBalance; 281 282 if (udlCreditBalInAsset >= transferAmountInAsset) { 283 //transfer all udl credit bal, swap surplus into exchange bal, credit hedging manager for exchange bal diff 284 diffAmountInExchangeBalance = udlCreditBalInAsset.sub(transferAmountInAsset); 285 if (assetBal < diffAmountInExchangeBalance){ 286 creditProvider.issueCredit(address(this), diffAmountInExchangeBalance.sub(assetBal)); 287 creditToken.swapForExchangeBalance(diffAmountInExchangeBalance.sub(assetBal)); 288 } 289 IERC20_2(asset).safeTransfer(msg.sender, diffAmountInExchangeBalance); 290 } else { 291 //transfer all, compute shortage amount, debit pool owner for exchange bal diff (protocol fees are charged for shortages) 292 diffAmountInExchangeBalance = transferAmountInAsset.sub(udlCreditBalInAsset); 293 creditProvider.processPayment(IBaseHedgingManager(msg.sender).pool(), address(this), diffAmountInExchangeBalance); 294 } 295 } 296 297 ITellerInterface(tellerInterfaceAddr).repayLoanFull(borrowerBidIdMap[msg.sender][asset][collateral]); 298 299 /** 300 * @notice Withdraws deposited collateral from the created escrow of a bid that has been successfully repaid. 301 * @param _bidId The id of the bid to withdraw collateral for. 302 */ 303 ITellerInterface(tellerInterfaceAddr).withdraw(borrowerBidIdMap[msg.sender][asset][collateral]); 304 305 if (collateral == address(exchange)) { 306 //(collateral == exchange balance, asset == udl credit) 307 //burn udl credit value 308 IUnderlyingCreditToken(address(bid.loanDetails.lendingToken)).burnBalance(bid.loanDetails.principal); 309 //rehypo manager transfers exchanage balance collateral to hedging manager 310 IERC20_2(collateral).safeTransfer(msg.sender, collateralAmountMap[msg.sender][asset][collateral]); 311 } else { 312 //(collateral == udl credit, asset == exchange balance) 313 //burn exchange balance in excess of collateral value 314 creditToken.burnBalance(bid.loanDetails.principal); 315 //burn udl credit of collateral value 316 IUnderlyingCreditToken(collateral).burnBalance(collateralAmountMap[msg.sender][asset][collateral]); 317 //transfers exchanage balance collateral to hedging manager 318 IERC20_2(asset).safeTransfer( 319 msg.sender, 320 collateralAmountMap[msg.sender][asset][collateral].mul(uint(udlPrice)).div(_volumeBase) 321 ); 322 } 323 324 borrowerBidIdMap[msg.sender][asset][collateral] = 0; 325 lenderCommitmentIdMap[msg.sender][asset][collateral] = 0; 326 notionalExposureMap[msg.sender][asset][collateral] = 0; 327 collateralAmountMap[msg.sender][asset][collateral] = 0; 328 } 329 330 function transferTokensToCreditProvider(address tokenAddr) override external {} 331 332 function transferTokensToVault(address tokenAddr) override external {} 333 }