-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathwfCashBase.sol
150 lines (126 loc) · 6.36 KB
/
wfCashBase.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// SPDX-License-Identifier: MIT
pragma solidity 0.8.11;
import "./lib/Constants.sol";
import "./lib/DateTime.sol";
import "./lib/EncodeDecode.sol";
import "../interfaces/notional/INotionalV2.sol";
import "../interfaces/notional/IWrappedfCash.sol";
import "../interfaces/WETH9.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin-upgradeable/contracts/token/ERC777/ERC777Upgradeable.sol";
abstract contract wfCashBase is ERC777Upgradeable, IWrappedfCash {
using SafeERC20 for IERC20;
/// @notice address to the NotionalV2 system
INotionalV2 public immutable NotionalV2;
WETH9 public immutable WETH;
/// @dev Storage slot for fCash id. Read only and set on initialization
uint256 private _fCashId;
/// @notice Constructor is called only on deployment to set the Notional address, rest of state
/// is initialized on the proxy.
/// @dev Ensure initializer modifier is on the constructor to prevent an attack on UUPSUpgradeable contracts
constructor(INotionalV2 _notional, WETH9 _weth) initializer {
NotionalV2 = _notional;
WETH = _weth;
}
/// @notice Initializes a proxy for a specific fCash asset
function initialize(uint16 currencyId, uint40 maturity) external override initializer {
CashGroupSettings memory cashGroup = NotionalV2.getCashGroup(currencyId);
require(cashGroup.maxMarketIndex > 0, "Invalid currency");
// Ensure that the maturity is not past the max market index, also ensure that the maturity
// is not in the past. This statement will allow idiosyncratic (non-tradable) fCash assets.
require(
DateTime.isValidMaturity(cashGroup.maxMarketIndex, maturity, block.timestamp),
"Invalid maturity"
);
// Get the corresponding fCash ID
_fCashId = EncodeDecode.encodeERC1155Id(currencyId, maturity, Constants.FCASH_ASSET_TYPE);
(IERC20 underlyingToken, /* */) = getUnderlyingToken();
(IERC20 assetToken, /* */, /* */) = getAssetToken();
string memory _symbol = address(underlyingToken) == Constants.ETH_ADDRESS
? "ETH"
: IERC20Metadata(address(underlyingToken)).symbol();
string memory _maturity = Strings.toString(maturity);
__ERC777_init(
// name
string(abi.encodePacked("Wrapped f", _symbol, " @ ", _maturity)),
// symbol
string(abi.encodePacked("wf", _symbol, ":", _maturity)),
// no default operators
new address[](0)
);
// Set approvals for Notional. It is possible for an asset token address to equal the underlying
// token address when there is no money market involved.
assetToken.safeApprove(address(NotionalV2), type(uint256).max);
if (
address(assetToken) != address(underlyingToken) &&
address(underlyingToken) != Constants.ETH_ADDRESS
) {
underlyingToken.safeApprove(address(NotionalV2), type(uint256).max);
}
}
/// @notice Returns the underlying fCash ID of the token
function getfCashId() public view override returns (uint256) {
return _fCashId;
}
/// @notice Returns the underlying fCash maturity of the token
function getMaturity() public view override returns (uint40 maturity) {
(/* */, maturity, /* */) = EncodeDecode.decodeERC1155Id(_fCashId);
}
/// @notice True if the fCash has matured, assets mature exactly on the block time
function hasMatured() public view override returns (bool) {
return getMaturity() <= block.timestamp;
}
/// @notice Returns the underlying fCash currency
function getCurrencyId() public view override returns (uint16 currencyId) {
(currencyId, /* */, /* */) = EncodeDecode.decodeERC1155Id(_fCashId);
}
/// @notice Returns the components of the fCash idd
function getDecodedID() public view override returns (uint16 currencyId, uint40 maturity) {
(currencyId, maturity, /* */) = EncodeDecode.decodeERC1155Id(_fCashId);
}
/// @notice fCash is always denominated in 8 decimal places
function decimals() public pure override returns (uint8) {
return 8;
}
/// @notice Returns the current market index for this fCash asset. If this returns
/// zero that means it is idiosyncratic and cannot be traded.
function getMarketIndex() public view override returns (uint8) {
(uint256 marketIndex, bool isIdiosyncratic) = DateTime.getMarketIndex(
Constants.MAX_TRADED_MARKET_INDEX,
getMaturity(),
block.timestamp
);
if (isIdiosyncratic) return 0;
// Market index as defined does not overflow this conversion
return uint8(marketIndex);
}
/// @notice Returns the token and precision of the token that this token settles
/// to. For example, fUSDC will return the USDC token address and 1e6. The zero
/// address will represent ETH.
function getUnderlyingToken() public view override returns (IERC20 underlyingToken, int256 underlyingPrecision) {
(Token memory asset, Token memory underlying) = NotionalV2.getCurrency(getCurrencyId());
if (asset.tokenType == TokenType.NonMintable) {
// In this case the asset token is the underlying
return (IERC20(asset.tokenAddress), asset.decimals);
} else {
return (IERC20(underlying.tokenAddress), underlying.decimals);
}
}
/// @notice Returns the asset token which the fCash settles to. This will be an interest
/// bearing token like a cToken or aToken.
function getAssetToken() public view override returns (IERC20 assetToken, int256 underlyingPrecision, TokenType tokenType) {
(Token memory asset, /* Token memory underlying */) = NotionalV2.getCurrency(getCurrencyId());
return (IERC20(asset.tokenAddress), asset.decimals, asset.tokenType);
}
function getToken(bool useUnderlying) public view returns (IERC20 token, bool isETH) {
if (useUnderlying) {
(token, /* */) = getUnderlyingToken();
} else {
(token, /* */, /* */) = getAssetToken();
}
isETH = address(token) == Constants.ETH_ADDRESS;
}
}