Fixing build errors

This commit is contained in:
Tianjie Wei 2021-11-29 12:38:43 -08:00
parent 45a85c09aa
commit 6bbfa348f0
3 changed files with 260 additions and 32 deletions

View File

@ -1,13 +1,111 @@
pragma solidity ^0.7.6;
pragma abicoder v2;
contract Helpers {
import {Token, NotionalInterface} from "./interface.sol";
import {Basic} from "../../common/basic.sol";
// function getUnderlyingToken(uint16 currencyId);
// function getAssetToken(uint16 currencyId);
// function getCashBalance(uint16 currencyId);
// function getNTokenBalance(uint16 currencyId);
// function convertToInternal(uint16 currencyId);
contract Helpers is Basic {
uint8 internal constant LEND_TRADE = 0;
uint8 internal constant BORROW_TRADE = 1;
int256 internal constant INTERNAL_TOKEN_PRECISION = 1e8;
uint256 internal constant ETH_CURRENCY_ID = 1;
int256 private constant _INT256_MIN = type(int256).min;
function getDepositAmountAndSetApproval(uint16 currencyId)
function executeActionWithBalanceChange(uint16 currencyId)
NotionalInterface internal constant notional =
NotionalInterface(0xE592427A0AEce92De3Edee1F18E0157C05861564);
function getUnderlyingToken(uint16 currencyId) internal returns (address) {
(, Token memory underlyingToken) = notional.getCurrency(currencyId);
return underlyingToken.tokenAddress;
}
function getAssetToken(uint16 currencyId) internal returns (address) {
(Token memory assetToken, ) = notional.getCurrency(currencyId);
return assetToken.tokenAddress;
}
function getCashBalance(uint16 currencyId)
internal
returns (int256 cashBalance)
{
(cashBalance, , ) = notional.getAccountBalance(
currencyId,
address(this)
);
}
function getNTokenBalance(uint16 currencyId)
internal
returns (int256 nTokenBalance)
{
(, nTokenBalance, ) = notional.getAccountBalance(
currencyId,
address(this)
);
}
function convertToInternal(uint16 currencyId, int256 amount)
internal
pure
returns (int256)
{
// If token decimals is greater than INTERNAL_TOKEN_PRECISION then this will truncate
// down to the internal precision. Resulting dust will accumulate to the protocol.
// If token decimals is less than INTERNAL_TOKEN_PRECISION then this will add zeros to the
// end of amount and will not result in dust.
(Token memory assetToken, ) = notional.getCurrency(currencyId);
if (assetToken.decimals == INTERNAL_TOKEN_PRECISION) return amount;
return div(mul(amount, INTERNAL_TOKEN_PRECISION), assetToken.decimals);
}
function encodeLendTrade(
uint8 marketIndex,
uint88 fCashAmount,
uint32 minLendRate
) internal returns (bytes32) {
return
abi.encodePacked(
LEND_TRADE,
marketIndex,
fCashAmount,
minLendRate,
uint120(0)
);
}
function encodeBorrowTrade(
uint8 marketIndex,
uint88 fCashAmount,
uint32 maxBorrowRate
) internal returns (bytes32) {
return
abi.encodePacked(
BORROW_TRADE,
marketIndex,
fCashAmount,
maxBorrowRate,
uint120(0)
);
}
function mul(int256 a, int256 b) internal pure returns (int256 c) {
c = a * b;
if (a == -1) require(b == 0 || c / b == a);
else require(a == 0 || c / a == b);
}
function div(int256 a, int256 b) internal pure returns (int256 c) {
require(!(b == -1 && a == _INT256_MIN)); // dev: int256 div overflow
// NOTE: solidity will automatically revert on divide by zero
c = a / b;
}
function sub(int256 x, int256 y) internal pure returns (int256 z) {
// taken from uniswap v3
require((z = x - y) <= x == (y >= 0));
}
//function getDepositAmountAndSetApproval(uint16 currencyId) internal;
//function executeActionWithBalanceChange(uint16 currencyId) internal;
}

View File

@ -0,0 +1,126 @@
pragma solidity ^0.7.6;
pragma abicoder v2;
/// @notice Different types of internal tokens
/// - UnderlyingToken: underlying asset for a cToken (except for Ether)
/// - cToken: Compound interest bearing token
/// - cETH: Special handling for cETH tokens
/// - Ether: the one and only
/// - NonMintable: tokens that do not have an underlying (therefore not cTokens)
enum TokenType {
UnderlyingToken,
cToken,
cETH,
Ether,
NonMintable
}
/// @notice Specifies different deposit actions that can occur during BalanceAction or BalanceActionWithTrades
enum DepositActionType {
// No deposit action
None,
// Deposit asset cash, depositActionAmount is specified in asset cash external precision
DepositAsset,
// Deposit underlying tokens that are mintable to asset cash, depositActionAmount is specified in underlying token
// external precision
DepositUnderlying,
// Deposits specified asset cash external precision amount into an nToken and mints the corresponding amount of
// nTokens into the account
DepositAssetAndMintNToken,
// Deposits specified underlying in external precision, mints asset cash, and uses that asset cash to mint nTokens
DepositUnderlyingAndMintNToken,
// Redeems an nToken balance to asset cash. depositActionAmount is specified in nToken precision. Considered a deposit action
// because it deposits asset cash into an account. If there are fCash residuals that cannot be sold off, will revert.
RedeemNToken,
// Converts specified amount of asset cash balance already in Notional to nTokens. depositActionAmount is specified in
// Notional internal 8 decimal precision.
ConvertCashToNToken
}
/// @notice Defines a balance action with a set of trades to do as well
struct BalanceActionWithTrades {
DepositActionType actionType;
uint16 currencyId;
uint256 depositActionAmount;
uint256 withdrawAmountInternalPrecision;
bool withdrawEntireCashBalance;
bool redeemToUnderlying;
// Array of tightly packed 32 byte objects that represent trades. See TradeActionType documentation
bytes32[] trades;
}
/// @notice Defines a balance action for batchAction
struct BalanceAction {
// Deposit action to take (if any)
DepositActionType actionType;
uint16 currencyId;
// Deposit action amount must correspond to the depositActionType, see documentation above.
uint256 depositActionAmount;
// Withdraw an amount of asset cash specified in Notional internal 8 decimal precision
uint256 withdrawAmountInternalPrecision;
// If set to true, will withdraw entire cash balance. Useful if there may be an unknown amount of asset cash
// residual left from trading.
bool withdrawEntireCashBalance;
// If set to true, will redeem asset cash to the underlying token on withdraw.
bool redeemToUnderlying;
}
struct Token {
address tokenAddress;
bool hasTransferFee;
int256 decimals;
TokenType tokenType;
uint256 maxCollateralBalance;
}
interface NotionalInterface {
function getCurrency(uint16 currencyId)
external
view
returns (Token memory assetToken, Token memory underlyingToken);
function getAccountBalance(uint16 currencyId, address account)
external
view
returns (
int256 cashBalance,
int256 nTokenBalance,
uint256 lastClaimTime
);
function depositUnderlyingToken(
address account,
uint16 currencyId,
uint256 amountExternalPrecision
) external payable returns (uint256);
function depositAssetToken(
address account,
uint16 currencyId,
uint256 amountExternalPrecision
) external returns (uint256);
function withdraw(
uint16 currencyId,
uint88 amountInternalPrecision,
bool redeemToUnderlying
) external returns (uint256);
function nTokenClaimIncentives() external returns (uint256);
function nTokenRedeem(
address redeemer,
uint16 currencyId,
uint96 tokensToRedeem_,
bool sellTokenAssets
) external returns (int256);
function batchBalanceAction(
address account,
BalanceAction[] calldata actions
) external payable;
function batchBalanceAndTradeAction(address account, BalanceActionWithTrades[] calldata actions)
external
payable;
}

View File

@ -1,8 +1,11 @@
pragma solidity ^0.7.6;
pragma abicoder v2;
import { Helpers } from "./helpers.sol";
import { Events } from "./events.sol";
import { TokenInterface } from "../common/interfaces.sol";
import { DepositActionType, BalanceActionWithTrades, BalanceAction } from "./interface.sol";
import { TokenInterface } from "../../common/interfaces.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @title Notional
@ -29,14 +32,14 @@ abstract contract NotionalResolver is Events, Helpers {
uint assetCashDeposited;
address tokenAddress = useUnderlying ? getUnderlyingToken(currencyId) : getAssetToken(currencyId);
depositAmount = getUint(getId, depositAmount);
if (depositAmount == uint(-1)) depositAmount = ERC20(tokenAddress).balanceOf(address(this));
if (depositAmount == uint(-1)) depositAmount = IERC20(tokenAddress).balanceOf(address(this));
approve(tokenAddress, address(notional), depositAmount);
if (useUnderlying && currencyId == ETH_CURRENCY_ID) {
assetCashDeposited = notional.depositUnderlying{value: depositAmount}(address(this), currencyId, depositAmount);
assetCashDeposited = notional.depositUnderlyingToken{value: depositAmount}(address(this), currencyId, depositAmount);
} else if (useUnderlying) {
assetCashDeposited = notional.depositUnderlying{value: depositAmount}(address(this), currencyId, depositAmount);
assetCashDeposited = notional.depositUnderlyingToken{value: depositAmount}(address(this), currencyId, depositAmount);
} else {
assetCashDeposited = notional.depositAssetToken(address(this), currencyId, depositAmount);
}
@ -166,14 +169,14 @@ abstract contract NotionalResolver is Events, Helpers {
getAssetToken(currencyId);
// TODO: handle ETH
balanceBefore = ERC20(tokenAddress).balanceOf(address(this));
balanceBefore = IERC20(tokenAddress).balanceOf(address(this));
}
notional.batchBalanceAction(address(this), action);
if (setId != 0) {
// TODO: handle ETH
uint netBalance = balanceBefore.sub(ERC20(tokenAddress).balanceOf(address(this)));
uint netBalance = sub(balanceBefore, IERC20(tokenAddress).balanceOf(address(this)));
// This can be used to determine the exact amount withdrawn
setUint(setId, netBalance);
}
@ -205,7 +208,7 @@ abstract contract NotionalResolver is Events, Helpers {
) external returns (string memory _eventName, bytes memory _eventParam) {
tokensToRedeem = getUint(getId, tokensToRedeem);
if (tokensToRedeem == uint(-1)) tokensToRedeem = getNTokenBalance(currencyId);
notional.nTokenRedeem(currencyId, tokensToRedeem, sellTokenAssets);
notional.nTokenRedeem(currencyId, tokensToRedeem, true);
BalanceActionWithTrades[] memory action = new BalanceActionWithTrades[1];
action[0].actionType = DepositActionType.RedeemNToken;
@ -217,7 +220,7 @@ abstract contract NotionalResolver is Events, Helpers {
trades[0] = encodeLendTrade(marketIndex, fCashAmount, minLendRate);
action[0].trades = trades;
notional.batchBalanceActionWithTrades(address(this), action);
notional.batchBalanceAndTradeAction(address(this), action);
_eventName = "LogRedeemNTokenAndDeleverage(address,uint16,uint,uint8,uint)";
_eventParam = abi.encode(address(this), currencyId, tokensToRedeem, marketIndex, fCashAmount);
@ -242,7 +245,7 @@ abstract contract NotionalResolver is Events, Helpers {
) external payable returns (string memory _eventName, bytes memory _eventParam) {
address tokenAddress = useUnderlying ? getUnderlyingToken(currencyId) : getAssetToken(currencyId);
depositAmount = getUint(getId, depositAmount);
if (depositAmount == uint(-1)) depositAmount = ERC20(tokenAddress).balanceOf(address(this));
if (depositAmount == uint(-1)) depositAmount = IERC20(tokenAddress).balanceOf(address(this));
approve(tokenAddress, address(notional), depositAmount);
BalanceAction[] memory action = new BalanceAction[1];
@ -251,17 +254,17 @@ abstract contract NotionalResolver is Events, Helpers {
action[0].depositActionAmount = depositAmount;
// withdraw amount, withdraw cash and redeem to underlying are all 0 and false
uint nTokenBefore
uint nTokenBefore;
if (setId != 0) {
nTokenBefore = getNTokenBalance(currencyId);
}
uint msgValue = currencyId == ETH_CURRENCY_ID ? depositAmount : 0;
notional.batchBalanceActionWithTrades{value: msgValue}(address(this), action);
notional.batchBalanceAndTradeAction{value: msgValue}(address(this), action);
if (setId != 0) {
// Set the amount of nTokens minted
setUint(setId, getNTokenBalance(currencyId).sub(nTokenBefore));
setUint(setId, sub(getNTokenBalance(currencyId), nTokenBefore));
}
// todo: events
@ -274,7 +277,7 @@ abstract contract NotionalResolver is Events, Helpers {
uint setId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
cashBalanceToMint = getUint(getId, cashBalanceToMint);
if (cashBalanceToMint == uint(-1)) = cashBalanceToMint = getCashBalance(currencyId);
if (cashBalanceToMint == uint(-1)) cashBalanceToMint = getCashBalance(currencyId);
BalanceAction[] memory action = new BalanceAction[1];
action[0].actionType = DepositActionType.ConvertCashToNToken;
@ -282,7 +285,7 @@ abstract contract NotionalResolver is Events, Helpers {
action[0].depositActionAmount = cashBalanceToMint;
// NOTE: withdraw amount, withdraw cash and redeem to underlying are all 0 and false
uint nTokenBefore
uint nTokenBefore;
if (setId != 0) {
nTokenBefore = getNTokenBalance(currencyId);
}
@ -308,7 +311,7 @@ abstract contract NotionalResolver is Events, Helpers {
) external payable returns (string memory _eventName, bytes memory _eventParam) {
address tokenAddress = useUnderlying ? getUnderlyingToken(currencyId) : getAssetToken(currencyId);
depositAmount = getUint(getId, depositAmount);
if (depositAmount == uint(-1)) depositAmount = ERC20(tokenAddress).balanceOf(address(this));
if (depositAmount == uint(-1)) depositAmount = IERC20(tokenAddress).balanceOf(address(this));
approve(tokenAddress, address(notional), depositAmount);
BalanceAction[] memory action = new BalanceAction[1];
@ -345,7 +348,7 @@ abstract contract NotionalResolver is Events, Helpers {
require(depositCurrencyId != borrowCurrencyId);
address tokenAddress = useUnderlying ? getUnderlyingToken(depositCurrencyId) : getAssetToken(depositCurrencyId);
depositAmount = getUint(getId, depositAmount);
if (depositAmount == uint(-1)) depositAmount = ERC20(tokenAddress).balanceOf(address(this));
if (depositAmount == uint(-1)) depositAmount = IERC20(tokenAddress).balanceOf(address(this));
approve(tokenAddress, address(notional), depositAmount);
BalanceActionWithTrades[] memory action = new BalanceActionWithTrades[](2);
@ -373,20 +376,20 @@ abstract contract NotionalResolver is Events, Helpers {
action[borrowIndex].redeemToUnderlying = useUnderlying;
bytes32[] memory trades = new bytes32[](1);
trades[borrowIndex] = encodeBorrowTrade(marketIndex, fCashAmount, minLendRate);
trades[borrowIndex] = encodeBorrowTrade(marketIndex, fCashAmount, maxBorrowRate);
action[borrowIndex].trades = trades;
address borrowToken;
uint balanceBefore;
if (setId != 0) {
address borrowToken = useUnderlying ? getUnderlyingToken(borrowCurrencyId) : getAssetToken(borrowCurrencyId);
balanceBefore = ERC20(borrowToken).balanceOf(address(this));
balanceBefore = IERC20(borrowToken).balanceOf(address(this));
}
notional.batchBalanceActionWithTrades{value: msgValue}(address(this), action);
if (setId != 0) {
setUint(setId, ERC20(borrowToken).balanceOf(address(this)).sub(balanceBefore));
setUint(setId, IERC20(borrowToken).balanceOf(address(this)).sub(balanceBefore));
}
// todo: events
@ -400,6 +403,7 @@ abstract contract NotionalResolver is Events, Helpers {
uint getId,
uint setId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
bool useUnderlying = currencyId != ETH_CURRENCY_ID;
BalanceActionWithTrades[] memory action = new BalanceActionWithTrades[]();
action[0].actionType = DepositActionType.None;
action[0].currencyId = currencyId;
@ -416,13 +420,13 @@ abstract contract NotionalResolver is Events, Helpers {
uint balanceBefore;
if (setId != 0) {
address tokenAddress = useUnderlying ? getUnderlyingToken(currencyId) : getAssetToken(currencyId);
balanceBefore = ERC20(borrowToken).balanceOf(address(this));
balanceBefore = IERC20(tokenAddress).balanceOf(address(this));
}
notional.batchBalanceActionWithTrades{value: msgValue}(address(this), action);
notional.batchBalanceActionWithTrades{value: msg.value}(address(this), action);
if (setId != 0) {
setUint(setId, ERC20(borrowToken).balanceOf(address(this)).sub(balanceBefore));
setUint(setId, IERC20(tokenAddress).balanceOf(address(this)).sub(balanceBefore));
}
}
@ -444,9 +448,9 @@ abstract contract NotionalResolver is Events, Helpers {
* @param actions a set of BatchActionWithTrades that will be executed for this account
*/
function batchActionRaw(
BatchActionWithTrades[] memory actions
BalanceActionWithTrades[] memory actions
) external payable returns (string memory _eventName, bytes memory _eventParam) {
notional.batchBalanceActionWithTrades(address(this), actions);
notional.batchBalanceAndTradeAction(address(this), actions);
// todo: events
}