• 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

51.19
/contracts/protocol/modules/v1/CustomOracleNAVIssuanceModule.sol
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
pragma experimental "ABIEncoderV2";
21

22
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
23
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
24
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
25
import { SafeCast } from "@openzeppelin/contracts/utils/SafeCast.sol";
26
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
27
import { SignedSafeMath } from "@openzeppelin/contracts/math/SignedSafeMath.sol";
28

29
import { AddressArrayUtils } from "../../../lib/AddressArrayUtils.sol";
30
import { IController } from "../../../interfaces/IController.sol";
31
import { ISetValuer } from "../../../interfaces/ISetValuer.sol";
32
import { INAVIssuanceHook } from "../../../interfaces/INAVIssuanceHook.sol";
33
import { Invoke } from "../../lib/Invoke.sol";
34
import { ISetToken } from "../../../interfaces/ISetToken.sol";
35
import { IWETH } from "../../../interfaces/external/IWETH.sol";
36
import { ModuleBase } from "../../lib/ModuleBase.sol";
37
import { Position } from "../../lib/Position.sol";
38
import { PreciseUnitMath } from "../../../lib/PreciseUnitMath.sol";
39
import { ResourceIdentifier } from "../../lib/ResourceIdentifier.sol";
40

41
/**
42
 * @title CustomOracleNavIssuanceModule
43
 * @author Set Protocol
44
 *
45
 * Module that enables issuance and redemption with any valid ERC20 token or ETH if allowed by the manager. Sender receives
46
 * a proportional amount of SetTokens on issuance or ERC20 token on redemption based on the calculated net asset value using
47
 * oracle prices. Manager is able to enforce a premium / discount on issuance / redemption to avoid arbitrage and front
48
 * running when relying on oracle prices. Managers can charge a fee (denominated in reserve asset).
49
 * 
50
 * @dev WARNING: Components using oracle adapters for NAV calculations must be evaluated for potential 
51
 * read-only reentrancy vulnerabilities in the oracle's read function. Vulnerable components should 
52
 * not use this module. 
53
 * 
54
 * * CHANGELOG:
55
 * - 9/19/24: Introduce MAX_POSITION_MULTIPLIER to prevent precision loss
56
 */
