Series Controller

Polygon Contract Address

View on Polygonscan

Source Code

SeriesController.sol

Overview

The SeriesController implements all of the logic involving the lifecycle of option tokens. It uses the ISeriesController.Series struct to represent all option series.

Any contract wishing to redeem SIREN option tokens will need the SeriesController contract address.

SeriesController.mintOptions can be used to mint onchain Call and Put options. The call to SeriesController.mintOptions locks X amount of collateral token to a Series and in return mints X bToken and X wToken to the caller. This bToken can then be sold at its premium to those wishing to purchase ERC20 options, and if after expiry the Series is in the money then the bToken can be redeemed for a portion of the Series' locked collateral. The wToken can be kept until the Series expires at which point the remaining locked collateral can be claimed. The SeriesController exposes functions for redeeming the bToken (SeriesController.exerciseOption) and wToken (SeriesController.claimCollateral).

Since X bToken + X wToken is worth X collateral token , there is a function SeriesController.closePosition which acts like the inverse of SeriesController.mintOptions; X bToken + X wToken can be burned in return for the X collateral token locked in the Series.

Functions

Modifiers

onlyOwner

modifier onlyOwner() {
require(
hasRole(DEFAULT_ADMIN_ROLE, msg.sender),
"SeriesController: Caller is not the owner"
);
_;
}

Certain functions can only be called by the SIREN protocol owner multisig address. The functions updateImplementation and transferOwnership exist so that the owners can update the contracts in the event of a critical vulnerability that puts user's funds at risk.

View/Pure Functions

latestIndex

function latestIndex(
) public view returns (uint64)

Returns the index to be used for the next call to SeriesController.createSeries. Each Series uses a monotonically incrementing uint64 as an index into the array of Series. To read the fields of any single Series struct, use the series function.

erc1155Controller

function erc1155Controller(
) public view returns (address)

Returns the address of this SeriesController's ERC1155Controller. This is the contract that implements all of the ERC1155 token functions.

priceDecimals

function priceDecimals(
) public view returns (uint8)

Returns the value of 8, which is the number of leading 0's in all of the oracle price values used by the SeriesController. More specifically, all of the current market prices returned by SeriesController.getSettlementPrice and SeriesController.getCurrentPrice use 8 leading decimals. For example, if the current price of WBTC[TODO glossary] is $34,000, then the value returned by SeriesController.getCurrentPrice for a WBTC Call Series would be 34_000 * 1e8.

state

function state(
uint64 seriesId,
) public view returns (ISeriesController.SeriesState)

Returns the state of given Series, which depends on the current block.timestamp. If block.timestamp is before the Series' expiration date, then it is in the state OPEN, otherwise it is in the state EXPIRED. Only certain functions can be executed when the state is OPEN, and some only when the state is EXPIRED.

Parameters

Name

Type

Description

seriesId

uint64

The ID of the Series

series

function series(
uint64 seriesId,
) external view override returns (ISeriesController.Series memory)

Returns a specific Series by its ID. This struct contains information about the option series.

Parameters

Name

Type

Description

seriesId

uint64

The ID of the Series

calculateFee

function calculateFee(
uint256 amount,
uint16 basisPoints
) public pure override returns (uint256)

Returns the fee amount in units of collateral token when exercising bToken, claiming `wToken, or closing a position.

Parameters

Name

Type

Description

amount

uint256

The amount of collateral token to take a percentage fee of

basisPoints

uint16

The fee percentage, expressed in basis points (100 basis points = 1%)

getExerciseAmount

function getExerciseAmount(
uint64 _seriesId,
uint256 _bTokenAmount
) public view override returns (uint256, uint256)

Returns the payout amount for exercising _bTokenAmount of this Series's bToken, as well as the exercise fee, both in units of collateral token. See the documentation section on protocol math for how the exercise payout is calculated.

This is useful to call before a call to `SeriesController.exerciseOption so you can see what the payout will be prior to exercising the bToken.

Parameters

Name

Type

Description

_seriesId

uint64

The ID of the Series

_bTokenAmount

uint256

The amount of bToken to exercise

Return Values

Name

Type

Description

buyerShare

uint256

The amount of collateral the exerciser receives

feeAmount

uint256

The amount of collateral the exerciser pays to the protocol for exercising

getClaimAmount

function getClaimAmount(
uint64 _seriesId,
uint256 _wTokenAmount
) public view override returns (uint256, uint256)

