🤓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. 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. 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. 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. 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. 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. 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!

Last updated