Skip to content

Commit 49bdf46

Browse files
authored
feat(module): Notional Trade Module (#251)
1 parent 56cb46e commit 49bdf46

28 files changed

+3691
-34
lines changed

.solhint.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
"extends": "solhint:recommended",
33
"rules": {
44
"reason-string": ["warn", { "maxLength": 50 }],
5-
"compiler-version": ["error", "0.6.10"]
5+
"compiler-version": ["error", ">=0.6.10"]
66
}
77
}

.solhintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ node_modules
22
contracts/mocks
33
contracts/Migrations.sol
44
contracts/external
5+
contracts/protocol/integration/wrap/notional
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// SPDX-License-Identifier: GPL-3.0-only
2+
pragma solidity 0.6.10;
3+
pragma experimental "ABIEncoderV2";
4+
5+
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
6+
7+
/// @notice Different types of internal tokens
8+
/// - UnderlyingToken: underlying asset for a cToken (except for Ether)
9+
/// - cToken: Compound interest bearing token
10+
/// - cETH: Special handling for cETH tokens
11+
/// - Ether: the one and only
12+
/// - NonMintable: tokens that do not have an underlying (therefore not cTokens)
13+
enum TokenType {
14+
UnderlyingToken,
15+
cToken,
16+
cETH,
17+
Ether,
18+
NonMintable
19+
}
20+
21+
interface IWrappedfCash {
22+
function initialize(uint16 currencyId, uint40 maturity) external;
23+
24+
/// @notice Mints wrapped fCash ERC20 tokens
25+
function mintViaAsset(
26+
uint256 depositAmountExternal,
27+
uint88 fCashAmount,
28+
address receiver,
29+
uint32 minImpliedRate
30+
) external;
31+
32+
function mintViaUnderlying(
33+
uint256 depositAmountExternal,
34+
uint88 fCashAmount,
35+
address receiver,
36+
uint32 minImpliedRate
37+
) external;
38+
39+
function redeemToAsset(uint256 amount, address receiver, uint32 maxImpliedRate) external;
40+
function redeemToUnderlying(uint256 amount, address receiver, uint32 maxImpliedRate) external;
41+
42+
/// @notice Returns the underlying fCash ID of the token
43+
function getfCashId() external view returns (uint256);
44+
45+
/// @notice True if the fCash has matured, assets mature exactly on the block time
46+
function hasMatured() external view returns (bool);
47+
48+
/// @notice Returns the components of the fCash idd
49+
function getDecodedID() external view returns (uint16 currencyId, uint40 maturity);
50+
51+
/// @notice Returns the current market index for this fCash asset. If this returns
52+
/// zero that means it is idiosyncratic and cannot be traded.
53+
function getMarketIndex() external view returns (uint8);
54+
55+
/// @notice Returns the token and precision of the token that this token settles
56+
/// to. For example, fUSDC will return the USDC token address and 1e6. The zero
57+
/// address will represent ETH.
58+
function getUnderlyingToken() external view returns (IERC20 underlyingToken, int256 underlyingPrecision);
59+
60+
/// @notice Returns the asset token which the fCash settles to. This will be an interest
61+
/// bearing token like a cToken or aToken.
62+
function getAssetToken() external view returns (IERC20 assetToken, int256 assetPrecision, TokenType tokenType);
63+
64+
function getToken(bool useUnderlying) external view returns (IERC20 token, bool isETH);
65+
}
66+
67+
68+
interface IWrappedfCashComplete is IWrappedfCash, IERC20 {}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// SPDX-License-Identifier: GPL-3.0-only
2+
pragma solidity 0.6.10;
3+
4+
interface IWrappedfCashFactory {
5+
function deployWrapper(uint16 currencyId, uint40 maturity) external returns(address);
6+
function computeAddress(uint16 currencyId, uint40 maturity) external view returns(address);
7+
}
8+
9+

