Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Notional Trade Module #251

Merged
merged 121 commits into from
May 23, 2022
Merged
Show file tree
Hide file tree
Changes from 93 commits
Commits
Show all changes
121 commits
Select commit Hold shift + click to select a range
119c11f
Add skeleton code for NotionalTradeModule
Apr 16, 2022
9db0704
Add skeleton test for NotionalTradeModule
Apr 16, 2022
83cdd7c
Add component issuance hook
Apr 17, 2022
816958e
Add WrappedfCash Interface and Mock
Apr 17, 2022
2ae8c4a
Remove wrongly cased file
Apr 17, 2022
905ef8b
Add WrappedFCash ERC-777 Interface and Mock
Apr 17, 2022
8e4e271
Extend notional trade module code with mint / and redeem helper funct…
Apr 20, 2022
88934db
Adjust redeem function to use operatorBurn and add input token transf…
Apr 20, 2022
9a0e041
Remove component after redemption
Apr 21, 2022
0143a47
Add skeleton integration test for NotionalTradeModule
Apr 21, 2022
0fc426e
Deploy notional trade module in test
Apr 21, 2022
a76bbeb
Install notional solidity sdk from github branch
Apr 21, 2022
cf669fb
Add 0.8.11 solidity compiler
Apr 22, 2022
d476e02
Import notional-solidity-sdk
Apr 22, 2022
15013d5
Create derivative contract from imported WrappedfCash
Apr 22, 2022
3841b8a
Upgrade notional-solidity-sdk
Apr 22, 2022
fe5c5e5
Deploy WrappedFCashBeacon in notional trade module test
Apr 22, 2022
a7df03a
Deploy WrappedFCashFactory in notional trade module test
Apr 22, 2022
f353aa9
Add some extra tests against notional proxy
Apr 22, 2022
6345496
Adjustments to enable the notional tests to be run against mainnet no…
Apr 22, 2022
8b2d37a
Import newer version of notional solidity sdk fork with MarketData st…
Apr 24, 2022
8772bfd
Test view methods of wrappedFCash contract deployed from factory
Apr 24, 2022
472e343
Downgrade notional-solidity-sdk version
Apr 24, 2022
7ca7726
Add mint test
Apr 25, 2022
df79de9
Try to deploy Notional upgrade in test
Apr 26, 2022
b502bc9
fix invalid bytecode
Apr 26, 2022
83eb617
Finish notional setup in integration test
Apr 26, 2022
508e9b9
Additional comment in failing mint test
Apr 27, 2022
eafb521
Log out selected maturity in test
Apr 27, 2022
f2ab1a6
Swap out notional compiled bytecodes to make test work
Apr 27, 2022
a202151
Refactor wrappeFCash tests
Apr 28, 2022
45d5076
Refactor and simplify integration tests
Apr 28, 2022
a172947
Sell and buy trades via notional trade module are working
Apr 29, 2022
7a69bef
More tests
Apr 29, 2022
d5a5ea7
add check on minReceiveQuantity when redeeming
Apr 29, 2022
ad77c24
Use logic to adjust positions from trade module
Apr 30, 2022
486b828
Additional tests
Apr 30, 2022
c6afc32
Adjust trade interface to remove useUnderlying parameter
May 2, 2022
82229cd
Add docstrings for notional trade module
May 2, 2022
114fed5
Refactoring and adding redeem / mint events to NotionalTradeModule
May 2, 2022
fdc8e60
Remove unneeded wrappedFCash test
May 2, 2022
07e10b7
Adjust wbtcRate in integration test to be in line with new pinned blo…
May 2, 2022
aed6f34
Remove unneeded contracts
May 4, 2022
e45147f
Import new wrappedFcash repo instead of old one
May 4, 2022
db0cf2d
Add WrappedFCash and nBeaconProxy back in
May 4, 2022
c79c075
Get minting to work with new interface
May 4, 2022
f980ed3
Replace operatorBurn with calling redeem method via set token
May 5, 2022
28c2517
Add tolerance in component position change of sendToken
May 5, 2022
3c8a62d
Update WrappedFCash interface and mock
May 6, 2022
a201eea
Deploy WrappedFCashMock in unittest
May 6, 2022
57a0a41
Add trade scenarios to unit tests
May 8, 2022
ab56837
Add unit tests for issuance / redemption hook
May 9, 2022
e4ef888
Add test for getFCashPositions getter
May 9, 2022
f503577
Remove logs from wrappedfCashMock
May 9, 2022
f711c80
Add unit test for removeModule method
May 9, 2022
aba4ad4
Add registerToModule unittest
May 9, 2022
ab30064
Fix registerToModule
May 9, 2022
62d9677
Add additional unit test cases
May 9, 2022
405917e
More unittest cases for managing allowed set tokens
May 9, 2022
84404ca
Test cases for wrong in/output amounts during trades
May 9, 2022
4071e0e
Fill coverage gaps in notionalModule unittests
May 9, 2022
7d03a77
Add test case executing trade twice to fill last coverage gap
May 10, 2022
f748e21
Deregister fcash positions when redeeming
May 10, 2022
f2db5bd
Add external methods to add / remove registered fcash positions
May 10, 2022
c0eb2cd
Add todo comments and javadoc params
May 10, 2022
e50ac7c
Undo empty lines added at end of contract files
May 10, 2022
0566d6c
Undo autoformatter changes
May 10, 2022
24f7cbb
Undo unnecessary changes to hardhat.config
May 10, 2022
6c040cc
Minor nits based on PR feedback
May 14, 2022
e7150ac
Add top level docstring to NotionalTradeModule
May 14, 2022
3276681
Add method to change redeemToUnderlying option and associated tests
May 14, 2022
1fef416
Fix unittests
May 14, 2022
7889355
Add unit test to set the redeemToUnderlying parameter
May 14, 2022
d7d167f
Adjust unit test to account for checks on wether given addresses are …
May 14, 2022
caf6e4b
Fix integrationtests
May 14, 2022
afb6e76
Add missing WrappedfCashFactory contracts
May 15, 2022
33b3cd9
Add test cases for invalid fcash wrapper address in addFCashPositions
May 15, 2022
c1b5769
Remove reliance on registered tokens when redeeming matured positions
May 15, 2022
fa85626
Add methods to redeem / mint fCash position using currencyId and matu…
May 15, 2022
9bb4ac8
Use new redeem / mint methods in integration test
May 15, 2022
abe0ce6
Remove old trade method
May 15, 2022
42b9fb5
Add test case for newly deployed wrapper in integration tests
May 15, 2022
95f0884
Reactivate the issuance / redeem hook tests
May 16, 2022
de22cde
Add additional test cases where isWrappedFCash returns false
May 16, 2022
6c55aa1
Fix typo
May 16, 2022
37113d6
Close more coverage gaps in isWrappedFCash
May 16, 2022
afbf54c
Add extra test case for negative position
May 16, 2022
d2e7ce1
Achieve negative unit on wrappedCash position in test
May 16, 2022
3bc0f2a
Add test case for trying to redeem an undeployed wrapper
May 16, 2022
1202d43
Add integration test case for trying to sell non deployed wrapper
May 16, 2022
c662f2b
Make wrapper factory immutable and add some comments
May 16, 2022
c42eb82
Add todo comment regarding inefficient fCash position listing
May 16, 2022
800c0f8
Combine all notional abis to single line files
May 17, 2022
a943e56
Replace separate calls to getCurrencyId and getMaturity with single c…
May 18, 2022
32436ed
Remove duplicated IERC20 import
May 18, 2022
ca922c4
Replace _getPaymentToken with getToken on fcash wrapper
May 18, 2022
f0b7ae6
Use function selectors instead of string signatures for encoding
May 18, 2022
678f988
Parameterize integration tests to be run for different asset tokens
May 18, 2022
688e0ae
Fix type error in integration test
May 18, 2022
07cba1f
Add eth case to parameterized integration tests
May 18, 2022
e06b679
remove duplicated test
May 18, 2022
280e347
Merge a few tests to speed up integration pipeline
May 18, 2022
aa75a94
Revert accidental change in synthetixExchangeAdapter test
May 18, 2022
bb1e376
Speed up tests using evm snapshots
May 19, 2022
15cec5b
Fix failing test due to rounding error / deviation
May 19, 2022
c18fccb
Reactivate cEth testcase
May 19, 2022
14195e0
Integrate updated wrapper version with weth support
May 19, 2022
23a58ec
Extend unittest to cover eth/weth case
May 19, 2022
e4eb15c
Fix unittests
May 19, 2022
ab1de26
Speed up unittests
May 19, 2022
ec9d610
Only approve as much as necessary
May 19, 2022
8f4ad8c
Fix unittests
May 20, 2022
d170363
Add test case for reverting mint call
May 20, 2022
725bdaf
Reactivate sell scenario
May 20, 2022
d0e6a19
Add test case for when token is not registered component
May 20, 2022
f1b11c0
Adjust unit tests and add check if module is smartContract in removeM…
May 21, 2022
f4eb2d9
Add unit test case illustrating issue when adding EOA as module
May 21, 2022
67d5bc6
Add check requiring send token to be registered component
May 22, 2022
17e65c1
Parameterize tests to trade less/equal/more than registered position
May 23, 2022
39f2e75
Remove outdated TODO comments and logs
May 23, 2022
2245d1b
Merge remote-tracking branch 'upstream/master' into ckoopmann/notiona…
May 23, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .solhint.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
"extends": "solhint:recommended",
"rules": {
"reason-string": ["warn", { "maxLength": 50 }],
"compiler-version": ["error", "0.6.10"]
"compiler-version": ["error", ">=0.6.10"]
}
}
1 change: 1 addition & 0 deletions .solhintignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ node_modules
contracts/mocks
contracts/Migrations.sol
contracts/external
contracts/protocol/integration/wrap/notional
73 changes: 73 additions & 0 deletions contracts/interfaces/IWrappedFCash.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.6.10;
pragma experimental "ABIEncoderV2";

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
ckoopmann marked this conversation as resolved.
Show resolved Hide resolved

