/ contracts / finance / rehypothecation / TellerRehypothecationManager.sol
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  }