Skip to content

Commit f2d67d1

Browse files
authored
Add Debt Issuance Module V2 (#125)
* Add AaveDebtIssuanceModule contract * Module designed to issue/redeem SetTokens which hold one or more aTokens as components. * Refactor integration tests * Moved validation logic to IssuanceUtils library * Fix compilation bugs * Fix imports * Refactor IssuanceUtils library contract * Add new function which considers external positions * Rename functions * Refactor DebtIssuanceModuleV2 contract * Override _resolveDebtPositions function * Use new IssuanceUtils library contract * Improve javadocs * Add IssuanceUtils mock contract * Refactor _setQuantity parameter in both library functions * Add standard token with rounding error mock * Returns a variable balance of value based on the error value set * Will be helpful to mock the behaviour of aTokens * Add tests for Debt issuance module V2 contract * Skip ALM <> DIM integration tests * Make validation logic statelesss * Earlier we were assuming that burn takes place before and mint takes place after the validation logic is called. But these assumptions make the validation logic stateful, which is not ideal. By removing those assumptions and passing in the required value from outside the function, the validation logic becomes stateless, which allows it to be used at multiple places flexibly. * The validation functions now perform just the check. All required state to perform the check is passed from outside. * Add external debt position to SetToken in tests * Improve coverage: Add tests for issue/redeem quantity is 0 * Rename IssuanceUtils to IssuanceValidationUtils * Update docs to specify redeem requires transferring in debt from caller * Add more tests and improve existing ones * Newly added tests are an EXACT copy of the tests for DebtIssuanceModule. Only difference is this SetToken contains tokenWithRoundingError instead of weth as a default position. This is to ensure the DebtIssuanceModuleV2 behaves exactly similar to DebtIssuanceModule when there is no rounding error present in it's constituent components. * Existing tests are improved by introducing fees and by different revert messages for different reverts * Add more tests and optimize tests to complete faster * Bump package to 0.0.52
1 parent 70f926f commit f2d67d1

File tree

11 files changed

+1452
-20
lines changed

11 files changed

+1452
-20
lines changed
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
/*
2+
Copyright 2020 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+
21+
import "./StandardTokenMock.sol";
22+
import "@openzeppelin/contracts/math/SignedSafeMath.sol";
23+
import "@openzeppelin/contracts/math/SafeMath.sol";
24+
import "@openzeppelin/contracts/utils/SafeCast.sol";
25+
26+
contract StandardTokenWithRoundingErrorMock {
27+
using SignedSafeMath for int256;
28+
using SafeCast for int256;
29+
using SafeMath for uint256;
30+
event Transfer(
31+
address indexed from,
32+
address indexed to,
33+
uint256 value
34+
);
35+
36+
event Approval(
37+
address indexed owner,
38+
address indexed spender,
39+
uint256 value
40+
);
41+
42+
uint256 constant public decimals = 18;
43+
string public name;
44+
string public symbol;
45+
int256 public err;
46+
47+
mapping (address => uint256) public _balances;
48+
49+
mapping (address => mapping (address => uint256)) public _allowed;
50+
51+
uint256 public _totalSupply;
52+
53+
54+
constructor(
55+
address _initialAccount,
56+
uint256 _initialBalance,
57+
int256 _err,
58+
string memory _name,
59+
string memory _symbol,
60+
uint8 _decimals
61+
)
62+
public
63+
64+
{
65+
_balances[_initialAccount] = _initialBalance;
66+
_totalSupply = _initialBalance;
67+
name = _name;
68+
symbol = _symbol;
69+
err = _err;
70+
}
71+
72+
/**
73+
* @dev Returns balance of owner with the rounding error applied
74+
* @param owner address whose balance is to be returned
75+
*/
76+
function balanceOf(address owner) external view returns (uint256) {
77+
uint256 balance = _balances[owner];
78+
if (err >= 0) {
79+
return balance.add(err.toUint256());
80+
} else {
81+
uint256 absoluteError = err.mul(-1).toUint256();
82+
if (balance >= absoluteError) {
83+
return balance.sub(absoluteError);
84+
} else {
85+
return 0;
86+
}
87+
}
88+
}
89+
90+
/**
91+
* @dev Transfer tokens from one address to another
92+
* @param _from address The address which you want to send tokens from
93+
* @param _to address The address which you want to transfer to
94+
* @param _value uint256 the amount of tokens to be transferred
95+
*/
96+
function transferFrom(address _from, address _to, uint256 _value) external returns (bool) {
97+
require(_to != address(0), "to null");
98+
require(_value <= _balances[_from], "value greater than from balance");
99+
require(_value <= _allowed[_from][msg.sender], "value greater than allowed");
100+
101+
_balances[_from] = _balances[_from].sub(_value);
102+
_balances[_to] = _balances[_to].add(_value);
103+
_allowed[_from][msg.sender] = _allowed[_from][msg.sender].sub(_value);
104+
emit Transfer(_from, _to, _value);
105+
return true;
106+
}
107+
108+
/**
109+
* @dev Transfer tokens from one address to another
110+
* @param _to The address to transfer to.
111+
* @param _value The amount to be transferred.
112+
*/
113+
function transfer(address _to, uint256 _value) external returns (bool) {
114+
require(_to != address(0), "to null");
115+
require(_value <= _balances[msg.sender], "value greater than sender balance");
116+
117+
_balances[msg.sender] = _balances[msg.sender].sub(_value);
118+
_balances[_to] = _balances[_to].add(_value);
119+
emit Transfer(msg.sender, _to, _value);
120+
return true;
121+
}
122+
123+
function setError(int256 _err) external returns (bool) {
124+
err = _err;
125+
return true;
126+
}
127+
128+
function totalSupply() external view returns (uint256) {
129+
return _totalSupply;
130+
}
131+
132+
function allowance(
133+
address owner,
134+
address spender
135+
)
136+
external
137+
view
138+
returns (uint256)
139+
{
140+
return _allowed[owner][spender];
141+
}
142+
143+
function approve(address spender, uint256 value) external returns (bool) {
144+
require(spender != address(0));
145+
146+
_allowed[msg.sender][spender] = value;
147+
emit Approval(msg.sender, spender, value);
148+
return true;
149+
}
150+
151+
function increaseAllowance(
152+
address spender,
153+
uint256 addedValue
154+
)
155+
external
156+
returns (bool)
157+
{
158+
require(spender != address(0));
159+
160+
_allowed[msg.sender][spender] = (
161+
_allowed[msg.sender][spender].add(addedValue));
162+
emit Approval(msg.sender, spender, _allowed[msg.sender][spender]);
163+
return true;
164+
}
165+
166+
function decreaseAllowance(
167+
address spender,
168+
uint256 subtractedValue
169+
)
170+
external
171+
returns (bool)
172+
{
173+
require(spender != address(0));
174+
175+
_allowed[msg.sender][spender] = (
176+
_allowed[msg.sender][spender].sub(subtractedValue));
177+
emit Approval(msg.sender, spender, _allowed[msg.sender][spender]);
178+
return true;
179+
}
180+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
Copyright 2021 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+
21+
import { ISetToken } from "../../../interfaces/ISetToken.sol";
22+
import { IssuanceValidationUtils } from "../../../protocol/lib/IssuanceValidationUtils.sol";
23+
24+
contract IssuanceValidationUtilsMock {
25+
/* ============ External Functions ============ */
26+
27+
function testValidateCollateralizationPostTransferInPreHook(
28+
ISetToken _setToken,
29+
address _component,
30+
uint256 _initialSetSupply,
31+
uint256 _componentQuantity
32+
)
33+
external
34+
view
35+
{
36+
IssuanceValidationUtils.validateCollateralizationPostTransferInPreHook(
37+
_setToken,
38+
_component,
39+
_initialSetSupply,
40+
_componentQuantity
41+
);
42+
}
43+
44+
function testValidateCollateralizationPostTransferOut(
45+
ISetToken _setToken,
46+
address _component,
47+
uint256 _finalSetSupply
48+
)
49+
external
50+
view
51+
{
52+
IssuanceValidationUtils.validateCollateralizationPostTransferOut(
53+
_setToken,
54+
_component,
55+
_finalSetSupply
56+
);
57+
}
58+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
Copyright 2021 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+
21+
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
22+
import { SafeCast } from "@openzeppelin/contracts/utils/SafeCast.sol";
23+
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
24+
25+
import { ISetToken } from "../../interfaces/ISetToken.sol";
26+
import { PreciseUnitMath } from "../../lib/PreciseUnitMath.sol";
27+
28+
/**
29+
* @title IssuanceValidationUtils
30+
* @author Set Protocol
31+
*
32+
* A collection of utility functions to help during issuance/redemption of SetToken.
33+
*/
34+
library IssuanceValidationUtils {
35+
using SafeMath for uint256;
36+
using SafeCast for int256;
37+
using PreciseUnitMath for uint256;
38+
39+
/**
40+
* Validates component transfer IN to SetToken during issuance/redemption. Reverts if Set is undercollateralized post transfer.
41+
* NOTE: Call this function immediately after transfer IN but before calling external hooks (if any).
42+
*
43+
* @param _setToken Instance of the SetToken being issued/redeemed
44+
* @param _component Address of component being transferred in/out
45+
* @param _initialSetSupply Initial SetToken supply before issuance/redemption
46+
* @param _componentQuantity Amount of component transferred into SetToken
47+
*/
48+
function validateCollateralizationPostTransferInPreHook(
49+
ISetToken _setToken,
50+
address _component,
51+
uint256 _initialSetSupply,
52+
uint256 _componentQuantity
53+
)
54+
internal
55+
view
56+
{
57+
uint256 newComponentBalance = IERC20(_component).balanceOf(address(_setToken));
58+
59+
uint256 defaultPositionUnit = _setToken.getDefaultPositionRealUnit(address(_component)).toUint256();
60+
61+
require(
62+
// Use preciseMulCeil to increase the lower bound and maintain over-collateralization
63+
newComponentBalance >= _initialSetSupply.preciseMulCeil(defaultPositionUnit).add(_componentQuantity),
64+
"Invalid transfer in. Results in undercollateralization"
65+
);
66+
}
67+
68+
/**
69+
* Validates component transfer OUT of SetToken during issuance/redemption. Reverts if Set is undercollateralized post transfer.
70+
*
71+
* @param _setToken Instance of the SetToken being issued/redeemed
72+
* @param _component Address of component being transferred in/out
73+
* @param _finalSetSupply Final SetToken supply after issuance/redemption
74+
*/
75+
function validateCollateralizationPostTransferOut(
76+
ISetToken _setToken,
77+
address _component,
78+
uint256 _finalSetSupply
79+
)
80+
internal
81+
view
82+
{
83+
uint256 newComponentBalance = IERC20(_component).balanceOf(address(_setToken));
84+
85+
uint256 defaultPositionUnit = _setToken.getDefaultPositionRealUnit(address(_component)).toUint256();
86+
87+
require(
88+
// Use preciseMulCeil to increase lower bound and maintain over-collateralization
89+
newComponentBalance >= _finalSetSupply.preciseMulCeil(defaultPositionUnit),
90+
"Invalid transfer out. Results in undercollateralization"
91+
);
92+
}
93+
}

contracts/protocol/modules/DebtIssuanceModule.sol

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ contract DebtIssuanceModule is ModuleBase, ReentrancyGuard {
112112
address _to
113113
)
114114
external
115+
virtual
115116
nonReentrant
116117
onlyValidAndInitializedSet(_setToken)
117118
{
@@ -151,8 +152,9 @@ contract DebtIssuanceModule is ModuleBase, ReentrancyGuard {
151152
}
152153

153154
/**
154-
* Returns components from the SetToken, unwinds any external module component positions and burns
155-
* the SetToken. If the token has a debt position all debt will be paid down first then equity positions
155+
* Returns components from the SetToken, unwinds any external module component positions and burns the SetToken.
156+
* If the token has debt positions, the module transfers in the required debt amounts from the caller and uses
157+
* those funds to repay the debts on behalf of the SetToken. All debt will be paid down first then equity positions
156158
* will be returned to the minting address. If specified, a fee will be charged on redeem.
157159
*
158160
* @param _setToken Instance of the SetToken to redeem
@@ -165,6 +167,7 @@ contract DebtIssuanceModule is ModuleBase, ReentrancyGuard {
165167
address _to
166168
)
167169
external
170+
virtual
168171
nonReentrant
169172
onlyValidAndInitializedSet(_setToken)
170173
{

0 commit comments

Comments
 (0)