/// @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
}

interface IWrappedfCash {
function initialize(uint16 currencyId, uint40 maturity) external;

/// @notice Mints wrapped fCash ERC20 tokens
function mintViaAsset(
uint256 depositAmountExternal,
uint88 fCashAmount,
address receiver,
uint32 minImpliedRate
) external;

function mintViaUnderlying(
uint256 depositAmountExternal,
uint88 fCashAmount,
address receiver,
uint32 minImpliedRate
) external;

function redeemToAsset(uint256 amount, address receiver, uint32 maxImpliedRate) external;
function redeemToUnderlying(uint256 amount, address receiver, uint32 maxImpliedRate) external;

/// @notice Returns the underlying fCash ID of the token
function getfCashId() external view returns (uint256);

/// @notice Returns the underlying fCash maturity of the token
function getMaturity() external view returns (uint40 maturity);

/// @notice True if the fCash has matured, assets mature exactly on the block time
function hasMatured() external view returns (bool);

/// @notice Returns the underlying fCash currency
function getCurrencyId() external view returns (uint16 currencyId);

/// @notice Returns the components of the fCash idd
function getDecodedID() external view returns (uint16 currencyId, uint40 maturity);

/// @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() external view returns (uint8);

/// @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() external view returns (IERC20 underlyingToken, int256 underlyingPrecision);

/// @notice Returns the asset token which the fCash settles to. This will be an interest
/// bearing token like a cToken or aToken.
function getAssetToken() external view returns (IERC20 assetToken, int256 assetPrecision, TokenType tokenType);
}


