Search
⌃K
🤓

Integration guide

Funky facts for nerdy devs

Deposit/Redeem/InitiateWithdraw/CompleteWithdraw

Providing and Removing liquidity from the liquidity pool. This mechanism follows a mutual fund mechanism described in Deposit and Withdraw Mechanism.
Refer to LiquidityPool.sol​

Deposit represents sending funds to the liquidity pool

/**
* @notice function for adding liquidity to the options liquidity pool
* @param _amount amount of the collateral asset to deposit in collateral decimals
* @return success
* @dev entry point to provide liquidity to dynamic hedging vault
*/
function deposit(uint256 _amount) external whenNotPaused nonReentrant returns (bool)
This function emits an event called Deposit(msg.sender, amount, depositEpoch); amount is the amount of collateral deposited in the collateral decimals (for usdc this is e6), the depositEpoch is the epoch you deposited in and is the index for the depositEpochPricePerShare, for when the epoch is run.
This function will create a depositReceipt which can be searched in the depositReceipts mapping in the liquidity pool by the address of the depositor.
struct DepositReceipt {
uint128 epoch;
uint128 amount; // collateral decimals
uint256 unredeemedShares; // e18
}
Reasons for this function failing include: insufficient funds for the deposit, not approving the liquidity pool as a spender, protocol paused, the collateral cap has been reached.

Redeem represents receiving your dhv lp tokens after an epoch has been executed

This function is not necessary if you are simply depositing and withdrawing
/**
* @notice function for allowing a user to redeem their shares from a previous epoch
* @param _shares the number of shares to redeem (in e18 decimals, to redeem the max amount just pass in the max uint)
* @return the number of shares actually returned
*/
function redeem(uint256 _shares) external nonReentrant returns (uint256)

InitiateWithdraw represents sending dhv lp tokens back to the liquidity pool for withdrawal

/**
* @notice function for initiating a withdraw request from the pool
* @param _shares amount of shares to return (in e18 decimals)
* @dev entry point to remove liquidity to dynamic hedging vault
*/
function initiateWithdraw(uint256 _shares) external whenNotPaused nonReentrant
​
This function will emit InitiateWithdraw(msg.sender, _shares, withdrawalEpoch); the shares are in e18 decimals and the withdrawalEpoch is the epoch you withdrew in and is the index for the withdrawEpochPricePerShare, for when the epoch is run.
This function will create a withdrawalReceipt which can be searched in the withdrawalReceipts mapping in the liquidity pool by the address of the withdrawer.
struct WithdrawalReceipt {
uint128 epoch;
uint128 shares; // e18
}
Reasons for this function failing include: insufficient funds for the withdrawal, protocol paused, if you have a withdrawal that you need to complete from a previous epoch

CompleteWithdraw represents receiving back your collateral after an epoch has been executed

/**
* @notice function for completing the withdraw from a pool
* @dev entry point to remove liquidity to dynamic hedging vault
*/
function completeWithdraw() external whenNotPaused nonReentrant returns (uint256)
This function will emit Withdraw(msg.sender, withdrawalAmount, withdrawalShares); the amount are in collateral decimals (for usdc this is e6) and the withdrawalShares is the shares you burnt for the withdrawal in e18 decimals.
Reasons for this function failing include: no existing withdrawal initiated, protocol paused, epoch not executed (note: if the utilisation for the epoch you attempted to withdraw was too high (i.e. would push the pool below the liquidity buffer), in order to avoid any insolvency risk on collateral vaults, the withdrawal epoch will not execute until there are free funds to process the withdrawal, however, once an epoch is executed a user is guaranteed to withdraw)

Making trades

For an entire easy view of the options chain you can call get the DHVLensMK1​

Buying

Refer to OptionExchange.sol for a detailed explanation of the contract. Remember prior to most transactions you need to approve the exchange to spend funds on your behalf.
To buy an option there are several steps:
  1. 1.
    Get the series address and proposed series (you need at least one to retrieve the other). You can call getOptionDetails() on the OptionExchange this takes in the option series with a strike in e8 decimals or the seriesAddress. It then outputs the seriesAddress, optionSeries with strike in e8 decimals and the strikePrice converted to e18 decimals
  2. 2.
    Check that the option series is tradeable, you can call checkHash on the OptionExchange providing the option series as above, the strikeDecimalConverted and the isSell boolean for a buy option action this should be false.
  3. 3.
    If the option is tradeable the next step is to get a quote to get an idea of what slippage would be tolerable.
await pricer.quoteOptionPrice({
expiration: 1685088000, // timestamp of the option expiry
strike: toWei("2500"), // strike price in e18 decimals
isPut: CALL_FLAVOR, // true for puts, false for calls
strikeAsset: "0x6775842ae82bf2f0f987b10526768ad89d79536e", // usdc
underlying: "0x53320bE2A35649E9B2a0f244f9E9474929d3B699", // weth
collateral: "0x6775842ae82bf2f0f987b10526768ad89d79536e" // usdc or weth depending on the trade
},
toWei("1"), // the amount to trade in e18 decimals
false, // isSell: whether the trade is a sell or buy
0)) // netDhvExposure: the exposure of the dhv for this specific option, this can be retrieved
// on the AlphaPortfolioValuesFeed: https://github.com/rysk-finance/dynamic-hedging/blob/rysk-beyond/packages/contracts/contracts/AlphaPortfolioValuesFeed.sol#L56
// search the netDhvExposure mapping using a hash of the option as the key as so
// ```typescript
const oHash = ethers.utils.solidityKeccak256(
["uint64", "uint128", "bool"],
[expiration, toWei("2500"), false]
)
```
  1. 4.
    Do the option transaction:
```typescript
const proposedSeries = {
expiration: <number>, // timestamp of expiration at 8am UTC e.g. 1684483200
strike: strikePrice, // strike price in e18 decimals NOT e8
isPut: <bool>, // true for puts, false for calls
strikeAsset: usd.address, // this is always usdc
underlying: weth.address, // this is always weth
collateral: usd.address // this can be usdc or weth
}
​
// In this example a user is issuing and buying a USD collateralised otoken
// (non-USD collateralised otokens will not pass the issue action)
await exchange.operate([
{
operation: 1, // operation type 1 indicates a rysk operation
operationQueue:
[
{
actionType: 0, // action type 0 on a rysk operation indicates an issue action,
// for all types: https://github.com/rysk-finance/dynamic-hedging/blob/rysk-beyond/packages/contracts/contracts/libraries/RyskActions.sol#L20
owner: ZERO_ADDRESS, // for issue action this is ignored
secondAddress: ZERO_ADDRESS, // for issue action this is ignored
asset: ZERO_ADDRESS, // for issue action this is ignored
vaultId: 0, // for issue action this is ignored
amount: 0, // for issue action this is ignored
optionSeries: proposedSeries, // see above for an example of proposed series
indexOrAcceptablePremium: 0, // for issue action this is ignored
data: "0x" // for all rysk actions this is empty
},
{
actionType: 1, // action type 1 on a rysk operation indicates a buy action
owner: ZERO_ADDRESS, // for buy action this is ignored
secondAddress: senderAddress, // this represents the recipient of the option tokens
asset: ZERO_ADDRESS, // this can either be the seriesAddress or ZERO as long as the proposedSeries field is not ZERO
vaultId: 0, // for buy action this is ignored
amount: amount, // the amount of contracts to buy in e18 decimals
optionSeries: proposedSeries, // see above for an example of proposed series, this can be nonsense as long as the series address is correct
indexOrAcceptablePremium: quote * 103%, // for rysk actions this field represents the acceptable
// slippage, this is mainly to prevent sandwich attacks
// for buy actions this is the maximium total premium you
// are willing to spend on your order in e6 decimals.
// so for buys this should generally be bigger than the quote.
data: "0x" // for all rysk actions this is empty
}
]
}
])
​
// In this example a user is buying a ETH collateralised otoken
// in order for this transaction to go through the DHV has to have already bought
// eth collateralised otokens from another user and is now selling this new user
// those ETH collateralised otokens, the amount must be less than or equal to the
// eth collateralised otoken balance that the dhv already has, note that there is
// no issue action, this is because the dhv can only issue USDC collateralised otokens,
// only the exchange can interact with weth collateralised otokens
await exchange.operate([
{
operation: 1, // operation type 1 indicates a rysk operation
operationQueue:
[
{
actionType: 1, // action type 1 on a rysk operation indicates a buy action
// for all types: https://github.com/rysk-finance/dynamic-hedging/blob/rysk-beyond/packages/contracts/contracts/libraries/RyskActions.sol#L20
owner: ZERO_ADDRESS, // for buy action this is ignored
secondAddress: senderAddress, // this represents the recipient of the option tokens
asset: ethCollateralisedOptionToken.address, // this can either be the seriesAddress or ZERO as long as the proposedSeries field is not ZERO
vaultId: 0, // for buy action this is ignored
amount: amount, // the amount of contracts to buy in e18 decimals
optionSeries: emptySeries, // see above for an example of proposed series, this can be nonsense as long as the series address is correct
indexOrAcceptablePremium: quote * 103%,
data: "0x" // for all rysk actions this is empty
}
]
}
])
```
Common reasons for failure: too much slippage, contract paused, not enough liquidity, insufficient funds, insufficient approval

Selling

Refer to OptionExchange.sol for a detailed explanation of the contract. Remember prior to most transactions you need to approve the exchange to spend funds on your behalf.
  1. 1.
    Approve the option exchange as an operator, this allows the exchange to interact with your collateral vaults on your behalf. This is a one time transaction and only needs to be done the first time you interact with the rysk option exchange contract, for extra security you could disable this after each of your transactions but you need to remember to reenable it on your next transaction.
await controller.setOperator(exchange.address, true)
1-3. same as buy except make sure isSell boolean is true.
  1. 4.
    Do the option transaction:
To create a sell order where the user also creates and collateralises the short position you will need to send some opyn actions then a sell rysk action to the exchange via the operate function as shown below. We just need the approval from the user for the exchange to spend usd on its behalf, then we open a vault, deposit collateral, mint an otoken and sell this otoken to the dhv.
await usd.approve(exchange.address, marginRequirement)
const vaultId = await (await controller.getAccountVaultCounter(senderAddress)).add(1)
const otoken = await exchange.callStatic.createOtoken(proposedSeries)
// await exchange.createOtoken(proposedSeries) // may be necessary if otoken doesnt already exist
await exchange.operate([
{
operation: 0, // 0 means opyn operation
operationQueue: [
{
actionType: 0, // 0 on an opyn operation means Open Vault which is represented by a vaultId
// https://github.com/rysk-finance/dynamic-hedging/blob/rysk-beyond/packages/contracts/contracts/packages/opyn/libs/Actions.sol#L39
owner: senderAddress, // must be the msg.sender
secondAddress: ZERO_ADDRESS, // not important here
asset: ZERO_ADDRESS, // not important here
vaultId: vaultId, // vaultId, each short position the user holds will be held in a unique vaultId, when opening a new vault the id must be the next vault id
amount: 0, // not important here
optionSeries: emptySeries, // not important here
indexOrAcceptablePremium: 0, // always 0 for opyn
data: abiCode.encode(["uint256"], [1]) // 1 here represents partially collateralised, 0 represents fully collateralised
},
{
actionType: 5, // 5 represents a Deposit Collateral action
owner: senderAddress, // must be the msg.sender
secondAddress: exchange.address, // this can be set as the senderAddress or exchange address, if set to the exchange address then the user approval goes to the exchange, if set to the sender address then the user approval goes to the opyn margin pool
asset: proposedSeries.collateral,
vaultId: vaultId, // vault id to deposit collateral into
amount: marginRequirement, // margin required to collateralise the position in collateral decimals,
// this is a bit more difficult to get https://github.com/rysk-finance/dynamic-hedging/blob/rysk-beyond/packages/contracts/contracts/packages/opyn/new/NewCalculator.sol#L367, make sure the decimals for strikePrice and underlyingPrice on this contract are e8
optionSeries: emptySeries, // not important
index: 0, // always 0 for opyn
data: ZERO_ADDRESS // not important
},
{
actionType: 1, // 1 represents a mint otoken operation (minting an option contract, this only works if there is enough collateral)
owner: senderAddress,
secondAddress: exchange.address, // most of the time this should be set to exchange address, this helps avoid an extra approval from the user on the otoken when selling to the dhv
vaultId: vaultId,
amount: amount.div(ethers.utils.parseUnits("1", 10)), // amount needs to be in e8 decimals
optionSeries: emptySeries, // not important
indexOrAcceptablePremium: 0, // always 0 for opyn
data: ZERO_ADDRESS // not important
}
]
},
{
operation: 1, // indicates a rysk operation
operationQueue: [
{
actionType: 2, // this is a sell action
owner: ZERO_ADDRESS, // not important
secondAddress: senderAddress, // recipient of premium
asset: ZERO_ADDRESS, // can be zero if the optionSeries field is populated
vaultId: 0, // not important
amount: amount, // amount needs to be in e18 decimals
optionSeries: proposedSeries, // proposed series with strike in e18 decimals
indexOrAcceptablePremium: quote * 97%, // acceptable premium (should be lower than quote)
data: "0x"
}
]
}
])

Closing a Buy

You are just selling an option back to the pool.
await optionToken.approve(exchange.address, amount)
const tx = await exchange.operate([
{
operation: 1, // 1 means a rysk operation
operationQueue: [{
actionType: 2, // 2 on a rysk operation means a sell action (there is an edge case here, if the premium/8 < fee then we need to do close option instead which would just be changing the number to 3 on this line),
owner: ZERO_ADDRESS, // not important
secondAddress: senderAddress, // recipient
asset: optionToken.address,
vaultId: 0, // not important
amount: amount, // in e18 decimals
optionSeries: emptySeries, // can be empty so long as asset is not zero or populated
indexOrAcceptablePremium: quote * 97%, // slippage in e6 decimals (should be lower than quote)
data: "0x" // not important
}]
}])

Closing a Sell

On the way! But just do a buy and withdraw collateral from your opyn vault

Redeeming Expired Options

If your options that you have purchased have expired ITM then you can redeem them to claim your winnings. Call the redeem function on the OptionRegistry, providing the seriesAddress of the option token: https://github.com/rysk-finance/dynamic-hedging/blob/rysk-beyond/packages/contracts/contracts/OptionRegistry.sol#L527​

Liquidation

You can see an implementation of a liquidation bot here: https://github.com/rysk-finance/gamma-rysk-liquidator this is currently used as a backstop. A liquidation bot that offloads any liquidated vaults to the dhv is still in the works. A test with an example of a liquidation in typescript can be seen here​

Settling Expired Options

On the way!

Adjusting Collateral

Adding Collateral

On the way!

Removing Collateral

On the way!
​