contracts/interfaces/external/ICErc20.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ interface ICErc20 is IERC20 {
4343

4444
function exchangeRateStored() external view returns (uint256);
4545

46-
function underlying() external returns (address);
46+
function underlying() external view returns (address);
4747

4848
/**
4949
* Sender supplies assets into the market and receives cTokens in exchange
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.11;
3+
import { NotionalProxy } from "notional-solidity-sdk/interfaces/notional/NotionalProxy.sol";
4+
5+
interface INotionalProxy is NotionalProxy {}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
Copyright 2022 Set Labs Inc.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
16+
SPDX-License-Identifier: Apache License, Version 2.0
17+
*/
18+
19+
pragma solidity 0.6.10;
20+
pragma experimental "ABIEncoderV2";
21+
22+
import { IWrappedfCashFactory } from "../interfaces/IWrappedFCashFactory.sol";
23+
import { WrappedfCashMock } from "./WrappedfCashMock.sol";
24+
25+
26+
// mock class using BasicToken
27+
contract WrappedfCashFactoryMock is IWrappedfCashFactory {
28+
29+
mapping(uint16 => mapping(uint40 => address)) paramsToAddress;
30+
bool private revertComputeAddress;
31+
32+
function registerWrapper(uint16 _currencyId, uint40 _maturity, address _fCashWrapper) external {
33+
paramsToAddress[_currencyId][_maturity] = _fCashWrapper;
34+
}
35+
36+
function deployWrapper(uint16 _currencyId, uint40 _maturity) external override returns(address) {
37+
return computeAddress(_currencyId, _maturity);
38+
}
39+
40+
function computeAddress(uint16 _currencyId, uint40 _maturity) public view override returns(address) {
41+
require(!revertComputeAddress, "Test revertion ComputeAddress");
42+
return paramsToAddress[_currencyId][_maturity];
43+
}
44+
45+
function setRevertComputeAddress(bool _revertComputeAddress) external{
46+
revertComputeAddress = _revertComputeAddress;
47+
}
48+
49+
50+
}

contracts/mocks/WrappedfCashMock.sol

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/*
2+
Copyright 2022 Set Labs Inc.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
16+
SPDX-License-Identifier: Apache License, Version 2.0
17+
*/
18+
19+
pragma solidity 0.6.10;
20+
pragma experimental "ABIEncoderV2";
21+
22+
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
23+
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
24+
import { TokenType, IWrappedfCash } from "../interfaces/IWrappedFCash.sol";
25+
26+
// mock class using BasicToken
27+
contract WrappedfCashMock is ERC20, IWrappedfCash {
28+
29+
uint256 private fCashId;
30+
uint40 private maturity;
31+
bool private matured;
32+
uint16 private currencyId;
33+
uint8 private marketIndex;
34+
IERC20 private underlyingToken;
35+
int256 private underlyingPrecision;
36+
IERC20 private assetToken;
37+
int256 private assetPrecision;
38+
TokenType private tokenType;
39+
40+
IERC20 private weth;
41+
42+
bool private revertDecodedID;
43+
44+
uint256 public redeemTokenReturned;
45+
uint256 public mintTokenSpent;
46+
47+
address internal constant ETH_ADDRESS = address(0);
48+
49+
constructor (IERC20 _assetToken, IERC20 _underlyingToken, IERC20 _weth) public ERC20("FCashMock", "FCM") {
50+
assetToken = _assetToken;
51+
underlyingToken = _underlyingToken;
52+
weth = _weth;
53+
}
54+
55+
function initialize(uint16 _currencyId, uint40 _maturity) external override {
56+
currencyId = _currencyId;
57+
maturity = _maturity;
58+
}
59+
60+
/// @notice Mints wrapped fCash ERC20 tokens
61+
function mintViaAsset(
62+
uint256 depositAmountExternal,
63+
uint88 fCashAmount,
64+
address receiver,
65+
uint32 /* minImpliedRate */
66+
) external override{
67+
uint256 assetTokenAmount = mintTokenSpent == 0 ? depositAmountExternal : mintTokenSpent;
68+
require(assetToken.transferFrom(msg.sender, address(this), assetTokenAmount), "WrappedfCashMock: Transfer failed");
69+
_mint(receiver, fCashAmount);
70+
}
71+
72+
function mintViaUnderlying(
73+
uint256 depositAmountExternal,
74+
uint88 fCashAmount,
75+
address receiver,
76+
uint32 /* minImpliedRate */
77+
) external override{
78+
uint256 underlyingTokenAmount = mintTokenSpent == 0 ? depositAmountExternal : mintTokenSpent;
79+
bool transferSuccess;
80+
if(address(underlyingToken) == ETH_ADDRESS) {
81+
transferSuccess = weth.transferFrom(msg.sender, address(this), underlyingTokenAmount);
82+
} else {
83+
transferSuccess = underlyingToken.transferFrom(msg.sender, address(this), underlyingTokenAmount);
84+
}
85+
require(transferSuccess, "WrappedfCashMock: Transfer failed");
86+
_mint(receiver, fCashAmount);
87+
}
88+
89+
90+
function redeemToAsset(
91+
uint256 amount,
92+
address receiver,
93+
uint32 /* maxImpliedRate */
94+
) external override {
95+
_burn(msg.sender, amount);
96+
uint256 assetTokenAmount = redeemTokenReturned == 0 ? amount : redeemTokenReturned;
97+
require(assetToken.transfer(receiver, assetTokenAmount), "WrappedfCashMock: Transfer failed");
98+
}
99+
100+
function redeemToUnderlying(
101+
uint256 amount,
102+
address receiver,
103+
uint32 /* maxImpliedRate */
104+
) external override {
105+
_burn(msg.sender, amount);
106+
uint256 underlyingTokenAmount = redeemTokenReturned == 0 ? amount : redeemTokenReturned;
107+
if(address(underlyingToken) == ETH_ADDRESS) {
108+
weth.transfer(receiver, underlyingTokenAmount);
109+
} else {
110+
underlyingToken.transfer(receiver, underlyingTokenAmount);
111+
}
112+
}
113+
114+
/// @notice Returns the underlying fCash ID of the token
115+
function getfCashId() external override view returns (uint256) {
116+
return fCashId;
117+
}
118+
119+
/// @notice True if the fCash has matured, assets mature exactly on the block time
120+
function hasMatured() external override view returns (bool) {
121+
return matured;
122+
}
123+
124+
/// @notice Returns the components of the fCash idd
125+
function getDecodedID() external override view returns (uint16, uint40) {
126+
require(!revertDecodedID, "Test revertion DecodedID");
127+
return (currencyId, maturity);
128+
}
129+
130+
/// @notice Returns the current market index for this fCash asset. If this returns
131+
/// zero that means it is idiosyncratic and cannot be traded.
132+
function getMarketIndex() external override view returns (uint8) {
133+
return marketIndex;
134+
}
135+
136+
/// @notice Returns the token and precision of the token that this token settles
137+
/// to. For example, fUSDC will return the USDC token address and 1e6. The zero
138+
/// address will represent ETH.
139+
function getUnderlyingToken() public override view returns (IERC20, int256) {
140+
return (underlyingToken, underlyingPrecision);
141+
}
142+
143+
/// @notice Returns the asset token which the fCash settles to. This will be an interest
144+
/// bearing token like a cToken or aToken.
145+
function getAssetToken() public override view returns (IERC20, int256, TokenType) {
146+
return (assetToken, assetPrecision, tokenType);
147+
}
148+
149+
function setMatured(bool _matured) external{
150+
matured = _matured;
151+
}
152+
153+
function setRedeemTokenReturned(uint256 _redeemTokenReturned) external{
154+
redeemTokenReturned = _redeemTokenReturned;
155+
}
156+
157+
function setMintTokenSpent(uint256 _mintTokenSpent) external{
158+
mintTokenSpent = _mintTokenSpent;
159+
}
160+
161+
function setRevertDecodedID(bool _revertDecodedID) external{
162+
revertDecodedID = _revertDecodedID;
163+
}
164+
165+
function getToken(bool useUnderlying) public view override returns (IERC20 token, bool isETH) {
166+
if (useUnderlying) {
167+
(token, /* */) = getUnderlyingToken();
168+
} else {
169+
(token, /* */, /* */) = getAssetToken();
170+
}
171+
isETH = address(token) == ETH_ADDRESS;
172+
}
173+
174+
175+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.11;
3+
import { wfCashERC4626 } from "wrapped-fcash/contracts/wfCashERC4626.sol";
4+
import { INotionalV2 } from "wrapped-fcash/interfaces/notional/INotionalV2.sol";
5+
import { IWETH9 } from "wrapped-fcash/interfaces/IWETH9.sol";
6+
7+
contract WrappedfCash is wfCashERC4626 {
8+
constructor(INotionalV2 _notionalProxy, IWETH9 _weth) wfCashERC4626(_notionalProxy, _weth){
9+
}
10+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.11;
3+
import { WrappedfCashFactory as WrappedfCashFactoryBase } from "wrapped-fcash/contracts/proxy/WrappedfCashFactory.sol";
4+
5+
contract WrappedfCashFactory is WrappedfCashFactoryBase {
6+
constructor(address _beacon) WrappedfCashFactoryBase(_beacon){
7+
}
8+
}

0 commit comments

Comments
 (0)