interface IWrappedfCashComplete is IWrappedfCash, IERC20 {}
9 changes: 9 additions & 0 deletions contracts/interfaces/IWrappedFCashFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.6.10;

interface IWrappedfCashFactory {
function deployWrapper(uint16 currencyId, uint40 maturity) external returns(address);
function computeAddress(uint16 currencyId, uint40 maturity) external view returns(address);
}


5 changes: 5 additions & 0 deletions contracts/interfaces/external/INotionalProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;
ckoopmann marked this conversation as resolved.
Show resolved Hide resolved
import { NotionalProxy } from "notional-solidity-sdk/interfaces/notional/NotionalProxy.sol";

interface INotionalProxy is NotionalProxy {}
50 changes: 50 additions & 0 deletions contracts/mocks/WrappedfCashFactoryMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
Copyright 2022 Set Labs Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

SPDX-License-Identifier: Apache License, Version 2.0
*/

pragma solidity 0.6.10;
pragma experimental "ABIEncoderV2";

import { IWrappedfCashFactory } from "../interfaces/IWrappedFCashFactory.sol";
import { WrappedfCashMock } from "./WrappedfCashMock.sol";


// mock class using BasicToken
contract WrappedfCashFactoryMock is IWrappedfCashFactory {

mapping(uint16 => mapping(uint40 => address)) paramsToAddress;
bool private revertComputeAddress;

function registerWrapper(uint16 _currencyId, uint40 _maturity, address _fCashWrapper) external {
paramsToAddress[_currencyId][_maturity] = _fCashWrapper;
}

function deployWrapper(uint16 _currencyId, uint40 _maturity) external override returns(address) {
return computeAddress(_currencyId, _maturity);
}

function computeAddress(uint16 _currencyId, uint40 _maturity) public view override returns(address) {
require(!revertComputeAddress, "Test revertion ComputeAddress");
return paramsToAddress[_currencyId][_maturity];
}

function setRevertComputeAddress(bool _revertComputeAddress) external{
revertComputeAddress = _revertComputeAddress;
}


}
168 changes: 168 additions & 0 deletions contracts/mocks/WrappedfCashMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/*
ckoopmann marked this conversation as resolved.
Show resolved Hide resolved
Copyright 2022 Set Labs Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

SPDX-License-Identifier: Apache License, Version 2.0
*/

