Set Documentation
Search…
NAV Issuance
and Redemption

What is the NAVIssuanceModule:

The NAV (Net Asset Value) Issuance Module enables issuance and redemption with a single valid ERC20 token or ETH if allowed by the manager. The issuer receives a proportional amount of SetTokens on issuance based on the calculated net asset value of the Set using oracle prices.
The manager is able to enforce a premium or discount on issuance and redemption to avoid arbitrage and front running when relying on oracle prices. Managers can also charge a fee (denominated in reserve asset).
The NAV Issuance Module differs from the Basic Issuance Module in that the end user only needs to bring a single asset versus needing to replicate each position in the SetToken. This dramatically saves gas costs for SetTokens that are more actively traded and participate in yield farming and margin trading.
NAV Issuance obtains the NAV via the SetValuer contract which utilizes price oracles in the Set V2 system. View the supported price oracles here.

Initializing the NAVIssuanceModule:

All modules need to be added to the SetToken as the first step. Learn how to add a module to your SetToken by referring to the Adding a Module guide.
As a manager, once you have added the NAVIssuanceModule to the SetToken, you must initialize the SetToken on the NAVIssuanceModule:
1
/**
2
* SET MANAGER ONLY. Initializes this module to the SetToken with hooks, allowed reserve assets,
3
* fees and issuance premium. Only callable by the SetToken's manager. Hook addresses are optional.
4
* Address(0) means that no hook will be called.
5
*
6
* @param _setToken Instance of the SetToken to issue
7
* @param _navIssuanceSettings NAVIssuanceSettings struct defining parameters
8
*/
9
function initialize(
10
ISetToken _setToken,
11
NAVIssuanceSettings memory _navIssuanceSettings
12
)
Copied!
The NAVIssuanceSettings struct is constructed as followed:
1
struct NAVIssuanceSettings {
2
INAVIssuanceHook managerIssuanceHook; // Issuance hook configurations
3
INAVIssuanceHook managerRedemptionHook; // Redemption hook configurations
4
address[] reserveAssets; // Allowed reserve assets - Must have a price enabled with the price oracle
5
address feeRecipient; // Manager fee recipient
6
uint256[2] managerFees; // Manager fees. 0 index is issue and 1 index is redeem fee (0.01% = 1e14, 1% = 1e16)
7
uint256 maxManagerFee; // Maximum fee manager is allowed to set for issue and redeem
8
uint256 premiumPercentage; // Premium percentage (0.01% = 1e14, 1% = 1e16). This premium is a buffer around oracle
9
// prices paid by user to the SetToken, which prevents arbitrage and oracle front running
10
uint256 maxPremiumPercentage; // Maximum premium percentage manager is allowed to set (configured by manager)
11
uint256 minSetTokenSupply; // Minimum SetToken supply required for issuance and redemption
12
// to prevent dramatic inflationary changes to the SetToken's position multiplier
13
}
Copied!
The managerIssuanceHook and managerRedemptionHook are smart contracts that can contain any custom logic you write prior to issuance and redemption. For example, the manager issuance contract can include whitelisting functionality, issuance limits and any other custom logic for operating your Set. Input the zero address if you do not have a manager issuance contract.
The reserveAssets are the list of ERC20 tokens that you allow users to issue / redeem with. E.g. USDC, WETH, WBTC. These assets must be supported by the Set PriceOracle contract for NAV issuance to work.
All managerFees are paid to the feeRecipient address. Additionally, you can set a maxManagerFee that is allowed on the SetToken. The premiumPercentage (0.01% = 1e14, 1% = 1e16) is a buffer around oracle price which prevents oracle front running and arbitrage. All premiums are paid to existing SetToken holders and not collected by the manager or protocol.
The minSetTokenSupply is to prevent dramatic inflationary changes to the SetToken's position and is the minimum quantity required for NAV issuance and redemption. For example if the SetToken currently contains 1 in total supply and a user issues 1000 of supply, the SetToken position multiplier will result in an inflationary change that affects precision.

Issuing Sets:

In order to issue Sets you must call issue or issueWithEther (if issuing with ETH) on the NAVIssuanceModule, the interface for issue is as follows:
1
/**
2
* Deposits the allowed reserve asset into the SetToken and mints the appropriate % of Net Asset Value of the SetToken
3
* to the specified _to address.
4
*
5
* @param _setToken Instance of the SetToken contract
6
* @param _reserveAsset Address of the reserve asset to issue with
7
* @param _reserveAssetQuantity Quantity of the reserve asset to issue with
8
* @param _minSetTokenReceiveQuantity Min quantity of SetToken to receive after issuance
9
* @param _to Address to mint SetToken to
10
*/
11
function issue(
12
ISetToken _setToken,
13
address _reserveAsset,
14
uint256 _reserveAssetQuantity,
15
uint256 _minSetTokenReceiveQuantity,
16
address _to
17
)
Copied!
1
/**
2
* Wraps ETH and deposits WETH if allowed into the SetToken and mints the appropriate % of Net Asset Value of the SetToken
3
* to the specified _to address.
4
*
5
* @param _setToken Instance of the SetToken contract
6
* @param _minSetTokenReceiveQuantity Min quantity of SetToken to receive after issuance
7
* @param _to Address to mint SetToken to
8
*/
9
function issueWithEther(
10
ISetToken _setToken,
11
uint256 _minSetTokenReceiveQuantity,
12
address _to
13
)
Copied!
The reserveAssetQuantity is the quantity denominated in the reserve asset, not the SetToken. The minSetTokenReceiveQuantity parameter prevents oracle changes that adversely affect issuance.
All ERC20 components must be approved to the NAVIssuanceModule contract with appropriate allowance for transfer. This is not required for issueWithEther.
In order to figure out the correct token amounts needed for issuance you can use the following helper functions:
1
/**
2
* Checks if issue is valid
3
*
4
* @param _setToken Instance of the SetToken
5
* @param _reserveAsset Address of the reserve asset
6
* @param _reserveAssetQuantity Quantity of the reserve asset to issue with
7
*
8
* @return bool Returns true if issue is valid
9
*/
10
function isIssueValid(
11
ISetToken _setToken,
12
address _reserveAsset,
13
uint256 _reserveAssetQuantity
14
)
15
​
16
/**
17
* Get the expected SetTokens minted to recipient on issuance
18
*
19
* @param _setToken Instance of the SetToken
20
* @param _reserveAsset Address of the reserve asset
21
* @param _reserveAssetQuantity Quantity of the reserve asset to issue with
22
*
23
* @return uint256 Expected SetTokens to be minted to recipient
24
*/
25
function getExpectedSetTokenIssueQuantity(
26
ISetToken _setToken,
27
address _reserveAsset,
28
uint256 _reserveAssetQuantity
29
)
Copied!

Redeeming Sets:

The logic is very similar just in reverse. The interface for redeem and redeemIntoEther is as follows:
1
/**
2
* Redeems a SetToken into a valid reserve asset representing the appropriate % of Net Asset Value of the SetToken
3
* to the specified _to address. Only valid if there are available reserve units on the SetToken.
4
*
5
* @param _setToken Instance of the SetToken contract
6
* @param _reserveAsset Address of the reserve asset to redeem with
7
* @param _setTokenQuantity Quantity of SetTokens to redeem
8
* @param _minReserveReceiveQuantity Min quantity of reserve asset to receive
9
* @param _to Address to redeem reserve asset to
10
*/
11
function redeem(
12
ISetToken _setToken,
13
address _reserveAsset,
14
uint256 _setTokenQuantity,
15
uint256 _minReserveReceiveQuantity,
16
address _to
17
)
Copied!
1
/**
2
* Redeems a SetToken into Ether (if WETH is valid) representing the appropriate % of Net Asset Value of the SetToken
3
* to the specified _to address. Only valid if there are available WETH units on the SetToken.
4
*
5
* @param _setToken Instance of the SetToken contract
6
* @param _setTokenQuantity Quantity of SetTokens to redeem
7
* @param _minReserveReceiveQuantity Min quantity of reserve asset to receive
8
* @param _to Address to redeem reserve asset to
9
*/
10
function redeemIntoEther(
11
ISetToken _setToken,
12
uint256 _setTokenQuantity,
13
uint256 _minReserveReceiveQuantity,
14
address payable _to
15
)
Copied!
You specify the amount of Sets you want to redeem and then those Sets are burned and the components are transferred to the calling address. The minReserveReceiveQuantity prevents faulty oracles that affect redemption.
In order to figure out the correct token amounts needed for redemption you can use the following helper functions:
1
/**
2
* Checks if redeem is valid
3
*
4
* @param _setToken Instance of the SetToken
5
* @param _reserveAsset Address of the reserve asset
6
* @param _setTokenQuantity Quantity of SetTokens to redeem
7
*
8
* @return bool Returns true if redeem is valid
9
*/
10
function isRedeemValid(
11
ISetToken _setToken,
12
address _reserveAsset,
13
uint256 _setTokenQuantity
14
)
15
​
16
​
17
/**
18
* Get the expected reserve asset to be redeemed
19
*
20
* @param _setToken Instance of the SetToken
21
* @param _reserveAsset Address of the reserve asset
22
* @param _setTokenQuantity Quantity of SetTokens to redeem
23
*
24
* @return uint256 Expected reserve asset quantity redeemed
25
*/
26
function getExpectedReserveRedeemQuantity(
27
ISetToken _setToken,
28
address _reserveAsset,
29
uint256 _setTokenQuantity
30
)
Copied!
Last modified 10mo ago