mirror of
				https://github.com/Instadapp/dsa-connectors.git
				synced 2024-07-29 22:37:00 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			814 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Solidity
		
	
	
	
	
	
			
		
		
	
	
			814 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Solidity
		
	
	
	
	
	
| // SPDX-License-Identifier: MIT
 | |
| pragma solidity ^0.7.6;
 | |
| pragma abicoder v2;
 | |
| 
 | |
| /**
 | |
|  * @title Notional
 | |
|  * @dev Fixed Rate Lending and Borrowing
 | |
|  */
 | |
| 
 | |
| import { Helpers } from "./helpers.sol";
 | |
| import { Events } from "./events.sol";
 | |
| import { DepositActionType, BalanceActionWithTrades, BalanceAction } from "./interface.sol";
 | |
| import { TokenInterface } from "../../common/interfaces.sol";
 | |
| 
 | |
| abstract contract NotionalResolver is Events, Helpers {
 | |
| 	/**
 | |
| 	 * @notice Deposit collateral into Notional, this should only be used for reducing risk of
 | |
| 	 * liquidation.
 | |
| 	 *  @dev Deposits into Notional are not earning fixed rates, they are earning the cToken
 | |
| 	 * lending rate. In order to lend at fixed rates use `depositAndLend`
 | |
| 	 * @param currencyId notional defined currency id to deposit
 | |
| 	 * @param useUnderlying if true, will accept a deposit in the underlying currency (i.e DAI), if false
 | |
| 	 * will use the asset currency (i.e. cDAI)
 | |
| 	 * @param depositAmount amount of tokens to deposit
 | |
| 	 * @param getId id of depositAmount
 | |
| 	 * @param setId id to set the value of notional cash deposit increase (denominated in asset cash, i.e. cDAI)
 | |
| 	 */
 | |
| 	function depositCollateral(
 | |
| 		uint16 currencyId,
 | |
| 		bool useUnderlying,
 | |
| 		uint256 depositAmount,
 | |
| 		uint256 getId,
 | |
| 		uint256 setId
 | |
| 	)
 | |
| 		external
 | |
| 		payable
 | |
| 		returns (string memory _eventName, bytes memory _eventParam)
 | |
| 	{
 | |
| 		depositAmount = getDepositAmountAndSetApproval(
 | |
| 			getId,
 | |
| 			currencyId,
 | |
| 			useUnderlying,
 | |
| 			depositAmount
 | |
| 		);
 | |
| 
 | |
| 		uint256 assetCashDeposited;
 | |
| 		if (useUnderlying && currencyId == ETH_CURRENCY_ID) {
 | |
| 			assetCashDeposited = notional.depositUnderlyingToken{
 | |
| 				value: depositAmount
 | |
| 			}(address(this), currencyId, depositAmount);
 | |
| 		} else if (useUnderlying) {
 | |
| 			assetCashDeposited = notional.depositUnderlyingToken(
 | |
| 				address(this),
 | |
| 				currencyId,
 | |
| 				depositAmount
 | |
| 			);
 | |
| 		} else {
 | |
| 			assetCashDeposited = notional.depositAssetToken(
 | |
| 				address(this),
 | |
| 				currencyId,
 | |
| 				depositAmount
 | |
| 			);
 | |
| 		}
 | |
| 
 | |
| 		setUint(setId, assetCashDeposited);
 | |
| 
 | |
| 		_eventName = "LogDepositCollateral(address,uint16,bool,uint256,uint256)";
 | |
| 		_eventParam = abi.encode(
 | |
| 			address(this),
 | |
| 			currencyId,
 | |
| 			useUnderlying,
 | |
| 			depositAmount,
 | |
| 			assetCashDeposited
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @notice Withdraw collateral from Notional
 | |
| 	 * @dev This spell allows users to withdraw collateral from Notional
 | |
| 	 * @param currencyId notional defined currency id to withdraw
 | |
| 	 * @param redeemToUnderlying if true, will redeem the amount withdrawn to the underlying currency (i.e. DAI),
 | |
| 	 * if false, will simply withdraw the asset token (i.e. cDAI)
 | |
| 	 * @param withdrawAmount amount of tokens to withdraw, denominated in asset tokens (i.e. cDAI)
 | |
| 	 * @param getId id of withdraw amount
 | |
| 	 * @param setId id to set the value of amount withdrawn, if redeemToUnderlying this amount will be in underlying
 | |
| 	 * (i.e. DAI), if not redeemToUnderlying this amount will be asset tokens (i.e. cDAI)
 | |
| 	 */
 | |
| 	function withdrawCollateral(
 | |
| 		uint16 currencyId,
 | |
| 		bool redeemToUnderlying,
 | |
| 		uint256 withdrawAmount,
 | |
| 		uint256 getId,
 | |
| 		uint256 setId
 | |
| 	)
 | |
| 		external
 | |
| 		payable
 | |
| 		returns (string memory _eventName, bytes memory _eventParam)
 | |
| 	{
 | |
| 		withdrawAmount = getUint(getId, withdrawAmount);
 | |
| 		uint88 amountInternalPrecision = withdrawAmount == type(uint256).max
 | |
| 			? toUint88(getCashOrNTokenBalance(currencyId, false))
 | |
| 			: toUint88(convertToInternal(currencyId, withdrawAmount));
 | |
| 
 | |
| 		uint256 amountWithdrawn = notional.withdraw(
 | |
| 			currencyId,
 | |
| 			amountInternalPrecision,
 | |
| 			redeemToUnderlying
 | |
| 		);
 | |
| 		// Sets the amount of tokens withdrawn to address(this), Notional returns this value
 | |
| 		// in the native precision of the token that was withdrawn
 | |
| 		setUint(setId, amountWithdrawn);
 | |
| 
 | |
| 		_eventName = "LogWithdrawCollateral(address,uint16,bool,uint256)";
 | |
| 		_eventParam = abi.encode(
 | |
| 			address(this),
 | |
| 			currencyId,
 | |
| 			redeemToUnderlying,
 | |
| 			amountWithdrawn
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @notice Claims NOTE tokens and transfers to the address
 | |
| 	 * @dev This spell allows users to claim nToken incentives
 | |
| 	 * @param setId the id to set the balance of NOTE tokens claimed
 | |
| 	 */
 | |
| 	function claimNOTE(uint256 setId)
 | |
| 		external
 | |
| 		payable
 | |
| 		returns (string memory _eventName, bytes memory _eventParam)
 | |
| 	{
 | |
| 		uint256 notesClaimed = notional.nTokenClaimIncentives();
 | |
| 		setUint(setId, notesClaimed);
 | |
| 
 | |
| 		_eventName = "LogClaimNOTE(address,uint256)";
 | |
| 		_eventParam = abi.encode(address(this), notesClaimed);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @notice Redeem nTokens allowing for accepting of fCash residuals
 | |
| 	 * @dev This spell allows users to redeem nTokens even when there are fCash residuals that
 | |
| 	 * cannot be sold when markets are at extremely high utilization
 | |
| 	 * @param currencyId notional defined currency id of nToken
 | |
| 	 * @param sellTokenAssets set to false to accept fCash residuals into portfolio, set to true will
 | |
| 	 * sell fCash residuals back to cash
 | |
| 	 * @param tokensToRedeem amount of nTokens to redeem
 | |
| 	 * @param getId id of amount of tokens to redeem
 | |
| 	 * @param setId id to set amount of asset cash from redeem
 | |
| 	 */
 | |
| 	function redeemNTokenRaw(
 | |
| 		uint16 currencyId,
 | |
| 		bool sellTokenAssets,
 | |
| 		uint96 tokensToRedeem,
 | |
| 		bool acceptResidualAssets,
 | |
| 		uint256 getId,
 | |
| 		uint256 setId
 | |
| 	)
 | |
| 		external
 | |
| 		payable
 | |
| 		returns (string memory _eventName, bytes memory _eventParam)
 | |
| 	{
 | |
| 		tokensToRedeem = getNTokenRedeemAmount(
 | |
| 			currencyId,
 | |
| 			tokensToRedeem,
 | |
| 			getId
 | |
| 		);
 | |
| 
 | |
| 		int256 _assetCashChange = notional.nTokenRedeem(
 | |
| 			address(this),
 | |
| 			currencyId,
 | |
| 			tokensToRedeem,
 | |
| 			sellTokenAssets,
 | |
| 			acceptResidualAssets
 | |
| 		);
 | |
| 
 | |
| 		// Floor asset cash change at zero in order to properly set the uint. If the asset cash change is negative
 | |
| 		// (this will almost certainly never happen), then no withdraw is possible.
 | |
| 		uint256 assetCashChange = _assetCashChange > 0
 | |
| 			? uint256(_assetCashChange)
 | |
| 			: 0;
 | |
| 
 | |
| 		setUint(setId, assetCashChange);
 | |
| 
 | |
| 		_eventName = "LogRedeemNTokenRaw(address,uint16,bool,uint96,int256)";
 | |
| 		_eventParam = abi.encode(
 | |
| 			address(this),
 | |
| 			currencyId,
 | |
| 			sellTokenAssets,
 | |
| 			tokensToRedeem,
 | |
| 			assetCashChange
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @notice Redeems nTokens to cash and withdraws the resulting cash
 | |
| 	 * @dev Also possible to use redeemNTokenRaw and withdrawCollateral to achieve the same
 | |
| 	 * result but this is more gas efficient, it does it in one call to Notional
 | |
| 	 * @param currencyId notional defined currency id of nToken
 | |
| 	 * @param tokensToRedeem amount of nTokens to redeem
 | |
| 	 * @param amountToWithdraw amount of asset cash to withdraw, if set to uint(-1) then will withdraw the
 | |
| 	 * entire cash balance in notional
 | |
| 	 * @param redeemToUnderlying if true, will redeem the asset cash withdrawn to underlying tokens
 | |
| 	 * @param getId id of amount of tokens to redeem
 | |
| 	 * @param setId id to set amount of asset cash or underlying tokens withdrawn
 | |
| 	 */
 | |
| 	function redeemNTokenAndWithdraw(
 | |
| 		uint16 currencyId,
 | |
| 		uint96 tokensToRedeem,
 | |
| 		uint256 amountToWithdraw,
 | |
| 		bool redeemToUnderlying,
 | |
| 		uint256 getId,
 | |
| 		uint256 setId
 | |
| 	)
 | |
| 		external
 | |
| 		payable
 | |
| 		returns (string memory _eventName, bytes memory _eventParam)
 | |
| 	{
 | |
| 		tokensToRedeem = getNTokenRedeemAmount(
 | |
| 			currencyId,
 | |
| 			tokensToRedeem,
 | |
| 			getId
 | |
| 		);
 | |
| 
 | |
| 		BalanceAction[] memory action = new BalanceAction[](1);
 | |
| 		action[0].actionType = DepositActionType.RedeemNToken;
 | |
| 		action[0].currencyId = currencyId;
 | |
| 		action[0].depositActionAmount = tokensToRedeem;
 | |
| 		action[0].redeemToUnderlying = redeemToUnderlying;
 | |
| 		if (amountToWithdraw == type(uint256).max) {
 | |
| 			// This setting will override the withdrawAmountInternalPrecision
 | |
| 			action[0].withdrawEntireCashBalance = true;
 | |
| 		} else {
 | |
| 			action[0].withdrawAmountInternalPrecision = amountToWithdraw;
 | |
| 		}
 | |
| 
 | |
| 		executeActionWithBalanceChange(
 | |
| 			action,
 | |
| 			0,
 | |
| 			currencyId,
 | |
| 			redeemToUnderlying,
 | |
| 			setId
 | |
| 		);
 | |
| 
 | |
| 		_eventName = "LogRedeemNTokenWithdraw(address,uint16,uint96,uint256,bool)";
 | |
| 		_eventParam = abi.encode(
 | |
| 			address(this),
 | |
| 			currencyId,
 | |
| 			tokensToRedeem,
 | |
| 			amountToWithdraw,
 | |
| 			redeemToUnderlying
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @notice Redeems nTokens and uses the cash to repay a borrow.
 | |
| 	 * @dev When specifying fCashAmount be sure to calculate it such that the account
 | |
| 	 * has enough cash after redeeming nTokens to pay down the debt. This can be done
 | |
| 	 * off-chain using the Notional SDK.
 | |
| 	 * @param currencyId notional defined currency id of nToken
 | |
| 	 * @param tokensToRedeem amount of nTokens to redeem
 | |
| 	 * @param marketIndex the market index that references where the account will lend
 | |
| 	 * @param fCashAmount amount of fCash to lend into the market (this has the effect or repaying
 | |
| 	 * the borrowed cash at current market rates), the corresponding amount of cash will be taken
 | |
| 	 * from the account after redeeming nTokens.
 | |
| 	 * @param minLendRate minimum rate where the user will lend, if the rate is lower will revert
 | |
| 	 * @param getId id of amount of tokens to redeem
 | |
| 	 */
 | |
| 	function redeemNTokenAndDeleverage(
 | |
| 		uint16 currencyId,
 | |
| 		uint96 tokensToRedeem,
 | |
| 		uint8 marketIndex,
 | |
| 		uint88 fCashAmount,
 | |
| 		uint32 minLendRate,
 | |
| 		uint256 getId
 | |
| 	)
 | |
| 		external
 | |
| 		payable
 | |
| 		returns (string memory _eventName, bytes memory _eventParam)
 | |
| 	{
 | |
| 		tokensToRedeem = getNTokenRedeemAmount(
 | |
| 			currencyId,
 | |
| 			tokensToRedeem,
 | |
| 			getId
 | |
| 		);
 | |
| 
 | |
| 		BalanceActionWithTrades[] memory action = new BalanceActionWithTrades[](
 | |
| 			1
 | |
| 		);
 | |
| 		action[0].actionType = DepositActionType.RedeemNToken;
 | |
| 		action[0].currencyId = currencyId;
 | |
| 		action[0].depositActionAmount = tokensToRedeem;
 | |
| 		// Withdraw amount, withdraw cash balance and redeemToUnderlying are all 0 or false
 | |
| 
 | |
| 		bytes32[] memory trades = new bytes32[](1);
 | |
| 		trades[0] = encodeLendTrade(marketIndex, fCashAmount, minLendRate);
 | |
| 		action[0].trades = trades;
 | |
| 
 | |
| 		notional.batchBalanceAndTradeAction(address(this), action);
 | |
| 
 | |
| 		_eventName = "LogRedeemNTokenAndDeleverage(address,uint16,uint96,uint8,uint88)";
 | |
| 		_eventParam = abi.encode(
 | |
| 			address(this),
 | |
| 			currencyId,
 | |
| 			tokensToRedeem,
 | |
| 			marketIndex,
 | |
| 			fCashAmount
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @notice Deposit asset or underlying tokens and mint nTokens in a single transaction
 | |
| 	 * @dev This spell allows users to deposit and mint nTokens (providing liquidity)
 | |
| 	 * @param currencyId notional defined currency id to deposit
 | |
| 	 * @param depositAmount amount of tokens to deposit
 | |
| 	 * @param useUnderlying if true, will accept a deposit in the underlying currency (i.e DAI), if false
 | |
| 	 * will use the asset currency (i.e. cDAI)
 | |
| 	 * @param getId id of depositAmount
 | |
| 	 * @param setId id to set the value of nToken balance change
 | |
| 	 */
 | |
| 	function depositAndMintNToken(
 | |
| 		uint16 currencyId,
 | |
| 		uint256 depositAmount,
 | |
| 		bool useUnderlying,
 | |
| 		uint256 getId,
 | |
| 		uint256 setId
 | |
| 	)
 | |
| 		external
 | |
| 		payable
 | |
| 		returns (string memory _eventName, bytes memory _eventParam)
 | |
| 	{
 | |
| 		depositAmount = getDepositAmountAndSetApproval(
 | |
| 			getId,
 | |
| 			currencyId,
 | |
| 			useUnderlying,
 | |
| 			depositAmount
 | |
| 		);
 | |
| 
 | |
| 		BalanceAction[] memory action = new BalanceAction[](1);
 | |
| 		action[0].actionType = useUnderlying
 | |
| 			? DepositActionType.DepositUnderlyingAndMintNToken
 | |
| 			: DepositActionType.DepositAssetAndMintNToken;
 | |
| 		action[0].currencyId = currencyId;
 | |
| 		action[0].depositActionAmount = depositAmount;
 | |
| 		// withdraw amount, withdraw cash and redeem to underlying are all 0 and false
 | |
| 
 | |
| 		uint256 nTokenBefore = getCashOrNTokenBalance(currencyId, true);
 | |
| 		uint256 msgValue = getMsgValue(
 | |
| 			currencyId,
 | |
| 			useUnderlying,
 | |
| 			depositAmount
 | |
| 		);
 | |
| 
 | |
| 		notional.batchBalanceAction{ value: msgValue }(address(this), action);
 | |
| 
 | |
| 		uint256 nTokenBalanceChange = sub(
 | |
| 			getCashOrNTokenBalance(currencyId, true),
 | |
| 			nTokenBefore
 | |
| 		);
 | |
| 
 | |
| 		if (setId != 0) {
 | |
| 			// Set the amount of nTokens minted
 | |
| 			setUint(setId, uint256(nTokenBalanceChange));
 | |
| 		}
 | |
| 
 | |
| 		_eventName = "LogDepositAndMintNToken(address,uint16,bool,uint256,int256)";
 | |
| 		_eventParam = abi.encode(
 | |
| 			address(this),
 | |
| 			currencyId,
 | |
| 			useUnderlying,
 | |
| 			depositAmount,
 | |
| 			nTokenBalanceChange
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @notice Uses existing Notional cash balance (deposits in Notional held as cTokens) and uses them to mint
 | |
| 	 * nTokens.
 | |
| 	 * @dev This spell allows users to mint nTokens (providing liquidity) from existing cash balance.
 | |
| 	 * @param currencyId notional defined currency id of the cash balance
 | |
| 	 * @param cashBalanceToMint amount of account's cash balance to convert to nTokens
 | |
| 	 * @param getId id of cash balance
 | |
| 	 * @param setId id to set the value of nToken increase
 | |
| 	 */
 | |
| 	function mintNTokenFromCash(
 | |
| 		uint16 currencyId,
 | |
| 		uint256 cashBalanceToMint,
 | |
| 		uint256 getId,
 | |
| 		uint256 setId
 | |
| 	)
 | |
| 		external
 | |
| 		payable
 | |
| 		returns (string memory _eventName, bytes memory _eventParam)
 | |
| 	{
 | |
| 		cashBalanceToMint = getUint(getId, cashBalanceToMint);
 | |
| 		if (cashBalanceToMint == type(uint256).max)
 | |
| 			cashBalanceToMint = getCashOrNTokenBalance(currencyId, false);
 | |
| 
 | |
| 		BalanceAction[] memory action = new BalanceAction[](1);
 | |
| 		action[0].actionType = DepositActionType.ConvertCashToNToken;
 | |
| 		action[0].currencyId = currencyId;
 | |
| 		action[0].depositActionAmount = cashBalanceToMint;
 | |
| 		// NOTE: withdraw amount, withdraw cash and redeem to underlying are all 0 and false
 | |
| 
 | |
| 		uint256 nTokenBefore = getCashOrNTokenBalance(currencyId, true);
 | |
| 
 | |
| 		notional.batchBalanceAction(address(this), action);
 | |
| 
 | |
| 		uint256 nTokenBalanceChange = sub(
 | |
| 			getCashOrNTokenBalance(currencyId, true),
 | |
| 			nTokenBefore
 | |
| 		);
 | |
| 
 | |
| 		if (setId != 0) {
 | |
| 			// Set the amount of nTokens minted
 | |
| 			setUint(setId, uint256(nTokenBalanceChange));
 | |
| 		}
 | |
| 
 | |
| 		_eventName = "LogMintNTokenFromCash(address,uint16,uint256,int256)";
 | |
| 		_eventParam = abi.encode(
 | |
| 			address(this),
 | |
| 			currencyId,
 | |
| 			cashBalanceToMint,
 | |
| 			nTokenBalanceChange
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @notice Deposits some amount of tokens and lends them in the specified market. This method can also be used to repay a
 | |
| 	 * borrow early by specifying the corresponding market index of an existing borrow.
 | |
| 	 * @dev Setting the fCash amount and minLendRate are best calculated using the Notional SDK off chain. They can
 | |
| 	 * be calculated on chain but there is a significant gas cost to doing so. If there is insufficient depositAmount for the
 | |
| 	 * fCashAmount specified Notional will revert. In most cases there will be some dust amount of cash left after lending and
 | |
| 	 * this method will withdraw that dust back to the account.
 | |
| 	 * @param currencyId notional defined currency id to lend
 | |
| 	 * @param depositAmount amount of cash to deposit to lend
 | |
| 	 * @param useUnderlying if true, will accept a deposit in the underlying currency (i.e DAI), if false
 | |
| 	 * will use the asset currency (i.e. cDAI)
 | |
| 	 * @param marketIndex the market index to lend to. This is a number from 1 to 7 which corresponds to the tenor
 | |
| 	 * of the fCash asset to lend. Tenors are described here: https://docs.notional.finance/notional-v2/quarterly-rolls/tenors
 | |
| 	 * @param fCashAmount amount of fCash for the account to receive, this is equal to how much the account will receive
 | |
| 	 * at maturity (principal plus interest).
 | |
| 	 * @param minLendRate the minimum interest rate that the account is willing to lend at, if set to zero the account will accept
 | |
| 	 * any lending rate
 | |
| 	 * @param getId returns the deposit amount
 | |
| 	 */
 | |
| 	function depositAndLend(
 | |
| 		uint16 currencyId,
 | |
| 		uint256 depositAmount,
 | |
| 		bool useUnderlying,
 | |
| 		uint8 marketIndex,
 | |
| 		uint88 fCashAmount,
 | |
| 		uint32 minLendRate,
 | |
| 		uint256 getId
 | |
| 	)
 | |
| 		external
 | |
| 		payable
 | |
| 		returns (string memory _eventName, bytes memory _eventParam)
 | |
| 	{
 | |
| 		depositAmount = getDepositAmountAndSetApproval(
 | |
| 			getId,
 | |
| 			currencyId,
 | |
| 			useUnderlying,
 | |
| 			depositAmount
 | |
| 		);
 | |
| 
 | |
| 		BalanceActionWithTrades[] memory action = new BalanceActionWithTrades[](
 | |
| 			1
 | |
| 		);
 | |
| 		action[0].actionType = useUnderlying
 | |
| 			? DepositActionType.DepositUnderlying
 | |
| 			: DepositActionType.DepositAsset;
 | |
| 		action[0].currencyId = currencyId;
 | |
| 		action[0].depositActionAmount = depositAmount;
 | |
| 		// Withdraw any residual cash from lending back to the token that was used
 | |
| 		action[0].withdrawEntireCashBalance = true;
 | |
| 		action[0].redeemToUnderlying = useUnderlying;
 | |
| 
 | |
| 		bytes32[] memory trades = new bytes32[](1);
 | |
| 		trades[0] = encodeLendTrade(marketIndex, fCashAmount, minLendRate);
 | |
| 		action[0].trades = trades;
 | |
| 
 | |
| 		uint256 msgValue = getMsgValue(
 | |
| 			currencyId,
 | |
| 			useUnderlying,
 | |
| 			depositAmount
 | |
| 		);
 | |
| 		notional.batchBalanceAndTradeAction{ value: msgValue }(
 | |
| 			address(this),
 | |
| 			action
 | |
| 		);
 | |
| 
 | |
| 		_eventName = "LogDepositAndLend(address,uint16,bool,uint256,uint8,uint88,uint32)";
 | |
| 		_eventParam = abi.encode(
 | |
| 			address(this),
 | |
| 			currencyId,
 | |
| 			useUnderlying,
 | |
| 			depositAmount,
 | |
| 			marketIndex,
 | |
| 			fCashAmount,
 | |
| 			minLendRate
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @notice Deposits some amount of tokens as collateral and borrows. This can be achieved by combining multiple spells but this
 | |
| 	 * method is more gas efficient by only making a single call to Notional.
 | |
| 	 * @dev Setting the fCash amount and maxBorrowRate are best calculated using the Notional SDK off chain. The amount of fCash
 | |
| 	 * when borrowing is more forgiving compared to lending since generally accounts will over collateralize and dust amounts are
 | |
| 	 * less likely to cause reverts. The Notional SDK will also provide calculations to tell the user what their LTV is for a given
 | |
| 	 * borrowing action.
 | |
| 	 * @param depositCurrencyId notional defined currency id of the collateral to deposit
 | |
| 	 * @param depositAction one of the following values which will define how the collateral is deposited:
 | |
| 	 *  - None: no collateral will be deposited
 | |
| 	 *  - DepositAsset: deposit amount will be specified in asset tokens (i.e. cTokens)
 | |
| 	 *  - DepositUnderlying: deposit amount will be specified in underlying tokens (i.e. DAI)
 | |
| 	 *  - DepositAssetAndMintNToken: deposit amount will be converted to nTokens
 | |
| 	 *  - DepositUnderlyingAndMintNToken: deposit amount will be converted to nTokens
 | |
| 	 *
 | |
| 	 *  Technically these two deposit types can be used, but there is not a clear reason why they would be used in combination
 | |
| 	 *  with borrowing:
 | |
| 	 *  - RedeemNToken
 | |
| 	 *  - ConvertCashToNToken
 | |
| 	 *
 | |
| 	 * @param depositAmount amount of cash to deposit as collateral
 | |
| 	 * @param borrowCurrencyId id of the currency to borrow
 | |
| 	 * @param marketIndex the market index to borrow from. This is a number from 1 to 7 which corresponds to the tenor
 | |
| 	 * of the fCash asset to borrow. Tenors are described here: https://docs.notional.finance/notional-v2/quarterly-rolls/tenors
 | |
| 	 * @param fCashAmount amount of fCash for the account to borrow, this is equal to how much the account must pay
 | |
| 	 * at maturity (principal plus interest).
 | |
| 	 * @param maxBorrowRate the maximum interest rate that the account is willing to borrow at, if set to zero the account will accept
 | |
| 	 * any borrowing rate
 | |
| 	 * @param redeemToUnderlying if true, redeems the borrowed balance from cTokens down to the underlying token before transferring
 | |
| 	 * to the account
 | |
| 	 * @param getId returns the collateral deposit amount
 | |
| 	 * @param setId sets the amount that the account borrowed (i.e. how much of borrowCurrencyId it has received)
 | |
| 	 */
 | |
| 	function depositCollateralBorrowAndWithdraw(
 | |
| 		uint16 depositCurrencyId,
 | |
| 		DepositActionType depositAction,
 | |
| 		uint256 depositAmount,
 | |
| 		uint16 borrowCurrencyId,
 | |
| 		uint8 marketIndex,
 | |
| 		uint88 fCashAmount,
 | |
| 		uint32 maxBorrowRate,
 | |
| 		bool redeemToUnderlying,
 | |
| 		uint256 getId,
 | |
| 		uint256 setId
 | |
| 	)
 | |
| 		external
 | |
| 		payable
 | |
| 		returns (string memory _eventName, bytes memory _eventParam)
 | |
| 	{
 | |
| 		bool useUnderlying = (depositAction ==
 | |
| 			DepositActionType.DepositUnderlying ||
 | |
| 			depositAction == DepositActionType.DepositUnderlyingAndMintNToken);
 | |
| 
 | |
| 		depositAmount = getDepositAmountAndSetApproval(
 | |
| 			getId,
 | |
| 			depositCurrencyId,
 | |
| 			useUnderlying,
 | |
| 			depositAmount
 | |
| 		);
 | |
| 
 | |
| 		BalanceActionWithTrades[]
 | |
| 			memory actions = getDepositCollateralBorrowAndWithdrawActions(
 | |
| 				depositCurrencyId,
 | |
| 				depositAction,
 | |
| 				depositAmount,
 | |
| 				borrowCurrencyId,
 | |
| 				marketIndex,
 | |
| 				fCashAmount,
 | |
| 				maxBorrowRate,
 | |
| 				redeemToUnderlying
 | |
| 			);
 | |
| 
 | |
| 		uint256 msgValue = getMsgValue(
 | |
| 			depositCurrencyId,
 | |
| 			useUnderlying,
 | |
| 			depositAmount
 | |
| 		);
 | |
| 		executeTradeActionWithBalanceChange(
 | |
| 			actions,
 | |
| 			msgValue,
 | |
| 			borrowCurrencyId,
 | |
| 			redeemToUnderlying,
 | |
| 			setId
 | |
| 		);
 | |
| 
 | |
| 		_eventName = "LogDepositCollateralBorrowAndWithdraw(address,bool,uint256,uint16,uint8,uint88,uint32,bool)";
 | |
| 		_eventParam = abi.encode(
 | |
| 			address(this),
 | |
| 			useUnderlying,
 | |
| 			depositAmount,
 | |
| 			borrowCurrencyId,
 | |
| 			marketIndex,
 | |
| 			fCashAmount,
 | |
| 			maxBorrowRate,
 | |
| 			redeemToUnderlying
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @notice Allows an account to withdraw from a fixed rate lend by selling the fCash back to the market. Equivalent to
 | |
| 	 * borrowing from the Notional perspective.
 | |
| 	 * @dev Setting the fCash amount and maxBorrowRate are best calculated using the Notional SDK off chain. Similar to borrowing,
 | |
| 	 * setting these amounts are a bit more forgiving since there is no change of reverts due to dust amounts.
 | |
| 	 * @param currencyId notional defined currency id of the lend asset to withdraw
 | |
| 	 * @param marketIndex the market index of the fCash asset. This is a number from 1 to 7 which corresponds to the tenor
 | |
| 	 * of the fCash asset. Tenors are described here: https://docs.notional.finance/notional-v2/quarterly-rolls/tenors
 | |
| 	 * @param fCashAmount amount of fCash at the marketIndex that should be sold
 | |
| 	 * @param maxBorrowRate the maximum interest rate that the account is willing to sell fCash at at, if set to zero the
 | |
| 	 * account will accept any rate
 | |
| 	 * @param setId sets the amount that the account has received when withdrawing its lend
 | |
| 	 */
 | |
| 	function withdrawLend(
 | |
| 		uint16 currencyId,
 | |
| 		uint8 marketIndex,
 | |
| 		uint88 fCashAmount,
 | |
| 		uint32 maxBorrowRate,
 | |
| 		uint256 setId
 | |
| 	)
 | |
| 		external
 | |
| 		payable
 | |
| 		returns (string memory _eventName, bytes memory _eventParam)
 | |
| 	{
 | |
| 		bool useUnderlying = currencyId != ETH_CURRENCY_ID;
 | |
| 		BalanceActionWithTrades[] memory action = new BalanceActionWithTrades[](
 | |
| 			1
 | |
| 		);
 | |
| 		action[0].actionType = DepositActionType.None;
 | |
| 		action[0].currencyId = currencyId;
 | |
| 		// Withdraw borrowed amount to wallet
 | |
| 		action[0].withdrawEntireCashBalance = true;
 | |
| 		action[0].redeemToUnderlying = useUnderlying;
 | |
| 
 | |
| 		bytes32[] memory trades = new bytes32[](1);
 | |
| 		trades[0] = encodeBorrowTrade(marketIndex, fCashAmount, maxBorrowRate);
 | |
| 		action[0].trades = trades;
 | |
| 
 | |
| 		executeTradeActionWithBalanceChange(
 | |
| 			action,
 | |
| 			0,
 | |
| 			currencyId,
 | |
| 			useUnderlying,
 | |
| 			setId
 | |
| 		);
 | |
| 
 | |
| 		_eventName = "LogWithdrawLend(address,uint16,uint8,uint88,uint32)";
 | |
| 		_eventParam = abi.encode(
 | |
| 			address(this),
 | |
| 			currencyId,
 | |
| 			marketIndex,
 | |
| 			fCashAmount,
 | |
| 			maxBorrowRate
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	/// @notice Mints sNOTE from the underlying BPT token.
 | |
| 	/// @dev Mints sNOTE from the underlying BPT token.
 | |
| 	/// @param bptAmount is the amount of BPT to transfer from the msg.sender.
 | |
| 	function mintSNoteFromBPT(uint256 bptAmount)
 | |
| 		external
 | |
| 		payable
 | |
| 		returns (string memory _eventName, bytes memory _eventParam)
 | |
| 	{
 | |
| 		if (bptAmount == type(uint256).max)
 | |
| 			bptAmount = bpt.balanceOf(address(this));
 | |
| 
 | |
| 		approve(bpt, address(staking), bptAmount);
 | |
| 
 | |
| 		staking.mintFromBPT(bptAmount);
 | |
| 
 | |
| 		_eventName = "LogMintSNoteFromBPT(address,uint256)";
 | |
| 		_eventParam = abi.encode(address(this), bptAmount);
 | |
| 	}
 | |
| 
 | |
| 	/// @notice Mints sNOTE from some amount of NOTE and ETH
 | |
| 	/// @dev Mints sNOTE from some amount of NOTE and ETH
 | |
| 	/// @param noteAmount amount of NOTE to transfer into the sNOTE contract
 | |
| 	/// @param minBPT slippage parameter to prevent front running
 | |
| 	function mintSNoteFromETH(
 | |
| 		uint256 noteAmount,
 | |
| 		uint256 ethAmount,
 | |
| 		uint256 minBPT,
 | |
| 		uint256 getId
 | |
| 	)
 | |
| 		external
 | |
| 		payable
 | |
| 		returns (string memory _eventName, bytes memory _eventParam)
 | |
| 	{
 | |
| 		noteAmount = getUint(getId, noteAmount);
 | |
| 		if (noteAmount == type(uint256).max)
 | |
| 			noteAmount = note.balanceOf(address(this));
 | |
| 
 | |
| 		if (ethAmount == type(uint256).max) ethAmount = address(this).balance;
 | |
| 
 | |
| 		approve(note, address(staking), noteAmount);
 | |
| 
 | |
| 		staking.mintFromETH{ value: ethAmount }(noteAmount, minBPT);
 | |
| 
 | |
| 		_eventName = "LogMintSNoteFromETH(address,uint256,uint256,uint256)";
 | |
| 		_eventParam = abi.encode(address(this), ethAmount, noteAmount, minBPT);
 | |
| 	}
 | |
| 
 | |
| 	/// @notice Mints sNOTE from some amount of NOTE and WETH
 | |
| 	/// @dev Mints sNOTE from some amount of NOTE and WETH
 | |
| 	/// @param noteAmount amount of NOTE to transfer into the sNOTE contract
 | |
| 	/// @param wethAmount amount of WETH to transfer into the sNOTE contract
 | |
| 	/// @param minBPT slippage parameter to prevent front running
 | |
| 	function mintSNoteFromWETH(
 | |
| 		uint256 noteAmount,
 | |
| 		uint256 wethAmount,
 | |
| 		uint256 minBPT,
 | |
| 		uint256 getId
 | |
| 	)
 | |
| 		external
 | |
| 		payable
 | |
| 		returns (string memory _eventName, bytes memory _eventParam)
 | |
| 	{
 | |
| 		noteAmount = getUint(getId, noteAmount);
 | |
| 		if (noteAmount == type(uint256).max)
 | |
| 			noteAmount = note.balanceOf(address(this));
 | |
| 
 | |
| 		if (wethAmount == type(uint256).max)
 | |
| 			wethAmount = weth.balanceOf(address(this));
 | |
| 
 | |
| 		approve(note, address(staking), noteAmount);
 | |
| 		approve(weth, address(staking), wethAmount);
 | |
| 
 | |
| 		staking.mintFromWETH(noteAmount, wethAmount, minBPT);
 | |
| 
 | |
| 		_eventName = "LogMintSNoteFromWETH(address,uint256,uint256,uint256)";
 | |
| 		_eventParam = abi.encode(address(this), noteAmount, wethAmount, minBPT);
 | |
| 	}
 | |
| 
 | |
| 	/// @notice Begins a cool down period for the sender
 | |
| 	/// @dev This is required to redeem tokens
 | |
| 	function startCoolDown()
 | |
| 		external
 | |
| 		payable
 | |
| 		returns (string memory _eventName, bytes memory _eventParam)
 | |
| 	{
 | |
| 		staking.startCoolDown();
 | |
| 
 | |
| 		_eventName = "LogStartCoolDown(address)";
 | |
| 		_eventParam = abi.encode(address(this));
 | |
| 	}
 | |
| 
 | |
| 	/// @notice Stops a cool down for the sender
 | |
| 	/// @dev User must start another cool down period in order to call redeemSNote
 | |
| 	function stopCoolDown()
 | |
| 		external
 | |
| 		payable
 | |
| 		returns (string memory _eventName, bytes memory _eventParam)
 | |
| 	{
 | |
| 		staking.stopCoolDown();
 | |
| 
 | |
| 		_eventName = "LogStopCoolDown(address)";
 | |
| 		_eventParam = abi.encode(address(this));
 | |
| 	}
 | |
| 
 | |
| 	/// @notice Redeems some amount of sNOTE to underlying constituent tokens (ETH and NOTE).
 | |
| 	/// @dev An account must have passed its cool down expiration before they can redeem
 | |
| 	/// @param sNOTEAmount amount of sNOTE to redeem
 | |
| 	/// @param minWETH slippage protection for ETH/WETH amount
 | |
| 	/// @param minNOTE slippage protection for NOTE amount
 | |
| 	/// @param redeemWETH true if redeeming to WETH to ETH
 | |
| 	function redeemSNote(
 | |
| 		uint256 sNOTEAmount,
 | |
| 		uint256 minWETH,
 | |
| 		uint256 minNOTE,
 | |
| 		bool redeemWETH
 | |
| 	)
 | |
| 		external
 | |
| 		payable
 | |
| 		returns (string memory _eventName, bytes memory _eventParam)
 | |
| 	{
 | |
| 		if (sNOTEAmount == type(uint256).max)
 | |
| 			sNOTEAmount = staking.balanceOf(address(this));
 | |
| 
 | |
| 		staking.redeem(sNOTEAmount, minWETH, minNOTE, redeemWETH);
 | |
| 
 | |
| 		_eventName = "LogRedeemSNote(address,uint256,uint256,uint256,bool)";
 | |
| 		_eventParam = abi.encode(
 | |
| 			address(this),
 | |
| 			sNOTEAmount,
 | |
| 			minWETH,
 | |
| 			minNOTE,
 | |
| 			redeemWETH
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @notice Executes a number of batch actions on the account without getId or setId integration
 | |
| 	 * @dev This method will allow the user to take almost any action on Notional but does not have any
 | |
| 	 * getId or setId integration. This can be used to roll lends and borrows forward.
 | |
| 	 * @param actions a set of BatchActionWithTrades that will be executed for this account
 | |
| 	 */
 | |
| 	function batchActionRaw(BalanceActionWithTrades[] memory actions)
 | |
| 		external
 | |
| 		payable
 | |
| 		returns (string memory _eventName, bytes memory _eventParam)
 | |
| 	{
 | |
| 		notional.batchBalanceAndTradeAction(address(this), actions);
 | |
| 
 | |
| 		_eventName = "LogBatchActionRaw(address)";
 | |
| 		_eventParam = abi.encode(address(this));
 | |
| 	}
 | |
| }
 | |
| 
 | |
| contract ConnectV2Notional is NotionalResolver {
 | |
| 	string public name = "Notional-v1.1";
 | |
| }
 | 