pragma solidity 0.6.10;
pragma experimental "ABIEncoderV2";

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { TokenType, IWrappedfCash } from "../interfaces/IWrappedFCash.sol";

import "hardhat/console.sol";

// mock class using BasicToken
contract WrappedfCashMock is ERC20, IWrappedfCash {

uint256 private fCashId;
uint40 private maturity;
bool private matured;
uint16 private currencyId;
uint8 private marketIndex;
IERC20 private underlyingToken;
int256 private underlyingPrecision;
IERC20 private assetToken;
int256 private assetPrecision;
TokenType private tokenType;

bool private revertCurrencyId;
bool private revertMaturity;

uint256 private redeemTokenReturned;
uint256 private mintTokenSpent;

constructor (IERC20 _assetToken, IERC20 _underlyingToken) public ERC20("FCashMock", "FCM") {
assetToken = _assetToken;
underlyingToken = _underlyingToken;
}

function initialize(uint16 _currencyId, uint40 _maturity) external override {
currencyId = _currencyId;
maturity = _maturity;
}

/// @notice Mints wrapped fCash ERC20 tokens
function mintViaAsset(
uint256 depositAmountExternal,
uint88 fCashAmount,
address receiver,
uint32 /* minImpliedRate */
) external override{
uint256 assetTokenAmount = mintTokenSpent == 0 ? depositAmountExternal : mintTokenSpent;
assetToken.transferFrom(msg.sender, address(this), assetTokenAmount);
_mint(receiver, fCashAmount);
}

function mintViaUnderlying(
uint256 depositAmountExternal,
uint88 fCashAmount,
address receiver,
uint32 /* minImpliedRate */
) external override{
uint256 underlyingTokenAmount = mintTokenSpent == 0 ? depositAmountExternal : mintTokenSpent;
underlyingToken.transferFrom(msg.sender, address(this), underlyingTokenAmount);
_mint(receiver, fCashAmount);
}


function redeemToAsset(
uint256 amount,
address receiver,
uint32 /* maxImpliedRate */
) external override {
_burn(msg.sender, amount);
uint256 assetTokenAmount = redeemTokenReturned == 0 ? amount : redeemTokenReturned;
assetToken.transfer(receiver, assetTokenAmount);
}

function redeemToUnderlying(
uint256 amount,
address receiver,
uint32 /* maxImpliedRate */
) external override {
_burn(msg.sender, amount);
uint256 underlyingTokenAmount = redeemTokenReturned == 0 ? amount : redeemTokenReturned;
underlyingToken.transfer(receiver, underlyingTokenAmount);
}

/// @notice Returns the underlying fCash ID of the token
function getfCashId() external override view returns (uint256) {
return fCashId;
}

/// @notice Returns the underlying fCash maturity of the token
function getMaturity() external override view returns (uint40) {
require(!revertMaturity, "Test revertion Maturity");
return maturity;
}

/// @notice True if the fCash has matured, assets mature exactly on the block time
function hasMatured() external override view returns (bool) {
return matured;
}

/// @notice Returns the underlying fCash currency
function getCurrencyId() external override view returns (uint16) {
require(!revertCurrencyId, "Test revertion CurrencyId");
return currencyId;
}

/// @notice Returns the components of the fCash idd
function getDecodedID() external override view returns (uint16, uint40) {
return (currencyId, maturity);
}

/// @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() external override view returns (uint8) {
return 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() external override view returns (IERC20, int256) {
return (underlyingToken, underlyingPrecision);
}

/// @notice Returns the asset token which the fCash settles to. This will be an interest
/// bearing token like a cToken or aToken.
function getAssetToken() external override view returns (IERC20, int256, TokenType) {
return (assetToken, assetPrecision, tokenType);
}

function setMatured(bool _matured) external{
matured = _matured;
}

function setRedeemTokenReturned(uint256 _redeemTokenReturned) external{
redeemTokenReturned = _redeemTokenReturned;
}

function setMintTokenSpent(uint256 _mintTokenSpent) external{
mintTokenSpent = _mintTokenSpent;
}

function setRevertCurrencyId(bool _revertCurrencyId) external{
revertCurrencyId = _revertCurrencyId;
}

function setRevertMaturity(bool _revertMaturity) external{
revertMaturity = _revertMaturity;
}

}
9 changes: 9 additions & 0 deletions contracts/protocol/integration/wrap/notional/WrappedfCash.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.11;
import { wfCashERC4626 } from "wrapped-fcash/contracts/wfCashERC4626.sol";
import { INotionalV2 } from "wrapped-fcash/interfaces/notional/INotionalV2.sol";

contract WrappedfCash is wfCashERC4626 {
constructor(INotionalV2 _notionalProxy) wfCashERC4626(_notionalProxy){
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.11;
import { WrappedfCashFactory as WrappedfCashFactoryBase } from "wrapped-fcash/contracts/proxy/WrappedfCashFactory.sol";

contract WrappedfCashFactory is WrappedfCashFactoryBase {
constructor(address _beacon) WrappedfCashFactoryBase(_beacon){
}
}
7 changes: 7 additions & 0 deletions contracts/protocol/integration/wrap/notional/nBeaconProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.11;
import { nBeaconProxy as nBeaconProxyBase } from "wrapped-fcash/contracts/proxy/nBeaconProxy.sol";

contract nBeaconProxy is nBeaconProxyBase {
constructor(address beacon, bytes memory data) payable nBeaconProxyBase(beacon, data) { }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.11;

import "openzeppelin-contracts-V4/proxy/beacon/UpgradeableBeacon.sol";

/// @dev Re-exporting to make available to brownie
/// UpgradeableBeacon is Ownable, default owner is the deployer
contract nUpgradeableBeacon is UpgradeableBeacon {
constructor(address implementation_) UpgradeableBeacon(implementation_) {}
}

Loading