Returns the payout amount for claiming _wTokenAmount of this Series's wToken, as well as the exercise fee, both in units of collateral token. See the documentation section on protocol math for how the claim payout is calculated.

Parameters

Name

Type

Description

_seriesId

uint64

The ID of the Series

_wTokenAmount

uint256

The amount of wToken to claim

Return Values

Name

Type

Description

writerShare

uint256

The amount of collateral the claimer receives

feeAmount

uint256

The amount of collateral the claimer pays to the protocol for claiming

strikePrice

function strikePrice(
uint64 _seriesId,
) external view override returns (uint256)

Returns the strike price of this Series. For Calls this is the price above which the bToken holder's option is ITM, and for Puts this is the price below which the bToken holder's option is ITM. The strike price always has 8 decimals. For example, a strikePrice of 3400000000000 indicates the price is $34,000.

The majority of SIREN options denominate prices (including strike prices) in units of USDC, but in the future the protocol might use different denominations (e.g. units of ETH) for prices.

Parameters

Name

Type

Description

_seriesId

uint64

The ID of the Series

expirationDate

function expirationDate(
uint64 _seriesId,
) external view override returns (uint40)

Returns the expiration date of the Series, in units of block time (i.e. seconds past epoch). Prior to this date the Series is in the OPEN state and can mint options and close positions, but cannot redeem option tokens. At and after this date the Series is in the EXPIRED state and can redeem option tokens, but can no longer mint or close out positions.

Parameters

Name

Type

Description

_seriesId

uint64

The ID of the Series

underlyingToken

function underlyingToken(
uint64 _seriesId,
) external view override returns (address)

