Options Without Price Feeds

One downside of cryptocurrencies is their volatility. Businesses accepting crypto tend to immediately exchange it for fiat, to make sure they don't sell their products at a loss. But with smart contracts we have ways to protect against loss.

One way is with subcurrencies backed by real-world assets, like gold or fiat. But then you're open to counterparty risk, perhaps due to government shutdown on accusations of money laundering. It happened to e-gold and it took six years for depositors to get their money back.

Another way is with a contract for difference. You want stable value, I want to double my sweet ether gains. We both put $100 worth of ether in a contract, which accepts a price feed we both trust. If the ether value goes up, you can still only withdraw $100 worth, and I get the extra. If the ether price goes down, you still get $100 worth and the extra ether comes out of my stash.

The difficulty with this contract is the need for a trusted price feed. But if our main concern is just protecting the downside for ether holders, instead of making a stable coin for them to use instead, then there's an easier way that doesn't require a price feed at all.

Option Contracts

Let's say we're both interested in ETH and Unicorns.

I might create an option contract into which I deposit UNI. I set a price in ETH at which I'm willing to sell that UNI to you, at any time before the expiration. In exchange for the right to buy my UNI for that much ETH, you pay me the option price up front.

Let's pretend that 1 UNI is worth 4 ETH. You deposit 10 UNI, worth 40 ETH, and sell me the right to buy it for 50 ETH. In exchange for that you charge me 5 ETH.

As long as ETH stays valuable enough so it takes less than 50 ETH to buy your 10 UNI, nothing happens. You just collect an extra 5 ETH.

But if the value of ETH drops, so the same 10 UNI is worth 60 ETH, then I can exercise the option and buy your 10 UNI for only 50 ETH. My loss is 15 ETH instead of 20 ETH, since my effective price is 10 ETH plus the 5 ETH I paid for the option. You have a loss of 5 ETH, since you're getting only 50 ETH for UNI that's worth 60, but that's partially offset by the 5 I paid up front. We've transferred a loss of 5 ETH from me to you.

This could be considered a call option on UNI, or a put option on ETH...i.e. you could think of yourself as coming out ahead when UNI goes up, or when ETH goes down.

The contract is quite easy to write (though don't use this yet, it's not tested or audited):

contract token { 
    mapping (address => uint) public balanceOf;
    function transfer(address _to, uint256 _value);
    function transferFrom(address _from, address _to, uint256 _value);
}

//seller deposits token, purchaser can pay ether to get it
contract EthPut { 
    address owner;
    address optionPurchaser;
    token public asset;
    uint etherAmount;
    uint assetAmount;
    uint optionPrice;
    bool exercised;
    uint deadline;

    function EthPut (address owner, uint etherAmount, uint assetAmount, uint optionPrice, uint numDays) {
        owner = msg.sender;
        etherAmount = msg.value;
        assetAmount = assetAmount;
        optionPrice = optionPrice;
        deadline = block.timestamp + numDays days;
    }

    modifier noeth() {
        if (msg.value > 0) throw;
        _
    }

    function deposit() noeth {
        asset.transferFrom(owner, this.address, assetAmount);
    }

    function buy() {
        if (OptionPurchaser != nil) throw; //option already purchased
        if (msg.value < optionPrice) throw;
        optionPurchaser = msg.sender; 
        if (!owner.call.value(msg.value)()) throw;
    }

    //to exercise the call, purchaser sends the ether and gets the asset
    function exercise() {
        if (msg.value < etherAmount) throw;
        if (msg.sender != optionPurchaser) throw;
        if (exercised) throw;
        if (block.timestamp > deadline) throw;
        exercised = true;
    }

    //Now allow withdrawals by each person after exercised
    function withdraw() noeth {
        if (!exercised) return;
        if (msg.sender == optionPurchaser) {
            asset.transferFrom(this.address, optionPurchaser, assetAmount);
        }
        if (msg.sender == owner) {
            owner.call.value(this.balance)();
        }
    }

    //option not exercised, owner who put up asset can reclaim it now
    function reclaim() noeth {
        if (msg.sender != owner) return;
        if (exercised) return;
        if (block.timestamp <= deadline) return;
        asset.transferFrom(this.address, owner, assetAmount);
    }

    function () { throw; }  // Prevents accidental sending of ether
}    

We can also go the other direction. I could deposit ETH when I set up the contract, and you could buy an option to sell me UNI for the ETH. In that case, by buying the option you're actually protecting from UNI going down, or conversely, hoping to multiply gains from ETH going up. This contract is even simpler:

//seller deposits ether, purchaser can pay asset to get it
contract EthCall { 
    address optionSeller;
    address optionPurchaser;
    token public asset;
    uint tokenAmount;
    uint OptionPrice;
    bool exercised;
    uint deadline;

    function EthCall (address owner, token asset, uint tokenAmount, uint optionPrice, uint numDays) {
        optionSeller = msg.sender;
        asset = asset;
        tokenAmount = tokenAmount;
        OptionPrice = optionPrice;
        deadline = block.timestamp + numDays days; //using block.number probably better
    }

    function buy() {
        if (optionPurchaser != nil) throw;
        if (msg.value < OptionPrice) throw;
        optionPurchaser = msg.sender;
        if (!optionSeller.call.value(msg.value)()) throw;
    }

    //to exercise the put, purchaser sends the tokens and gets the ether
    function exercise() noeth {
        if (exercised) return;
        if (msg.sender != optionPurchaser) return;
        if (block.timestamp > deadline) return;
        exercised = true;
        if (!asset.transferFrom(optionPurchaser, optionSeller, tokenAmount)) throw;
    }

    function buyerGetEther() noeth {
        if (!exercised) return;
        if (msg.sender != optionPurchaser) return;
        optionPurchaser.call.value(this.balance)();
    }

    function sellerReclaimEther() noeth {
        if (exercised) return;
        if (msg.sender != optionSeller) return;
        if (block.timestamp <= deadline) return;
        optionSeller.call.value(this.balance);
    }

    function () { throw; }  // Prevents accidental sending of ether
}    

Obviously, instead of using two-party contracts we could make a contract that handles lots of options, buyers, and sellers. There's more than one way to approach that; I might post more on it later.