MinterAmm
The
MinterAmm
is an Automated Market Maker or "AMM" designed specifically for trading options for ERC20
tokens. It exposes functions for buying and selling bToken
as well as selling wToken
[^1]. Each MinterAmm
trades one of the two types of option Series: Puts or Calls. All option premiums will be in units of the AMM's collateral token, which for a Call AMM will be equal to the underlying token (e.g. a WBTC Call AMM's collateral token is WBTC), and for a Put AMM be equal to USDC (e.g. a WBTC Put AMM's collateral token is USDC). It uses the collateral token added by liquidity providers to mint bTokens
and wTokens
, keeping the wTokens
for itself and selling the bTokens
to traders. The AMM exposes functions for providing and withdrawing liquidity in the AMM pool. It is often useful to know the total value of all the option tokens and collateral token that comprise a pool, and there is a function for that as well.When a trader goes to buy or sell option tokens via the
MinterAmm
, the quoted premium includes consists of the component from the onchain Black-Scholes approximation formula, as well as a price impact component. It is not possible to guarantee an exact option token price, because the price depends on the reserves of bToken
and wToken
held by the AMM, and those reserves might change if another transaction which affects the reserve amounts gets mined before the original trader's transaction gets mined. This is also true for when an LP withdraws their liquidity from the AMM and sets sellTokens = true
, which will sell the wTokens
owed to trader back to AMM in return for collateral token. This is why all of these functions take some slippage function argument, either collateralMinimum
when selling option tokens, or collateralMaximum
when buying option tokens.However, it's still useful for the trader to know what the expected price will be, and if there are no changes in the reserves in between when the trader broadcasts the transaction and when the trader's transaction gets mined, this will indeed be the price of the trade. The AMM exposes functions for calculating the expected price when selling
bTokens
, buying 'bTokens', and selling wTokens
.For more details on the pricing formulas used throughout the AMM, see the [protocol math section](TODO point to this)
modifier minTradeSize(uint256 tradeSize) {
require(
tradeSize >= MINIMUM_TRADE_SIZE,
"Buy/Sell amount below min size"
);
_;
}
Prevents low-valued trades from occurring. Due to numeric rounding issues with Solidity, if a user passes a low-valued argument in
bTokenBuy
, bTokenSell
, or wTokenSell
unexpected behavior can occur. So to preclude this from happening we gate these function calls with the minTradeSize
modifier modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: 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. function lpToken(
) public view returns (ISimpleToken)
function underlyingToken(
) public view returns (IERC20)
Returns the address of the
ERC20
token whose price determines the (glossary.md#moneyness) of the Series this AMM trades. 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 AMM trades Calls then this will be the same token as the collateralToken. function priceToken(
) public view returns (IERC20)
Returns the address of the
ERC20
token used to denominate the strike price
of the Series this AMM trades. This will almost always be USDC, but in the future could be different when SIREN denominates prices in something other than USDC. If this AMM trades Puts then this will be the same token as the collateralToken function collateralToken(
) public view returns (IERC20)
Returns the address of the
ERC20
token this AMM uses for collateral. This is the token used to denominate all option prices (a.k.a. premiums) in the MinterAmm
, as well as the liquidity provided by LPsIf 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.
function seriesController(
) public view returns (ISeriesController)
Returns the
SeriesController
associated with this MinterAmm
. The MinterAmm
uses the SeriesController
to interact with the Series it trades. function erc1155Controller(
) public view returns (IERC1155)
Returns the
ERC1155Controller
associated with this MinterAmm
. The MinterAmm
uses the ERC1155Controller
to interact with the the option tokens of the Series it trades. function tradeFeeBasisPoints(
) public view returns (uint16)
The fee, denominated in bips (i.e. hundredths of a percent), that the
MinterAmm
takes when it executes a trade. function volatilityFactor(
) public view returns (uint256)
The volatility coefficient to use in the Black-Scholes approximation formula. This value is calculated offchain to save gas, and updated by the contract owner. It is equal to the implied volatility multiplied by
0.4
, multiplied by the square root of the number of seconds in a year (i.e. implied_volatility * 0.4 * Math.sqrt(365 * 24 * 60 * 60)
) function MINIMUM_TRADE_SIZE(
) public view returns (uint256)
function getTotalPoolValue(bool includeUnclaimed)
public
view
returns (uint256)
Returns the total value of all the tokens held by the pool, denominated in units of collateral token. The AMM's value stems from several different tokens: The option tokens it holds, as well as the [collateral token] is has received from LP's and from collecting option premiums.
The argument
includeUnclaimed
exists only as a way to save on gas when calculating total pool value on chain; if the caller knows all option tokens have been claimed (which will be true if claimAllExpiredTokens
has just been called), it can pass false
for includeUnclaimed
and skip calculating their value.Parameters
Name | Type | Description |
---|---|---|
includeUnclaimed | bool | true if the value of expired of unclaimed wToken should be included in the total pool calculation, and false if it should not be included |
function getAllSeries() external view returns (uint64[] memory)
function getSeries(uint64 seriesId)
external
view
returns (ISeriesController.Series memory)
Parameters
Name | Type | Description |
---|---|---|
seriesId | uint64 | The ID of the Series |
function getVirtualReserves(uint64 seriesId)
public
view
returns (uint256, uint256)
Returns the balances of
bToken
and wToken
available to the AMM for the given seriesId
. The AMM uses these balances as inputs into its bonding curve equation to compute the premiums for buying and selling option tokens.We use the word "virtual" because the balances returned by this function include not only the raw balances of
bToken
and wToken
held by the AMM, but additionally those option tokens that could be minted using the AMM's collateral token. The amounts returned will likely be slightly less than the full bToken
and wToken
amounts owned by the AMM, because the amounts returned must satisfy the ratio of the wToken
and bToken
prices computed by the onchain Black-Scholes approximation formula. See the documentation section on [protocol math](TODO point to this) for the motivation behind calculating virtual reserve balances in this manner.Parameters
Name | Type | Description |
---|---|---|
seriesId | uint64 | The ID of the Series |
Return Values
Name | Type | Description |
---|---|---|
bTokenVirtualBalance | uint256 | The amount of bToken available on the AMM |
wTokenVirtualBalance | uint256 | The amount of wToken available on the AMM |
function getPriceForSeries(uint64 seriesId)
external
view
returns (uint256)
Returns the price of the underlyingToken of the given
seriesId
denominated in the priceToken. The returned values always use 8
decimal places. For example, if the Series' underlying == WBTC and price == USDC, then this function will return 4500000000000
($45_000 in human readable units).Parameters
Name | Type | Description |
---|---|---|
seriesId | uint64 | The ID of the Series |
function calcPrice(
uint256 timeUntilExpiry,
uint256 strike,
uint256 currentPrice,
uint256 volatility,
bool isPutOption
) public pure returns (uint256)
Returns the price of a Series'
bToken
as a percentage of collateral locked by 1 option token, multiplied by a scaling factor of 1e18
. For instance, if the price of 1 bToken
is 0.1 * 1e18
, and the bToken
's Series has a strike price of $35,000, then the price of 1 bToken
in units of USDC is $3,500.The scaling factor exists as a workaround to Solidity's lack of floating point numerics. If we did not use the scaling factor, then any division of the price variable would round down to 0. So a price of
0.5 * 1e18
and be thought treated logically as simply 0.5
.calcPrice
uses a Black-Scholes approximation formula for pricing bTokens
. See the section [protocol math](TODO link to this) for more details on the approximation formula.The price of a Series'
wToken
is always 1e18 - price_of_btoken
, so in the above scenario, the price of the wToken
would be $35,000 - $3,500 = $31,500.Parameters
Name | Type | Description |
---|---|---|
timeUntilExpiry | uint256 | |
strike | uint256 | The strike price of the Series'. See The Series struct parameters section for more details on the strike price |
currentPrice | uint256 | The spot price of the Series' underlying token, denominated in units of price token with 8 decimals |
volatility | uint256 | |
isPutOption | uint256 |
function bTokenGetCollateralIn(uint64 seriesId, uint256 bTokenAmount)
public
view
returns (uint256)
Returns the premium for purchasing from the AMM
bTokenAmount
of bTokens
on the given seriesId
in units of collateral token. The premium has two components: the price returned by the Black-Scholes approximation formula, and the added cost of price impact.Parameters
Name | Type | Description |
---|---|---|
seriesId | uint64 | The ID of the Series |
bTokenAmount | uint256 | The amount of bToken the caller wants to receive in return for paying the premium |
function bTokenGetCollateralOut(uint64 seriesId, uint256 bTokenAmount)
public
view
returns (uint256)
Returns the premium for selling to the AMM
bTokenAmount
of bTokens
on the given seriesId
in units of collateral token. The premium has two components: the price returned by the Black-Scholes approximation formula, and the price impact.Parameters
Name | Type | Description |
---|---|---|
seriesId | uint64 | The ID of the Series |
bTokenAmount | uint256 | The amount of bToken the caller wants to sell to the AMM |
function wTokenGetCollateralOut(uint64 seriesId, uint256 wTokenAmount)
public
view
returns (uint256)
Returns the premium for selling to the AMM
wTokenAmount
of wTokens
on the given seriesId
in units of collateral token. The premium has two components: the price returned by the Black-Scholes approximation formula, and the price impact.Parameters
Name | Type | Description |
---|---|---|
seriesId | uint64 | The ID of the Series |
wTokenAmount | uint256 | The amount of wToken the caller wants to sell to the AMM |
function getCollateralValueOfAllExpiredOptionTokens()
public
view
returns (uint256)
Returns the amount of collateral token the AMM would receive by redeeming all expired
bTokens
and wTokens
held by the AMM. This function is useful for offchain clients to calculate the total collateral token available to the AMM. The offchain clients can use this to calculate the collateral owed to LPs when The LPs withdraw their LP Token function getOptionTokensSaleValue(uint256 lpTokenAmount)
external
view
returns (uint256)
Returns the expected amount of collateral token an LP will receive for selling the
bTokens
and wTokens
held by the AMM on the LP's behalf. This function is useful for offchain clients to calculate value of the option tokens owed to the holder of lpTokenAmount
of LP Token.For example, if
lpTokenAmount
is equal to 10% of the total supply of LP Token, then the return value of MinterAmm.getOptionTokensSaleValue
will be equal to the premiums for selling 10% of the bTokens
and wTokens
held by the AMM. See MinterAmm.bTokenGetCollateralOut
and MinterAmm.wTokenGetCollateralOut
for how to calculate the premiums when selling bTokens
and wTokens
.Parameters
Name | Type | Description |
---|---|---|
lpTokenAmount | uint256 | The amount of LP token that the caller will burn in return for collateral |
function provideCapital(uint256 collateralAmount, uint256 lpTokenMinimum)
external
Transfers the caller's collateral token to the AMM so it can be used for minting options, and in return sends LP Token the caller can use to later withdraw their share of the AMM's total value.
The amount of LP Token the caller receives depends on the AMM's total value, which itself depends on the AMM's reserves of option tokens and collateral token. The reserve amounts may change when the caller's transaction finally gets included in the blockchain, so this function takes an
lpTokenMinimum
argument to specify the minimum acceptable amount of LP Token the caller will receive, and any lower than this will cause the function call to revert.LPs profit when the fees they acquire from selling options is greater than their losses from the options they sell going deeper in the money.
Parameters
Name | Type | Description |
---|---|---|
collateralAmount | uint256 | |
lpTokenMinimum | uint256 | The minimum amount of LP Token the caller is willing to receive for providing collateraAmount of collateral token |
function withdrawCapital(
uint256 lpTokenAmount,
bool sellTokens,
uint256 collateralMinimum
) public
The caller burns
lpTokenAmount
of their LP Token and in exchange receives their pro-rata share of the AMM's tokens. For example, if lpTokenAmount
is equal to 10% of the total supply of LP Tokens, then the caller will receive 10% of the AMM's collateral token as well as 10% of each of the option tokens the AMM holds. If sellTokens
is set to true
, then the caller will receive only collateral token, because all of LP's option tokens will be sold to the AMM in exchange for collateral token. However, because of price impact the value of the collateral token the LP receives may be less than 10% of the AMM's total value.The amount of collateral token the caller receives depends on the AMM's total value, which itself depends on the AMM's reserves of option tokens and collateral token. The reserve amounts may change when the caller's transaction finally gets included in the blockchain, so this function takes a
collateralMinimum
argument to specify the minimum acceptable amount of collateral token the caller will receive, and any lower than this will cause the function call to revert.Parameters
Name | Type | Description |
---|---|---|
lpTokenAmount | uint256 | The amount of LP Token to burn |
sellTokens | bool | true if the caller wishes to sell their option tokens to the AMM and only receive collateral token, and false if they wish to receive less collateral token but receive their pro-rata share of the AMM's option tokens which they can sell at a later time |
collateralMinimum | uint256 | The minimum amount of collateral token the caller wishes to receive for burning their LP Token |
function claimAllExpiredTokens() public
Redeems all the option tokens held by the AMM that have recently expired. See
MinterAmm.claimExpiredTokens
function claimExpiredTokens(uint64 seriesId) public
Redeems the given Series' expired
bTokens
and wTokens
held by the AMM. If the Series is out of the money then all bTokens
will be worthless, and the wTokens
will be claimed for the collateral value locked when minting them. If the Series is in the money then both the bTokens
and wTokens
will have some non-zero value.The AMM redeems all expired option tokens every time an LP provides or withdraws liquidity, so the AMM is constantly converting expired option tokens back into collateral it can use to mint further options
Parameters
Name | Type | Description |
---|---|---|
seriesId | uint64 | The ID of the Series |
function bTokenBuy(
uint64 seriesId,
uint256 bTokenAmount,
uint256 collateralMaximum
) external minTradeSize(bTokenAmount) returns (uint256)
Purchases
bTokenAmount
of bToken
from the AMM, but will revert if the premium calculated by the AMM exceeds collateralMaximum
. The return value is the premium (in units of collateral token) the caller pays. Prior to calling this function, the caller must approve the AMM for the collateralMaximum
amount. See MinterAmm.bTokenGetCollateralIn
for how the AMM calculates the premium.Parameters
Name | Type | Description |
---|---|---|
seriesId | uint64 | The ID of the Series |
bTokenAmount | uint256 | The amount of bToken the caller wishes to purchase |
collateralMaximum | uint256 | The maximum amount of collateral the caller wishes to pay for the bToken . The actual premium can exceed this value due to slippage |
function bTokenSell(
uint64 seriesId,
uint256 bTokenAmount,
uint256 collateralMinimum
) external minTradeSize(bTokenAmount) returns (uint256)
Sells
bTokenAmount
of bToken
to the AMM, but will revert if the premium calculated by the AMM goes below collateralMinimum
. The return value is the premium (in units of collateral token) the caller receives. Prior to calling this function, the caller must approve the AMM for the bTokenAmount
. See MinterAmm.bTokenGetCollateralOut
for how the AMM calculates the premium.Parameters
Name | Type | Description |
---|---|---|
seriesId | uint64 | The ID of the Series |
bTokenAmount | uint256 | The amount of bToken the caller wishes to purchase |
collateralMinimum | uint256 | The minimum amount of collateral the caller wishes to receive for selling their bToken , The actual premium can go below this value due to slippage |
function wTokenSell(
uint64 seriesId,
uint256 wTokenAmount,
uint256 collateralMinimum
) external minTradeSize(wTokenAmount) returns (uint256)
Sells
wTokenAmount
of wToken
to the AMM, but will revert if the premium calculated by the AMM goes below collateralMinimum
. The return value is the premium (in units of collateral token) the caller receives. Prior to calling this function, the caller must approve the AMM for the wTokenAmount
. See MinterAmm.wTokenGetCollateralOut
for how the AMM calculates the premium.Parameters
Name | Type | Description |
---|---|---|
seriesId | uint64 | The ID of the Series |
wTokenAmount | uint256 | The amount of wToken the caller wishes to purchase |
collateralMinimum | uint256 | The minimum amount of collateral the caller wishes to receive for selling their wToken , The actual premium can go below this value due to slippage |
function setVolatilityFactor(uint256 _volatilityFactor) public onlyOwner
Sets a new volatility factor for this AMM to use when pricing option premiums. See
MinterAmm.calcPrice
for how it is used.Parameters
Name | Type | Description |
---|---|---|
_volatilityFactor | uint256 | The new volatility factor, equal to 0.4 * implied_vol * sqrt(num_seconds_in_year) |
function updateImplementation(
address _newImplementation
) external onlyOwner
Updates this
MinterAmm
'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 users' option tokens 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 MinterAmm 's function implementations |
function transferOwnership(address newOwner) external onlyOwner
Removes ownership from the current owner and assigns ownership to the
newOwner
address. See the onlyOwner
modifier for the permissions granted to the protocol admin.Parameters
Name | Type | Description |
---|---|---|
newOwner | address | The address of the new admin for this contract |
[^1]: It is currently not possible for the AMM to buy
wToken
because of a technical blocker in the SIREN protocol's Black-Scholes approximation algorithm. The algorithm over-prices bToken
's and underprices wToken
's, and so if the AMM were to sell wToken
's then through arbitrage the AMM would quickly be drained of all wToken
's. For that reason, we cannot add functionality for selling wToken
's to the AMMLast modified 1yr ago