Returns the address of the ERC20 token whose price determines the (glossary.md#moneyness) of the Series. For example, both WBTC Call option series and WBTC Put option series share WBTC as their underlying token, because the price of WBTC determines whether the Series is in or out of the money. If this is a Call Series then this will be the same token as the collateralToken.

Parameters

Name

Type

Description

_seriesId

uint64

The ID of the Series

priceToken

function priceToken(
uint64 _seriesId,
) external view override returns (address)

Returns the address of the ERC20 token used to denominate the strike price. This will almost always be USDC, but in the future could be different if we want to denominate prices in something other than USDC. If this a Put Series then this will be the same token as the collateralToken

Parameters

Name

Type

Description

_seriesId

uint64

The ID of the Series

collateralToken

function collateralToken(
uint64 _seriesId,
) external view override returns (address)

Returns the address of the ERC20 token used to underwrite this Series' option positions. This token will be used to denominate all option prices in the MinterAmm

If this is a Call Series then the collateral token will be equal to the underlyingToken, because a Call gives the holder the right to buy the underlying. And if this is a Put Series then the collateral token will be equal to the priceToken, because a Put gives the holder the right to sell the underlying.

Parameters

Name

Type

Description

_seriesId

uint64

The ID of the Series

wTokenIndex

function wTokenIndex(
uint64 _seriesId,
) external view override returns (uint256)

Returns the ERC1155 token index of the wToken for this Series. This index can then be used on the ERC1155Controller to query for wToken balances.

Parameters

Name

Type

Description

_seriesId

uint64

The ID of the Series

Parameters

Name

Type

Description

_seriesId

uint64

The ID of the Series

bTokenIndex

function bTokenIndex(
uint64 _seriesId,
) external view override returns (uint256)

Returns the ERC1155 token index of the bToken for this Series. This index can then be used on the ERC1155Controller to query for bToken balances.

Parameters

Name

Type

Description

_seriesId

uint64

The ID of the Series

isPutOption

function isPutOption(
uint64 _seriesId,
) external view override returns (bool)

Returns true if this Series is for a Put option, and false if it's for a Call option.

Parameters

Name

Type

Description

_seriesId

uint64

The ID of the Series

getSeriesERC20Balance

function getSeriesERC20Balance(
uint64 _seriesId,
) external view override returns (uint256)

Returns the amount of collateral token locked in this Series. The amount of locked tokens increases with every call to SeriesController.mintOptions and decreases with every call to SeriesController.exerciseOption, SeriesController.claimCollateral, and SeriesController.closePosition. This function is also useful for calculating part of the TVL (Total Value Locked) in the protocol.

Parameters

Name

Type

Description

_seriesId

uint64

The ID of the Series

getCollateralPerOptionToken

function getCollateralPerOptionToken(
uint64 _seriesId,
uint256 _optionTokenAmount
) public view override returns (uint256)

Returns the amount of collateral token locked in the Series for every _optionTokenAmount of option tokens. For Call options this is simply equal to _optionTokenAmount; 1 bToken or 1 wToken are backed by 1 collateral token. However, for Puts it's slightly more complicated. Because a Put option gives the bToken holder the right to sell the underlying token at the strike price, 1 bToken or 1 wToken must be backed by 1 * strike_price * decimals_coefficient amount of collateral token. The decimals_coefficient is term which divides out the price token and underlying token decimals, leaving only the correct decimals of the collateral token remaining.

Parameters

Name

Type

Description

_seriesId

uint64

The ID of the Series

_optionTokenAmount

uint256

The amount of wToken or bToken

getSettlementPrice

function getSettlementPrice(
uint64 _seriesId
) external view override returns (bool, uint256)

Returns a tuple of 2 values, the first indicates whether or not the settlement price has been set by the settlement bot and second value is the settlement price, or 0 if it has not been set yet. A Series' settlement price is set by an offchain bot as soon as that Series expires. The settlement price will have 8 decimals, and use the same units as the priceToken. For example, if the settlement price of a certain Series is $34,000, then settlement_price = 34_000 * 1e8.

Setting the Settlement Price

The settlement price will be equal to 0 if the current block timestamp is prior to the Series expiration date. It's also possible the settlement date will be equal to 0 for a small amount of time (1-15 minutes) after the expiration date. This is because the EVM does not provide any functionality for automatically calling contract functions on a predefined schedule (such as cron), and so an offchain process must send a transaction calling PriceOracle.setSettlementPrice and set the current underlying token's price manually. The EVM gives no guarantees on when a transaction will be included in the blockchain, so the protocol cannot guarantee the settlement price will be set exactly at the Series' expiration date.

Parameters

Name

Type

Description

_seriesId

uint64

The ID of the Series

Return Values

Name

Type

Description

isSet

bool

true if the Series price at expiration (the settlement date) has been set, false otherwise

settlementPrice

uint256

The price of the Series' underlying token at the time of the Series' expiration date

getCurrentPrice

function getCurrentPrice(
address underlyingToken,
address priceToken
) public view override returns (uint256)

Returns the current price of the underlying token denominated in units of the priceToken. For example, if the current price of WBTC[TODO glossary] was $34,000, then the value returned by SeriesController.getCurrentPrice for a WBTC Series would be 34_000 * 1e8. This price is fetched via the PriceOracle, which in turn fetches it from an onchain oracle.

Parameters

Name

Type

Description

underlyingToken

address

An ERC20 token

priceToken

address

An ERC20 token

Mutating Functions

createSeries

function createSeries(
ISeriesController.Tokens calldata _tokens,
uint256[] calldata _strikePrices,
uint40[] calldata _expirationDates,
address[] calldata _restrictedMinters,
bool _isPutOption
) external onlyOwner

Creates one or more Series structs with the given parameters. This function has the onlyOwner modifier so it can only be called by the protocol admins.

The choice of underlyingToken determines the ERC20 token the Series will be for (i.e. the token whose price movements will determine the long and short payouts). The priceToken will be the token used to denominate the price in (e.g. for a WBTC option denominated in USDC, the underlying token will be WBTC and the price token will be USDC, but for a WBTC option denominated in ETH, the underlying token will be WBTC and the price token will be ETH). The collateralToken determines the ERC20 token used to underwrite the covered options. For Put options the collateralToken must be equal to the priceToken, and for Call options the collateralToken must be equal to the underlyingToken.

Parameters

Name

Type

Description

_tokens

ISeriesController.Tokens calldata

The underlyingToken, priceToken, and collateralToken for these Series

_strikePrices

uint256[]

An array of the strike prices at which the Series will become in the money

_expirationDates

uint40[]

An array of the block timestamps these Series will expire. These must be after the current block timestamp

_restrictedMinters

address[]

An array of MinterAmm addresses which will be the only AMM's permitted to mint option tokens for these Series

_isPutOption

bool

true if these Series should be Put options, and false if they should be Call options

mintOptions

function mintOptions(
uint64 _seriesId,
uint256 _optionTokenAmount
) external override whenNotPaused

Creates new _optionTokenAmount amount of a given Series's wTokens and bTokens by locking collateral in that Series. The option tokens are transferred to the caller. One of the fundamental formulas for the SIREN protocol is:

X collateralToken ==> X wToken and X bToken

or, for every X amount of collateral token a minter locks in the protocol, the minter receives X amount of wToken and X amount of bToken.

The MinterAmm contracts are the only accounts permitted to call this function. They use this to mint additional option tokens using LP liquidity and sell them to traders.

The exact amount of collateral required depends on whether or not the given Series is a Put or a Call option. See the function SeriesController.getCollateralPerOptionToken for more details on computing the exact amount of collateral needed to mint a given amount of option tokens.

Parameters

Name

Type

Description

seriesId

uint64

The ID of the Series

_optionTokenAmount

uint256

The amount of wTokens or bTokens to mint

exerciseOption

function exerciseOption(
uint64 _seriesId,
uint256 _bTokenAmount,
bool _revertOtm
) external override whenNotPaused

Burns _bTokenAmount amount of bToken and transfers the bToken's payout to the caller. The SIREN protocol are cash-settled, so the payout for bTokens is calculated as some fraction of the collateral token locked in the Series. The payout of the bToken depends on the Series' strike price and the price of the underlying token. For Puts, the payout increases as the underlying token's price decreases and for Calls the payout increases as the underlying tokne's price increases. See the section Protocol Math for the details on how the protocol calculates bToken payout.

Because SIREN's options are [European] style, this function can only be executed after the Series has expired.

Parameters

Name

Type

Description

seriesId

uint64

The ID of the Series

_bTokenAmount

uint256

The amount of bTokens to exercise

_revertOtm

bool

true if you want this function call to revert if the Series is OTM, false if you want it to continue executing even if it's OTM. Passing true will save gas but will require the calling context to handle the error (possibly resulting in the entire transaction reverting). Passing false will use more gas, but will effectively be a no-op and preclude the calling context from worrying about their transaction reverting

claimCollateral

function claimCollateral(
uint64 _seriesId,
uint256 _wTokenAmount
) external override whenNotPaused

Burns _wTokenAmount amount of wToken and transfers the wToken's payout to the caller. The SIREN protocol are cash-settled, so the payout for wTokens is calculated as some fraction of the collateral token locked in the Series. The payout of the wToken depends on the Series' strike price and the price of the underlying token. For Puts, the payout decreases as the underlying token's price decreases and for Calls the payout decreases as the underlying token's price increases. See the section Protocol Math for the details on how the protocol calculates wToken payout.

Because SIREN's options are [European] style, this function can only be executed after the Series has expired.

Parameters

Name

Type

Description

seriesId

uint64

The ID of the Series

_wTokenAmount

uint256

The amount of wTokens to claim. It uses the same decimals as the Series' underlying token

closePosition

function closePosition(
uint64 _seriesId,
uint256 _optionTokenAmount
) external override whenNotPaused

Burns _optionTokenAmount amount of wToken and bToken, then transfers the equivalent amount of collateral token to the caller. For Calls the amount of collateral token received will be equal to _optionTokenAmount, and for Puts the amount of collateral token received will be equal to _optionTokenAmount * strikePrice (see the getCollateralPerOptionToken function for more details on this calculations).

This function can only be called while the Series' has not yet expired. If the Series has expired and you can't call closePosition but you still want your collateral, you can call exerciseOption and claimCollateral to convert your bToken and wToken to collateral token.

Parameters

Name

Type

Description

seriesId

uint64

The ID of the Series

_optionTokenAmount

uint256

The amount of option tokens to burn in exchange for collateral token. It uses the same decimals as the Series' underlying token

updateImplementation

function updateImplementation(
address _newImplementation
) external onlyOwner

Updates this SeriesController's logic contract. The SIREN protocol's contracts use the EIP-1822 standard for implementing upgradeable contracts. This allows us to update vulnerable contracts and keep LP's liquidity and the collateral locked in Series safe. When the SIREN protocol has reached a certain level of stability, we can remove these safety guards and ensure no one on the Siren team can swap out the smart contract functionality.

Parameters

Name

Type

Description

_newImplementation

address

The address of the new logic contract to use for the SeriesController's function implementations

transferOwnership

function transferOwnership(address _newAdmin) external onlyOwner

Removes ownership from the current owner and assigns ownership to the _newAdmin address. See the onlyOwner modifier for the permissions granted to the protocol admin.

Name

Type

Description

_newAdmin

address

The address of the new admin for this contract