• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IndexCoop / index-protocol / 3386a5b4-1090-4a99-af92-0eee2a8178c4

05 Feb 2025 06:12AM UTC coverage: 90.86% (-5.1%) from 95.955%
3386a5b4-1090-4a99-af92-0eee2a8178c4

push

circleci

ckoopmann
feat: Aero Slipstream Exchange Adapter

2178 of 2520 branches covered (86.43%)

Branch coverage included in aggregate %.

0 of 11 new or added lines in 1 file covered. (0.0%)

127 existing lines in 10 files now uncovered.

3429 of 3651 relevant lines covered (93.92%)

204.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

60.38
/contracts/protocol/modules/v1/BasicIssuanceModule.sol
1
/*
2
    Copyright 2020 Set Labs Inc.
3
    Licensed under the Apache License, Version 2.0 (the "License");
4
    you may not use this file except in compliance with the License.
5
    You may obtain a copy of the License at
6
    http://www.apache.org/licenses/LICENSE-2.0
7
    Unless required by applicable law or agreed to in writing, software
8
    distributed under the License is distributed on an "AS IS" BASIS,
9
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
    See the License for the specific language governing permissions and
11
    limitations under the License.
12
    SPDX-License-Identifier: Apache License, Version 2.0
13
*/
14

15
pragma solidity 0.6.10;
16
pragma experimental "ABIEncoderV2";
17

18
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
19
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
20
import { SafeCast } from "@openzeppelin/contracts/utils/SafeCast.sol";
21
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
22

23
import { IController } from "../../../interfaces/IController.sol";
24
import { IManagerIssuanceHook } from "../../../interfaces/IManagerIssuanceHook.sol";
25
import { Invoke } from "../../lib/Invoke.sol";
26
import { ISetToken } from "../../../interfaces/ISetToken.sol";
27
import { ModuleBase } from "../../lib/ModuleBase.sol";
28
import { Position } from "../../lib/Position.sol";
29
import { PreciseUnitMath } from "../../../lib/PreciseUnitMath.sol";
30

31
/**
32
 * @title BasicIssuanceModule
33
 * @author Set Protocol
34
 *
35
 * Module that enables issuance and redemption functionality on a SetToken. This is a module that is
36
 * required to bring the totalSupply of a Set above 0.
37
 */