57
contract CustomOracleNavIssuanceModule is ModuleBase, ReentrancyGuard {
58
    using AddressArrayUtils for address[];
59
    using Invoke for ISetToken;
60
    using Position for ISetToken;
61
    using PreciseUnitMath for uint256;
62
    using PreciseUnitMath for int256;
63
    using ResourceIdentifier for IController;
64
    using SafeMath for uint256;
65
    using SafeCast for int256;
66
    using SafeCast for uint256;
67
    using SignedSafeMath for int256;
68

69
    /* ============ Events ============ */
70

71
    event SetTokenNAVIssued(
72
        ISetToken indexed _setToken,
73
        address _issuer,
74
        address _to,
75
        address _reserveAsset,
76
        address _hookContract,
77
        uint256 _setTokenQuantity,
78
        uint256 _managerFee,
79
        uint256 _premium
80
    );
81

82
    event SetTokenNAVRedeemed(
83
        ISetToken indexed _setToken,
84
        address _redeemer,
85
        address _to,
86
        address _reserveAsset,
87
        address _hookContract,
88
        uint256 _setTokenQuantity,
89
        uint256 _managerFee,
90
        uint256 _premium
91
    );
92

93
    event ReserveAssetAdded(
94
        ISetToken indexed _setToken,
95
        address _newReserveAsset
96
    );
97

98
    event ReserveAssetRemoved(
99
        ISetToken indexed _setToken,
100
        address _removedReserveAsset
101
    );
102

103
    event PremiumEdited(
104
        ISetToken indexed _setToken,
105
        uint256 _newPremium
106
    );
107

108
    event ManagerFeeEdited(
109
        ISetToken indexed _setToken,
110
        uint256 _newManagerFee,
111
        uint256 _index
112
    );
113

114
    event FeeRecipientEdited(
115
        ISetToken indexed _setToken,
116
        address _feeRecipient
117
    );
118

119
    /* ============ Structs ============ */
120

121
    struct NAVIssuanceSettings {
122
        INAVIssuanceHook managerIssuanceHook;          // Issuance hook configurations
123
        INAVIssuanceHook managerRedemptionHook;        // Redemption hook configurations
124
        ISetValuer setValuer;                          // Optional custom set valuer. If address(0) is provided, fetch the default one from the controller
125
        address[] reserveAssets;                       // Allowed reserve assets - Must have a price enabled with the price oracle
126
        address feeRecipient;                          // Manager fee recipient
127
        uint256[2] managerFees;                        // Manager fees. 0 index is issue and 1 index is redeem fee (0.01% = 1e14, 1% = 1e16)
128
        uint256 maxManagerFee;                         // Maximum fee manager is allowed to set for issue and redeem
129
        uint256 premiumPercentage;                     // Premium percentage (0.01% = 1e14, 1% = 1e16). This premium is a buffer around oracle
130
                                                       // prices paid by user to the SetToken, which prevents arbitrage and oracle front running
131
        uint256 maxPremiumPercentage;                  // Maximum premium percentage manager is allowed to set (configured by manager)
132
        uint256 minSetTokenSupply;                     // Minimum SetToken supply required for issuance and redemption
133
                                                       // to prevent dramatic inflationary changes to the SetToken's position multiplier
134
    }
135

136
    struct ActionInfo {
137
        uint256 preFeeReserveQuantity;                 // Reserve value before fees; During issuance, represents raw quantity
138
                                                       // During redeem, represents post-premium value
139
        uint256 protocolFees;                          // Total protocol fees (direct + manager revenue share)
140
        uint256 managerFee;                            // Total manager fee paid in reserve asset
141
        uint256 netFlowQuantity;                       // When issuing, quantity of reserve asset sent to SetToken
142
                                                       // When redeeming, quantity of reserve asset sent to redeemer
143
        uint256 setTokenQuantity;                      // When issuing, quantity of SetTokens minted to mintee
144
                                                       // When redeeming, quantity of SetToken redeemed
145
        uint256 previousSetTokenSupply;                // SetToken supply prior to issue/redeem action
146
        uint256 newSetTokenSupply;                     // SetToken supply after issue/redeem action
147
        int256 newPositionMultiplier;                  // SetToken position multiplier after issue/redeem
148
        uint256 newReservePositionUnit;                // SetToken reserve asset position unit after issue/redeem
149
    }
150

151
    /* ============ State Variables ============ */
152

153
    // Wrapped ETH address
154
    IWETH public immutable weth;
155

156
    // Mapping of SetToken to NAV issuance settings struct
157
    mapping(ISetToken => NAVIssuanceSettings) public navIssuanceSettings;
158

159
    // Mapping to efficiently check a SetToken's reserve asset validity
160
    // SetToken => reserveAsset => isReserveAsset
161
    mapping(ISetToken => mapping(address => bool)) public isReserveAsset;
162

163
    /* ============ Constants ============ */
164

165
    // 1e20 is the maximum value the position multiplier can be inflated to. This is a security feature to prevent precision loss
166
    int256 constant internal MAX_POSITION_MULTIPLIER = 1e20;
167

168
    // 0 index stores the manager fee in managerFees array, percentage charged on issue (denominated in reserve asset)
169
    uint256 constant internal MANAGER_ISSUE_FEE_INDEX = 0;
170

171
    // 1 index stores the manager fee percentage in managerFees array, charged on redeem
172
    uint256 constant internal MANAGER_REDEEM_FEE_INDEX = 1;
173

174
    // 0 index stores the manager revenue share protocol fee % on the controller, charged in the issuance function
175
    uint256 constant internal PROTOCOL_ISSUE_MANAGER_REVENUE_SHARE_FEE_INDEX = 0;
176

177
    // 1 index stores the manager revenue share protocol fee % on the controller, charged in the redeem function
178
    uint256 constant internal PROTOCOL_REDEEM_MANAGER_REVENUE_SHARE_FEE_INDEX = 1;
179

180
    // 2 index stores the direct protocol fee % on the controller, charged in the issuance function
181
    uint256 constant internal PROTOCOL_ISSUE_DIRECT_FEE_INDEX = 2;
182

183
    // 3 index stores the direct protocol fee % on the controller, charged in the redeem function
184
    uint256 constant internal PROTOCOL_REDEEM_DIRECT_FEE_INDEX = 3;
185

186
    /* ============ Constructor ============ */
187

188
    /**
189
     * @param _controller               Address of controller contract
190
     * @param _weth                     Address of wrapped eth
191
     */
192
    constructor(IController _controller, IWETH _weth) public ModuleBase(_controller) {
193
        weth = _weth;
194
    }
195

196
    /* ============ External Functions ============ */
197

198
    /**
199
     * Deposits the allowed reserve asset into the SetToken and mints the appropriate % of Net Asset Value of the SetToken
200
     * to the specified _to address.
201
     *
202
     * @param _setToken                     Instance of the SetToken contract
203
     * @param _reserveAsset                 Address of the reserve asset to issue with
204
     * @param _reserveAssetQuantity         Quantity of the reserve asset to issue with
205
     * @param _minSetTokenReceiveQuantity   Min quantity of SetToken to receive after issuance
206
     * @param _to                           Address to mint SetToken to
207
     */
208
    function issue(
209
        ISetToken _setToken,
210
        address _reserveAsset,
211
        uint256 _reserveAssetQuantity,
212
        uint256 _minSetTokenReceiveQuantity,
213
        address _to
214
    )
215
        external
216
        nonReentrant
6!
217
        onlyValidAndInitializedSet(_setToken)
6!
218
    {
219
        _validateCommon(_setToken, _reserveAsset, _reserveAssetQuantity);
6✔
220

221
        _callPreIssueHooks(_setToken, _reserveAsset, _reserveAssetQuantity, msg.sender, _to);
6✔
222

223
        ActionInfo memory issueInfo = _createIssuanceInfo(_setToken, _reserveAsset, _reserveAssetQuantity);
5✔
224

225
        _validateIssuanceInfo(_setToken, _minSetTokenReceiveQuantity, issueInfo);
5✔
226

227
        _transferCollateralAndHandleFees(_setToken, IERC20(_reserveAsset), issueInfo);
5✔
228

229
        _handleIssueStateUpdates(_setToken, _reserveAsset, _to, issueInfo);
5✔
230
    }
231

232
    /**
233
     * Wraps ETH and deposits WETH if allowed into the SetToken and mints the appropriate % of Net Asset Value of the SetToken
234
     * to the specified _to address.
235
     *
236
     * @param _setToken                     Instance of the SetToken contract
237
     * @param _minSetTokenReceiveQuantity   Min quantity of SetToken to receive after issuance
238
     * @param _to                           Address to mint SetToken to
239
     */
240
    function issueWithEther(
241
        ISetToken _setToken,
242
        uint256 _minSetTokenReceiveQuantity,
243
        address _to
244
    )
245
        external
246
        payable
247
        nonReentrant
×
248
        onlyValidAndInitializedSet(_setToken)
×
249
    {
UNCOV
250
        weth.deposit{ value: msg.value }();
×
251

UNCOV
252
        _validateCommon(_setToken, address(weth), msg.value);
×
253

UNCOV
254
        _callPreIssueHooks(_setToken, address(weth), msg.value, msg.sender, _to);
×
255

UNCOV
256
        ActionInfo memory issueInfo = _createIssuanceInfo(_setToken, address(weth), msg.value);
×
257

UNCOV
258
        _validateIssuanceInfo(_setToken, _minSetTokenReceiveQuantity, issueInfo);
×
259

UNCOV
260
        _transferWETHAndHandleFees(_setToken, issueInfo);
×
261

UNCOV
262
        _handleIssueStateUpdates(_setToken, address(weth), _to, issueInfo);
×
263
    }
264

265
    /**
266
     * Redeems a SetToken into a valid reserve asset representing the appropriate % of Net Asset Value of the SetToken
267
     * to the specified _to address. Only valid if there are available reserve units on the SetToken.
268
     *
269
     * @param _setToken                     Instance of the SetToken contract
270
     * @param _reserveAsset                 Address of the reserve asset to redeem with
271
     * @param _setTokenQuantity             Quantity of SetTokens to redeem
272
     * @param _minReserveReceiveQuantity    Min quantity of reserve asset to receive
273
     * @param _to                           Address to redeem reserve asset to
274
     */
275
    function redeem(
276
        ISetToken _setToken,
277
        address _reserveAsset,
278
        uint256 _setTokenQuantity,
279
        uint256 _minReserveReceiveQuantity,
280
        address _to
281
    )
282
        external
283
        nonReentrant
3!
284
        onlyValidAndInitializedSet(_setToken)
3!
285
    {
286
        _validateCommon(_setToken, _reserveAsset, _setTokenQuantity);
3✔
287

288
        _callPreRedeemHooks(_setToken, _setTokenQuantity, msg.sender, _to);
3✔
289

290
        ActionInfo memory redeemInfo = _createRedemptionInfo(_setToken, _reserveAsset, _setTokenQuantity);
2✔
291

292
        _validateRedemptionInfo(_setToken, _minReserveReceiveQuantity, redeemInfo);
2✔
293

294
        _setToken.burn(msg.sender, _setTokenQuantity);
2✔
295

296
        // Instruct the SetToken to transfer the reserve asset back to the user
297
        _setToken.strictInvokeTransfer(
2✔
298
            _reserveAsset,
299
            _to,
300
            redeemInfo.netFlowQuantity
301
        );
302

303
        _handleRedemptionFees(_setToken, _reserveAsset, redeemInfo);
2✔
304

305
        _handleRedeemStateUpdates(_setToken, _reserveAsset, _to, redeemInfo);
2✔
306
    }
307

308
    /**
309
     * Redeems a SetToken into Ether (if WETH is valid) representing the appropriate % of Net Asset Value of the SetToken
310
     * to the specified _to address. Only valid if there are available WETH units on the SetToken.
311
     *
312
     * @param _setToken                     Instance of the SetToken contract
313
     * @param _setTokenQuantity             Quantity of SetTokens to redeem
314
     * @param _minReserveReceiveQuantity    Min quantity of reserve asset to receive
315
     * @param _to                           Address to redeem reserve asset to
316
     */
317
    function redeemIntoEther(
318
        ISetToken _setToken,
319
        uint256 _setTokenQuantity,
320
        uint256 _minReserveReceiveQuantity,
321
        address payable _to
322
    )
323
        external
324
        nonReentrant
×
325
        onlyValidAndInitializedSet(_setToken)
×
326
    {
UNCOV
327
        _validateCommon(_setToken, address(weth), _setTokenQuantity);
×
328

UNCOV
329
        _callPreRedeemHooks(_setToken, _setTokenQuantity, msg.sender, _to);
×
330

UNCOV
331
        ActionInfo memory redeemInfo = _createRedemptionInfo(_setToken, address(weth), _setTokenQuantity);
×
332

UNCOV
333
        _validateRedemptionInfo(_setToken, _minReserveReceiveQuantity, redeemInfo);
×
334

UNCOV
335
        _setToken.burn(msg.sender, _setTokenQuantity);
×
336

337
        // Instruct the SetToken to transfer WETH from SetToken to module
UNCOV
338
        _setToken.strictInvokeTransfer(
×
339
            address(weth),
340
            address(this),
341
            redeemInfo.netFlowQuantity
342
        );
343

UNCOV
344
        weth.withdraw(redeemInfo.netFlowQuantity);
×
345

UNCOV
346
        _to.transfer(redeemInfo.netFlowQuantity);
×
347

UNCOV
348
        _handleRedemptionFees(_setToken, address(weth), redeemInfo);
×
349

UNCOV
350
        _handleRedeemStateUpdates(_setToken, address(weth), _to, redeemInfo);
×
351
    }
352

353
    /**
354
     * SET MANAGER ONLY. Add an allowed reserve asset
355
     *
356
     * @param _setToken                     Instance of the SetToken
357
     * @param _reserveAsset                 Address of the reserve asset to add
358
     */
359
    function addReserveAsset(ISetToken _setToken, address _reserveAsset) external onlyManagerAndValidSet(_setToken) {
×
UNCOV
360
        require(!isReserveAsset[_setToken][_reserveAsset], "Reserve asset already exists");
×
361

UNCOV
362
        navIssuanceSettings[_setToken].reserveAssets.push(_reserveAsset);
×
363
        isReserveAsset[_setToken][_reserveAsset] = true;
364

UNCOV
365
        emit ReserveAssetAdded(_setToken, _reserveAsset);
×
366
    }
367

368
    /**
369
     * SET MANAGER ONLY. Remove a reserve asset
370
     *
371
     * @param _setToken                     Instance of the SetToken
372
     * @param _reserveAsset                 Address of the reserve asset to remove
373
     */
374
    function removeReserveAsset(ISetToken _setToken, address _reserveAsset) external onlyManagerAndValidSet(_setToken) {
×
UNCOV
375
        require(isReserveAsset[_setToken][_reserveAsset], "Reserve asset does not exist");
×
376

377
        navIssuanceSettings[_setToken].reserveAssets = navIssuanceSettings[_setToken].reserveAssets.remove(_reserveAsset);
378
        delete isReserveAsset[_setToken][_reserveAsset];
379

UNCOV
380
        emit ReserveAssetRemoved(_setToken, _reserveAsset);
×
381
    }
382

383
    /**
384
     * SET MANAGER ONLY. Edit the premium percentage
385
     *
386
     * @param _setToken                     Instance of the SetToken
387
     * @param _premiumPercentage            Premium percentage in 10e16 (e.g. 10e16 = 1%)
388
     */
389
    function editPremium(ISetToken _setToken, uint256 _premiumPercentage) external onlyManagerAndValidSet(_setToken) {
×
UNCOV
390
        require(_premiumPercentage <= navIssuanceSettings[_setToken].maxPremiumPercentage, "Premium must be less than maximum allowed");
×
391

392
        navIssuanceSettings[_setToken].premiumPercentage = _premiumPercentage;
393

UNCOV
394
        emit PremiumEdited(_setToken, _premiumPercentage);
×
395
    }
396

397
    /**
398
     * SET MANAGER ONLY. Edit manager fee
399
     *
400
     * @param _setToken                     Instance of the SetToken
401
     * @param _managerFeePercentage         Manager fee percentage in 10e16 (e.g. 10e16 = 1%)
402
     * @param _managerFeeIndex              Manager fee index. 0 index is issue fee, 1 index is redeem fee
403
     */
404
    function editManagerFee(
405
        ISetToken _setToken,
406
        uint256 _managerFeePercentage,
407
        uint256 _managerFeeIndex
408
    )
409
        external
410
        onlyManagerAndValidSet(_setToken)
×
411
    {
UNCOV
412
        require(_managerFeePercentage <= navIssuanceSettings[_setToken].maxManagerFee, "Manager fee must be less than maximum allowed");
×
413

414
        navIssuanceSettings[_setToken].managerFees[_managerFeeIndex] = _managerFeePercentage;
415

UNCOV
416
        emit ManagerFeeEdited(_setToken, _managerFeePercentage, _managerFeeIndex);
×
417
    }
418

419
    /**
420
     * SET MANAGER ONLY. Edit the manager fee recipient
421
     *
422
     * @param _setToken                     Instance of the SetToken
423
     * @param _managerFeeRecipient          Manager fee recipient
424
     */
425
    function editFeeRecipient(ISetToken _setToken, address _managerFeeRecipient) external onlyManagerAndValidSet(_setToken) {
×
UNCOV
426
        require(_managerFeeRecipient != address(0), "Fee recipient must not be 0 address");
×
427

428
        navIssuanceSettings[_setToken].feeRecipient = _managerFeeRecipient;
429

UNCOV
430
        emit FeeRecipientEdited(_setToken, _managerFeeRecipient);
×
431
    }
432

433
    /**
434
     * SET MANAGER ONLY. Initializes this module to the SetToken with hooks, allowed reserve assets,
435
     * fees and issuance premium. Only callable by the SetToken's manager. Hook addresses are optional.
436
     * Address(0) means that no hook will be called.
437
     *
438
     * @param _setToken                     Instance of the SetToken to issue
439
     * @param _navIssuanceSettings          NAVIssuanceSettings struct defining parameters
440
     */
441
    function initialize(
442
        ISetToken _setToken,
443
        NAVIssuanceSettings memory _navIssuanceSettings
444
    )
445
        external
446
        onlySetManager(_setToken, msg.sender)
2!
447
        onlyValidAndPendingSet(_setToken)
2!
448
    {
449
        require(_navIssuanceSettings.reserveAssets.length > 0, "Reserve assets must be greater than 0");
2!
450
        require(_navIssuanceSettings.maxManagerFee < PreciseUnitMath.preciseUnit(), "Max manager fee must be less than 100%");
2!
451
        require(_navIssuanceSettings.maxPremiumPercentage < PreciseUnitMath.preciseUnit(), "Max premium percentage must be less than 100%");
2!
452
        require(_navIssuanceSettings.managerFees[0] <= _navIssuanceSettings.maxManagerFee, "Manager issue fee must be less than max");
2!
453
        require(_navIssuanceSettings.managerFees[1] <= _navIssuanceSettings.maxManagerFee, "Manager redeem fee must be less than max");
2!
454
        require(_navIssuanceSettings.premiumPercentage <= _navIssuanceSettings.maxPremiumPercentage, "Premium must be less than max");
2!
455
        require(_navIssuanceSettings.feeRecipient != address(0), "Fee Recipient must be non-zero address.");
2!
456
        // Initial mint of Set cannot use NAVIssuance since minSetTokenSupply must be > 0
457
        require(_navIssuanceSettings.minSetTokenSupply > 0, "Min SetToken supply must be greater than 0");
2!
458

459
        for (uint256 i = 0; i < _navIssuanceSettings.reserveAssets.length; i++) {
2✔
460
            require(!isReserveAsset[_setToken][_navIssuanceSettings.reserveAssets[i]], "Reserve assets must be unique");
4!
461
            isReserveAsset[_setToken][_navIssuanceSettings.reserveAssets[i]] = true;
462
        }
463

464
        navIssuanceSettings[_setToken] = _navIssuanceSettings;
465

466
        _setToken.initializeModule();
2✔
467
    }
468

469
    /**
470
     * Removes this module from the SetToken, via call by the SetToken. Issuance settings and
471
     * reserve asset states are deleted.
472
     */
473
    function removeModule() external override {
UNCOV
474
        ISetToken setToken = ISetToken(msg.sender);
×
UNCOV
475
        for (uint256 i = 0; i < navIssuanceSettings[setToken].reserveAssets.length; i++) {
×
476
            delete isReserveAsset[setToken][navIssuanceSettings[setToken].reserveAssets[i]];
477
        }
478

479
        delete navIssuanceSettings[setToken];
480
    }
481

482
    receive() external payable {}
483

484
    /* ============ External Getter Functions ============ */
485

486
    function getReserveAssets(ISetToken _setToken) external view returns (address[] memory) {
UNCOV
487
        return navIssuanceSettings[_setToken].reserveAssets;
×
488
    }
489

490
    function getIssuePremium(
491
        ISetToken _setToken,
492
        address _reserveAsset,
493
        uint256 _reserveAssetQuantity
494
    )
495
        external
496
        view
497
        returns (uint256)
498
    {
UNCOV
499
        return _getIssuePremium(_setToken, _reserveAsset, _reserveAssetQuantity);
×
500
    }
501

502
    function getRedeemPremium(
503
        ISetToken _setToken,
504
        address _reserveAsset,
505
        uint256 _setTokenQuantity
506
    )
507
        external
508
        view
509
        returns (uint256)
510
    {
UNCOV
511
        return _getRedeemPremium(_setToken, _reserveAsset, _setTokenQuantity);
×
512
    }
513

514
    function getManagerFee(ISetToken _setToken, uint256 _managerFeeIndex) external view returns (uint256) {
UNCOV
515
        return navIssuanceSettings[_setToken].managerFees[_managerFeeIndex];
×
516
    }
517

518
    /**
519
     * Get the expected SetTokens minted to recipient on issuance
520
     *
521
     * @param _setToken                     Instance of the SetToken
522
     * @param _reserveAsset                 Address of the reserve asset
523
     * @param _reserveAssetQuantity         Quantity of the reserve asset to issue with
524
     *
525
     * @return  uint256                     Expected SetTokens to be minted to recipient
526
     */
527
    function getExpectedSetTokenIssueQuantity(
528
        ISetToken _setToken,
529
        address _reserveAsset,
530
        uint256 _reserveAssetQuantity
531
    )
532
        external
533
        view
534
        returns (uint256)
535
    {
UNCOV
536
        (,, uint256 netReserveFlow) = _getFees(
×
537
            _setToken,
538
            _reserveAssetQuantity,
539
            PROTOCOL_ISSUE_MANAGER_REVENUE_SHARE_FEE_INDEX,
540
            PROTOCOL_ISSUE_DIRECT_FEE_INDEX,
541
            MANAGER_ISSUE_FEE_INDEX
542
        );
543

UNCOV
544
        uint256 setTotalSupply = _setToken.totalSupply();
×
545

UNCOV
546
        return _getSetTokenMintQuantity(
×
547
            _setToken,
548
            _reserveAsset,
549
            netReserveFlow,
550
            setTotalSupply
551
        );
552
    }
553

554
    /**
555
     * Get the expected reserve asset to be redeemed
556
     *
557
     * @param _setToken                     Instance of the SetToken
558
     * @param _reserveAsset                 Address of the reserve asset
559
     * @param _setTokenQuantity             Quantity of SetTokens to redeem
560
     *
561
     * @return  uint256                     Expected reserve asset quantity redeemed
562
     */
563
    function getExpectedReserveRedeemQuantity(
564
        ISetToken _setToken,
565
        address _reserveAsset,
566
        uint256 _setTokenQuantity
567
    )
568
        external
569
        view
570
        returns (uint256)
571
    {
UNCOV
572
        uint256 preFeeReserveQuantity = _getRedeemReserveQuantity(_setToken, _reserveAsset, _setTokenQuantity);
×
573

UNCOV
574
        (,, uint256 netReserveFlows) = _getFees(
×
575
            _setToken,
576
            preFeeReserveQuantity,
577
            PROTOCOL_REDEEM_MANAGER_REVENUE_SHARE_FEE_INDEX,
578
            PROTOCOL_REDEEM_DIRECT_FEE_INDEX,
579
            MANAGER_REDEEM_FEE_INDEX
580
        );
581

UNCOV
582
        return netReserveFlows;
×
583
    }
584

585
    /**
586
     * Checks if issue is valid
587
     *
588
     * @param _setToken                     Instance of the SetToken
589
     * @param _reserveAsset                 Address of the reserve asset
590
     * @param _reserveAssetQuantity         Quantity of the reserve asset to issue with
591
     *
592
     * @return  bool                        Returns true if issue is valid
593
     */
594
    function isIssueValid(
595
        ISetToken _setToken,
596
        address _reserveAsset,
597
        uint256 _reserveAssetQuantity
598
    )
599
        external
600
        view
601
        returns (bool)
602
    {
UNCOV
603
        uint256 setTotalSupply = _setToken.totalSupply();
×
604

UNCOV
605
    return _reserveAssetQuantity != 0
×
606
            && isReserveAsset[_setToken][_reserveAsset]
607
            && setTotalSupply >= navIssuanceSettings[_setToken].minSetTokenSupply;
608
    }
609

610
    /**
611
     * Checks if redeem is valid
612
     *
613
     * @param _setToken                     Instance of the SetToken
614
     * @param _reserveAsset                 Address of the reserve asset
615
     * @param _setTokenQuantity             Quantity of SetTokens to redeem
616
     *
617
     * @return  bool                        Returns true if redeem is valid
618
     */
619
    function isRedeemValid(
620
        ISetToken _setToken,
621
        address _reserveAsset,
622
        uint256 _setTokenQuantity
623
    )
624
        external
625
        view
626
        returns (bool)
627
    {
UNCOV
628
        uint256 setTotalSupply = _setToken.totalSupply();
×
629

UNCOV
630
        if (
×
631
            _setTokenQuantity == 0
×
632
            || !isReserveAsset[_setToken][_reserveAsset]
633
            || setTotalSupply < navIssuanceSettings[_setToken].minSetTokenSupply.add(_setTokenQuantity)
634
        ) {
UNCOV
635
            return false;
×
636
        } else {
UNCOV
637
            uint256 totalRedeemValue =_getRedeemReserveQuantity(_setToken, _reserveAsset, _setTokenQuantity);
×
638

UNCOV
639
            (,, uint256 expectedRedeemQuantity) = _getFees(
×
640
                _setToken,
641
                totalRedeemValue,
642
                PROTOCOL_REDEEM_MANAGER_REVENUE_SHARE_FEE_INDEX,
643
                PROTOCOL_REDEEM_DIRECT_FEE_INDEX,
644
                MANAGER_REDEEM_FEE_INDEX
645
            );
646

UNCOV
647
            uint256 existingUnit = _setToken.getDefaultPositionRealUnit(_reserveAsset).toUint256();
×
648

UNCOV
649
            return existingUnit.preciseMul(setTotalSupply) >= expectedRedeemQuantity;
×
650
        }
651
    }
652

653
    /* ============ Internal Functions ============ */
654

655
    function _validateCommon(ISetToken _setToken, address _reserveAsset, uint256 _quantity) internal view {
656
        require(_quantity > 0, "Quantity must be > 0");
9!
657
        require(isReserveAsset[_setToken][_reserveAsset], "Must be valid reserve asset");
9!
658
    }
659

660
    function _validateIssuanceInfo(ISetToken _setToken, uint256 _minSetTokenReceiveQuantity, ActionInfo memory _issueInfo) internal view {
661
        // Check that total supply is greater than min supply needed for issuance
662
        // Note: A min supply amount is needed to avoid division by 0 when SetToken supply is 0
663
        require(
5!
664
            _issueInfo.previousSetTokenSupply >= navIssuanceSettings[_setToken].minSetTokenSupply,
665
            "Supply must be greater than minimum to enable issuance"
666
        );
667

668
        require(_issueInfo.setTokenQuantity >= _minSetTokenReceiveQuantity, "Must be greater than min SetToken");
5!
669
    }
670

671
    function _validateRedemptionInfo(
672
        ISetToken _setToken,
673
        uint256 _minReserveReceiveQuantity,
674
        ActionInfo memory _redeemInfo
675
    )
676
        internal
677
        view
678
    {
679
        // Check that new supply is more than min supply needed for redemption
680
        // Note: A min supply amount is needed to avoid division by 0 when redeeming SetToken to 0
681
        require(
2!
682
            _redeemInfo.newSetTokenSupply >= navIssuanceSettings[_setToken].minSetTokenSupply,
683
            "Supply must be greater than minimum to enable redemption"
684
        );
685

686
        require(_redeemInfo.netFlowQuantity >= _minReserveReceiveQuantity, "Must be greater than min receive reserve quantity");
2!
687
    }
688

689
    function _createIssuanceInfo(
690
        ISetToken _setToken,
691
        address _reserveAsset,
692
        uint256 _reserveAssetQuantity
693
    )
694
        internal
695
        view
696
        returns (ActionInfo memory)
697
    {
698
        ActionInfo memory issueInfo;
5✔
699

700
        issueInfo.previousSetTokenSupply = _setToken.totalSupply();
701

702
        issueInfo.preFeeReserveQuantity = _reserveAssetQuantity;
703

704
        (issueInfo.protocolFees, issueInfo.managerFee, issueInfo.netFlowQuantity) = _getFees(
705
            _setToken,
706
            issueInfo.preFeeReserveQuantity,
707
            PROTOCOL_ISSUE_MANAGER_REVENUE_SHARE_FEE_INDEX,
708
            PROTOCOL_ISSUE_DIRECT_FEE_INDEX,
709
            MANAGER_ISSUE_FEE_INDEX
710
        );
711

712
        issueInfo.setTokenQuantity = _getSetTokenMintQuantity(
713
            _setToken,
714
            _reserveAsset,
715
            issueInfo.netFlowQuantity,
716
            issueInfo.previousSetTokenSupply
717
        );
718

719
        (issueInfo.newSetTokenSupply, issueInfo.newPositionMultiplier) = _getIssuePositionMultiplier(_setToken, issueInfo);
720

721
        issueInfo.newReservePositionUnit = _getIssuePositionUnit(_setToken, _reserveAsset, issueInfo);
722

723
        return issueInfo;
5✔
724
    }
725

726
    function _createRedemptionInfo(
727
        ISetToken _setToken,
728
        address _reserveAsset,
729
        uint256 _setTokenQuantity
730
    )
731
        internal
732
        view
733
        returns (ActionInfo memory)
734
    {
735
        ActionInfo memory redeemInfo;
2✔
736

737
        redeemInfo.setTokenQuantity = _setTokenQuantity;
738

739
        redeemInfo.preFeeReserveQuantity =_getRedeemReserveQuantity(_setToken, _reserveAsset, _setTokenQuantity);
740

741
        (redeemInfo.protocolFees, redeemInfo.managerFee, redeemInfo.netFlowQuantity) = _getFees(
742
            _setToken,
743
            redeemInfo.preFeeReserveQuantity,
744
            PROTOCOL_REDEEM_MANAGER_REVENUE_SHARE_FEE_INDEX,
745
            PROTOCOL_REDEEM_DIRECT_FEE_INDEX,
746
            MANAGER_REDEEM_FEE_INDEX
747
        );
748

749
        redeemInfo.previousSetTokenSupply = _setToken.totalSupply();
750

751
        (redeemInfo.newSetTokenSupply, redeemInfo.newPositionMultiplier) = _getRedeemPositionMultiplier(_setToken, _setTokenQuantity, redeemInfo);
752

753
        redeemInfo.newReservePositionUnit = _getRedeemPositionUnit(_setToken, _reserveAsset, redeemInfo);
754

755
        return redeemInfo;
2✔
756
    }
757

758
    /**
759
     * Transfer reserve asset from user to SetToken and fees from user to appropriate fee recipients
760
     */
761
    function _transferCollateralAndHandleFees(ISetToken _setToken, IERC20 _reserveAsset, ActionInfo memory _issueInfo) internal {
762
        transferFrom(_reserveAsset, msg.sender, address(_setToken), _issueInfo.netFlowQuantity);
5✔
763

764
        if (_issueInfo.protocolFees > 0) {
5!
UNCOV
765
            transferFrom(_reserveAsset, msg.sender, controller.feeRecipient(), _issueInfo.protocolFees);
×
766
        }
767

768
        if (_issueInfo.managerFee > 0) {
5!
769
            transferFrom(_reserveAsset, msg.sender, navIssuanceSettings[_setToken].feeRecipient, _issueInfo.managerFee);
5✔
770
        }
771
    }
772

773

774
    /**
775
      * Transfer WETH from module to SetToken and fees from module to appropriate fee recipients
776
     */
777
    function _transferWETHAndHandleFees(ISetToken _setToken, ActionInfo memory _issueInfo) internal {
UNCOV
778
        weth.transfer(address(_setToken), _issueInfo.netFlowQuantity);
×
779

UNCOV
780
        if (_issueInfo.protocolFees > 0) {
×
UNCOV
781
            weth.transfer(controller.feeRecipient(), _issueInfo.protocolFees);
×
782
        }
783

UNCOV
784
        if (_issueInfo.managerFee > 0) {
×
UNCOV
785
            weth.transfer(navIssuanceSettings[_setToken].feeRecipient, _issueInfo.managerFee);
×
786
        }
787
    }
788

789
    function _handleIssueStateUpdates(
790
        ISetToken _setToken,
791
        address _reserveAsset,
792
        address _to,
793
        ActionInfo memory _issueInfo
794
    )
795
        internal
796
    {
797
        _setToken.editPositionMultiplier(_issueInfo.newPositionMultiplier);
5✔
798

799
        _setToken.editDefaultPosition(_reserveAsset, _issueInfo.newReservePositionUnit);
5✔
800

801
        _setToken.mint(_to, _issueInfo.setTokenQuantity);
5✔
802

803
        emit SetTokenNAVIssued(
5✔
804
            _setToken,
805
            msg.sender,
806
            _to,
807
            _reserveAsset,
808
            address(navIssuanceSettings[_setToken].managerIssuanceHook),
809
            _issueInfo.setTokenQuantity,
810
            _issueInfo.managerFee,
811
            _issueInfo.protocolFees
812
        );
813
    }
814

815
    function _handleRedeemStateUpdates(
816
        ISetToken _setToken,
817
        address _reserveAsset,
818
        address _to,
819
        ActionInfo memory _redeemInfo
820
    )
821
        internal
822
    {
823
        _setToken.editPositionMultiplier(_redeemInfo.newPositionMultiplier);
2✔
824

825
        _setToken.editDefaultPosition(_reserveAsset, _redeemInfo.newReservePositionUnit);
2✔
826

827
        emit SetTokenNAVRedeemed(
2✔
828
            _setToken,
829
            msg.sender,
830
            _to,
831
            _reserveAsset,
832
            address(navIssuanceSettings[_setToken].managerRedemptionHook),
833
            _redeemInfo.setTokenQuantity,
834
            _redeemInfo.managerFee,
835
            _redeemInfo.protocolFees
836
        );
837
    }
838

839
    function _handleRedemptionFees(ISetToken _setToken, address _reserveAsset, ActionInfo memory _redeemInfo) internal {
840
        // Instruct the SetToken to transfer protocol fee to fee recipient if there is a fee
841
        payProtocolFeeFromSetToken(_setToken, _reserveAsset, _redeemInfo.protocolFees);
2✔
842

843
        // Instruct the SetToken to transfer manager fee to manager fee recipient if there is a fee
844
        if (_redeemInfo.managerFee > 0) {
2!
UNCOV
845
            _setToken.strictInvokeTransfer(
×
846
                _reserveAsset,
847
                navIssuanceSettings[_setToken].feeRecipient,
848
                _redeemInfo.managerFee
849
            );
850
        }
851
    }
852

853
    /**
854
     * Returns the issue premium percentage. Virtual function that can be overridden in future versions of the module
855
     * and can contain arbitrary logic to calculate the issuance premium.
856
     */
857
    function _getIssuePremium(
858
        ISetToken _setToken,
859
        address /* _reserveAsset */,
860
        uint256 /* _reserveAssetQuantity */
861
    )
862
        virtual
863
        internal
864
        view
865
        returns (uint256)
866
    {
867
        return navIssuanceSettings[_setToken].premiumPercentage;
5✔
868
    }
869

870
    /**
871
     * Returns the redeem premium percentage. Virtual function that can be overridden in future versions of the module
872
     * and can contain arbitrary logic to calculate the redemption premium.
873
     */
874
    function _getRedeemPremium(
875
        ISetToken _setToken,
876
        address /* _reserveAsset */,
877
        uint256 /* _setTokenQuantity */
878
    )
879
        virtual
880
        internal
881
        view
882
        returns (uint256)
883
    {
884
        return navIssuanceSettings[_setToken].premiumPercentage;
2✔
885
    }
886

887
    /**
888
     * Returns the fees attributed to the manager and the protocol. The fees are calculated as follows:
889
     *
890
     * ManagerFee = (manager fee % - % to protocol) * reserveAssetQuantity
891
     * Protocol Fee = (% manager fee share + direct fee %) * reserveAssetQuantity
892
     *
893
     * @param _setToken                     Instance of the SetToken
894
     * @param _reserveAssetQuantity         Quantity of reserve asset to calculate fees from
895
     * @param _protocolManagerFeeIndex      Index to pull rev share NAV Issuance fee from the Controller
896
     * @param _protocolDirectFeeIndex       Index to pull direct NAV issuance fee from the Controller
897
     * @param _managerFeeIndex              Index from NAVIssuanceSettings (0 = issue fee, 1 = redeem fee)
898
     *
899
     * @return  uint256                     Fees paid to the protocol in reserve asset
900
     * @return  uint256                     Fees paid to the manager in reserve asset
901
     * @return  uint256                     Net reserve to user net of fees
902
     */
903
    function _getFees(
904
        ISetToken _setToken,
905
        uint256 _reserveAssetQuantity,
906
        uint256 _protocolManagerFeeIndex,
907
        uint256 _protocolDirectFeeIndex,
908
        uint256 _managerFeeIndex
909
    )
910
        internal
911
        view
912
        returns (uint256, uint256, uint256)
913
    {
914
        (uint256 protocolFeePercentage, uint256 managerFeePercentage) = _getProtocolAndManagerFeePercentages(
7✔
915
            _setToken,
916
            _protocolManagerFeeIndex,
917
            _protocolDirectFeeIndex,
918
            _managerFeeIndex
919
        );
920

921
        // Calculate total notional fees
922
        uint256 protocolFees = protocolFeePercentage.preciseMul(_reserveAssetQuantity);
7✔
923
        uint256 managerFee = managerFeePercentage.preciseMul(_reserveAssetQuantity);
7✔
924

925
        uint256 netReserveFlow = _reserveAssetQuantity.sub(protocolFees).sub(managerFee);
7✔
926

927
        return (protocolFees, managerFee, netReserveFlow);
7✔
928
    }
929

930
    function _getProtocolAndManagerFeePercentages(
931
        ISetToken _setToken,
932
        uint256 _protocolManagerFeeIndex,
933
        uint256 _protocolDirectFeeIndex,
934
        uint256 _managerFeeIndex
935
    )
936
        internal
937
        view
938
        returns(uint256, uint256)
939
    {
940
        // Get protocol fee percentages
941
        uint256 protocolDirectFeePercent = controller.getModuleFee(address(this), _protocolDirectFeeIndex);
7✔
942
        uint256 protocolManagerShareFeePercent = controller.getModuleFee(address(this), _protocolManagerFeeIndex);
7✔
943
        uint256 managerFeePercent = navIssuanceSettings[_setToken].managerFees[_managerFeeIndex];
7✔
944

945
        // Calculate revenue share split percentage
946
        uint256 protocolRevenueSharePercentage = protocolManagerShareFeePercent.preciseMul(managerFeePercent);
7✔
947
        uint256 managerRevenueSharePercentage = managerFeePercent.sub(protocolRevenueSharePercentage);
7✔
948
        uint256 totalProtocolFeePercentage = protocolRevenueSharePercentage.add(protocolDirectFeePercent);
7✔
949

950
        return (totalProtocolFeePercentage, managerRevenueSharePercentage);
7✔
951
    }
952

953
    function _getSetTokenMintQuantity(
954
        ISetToken _setToken,
955
        address _reserveAsset,
956
        uint256 _netReserveFlows,            // Value of reserve asset net of fees
957
        uint256 _setTotalSupply
958
    )
959
        internal
960
        view
961
        returns (uint256)
962
    {
963
        uint256 premiumPercentage = _getIssuePremium(_setToken, _reserveAsset, _netReserveFlows);
5✔
964
        uint256 premiumValue = _netReserveFlows.preciseMul(premiumPercentage);
5✔
965

966
        // If the set manager provided a custom valuer at initialization time, use it. Otherwise get it from the controller
967
        // Get valuation of the SetToken with the quote asset as the reserve asset. Returns value in precise units (1e18)
968
        // Reverts if price is not found
969
        uint256 setTokenValuation = _getSetValuer(_setToken).calculateSetTokenValuation(_setToken, _reserveAsset);
5✔
970

971
        // Get reserve asset decimals
972
        uint256 reserveAssetDecimals = ERC20(_reserveAsset).decimals();
5✔
973
        uint256 normalizedTotalReserveQuantityNetFees = _netReserveFlows.preciseDiv(10 ** reserveAssetDecimals);
5✔
974
        uint256 normalizedTotalReserveQuantityNetFeesAndPremium = _netReserveFlows.sub(premiumValue).preciseDiv(10 ** reserveAssetDecimals);
5✔
975

976
        // Calculate SetTokens to mint to issuer
977
        uint256 denominator = _setTotalSupply.preciseMul(setTokenValuation).add(normalizedTotalReserveQuantityNetFees).sub(normalizedTotalReserveQuantityNetFeesAndPremium);
5✔
978
        return normalizedTotalReserveQuantityNetFeesAndPremium.preciseMul(_setTotalSupply).preciseDiv(denominator);
5✔
979
    }
980

981
    function _getRedeemReserveQuantity(
982
        ISetToken _setToken,
983
        address _reserveAsset,
984
        uint256 _setTokenQuantity
985
    )
986
        internal
987
        view
988
        returns (uint256)
989
    {
990
        // Get valuation of the SetToken with the quote asset as the reserve asset. Returns value in precise units (10e18)
991
        // Reverts if price is not found
992
        uint256 setTokenValuation = _getSetValuer(_setToken).calculateSetTokenValuation(_setToken, _reserveAsset);
2✔
993

994
        uint256 totalRedeemValueInPreciseUnits = _setTokenQuantity.preciseMul(setTokenValuation);
2✔
995
        // Get reserve asset decimals
996
        uint256 reserveAssetDecimals = ERC20(_reserveAsset).decimals();
2✔
997
        uint256 prePremiumReserveQuantity = totalRedeemValueInPreciseUnits.preciseMul(10 ** reserveAssetDecimals);
2✔
998

999
        uint256 premiumPercentage = _getRedeemPremium(_setToken, _reserveAsset, _setTokenQuantity);
2✔
1000
        uint256 premiumQuantity = prePremiumReserveQuantity.preciseMulCeil(premiumPercentage);
2✔
1001

1002
        return prePremiumReserveQuantity.sub(premiumQuantity);
2✔
1003
    }
1004

1005
    /**
1006
     * The new position multiplier is calculated as follows:
1007
     * inflationPercentage = (newSupply - oldSupply) / newSupply
1008
     * newMultiplier = (1 - inflationPercentage) * positionMultiplier
1009
     */
1010
    function _getIssuePositionMultiplier(
1011
        ISetToken _setToken,
1012
        ActionInfo memory _issueInfo
1013
    )
1014
        internal
1015
        view
1016
        returns (uint256, int256)
1017
    {
1018
        // Calculate inflation and new position multiplier. Note: Round inflation up in order to round position multiplier down
1019
        uint256 newTotalSupply = _issueInfo.setTokenQuantity.add(_issueInfo.previousSetTokenSupply);
5✔
1020
        int256 newPositionMultiplier = _setToken.positionMultiplier()
5✔
1021
            .mul(_issueInfo.previousSetTokenSupply.toInt256())
1022
            .div(newTotalSupply.toInt256());
1023

1024
        return (newTotalSupply, newPositionMultiplier);
5✔
1025
    }
1026

1027
    /**
1028
     * Calculate deflation and new position multiplier. Note: Round deflation down in order to round position multiplier down
1029
     *
1030
     * The new position multiplier is calculated as follows:
1031
     * deflationPercentage = (oldSupply - newSupply) / newSupply
1032
     * newMultiplier = (1 + deflationPercentage) * positionMultiplier
1033
     */
1034
    function _getRedeemPositionMultiplier(
1035
        ISetToken _setToken,
1036
        uint256 _setTokenQuantity,
1037
        ActionInfo memory _redeemInfo
1038
    )
1039
        internal
1040
        view
1041
        returns (uint256, int256)
1042
    {
1043
        uint256 newTotalSupply = _redeemInfo.previousSetTokenSupply.sub(_setTokenQuantity);
2✔
1044
        int256 newPositionMultiplier = _setToken.positionMultiplier()
2✔
1045
            .mul(_redeemInfo.previousSetTokenSupply.toInt256())
1046
            .div(newTotalSupply.toInt256());
1047

1048
        // Require inflated position multiplier does not exceed maximum
1049
        require(newPositionMultiplier <= MAX_POSITION_MULTIPLIER, "New position multiplier must not exceed max");
2!
1050

1051
        return (newTotalSupply, newPositionMultiplier);
2✔
1052
    }
1053

1054
    /**
1055
     * The new position reserve asset unit is calculated as follows:
1056
     * totalReserve = (oldUnit * oldSetTokenSupply) + reserveQuantity
1057
     * newUnit = totalReserve / newSetTokenSupply
1058
     */
1059
    function _getIssuePositionUnit(
1060
        ISetToken _setToken,
1061
        address _reserveAsset,
1062
        ActionInfo memory _issueInfo
1063
    )
1064
        internal
1065
        view
1066
        returns (uint256)
1067
    {
1068
        uint256 existingUnit = _setToken.getDefaultPositionRealUnit(_reserveAsset).toUint256();
5✔
1069
        uint256 totalReserve = existingUnit
5✔
1070
            .preciseMul(_issueInfo.previousSetTokenSupply)
1071
            .add(_issueInfo.netFlowQuantity);
1072

1073
        return totalReserve.preciseDiv(_issueInfo.newSetTokenSupply);
5✔
1074
    }
1075

1076
    /**
1077
     * The new position reserve asset unit is calculated as follows:
1078
     * totalReserve = (oldUnit * oldSetTokenSupply) - reserveQuantityToSendOut
1079
     * newUnit = totalReserve / newSetTokenSupply
1080
     */
1081
    function _getRedeemPositionUnit(
1082
        ISetToken _setToken,
1083
        address _reserveAsset,
1084
        ActionInfo memory _redeemInfo
1085
    )
1086
        internal
1087
        view
1088
        returns (uint256)
1089
    {
1090
        uint256 existingUnit = _setToken.getDefaultPositionRealUnit(_reserveAsset).toUint256();
2✔
1091
        uint256 totalExistingUnits = existingUnit.preciseMul(_redeemInfo.previousSetTokenSupply);
2✔
1092

1093
        uint256 outflow = _redeemInfo.netFlowQuantity.add(_redeemInfo.protocolFees).add(_redeemInfo.managerFee);
2✔
1094

1095
        // Require withdrawable quantity is greater than existing collateral
1096
        require(totalExistingUnits >= outflow, "Must be greater than total available collateral");
2!
1097

1098
        return totalExistingUnits.sub(outflow).preciseDiv(_redeemInfo.newSetTokenSupply);
2✔
1099
    }
1100

1101
    /**
1102
     * If a pre-issue hook has been configured, call the external-protocol contract. Pre-issue hook logic
1103
     * can contain arbitrary logic including validations, external function calls, etc.
1104
     */
1105
    function _callPreIssueHooks(
1106
        ISetToken _setToken,
1107
        address _reserveAsset,
1108
        uint256 _reserveAssetQuantity,
1109
        address _caller,
1110
        address _to
1111
    )
1112
        internal
1113
    {
1114
        INAVIssuanceHook preIssueHook = navIssuanceSettings[_setToken].managerIssuanceHook;
6✔
1115
        if (address(preIssueHook) != address(0)) {
6!
1116
            preIssueHook.invokePreIssueHook(_setToken, _reserveAsset, _reserveAssetQuantity, _caller, _to);
6✔
1117
        }
1118
    }
1119

1120
    /**
1121
     * If a pre-redeem hook has been configured, call the external-protocol contract.
1122
     */
1123
    function _callPreRedeemHooks(ISetToken _setToken, uint256 _setQuantity, address _caller, address _to) internal {
1124
        INAVIssuanceHook preRedeemHook = navIssuanceSettings[_setToken].managerRedemptionHook;
3✔
1125
        if (address(preRedeemHook) != address(0)) {
3!
1126
            preRedeemHook.invokePreRedeemHook(_setToken, _setQuantity, _caller, _to);
3✔
1127
        }
1128
    }
1129

1130
    /**
1131
     * If a custom set valuer has been configured, use it. Otherwise fetch the default one form the
1132
     * controller.
1133
     */
1134
    function _getSetValuer(ISetToken _setToken) internal view returns (ISetValuer) {
1135
        ISetValuer customValuer =  navIssuanceSettings[_setToken].setValuer;
7✔
1136
        return address(customValuer) == address(0) ? controller.getSetValuer() : customValuer;
7!
1137
    }
1138
}
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

© 2026 Coveralls, Inc