Skip to content

Commit a0647b9

Browse files
authored
update IssuanceModule (#201)
* update IssuanceModule * Bump package to 0.1.14
1 parent 9d1a311 commit a0647b9

File tree

3 files changed

+18
-726
lines changed

3 files changed

+18
-726
lines changed
Lines changed: 7 additions & 262 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2020 Set Labs Inc.
2+
Copyright 2022 Set Labs Inc.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -19,278 +19,23 @@
1919
pragma solidity 0.6.10;
2020
pragma experimental "ABIEncoderV2";
2121

22-
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
23-
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
24-
import { SafeCast } from "@openzeppelin/contracts/utils/SafeCast.sol";
25-
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
26-
import { SignedSafeMath } from "@openzeppelin/contracts/math/SignedSafeMath.sol";
27-
22+
import { DebtIssuanceModuleV2 } from "./DebtIssuanceModuleV2.sol";
2823
import { IController } from "../../interfaces/IController.sol";
29-
import { IManagerIssuanceHook } from "../../interfaces/IManagerIssuanceHook.sol";
30-
import { IModuleIssuanceHook } from "../../interfaces/IModuleIssuanceHook.sol";
31-
import { Invoke } from "../lib/Invoke.sol";
32-
import { ISetToken } from "../../interfaces/ISetToken.sol";
33-
import { ModuleBase } from "../lib/ModuleBase.sol";
34-
import { Position } from "../lib/Position.sol";
35-
import { PreciseUnitMath } from "../../lib/PreciseUnitMath.sol";
3624

3725
/**
3826
* @title IssuanceModule
3927
* @author Set Protocol
4028
*
41-
* The IssuanceModule is a module that enables users to issue and redeem SetTokens that contain default and
42-
* non-debt external Positions. Managers are able to set an external contract hook that is called before an
43-
* issuance is called.
29+
* The IssuanceModule is a module that enables users to issue and redeem SetTokens that contain default and all
30+
* external positions, including debt positions. The manager can define arbitrary issuance logic in the manager
31+
* hook, as well as specify issue and redeem fees. The manager can remove the module.
4432
*/
45-
contract IssuanceModule is ModuleBase, ReentrancyGuard {
46-
using Invoke for ISetToken;
47-
using Position for ISetToken;
48-
using PreciseUnitMath for uint256;
49-
using SafeMath for uint256;
50-
using SafeCast for int256;
51-
using SignedSafeMath for int256;
52-
53-
/* ============ Events ============ */
54-
55-
event SetTokenIssued(address indexed _setToken, address _issuer, address _to, address _hookContract, uint256 _quantity);
56-
event SetTokenRedeemed(address indexed _setToken, address _redeemer, address _to, uint256 _quantity);
57-
58-
/* ============ State Variables ============ */
59-
60-
// Mapping of SetToken to Issuance hook configurations
61-
mapping(ISetToken => IManagerIssuanceHook) public managerIssuanceHook;
33+
contract IssuanceModule is DebtIssuanceModuleV2 {
6234

6335
/* ============ Constructor ============ */
6436

6537
/**
6638
* Set state controller state variable
6739
*/
68-
constructor(IController _controller) public ModuleBase(_controller) {}
69-
70-
/* ============ External Functions ============ */
71-
72-
/**
73-
* Deposits components to the SetToken and replicates any external module component positions and mints
74-
* the SetToken. Any issuances with SetTokens that have external positions with negative unit will revert.
75-
*
76-
* @param _setToken Instance of the SetToken contract
77-
* @param _quantity Quantity of the SetToken to mint
78-
* @param _to Address to mint SetToken to
79-
*/
80-
function issue(
81-
ISetToken _setToken,
82-
uint256 _quantity,
83-
address _to
84-
)
85-
external
86-
nonReentrant
87-
onlyValidAndInitializedSet(_setToken)
88-
{
89-
require(_quantity > 0, "Issue quantity must be > 0");
90-
91-
address hookContract = _callPreIssueHooks(_setToken, _quantity, msg.sender, _to);
92-
93-
(
94-
address[] memory components,
95-
uint256[] memory componentQuantities
96-
) = getRequiredComponentIssuanceUnits(_setToken, _quantity, true);
97-
98-
// For each position, transfer the required underlying to the SetToken and call external module hooks
99-
for (uint256 i = 0; i < components.length; i++) {
100-
transferFrom(
101-
IERC20(components[i]),
102-
msg.sender,
103-
address(_setToken),
104-
componentQuantities[i]
105-
);
106-
107-
_executeExternalPositionHooks(_setToken, _quantity, IERC20(components[i]), true);
108-
}
109-
110-
_setToken.mint(_to, _quantity);
111-
112-
emit SetTokenIssued(address(_setToken), msg.sender, _to, hookContract, _quantity);
113-
}
114-
115-
/**
116-
* Burns a user's SetToken of specified quantity, unwinds external positions, and returns components
117-
* to the specified address. Does not work for debt/negative external positions.
118-
*
119-
* @param _setToken Instance of the SetToken contract
120-
* @param _quantity Quantity of the SetToken to redeem
121-
* @param _to Address to send component assets to
122-
*/
123-
function redeem(
124-
ISetToken _setToken,
125-
uint256 _quantity,
126-
address _to
127-
)
128-
external
129-
nonReentrant
130-
onlyValidAndInitializedSet(_setToken)
131-
{
132-
require(_quantity > 0, "Redeem quantity must be > 0");
133-
134-
_setToken.burn(msg.sender, _quantity);
135-
136-
(
137-
address[] memory components,
138-
uint256[] memory componentQuantities
139-
) = getRequiredComponentIssuanceUnits(_setToken, _quantity, false);
140-
141-
for (uint256 i = 0; i < components.length; i++) {
142-
_executeExternalPositionHooks(_setToken, _quantity, IERC20(components[i]), false);
143-
144-
_setToken.strictInvokeTransfer(
145-
components[i],
146-
_to,
147-
componentQuantities[i]
148-
);
149-
}
150-
151-
emit SetTokenRedeemed(address(_setToken), msg.sender, _to, _quantity);
152-
}
153-
154-
/**
155-
* Initializes this module to the SetToken with issuance-related hooks. Only callable by the SetToken's manager.
156-
* Hook addresses are optional. Address(0) means that no hook will be called
157-
*
158-
* @param _setToken Instance of the SetToken to issue
159-
* @param _preIssueHook Instance of the Manager Contract with the Pre-Issuance Hook function
160-
*/
161-
function initialize(
162-
ISetToken _setToken,
163-
IManagerIssuanceHook _preIssueHook
164-
)
165-
external
166-
onlySetManager(_setToken, msg.sender)
167-
onlyValidAndPendingSet(_setToken)
168-
{
169-
managerIssuanceHook[_setToken] = _preIssueHook;
170-
171-
_setToken.initializeModule();
172-
}
173-
174-
/**
175-
* Reverts as this module should not be removable after added. Users should always
176-
* have a way to redeem their Sets
177-
*/
178-
function removeModule() external override {
179-
revert("The IssuanceModule module cannot be removed");
180-
}
181-
182-
/* ============ External Getter Functions ============ */
183-
184-
/**
185-
* Retrieves the addresses and units required to issue/redeem a particular quantity of SetToken.
186-
*
187-
* @param _setToken Instance of the SetToken to issue
188-
* @param _quantity Quantity of SetToken to issue
189-
* @param _isIssue Boolean whether the quantity is issuance or redemption
190-
* @return address[] List of component addresses
191-
* @return uint256[] List of component units required for a given SetToken quantity
192-
*/
193-
function getRequiredComponentIssuanceUnits(
194-
ISetToken _setToken,
195-
uint256 _quantity,
196-
bool _isIssue
197-
)
198-
public
199-
view
200-
returns (address[] memory, uint256[] memory)
201-
{
202-
(
203-
address[] memory components,
204-
uint256[] memory issuanceUnits
205-
) = _getTotalIssuanceUnits(_setToken);
206-
207-
uint256[] memory notionalUnits = new uint256[](components.length);
208-
for (uint256 i = 0; i < issuanceUnits.length; i++) {
209-
// Use preciseMulCeil to round up to ensure overcollateration when small issue quantities are provided
210-
// and preciseMul to round down to ensure overcollateration when small redeem quantities are provided
211-
notionalUnits[i] = _isIssue ?
212-
issuanceUnits[i].preciseMulCeil(_quantity) :
213-
issuanceUnits[i].preciseMul(_quantity);
214-
}
215-
216-
return (components, notionalUnits);
217-
}
218-
219-
/* ============ Internal Functions ============ */
220-
221-
/**
222-
* Retrieves the component addresses and list of total units for components. This will revert if the external unit
223-
* is ever equal or less than 0 .
224-
*/
225-
function _getTotalIssuanceUnits(ISetToken _setToken) internal view returns (address[] memory, uint256[] memory) {
226-
address[] memory components = _setToken.getComponents();
227-
uint256[] memory totalUnits = new uint256[](components.length);
228-
229-
for (uint256 i = 0; i < components.length; i++) {
230-
address component = components[i];
231-
int256 cumulativeUnits = _setToken.getDefaultPositionRealUnit(component);
232-
233-
address[] memory externalModules = _setToken.getExternalPositionModules(component);
234-
if (externalModules.length > 0) {
235-
for (uint256 j = 0; j < externalModules.length; j++) {
236-
int256 externalPositionUnit = _setToken.getExternalPositionRealUnit(component, externalModules[j]);
237-
238-
require(externalPositionUnit > 0, "Only positive external unit positions are supported");
239-
240-
cumulativeUnits = cumulativeUnits.add(externalPositionUnit);
241-
}
242-
}
243-
244-
totalUnits[i] = cumulativeUnits.toUint256();
245-
}
246-
247-
return (components, totalUnits);
248-
}
249-
250-
/**
251-
* If a pre-issue hook has been configured, call the external-protocol contract. Pre-issue hook logic
252-
* can contain arbitrary logic including validations, external function calls, etc.
253-
* Note: All modules with external positions must implement ExternalPositionIssueHooks
254-
*/
255-
function _callPreIssueHooks(
256-
ISetToken _setToken,
257-
uint256 _quantity,
258-
address _caller,
259-
address _to
260-
)
261-
internal
262-
returns(address)
263-
{
264-
IManagerIssuanceHook preIssueHook = managerIssuanceHook[_setToken];
265-
if (address(preIssueHook) != address(0)) {
266-
preIssueHook.invokePreIssueHook(_setToken, _quantity, _caller, _to);
267-
return address(preIssueHook);
268-
}
269-
270-
return address(0);
271-
}
272-
273-
/**
274-
* For each component's external module positions, calculate the total notional quantity, and
275-
* call the module's issue hook or redeem hook.
276-
* Note: It is possible that these hooks can cause the states of other modules to change.
277-
* It can be problematic if the a hook called an external function that called back into a module, resulting in state inconsistencies.
278-
*/
279-
function _executeExternalPositionHooks(
280-
ISetToken _setToken,
281-
uint256 _setTokenQuantity,
282-
IERC20 _component,
283-
bool isIssue
284-
)
285-
internal
286-
{
287-
address[] memory externalPositionModules = _setToken.getExternalPositionModules(address(_component));
288-
for (uint256 i = 0; i < externalPositionModules.length; i++) {
289-
if (isIssue) {
290-
IModuleIssuanceHook(externalPositionModules[i]).componentIssueHook(_setToken, _setTokenQuantity, _component, true);
291-
} else {
292-
IModuleIssuanceHook(externalPositionModules[i]).componentRedeemHook(_setToken, _setTokenQuantity, _component, true);
293-
}
294-
}
295-
}
40+
constructor(IController _controller) public DebtIssuanceModuleV2(_controller) {}
29641
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@setprotocol/set-protocol-v2",
3-
"version": "0.1.13",
3+
"version": "0.1.14",
44
"description": "",
55
"main": "dist",
66
"types": "dist/types",

0 commit comments

Comments
 (0)