38
contract BasicIssuanceModule is ModuleBase, ReentrancyGuard {
39
    using Invoke for ISetToken;
40
    using Position for ISetToken.Position;
41
    using Position for ISetToken;
42
    using PreciseUnitMath for uint256;
43
    using SafeMath for uint256;
44
    using SafeCast for int256;
45

46
    /* ============ Events ============ */
47

48
    event SetTokenIssued(
49
        address indexed _setToken,
50
        address indexed _issuer,
51
        address indexed _to,
52
        address _hookContract,
53
        uint256 _quantity
54
    );
55
    event SetTokenRedeemed(
56
        address indexed _setToken,
57
        address indexed _redeemer,
58
        address indexed _to,
59
        uint256 _quantity
60
    );
61

62
    /* ============ State Variables ============ */
63

64
    // Mapping of SetToken to Issuance hook configurations
65
    mapping(ISetToken => IManagerIssuanceHook) public managerIssuanceHook;
66

67
    /* ============ Constructor ============ */
68

69
    /**
70
     * Set state controller state variable
71
     *
72
     * @param _controller             Address of controller contract
73
     */
74
    constructor(IController _controller) public ModuleBase(_controller) {}
75

76
    /* ============ External Functions ============ */
77

78
    /**
79
     * Deposits the SetToken's position components into the SetToken and mints the SetToken of the given quantity
80
     * to the specified _to address. This function only handles Default Positions (positionState = 0).
81
     *
82
     * @param _setToken             Instance of the SetToken contract
83
     * @param _quantity             Quantity of the SetToken to mint
84
     * @param _to                   Address to mint SetToken to
85
     */
86
    function issue(
87
        ISetToken _setToken,
88
        uint256 _quantity,
89
        address _to
90
    )
91
        external
92
        nonReentrant
319!
93
        onlyValidAndInitializedSet(_setToken)
319!
94
    {
95
        require(_quantity > 0, "Issue quantity must be > 0");
319!
96

97
        address hookContract = _callPreIssueHooks(_setToken, _quantity, msg.sender, _to);
319✔
98

99
        (
319✔
100
            address[] memory components,
101
            uint256[] memory componentQuantities
102
        ) = getRequiredComponentUnitsForIssue(_setToken, _quantity);
103

104
        // For each position, transfer the required underlying to the SetToken
105
        for (uint256 i = 0; i < components.length; i++) {
319✔
106
            // Transfer the component to the SetToken
107
            transferFrom(
561✔
108
                IERC20(components[i]),
109
                msg.sender,
110
                address(_setToken),
111
                componentQuantities[i]
112
            );
113
        }
114

115
        // Mint the SetToken
116
        _setToken.mint(_to, _quantity);
319✔
117

118
        emit SetTokenIssued(address(_setToken), msg.sender, _to, hookContract, _quantity);
318✔
119
    }
120

121
    /**
122
     * Redeems the SetToken's positions and sends the components of the given
123
     * quantity to the caller. This function only handles Default Positions (positionState = 0).
124
     *
125
     * @param _setToken             Instance of the SetToken contract
126
     * @param _quantity             Quantity of the SetToken to redeem
127
     * @param _to                   Address to send component assets to
128
     */
129
    function redeem(
130
        ISetToken _setToken,
131
        uint256 _quantity,
132
        address _to
133
    )
134
        external
135
        nonReentrant
1!
136
        onlyValidAndInitializedSet(_setToken)
1!
137
    {
138
        require(_quantity > 0, "Redeem quantity must be > 0");
1!
139

140
        // Burn the SetToken - ERC20's internal burn already checks that the user has enough balance
141
        _setToken.burn(msg.sender, _quantity);
1✔
142

143
        // For each position, invoke the SetToken to transfer the tokens to the user
UNCOV
144
        address[] memory components = _setToken.getComponents();
×
UNCOV
145
        for (uint256 i = 0; i < components.length; i++) {
×
UNCOV
146
            address component = components[i];
×
UNCOV
147
            require(!_setToken.hasExternalPosition(component), "Only default positions are supported");
×
148

UNCOV
149
            uint256 unit = _setToken.getDefaultPositionRealUnit(component).toUint256();
×
150

151
            // Use preciseMul to round down to ensure overcollateration when small redeem quantities are provided
UNCOV
152
            uint256 componentQuantity = _quantity.preciseMul(unit);
×
153

154
            // Instruct the SetToken to transfer the component to the user
UNCOV
155
            _setToken.strictInvokeTransfer(
×
156
                component,
157
                _to,
158
                componentQuantity
159
            );
160
        }
161

UNCOV
162
        emit SetTokenRedeemed(address(_setToken), msg.sender, _to, _quantity);
×
163
    }
164

165
    /**
166
     * Initializes this module to the SetToken with issuance-related hooks. Only callable by the SetToken's manager.
167
     * Hook addresses are optional. Address(0) means that no hook will be called
168
     *
169
     * @param _setToken             Instance of the SetToken to issue
170
     * @param _preIssueHook         Instance of the Manager Contract with the Pre-Issuance Hook function
171
     */
172
    function initialize(
173
        ISetToken _setToken,
174
        IManagerIssuanceHook _preIssueHook
175
    )
176
        external
177
        onlySetManager(_setToken, msg.sender)
235!
178
        onlyValidAndPendingSet(_setToken)
235!
179
    {
180
        managerIssuanceHook[_setToken] = _preIssueHook;
181

182
        _setToken.initializeModule();
235✔
183
    }
184

185
    /**
186
     * Reverts as this module should not be removable after added. Users should always
187
     * have a way to redeem their Sets
188
     */
189
    function removeModule() external override {
UNCOV
190
        revert("The BasicIssuanceModule module cannot be removed");
×
191
    }
192

193
    /* ============ External Getter Functions ============ */
194

195
    /**
196
     * Retrieves the addresses and units required to mint a particular quantity of SetToken.
197
     *
198
     * @param _setToken             Instance of the SetToken to issue
199
     * @param _quantity             Quantity of SetToken to issue
200
     * @return address[]            List of component addresses
201
     * @return uint256[]            List of component units required to issue the quantity of SetTokens
202
     */
203
    function getRequiredComponentUnitsForIssue(
204
        ISetToken _setToken,
205
        uint256 _quantity
206
    )
207
        public
208
        view
209
        onlyValidAndInitializedSet(_setToken)
319!
210
        returns (address[] memory, uint256[] memory)
211
    {
212
        address[] memory components = _setToken.getComponents();
319✔
213

214
        uint256[] memory notionalUnits = new uint256[](components.length);
319✔
215

216
        for (uint256 i = 0; i < components.length; i++) {
319✔
217
            require(!_setToken.hasExternalPosition(components[i]), "Only default positions are supported");
561!
218

219
            notionalUnits[i] = _setToken.getDefaultPositionRealUnit(components[i]).toUint256().preciseMulCeil(_quantity);
220
        }
221

222
        return (components, notionalUnits);
319✔
223
    }
224

225
    /* ============ Internal Functions ============ */
226

227
    /**
228
     * If a pre-issue hook has been configured, call the external-protocol contract. Pre-issue hook logic
229
     * can contain arbitrary logic including validations, external function calls, etc.
230
     */
231
    function _callPreIssueHooks(
232
        ISetToken _setToken,
233
        uint256 _quantity,
234
        address _caller,
235
        address _to
236
    )
237
        internal
238
        returns(address)
239
    {
240
        IManagerIssuanceHook preIssueHook = managerIssuanceHook[_setToken];
319✔
241
        if (address(preIssueHook) != address(0)) {
319✔
242
            preIssueHook.invokePreIssueHook(_setToken, _quantity, _caller, _to);
10✔
243
            return address(preIssueHook);
10✔
244
        }
245

246
        return address(0);
309✔
247
    }
248
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc