Creating a Decentralized Options Exchange

Rodrigo Villar
6 min readApr 5, 2022

--

Throughout the process of learning Solidity and its relationship to the Ethereum Virtual Machine (EVM), I’ve always wanted to create a decentralized application (dapp). Of course, I could have deployed the following smart contract and called it a day:

contract HelloWorld {
function talk() public pure returns(string memory) {
return "Hello World!";
}
}

However, I wanted to build a dapp that could provide utility, even if no one were to use it. As someone interested in decentralized finance (DeFi) and traditional options trading, I saw the opportunity to create a decentralized options exchange where hedgers could create options while buyers could buy those options to make a profit.

Overview

OptionsDEX is a DEX where each underlying asset of an option is an ERC-20 token and all options are priced in terms of the blockchain’s native coin (for Avalanche, this would be AVAX; likewise for Ethereum, this would be ETH). Listed below are the following attributes that all options on OptionsDEX share:

  • Asset: the contract address of the underlying asset
  • Strike Price: the price at which the underlying asset can be bought at
  • Writer: the bearish seller
  • Premium: the price per asset that a buyer must purchase to gain the rights to an option
  • Holder: the bullish buyer
  • Block Expiration: the expiration date of the option (in terms of block number)
  • Holder Sell Price: the price at which the current holder of an option decides to their position at
  • Writer Sell Price: the price at which the current writer of an option decides to sell their position at

Like their traditional counterparts, all OptionsDEX options represent 100 tokens of the underlying asset. To prevent malicious ERC-20 tokens from being utilized as an underlying asset, OptionsDEX only permits the following tokens to be used as underlying assets:

  • Wrapped AVAX
  • Shiba Inu
  • Chainlink Token
  • Maker Token
  • Uniswap Token
  • Graph Token
  • AAVE Token
  • CurveDAO Token
  • Sushiswap Token
  • Spell Token

Listed below is the conventional life of an OptionsDEX option, with Alice representing the writer of an option and Bob representing the holder of an option:

  1. Alice creates an option and sets the block expiration, option premium, strike price, and the address of the underlying asset (Note: Alice needs to have given OptionsDEX approval to transfer 10²⁰ units of the underlying asset before creating the option, otherwise the transaction will revert).
  2. Bob, seeing Alice’s option emitted as an event on the blockchain, decides to buy the rights to the option.
  3. Bob exercises the option before the expiration date, exchanging AVAX in return for 100 tokens of the underlying asset.

Development

Originally, I had planned to deploy OptionsDEX on Ethereum since it was the blockchain which I had the most experience with. However, after seeing initial gas estimates for the cost to deploy the OptionsDEX smart contract, I shifted my attention towards Avalanche and in particular, their C-chain. Avalanche’s fast transaction time and extremely low fees made it feasible for a college student like me to interact with their blockchain.

Although utilizing a blockchain like Avalanche would mean gas costs could be manageable, I decided that it would be great practice to implement gas-efficient strategies in my code. In particular, I found struct packing to be extremely useful here. Take the following code as an example:

Inefficient option struct

The above implementation of an option, while intuitive, is not efficient in terms of gas and storage utilization since it takes up 7 storage slots. When working with the EVM, one should avoid using storage since operations such as SLOAD and SSTORE are some of the most expensive operations in terms of gas. While I couldn’t get around the fact that options, in general, would need to interact with storage, I could optimize my option struct so that it takes up fewer storage slots which would therefore imply a great amount of gas savings in the long run. The following facts about OptionsDEX option helped motivate the reconstruction of the option struct:

  • Strike Price: the inefficient implementation of an option represents the strike price of an underlying asset as an unsigned 256 -bit integer, meaning that the maximum strike price of a token would be 2²⁵⁶-1 Wei or 1.16*10⁵⁹ AVAX. It would be much more realistic to represent the strike price of an option as an unsigned 96 bit integer, meaning that the maximum strike price, in this case, would be 2⁹⁶-1 Wei, or 7.92*10¹⁰ AVAX.
  • Seller Nonce: the seller nonce was originally implemented as a way to prevent two options with the same initialization info from having the same hash. However, we can remove the seller nonce from the option struct if we utilize it when calling abi.encode()
  • Premium: the inefficient implementation of an option represents the premium per token of an option as an unsigned 256-bit integer, meaning that the maximum premium per token would be 2²⁵⁶-1 Wei or 1.16*1⁰⁵⁹ AVAX. Representing the premium as an unsigned 96-bit integer would be more realistic as the maximum premium, in this case, would be ²⁹⁶-1 Wei or 7.92*1⁰¹⁰ AVAX.
  • Holder Sell Price, Writer Sell Price: again, these values are represented as 256-bit unsigned integers. However, if we change these values to be 128-bit unsigned integers, then the maximum holder/writer sell price would be 2¹²⁸-1 Wei or 3.40*10²⁰ AVAX.

With this in mind, the following is the implementation of the option struct seen in the OptionsDEX smart contract:

Efficient option struct

This new implementation of the option struct now utilizes 4 storage slots, which now equates to fewer SSTORE and SLOAD operations, therefore saving the user on gas fees.

Testing

Throughout the development of OptionsDEX, I quickly concluded that testing is invaluable to the overall success of a smart contract. Granted, in any software development setting, testing is a crucial tool in finding bugs and verifying the correctness of a program. However, the stakes are much higher when it comes to developing smart contracts because once a smart contract is deployed, its code can never be modified.

The majority of my tests (in tests/test_cases.py) focus on making sure that all functions of OptionsDEX follow the logic that I intended them to have (ex. assigning an address as the buyer of an option, deleting an option from storage, etc.) However, some of these functions deal with transferring AVAX from the smart contract to other accounts (whether they be EOAs or other smart contracts). For this reason, I made sure to test functions that deal with transferring eth against re-entrancy attacks. As an example, let’s examine Evil.sol, the file that contains the smart contract used in the re-entrancy attack against buyOption().

Evil smart contract code

In this case, the Evil smart contract is the writer of an option while an EOA (holder A) is the account that decides to buy the Evil smart contract option, initiating the re-entrancy attack. However, because all assertions and necessary logic occur before the seller of an option is sent their AVAX, such a re-entrancy attack is reverted by the EVM.

Re-entrancy safeguards in buyOption()

Conclusion

The efficiency and security required throughout the development of OptionsDEX truly embody the smart contract development process. For the foreseeable future, I will most likely continue exploring the EVM and in particular, writing code utilizing just opcodes. However, what I am truly excited to start learning about are zk-rollups; throughout the development of OptionsDEX I came across dydx, a decentralized perpetuals exchange that utilizes zk-rollups to transfer all interactions off-chain. As a result, dydx can offer almost instant transaction times with close to zero gas fees. While the mathematics and logic behind zk-rollups are some of the most difficult topics to understand in blockchain, I believe that zk-rollups are bound to become more prevalent in the future and for this reason I certainly wouldn’t mind learning more about zk-rollups and the development process behind them.

-rjv

Sources

--

--

Rodrigo Villar
Rodrigo Villar

Written by Rodrigo Villar

Computer Science and Mathematics Student at Cornell University.

No responses yet