Enabling Fees

SetToken managers can charge fees for their services by enabling modules which support it. The most commonly used fee collection modules are for streaming fees and issuance.

Streaming fees are time-based and similar to annual management fees. Issuance fees accrue when someone issues or redeems a SetToken. Both are paid in SetToken by inflating the token's total supply and apportioning the additional balance to designated fee recipients.

Typically the fee collection modules you'll require are added to your SetToken as part of the token creation process.

Fee setting and collection are managed using a separately deployed FeeSplitExtension contract which allows you to:

  • set initial fee structures in place

  • update fees as necessary,

  • split fees between beneficiaries

  • accrue any owed fees at will

The FeeExtension contract is also the temporary holder of any fees that accrue prior to distribution.

The set-v2-strategies-deployments repo provides an annotated script to execute and initialize this extension deploymentfrom the command line. The FeeExtension constructor allow you to to split fees with the manager contract's operator and set the magnitude of this split.

Below we'll look at FeeExtension's methods and how to use them via the Etherscan write UI

accrueFeesAndDistribute

ANYONE CALLABLE: Accrues and distributes fees from streaming fee module. Gets resulting balance after fee accrual, calculates fees for operator and methodologist, and sends to operator fee recipient and methodologist respectively. NOTE: mint/redeem fees will automatically be sent to the FeeExtension contract's address so reading the balance of the SetToken in the contract after accrual is sufficient for accounting for all collected fees.

This method takes no parameters and can be called by anyone.

updateStreamingFee

OPERATOR ONLY: Updates streaming fee on StreamingFeeModule. By default, this method is time-locked to protect token owners from sudden changes in fee structure which they would rather not bear. The delay gives them a chance to exit their positions without penalty. The time-lock requires that the method is called twice, once to set the lock (indicating a fee update is pending) and once to execute the update (after the waiting period has elapsed).

NOTE: Invoking this method accrues streaming fees without forwarding them to operator fee recipient and methodologist. To distribute the fees you should call accrueFeesAndDistribute()

Parameters

  • _newFee Percent of Set accruing to fee extension annually (1% = 1e16, 100% = 1e18)

updateIssueFee

OPERATOR ONLY: Updates the issuance fee on the Issuance module. By default, this method is timelocked to protect token owners from sudden changes in fee structure which they would rather not bear. The delay gives them a chance to exit their positions without penalty. The timelock requires that the method is called twice, once to set the lock (indicating a fee update is pending) and once to execute the update (after the waiting period has elapsed).

  • _newFee New issue fee percentage in precise units (1% = 1e16, 100% = 1e18)

updateRedeemFee

OPERATOR ONLY: Updates the redeem fee on the Issuance module. By default, this method is timelocked to protect token owners from sudden changes in fee structure which they would rather not bear. The delay gives them a chance to exit their positions without penalty. The timelock requires that the method is called twice, once to set the lock (indicating a fee update is pending) and once to execute the update (after the waiting period has elapsed).

  • _newFee New redeem fee percentage in precise units (1% = 1e16, 100% = 1e18)

updateFeeSplit

OPERATOR ONLY: Updates the fee split between operator and methodologist.

  • _newFeeSplit: Percent of fees in precise units (10^16 = 1%) sent to operator, (rest go to the methodologist).

updateOperatorFeeRecipient

Updates the address that receives the operator's share of the fees

  • _newOperatorFeeRecipient Address to send operator's fees to.

initializeStreamingFeeModule

OPERATOR ONLY: This method is called to configure the streaming fee module's initial fee settings. It accepts a FeeState struct with the following format:

struct FeeState {
  address feeRecipient;                // Address to accrue fees to
  uint256 maxStreamingFeePercentage;   // Max streaming fee maanager commits to using (1% = 1e16, 100% = 1e18)
  uint256 streamingFeePercentage;      // Percent of Set accruing to manager annually (1% = 1e16, 100% = 1e18)
  uint256 lastStreamingFeeTimestamp;   // Timestamp last streaming fee was accrued
}

It's convenient to execute this initialization as part of a command line deployment script. An example of an initializing streaming fees using Ethers in Typescript looks like:

// `ether()` converts a number to 1e18
const feeSettings = {
  feeRecipient: feeExtensionInstance.address,
  maxStreamingFeePercentage: ether(.01),
  streamingFeePercentage: ether(.01),
  lastStreamingFeeTimestamp: ZERO,
};

await feeExtensionInstance
  .connect(caller.wallet)
  .initializeStreamingFeeModule(subjectFeeSettings);

initializeIssuanceModule

OPERATOR ONLY: This method is called to configure the issuance module's initial fee settings.

Parameters

  • _maxManagerFee Max size of issuance and redeem fees in precise units (10^16 = 1%).

  • _managerIssueFee Manager issuance fees in precise units (10^16 = 1%)

  • _managerRedeemFee Manager redeem fees in precise units (10^16 = 1%)

  • _feeRecipient Address that receives all manager issue and redeem fees

  • _managerIssuanceHook Address of manager defined hook contract

It's convenient to execute this initialization as part of a command line deployment script. An example of an initializing issuance fees using Ethers in Typescript looks like:

// `ether()` converts a number to 1e18
await feeExtensionInstance.connect(operator.wallet).initializeIssuanceModule(
 ether(.1),                   // Max manager fee = 10%
 ether(.01),                  // Manager issue fee = 1%
 ether(.005),                 // Manager redeem fee = .5%
 feeExtensionInstance.address // Set this extension address as the pre-distribution recipient of all fees
 ADDRESS_ZERO                 // Issuance hook contract, if applicable 
);

Last updated