/** * Source Code first verified at https://etherscan.io on Thursday, April 25, 2019 (UTC) */ pragma solidity ^0.5; contract ERC20xVariables { address public creator; address public lib; uint256 constant public MAX_UINT256 = 2**256 - 1; mapping(address => uint) public balances; mapping(address => mapping(address => uint)) public allowed; uint8 public constant decimals = 18; string public name; string public symbol; uint public totalSupply; event Transfer(address indexed _from, address indexed _to, uint256 _value); event Approval(address indexed _owner, address indexed _spender, uint256 _value); event Created(address creator, uint supply); function balanceOf(address _owner) public view returns (uint256 balance) { return balances[_owner]; } function allowance(address _owner, address _spender) public view returns (uint256 remaining) { return allowed[_owner][_spender]; } } contract ERC20x is ERC20xVariables { function transfer(address _to, uint256 _value) public returns (bool success) { _transferBalance(msg.sender, _to, _value); emit Transfer(msg.sender, _to, _value); return true; } function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) { uint256 allowance = allowed[_from][msg.sender]; require(allowance >= _value); _transferBalance(_from, _to, _value); if (allowance < MAX_UINT256) { allowed[_from][msg.sender] -= _value; } emit Transfer(_from, _to, _value); return true; } function approve(address _spender, uint256 _value) public returns (bool success) { allowed[msg.sender][_spender] = _value; emit Approval(msg.sender, _spender, _value); return true; } function transferToContract(address _to, uint256 _value, bytes memory data) public returns (bool) { _transferBalance(msg.sender, _to, _value); bytes4 sig = bytes4(keccak256("receiveTokens(address,uint256,bytes)")); (bool result,) = _to.call(abi.encodePacked(sig, msg.sender, _value, data)); require(result); emit Transfer(msg.sender, _to, _value); return true; } function _transferBalance(address _from, address _to, uint _value) internal { require(balances[_from] >= _value); balances[_from] -= _value; balances[_to] += _value; } } contract BidderInterface { function receiveETH(address series, uint256 amount) public; function receiveUSD(address series, uint256 amount) public; } contract VariableSupplyToken is ERC20x { function grant(address to, uint256 amount) public returns (bool) { require(msg.sender == creator); require(balances[to] + amount >= amount); balances[to] += amount; totalSupply += amount; return true; } function burn(address from, uint amount) public returns (bool) { require(msg.sender == creator); require(balances[from] >= amount); balances[from] -= amount; totalSupply -= amount; return true; } } contract OptionToken is VariableSupplyToken { constructor(string memory _name, string memory _symbol) public { creator = msg.sender; name = _name; symbol = _symbol; } } contract Protocol { address public lib; ERC20x public usdERC20; ERC20x public protocolToken; // We use "flavor" because type is a reserved word in many programming languages enum Flavor { Call, Put } struct OptionSeries { uint expiration; Flavor flavor; uint strike; } uint public constant DURATION = 12 hours; uint public constant HALF_DURATION = DURATION / 2; mapping(address => uint) public openInterest; mapping(address => uint) public earlyExercised; mapping(address => uint) public totalInterest; mapping(address => mapping(address => uint)) public writers; mapping(address => OptionSeries) public seriesInfo; mapping(address => uint) public holdersSettlement; mapping(address => uint) public expectValue; bool isAuction; uint public constant ONE_MILLION = 1000000; // maximum token holder rights capped at 3.7% of total supply? // Why 3.7%? // I could make up some fancy explanation // and use the phrase "byzantine fault tolerance" somehow // Or I could just say that 3.7% allows for a total of 27 independent actors // that are all receiving the maximum benefit, and it solves all the other // issues of disincentivizing centralization and "rich get richer" mechanics, so I chose 27 'cause it just has a nice "decentralized" feel to it. // 21 would have been fine, as few as ten probably would have been ok 'cause people can just pool anyways // up to a thousand or so probably wouldn't have hurt either. // In the end it really doesn't matter as long as the game ends up being played fairly. // I'm sure someone will take my system and parameterize it differently at some point and bill it as a totally new product. uint public constant PREFERENCE_MAX = 0.037 ether; constructor(address _token, address _usd) public { lib = address(new VariableSupplyToken()); protocolToken = ERC20x(_token); usdERC20 = ERC20x(_usd); } function() external payable { revert(); } event SeriesIssued(address series); function issue(string memory name, string memory symbol, uint expiration, Flavor flavor, uint strike) public returns (address) { address series = address(new OptionToken(name, symbol)); seriesInfo[series] = OptionSeries(expiration, flavor, strike); emit SeriesIssued(series); return series; } function open(address _series, uint amount) public payable returns (bool) { OptionSeries memory series = seriesInfo[_series]; require(now < series.expiration); VariableSupplyToken(_series).grant(msg.sender, amount); if (series.flavor == Flavor.Call) { require(msg.value == amount); } else { require(msg.value == 0); uint escrow = amount * series.strike; require(escrow / amount == series.strike); escrow /= 1 ether; require(usdERC20.transferFrom(msg.sender, address(this), escrow)); } openInterest[_series] += amount; totalInterest[_series] += amount; writers[_series][msg.sender] += amount; return true; } function close(address _series, uint amount) public returns (bool) { OptionSeries memory series = seriesInfo[_series]; require(now < series.expiration); require(openInterest[_series] >= amount); VariableSupplyToken(_series).burn(msg.sender, amount); require(writers[_series][msg.sender] >= amount); writers[_series][msg.sender] -= amount; openInterest[_series] -= amount; totalInterest[_series] -= amount; if (series.flavor == Flavor.Call) { msg.sender.transfer(amount); } else { usdERC20.transfer(msg.sender, amount * series.strike / 1 ether); } return true; } function exercise(address _series, uint amount) public payable { OptionSeries memory series = seriesInfo[_series]; require(now < series.expiration); require(openInterest[_series] >= amount); VariableSupplyToken(_series).burn(msg.sender, amount); uint usd = amount * series.strike; require(usd / amount == series.strike); usd /= 1 ether; openInterest[_series] -= amount; earlyExercised[_series] += amount; if (series.flavor == Flavor.Call) { msg.sender.transfer(amount); require(msg.value == 0); usdERC20.transferFrom(msg.sender, address(this), usd); } else { require(msg.value == amount); usdERC20.transfer(msg.sender, usd); } } function receive() public payable returns (bool) { require(expectValue[msg.sender] == msg.value); expectValue[msg.sender] = 0; return true; } function bid(address _series, uint amount) public payable returns (bool) { require(isAuction == false); isAuction = true; OptionSeries memory series = seriesInfo[_series]; uint start = series.expiration; uint time = now + _timePreference(msg.sender); require(time > start); require(time < start + DURATION); uint elapsed = time - start; amount = _min(amount, openInterest[_series]); openInterest[_series] -= amount; uint offer; uint givGet; BidderInterface bidder = BidderInterface(msg.sender); if (series.flavor == Flavor.Call) { require(msg.value == 0); offer = (series.strike * DURATION) / elapsed; givGet = offer * amount / 1 ether; holdersSettlement[_series] += givGet - amount * series.strike / 1 ether; bool hasFunds = usdERC20.balanceOf(msg.sender) >= givGet && usdERC20.allowance(msg.sender, address(this)) >= givGet; if (hasFunds) { msg.sender.transfer(amount); } else { bidder.receiveETH(_series, amount); } require(usdERC20.transferFrom(msg.sender, address(this), givGet)); } else { offer = (DURATION * 1 ether * 1 ether) / (series.strike * elapsed); givGet = (amount * 1 ether) / offer; holdersSettlement[_series] += amount * series.strike / 1 ether - givGet; usdERC20.transfer(msg.sender, givGet); if (msg.value == 0) { require(expectValue[msg.sender] == 0); expectValue[msg.sender] = amount; bidder.receiveUSD(_series, givGet); require(expectValue[msg.sender] == 0); } else { require(msg.value >= amount); msg.sender.transfer(msg.value - amount); } } isAuction = false; return true; } function redeem(address _series) public returns (bool) { OptionSeries memory series = seriesInfo[_series]; require(now > series.expiration + DURATION); uint unsettledPercent = openInterest[_series] * 1 ether / totalInterest[_series]; uint exercisedPercent = (totalInterest[_series] - openInterest[_series]) * 1 ether / totalInterest[_series]; uint owed; if (series.flavor == Flavor.Call) { owed = writers[_series][msg.sender] * unsettledPercent / 1 ether; if (owed > 0) { msg.sender.transfer(owed); } owed = writers[_series][msg.sender] * exercisedPercent / 1 ether; owed = owed * series.strike / 1 ether; if (owed > 0) { usdERC20.transfer(msg.sender, owed); } } else { owed = writers[_series][msg.sender] * unsettledPercent / 1 ether; owed = owed * series.strike / 1 ether; if (owed > 0) { usdERC20.transfer(msg.sender, owed); } owed = writers[_series][msg.sender] * exercisedPercent / 1 ether; if (owed > 0) { msg.sender.transfer(owed); } } writers[_series][msg.sender] = 0; return true; } function settle(address _series) public returns (bool) { OptionSeries memory series = seriesInfo[_series]; require(now > series.expiration + DURATION); uint bal = ERC20x(_series).balanceOf(msg.sender); VariableSupplyToken(_series).burn(msg.sender, bal); uint percent = bal * 1 ether / (totalInterest[_series] - earlyExercised[_series]); uint owed = holdersSettlement[_series] * percent / 1 ether; usdERC20.transfer(msg.sender, owed); return true; } function _timePreference(address from) public view returns (uint) { return (_unsLn(_preference(from) * 1000000 + 1 ether) * 171) / 1 ether; } function _preference(address from) public view returns (uint) { return _min( protocolToken.balanceOf(from) * 1 ether / protocolToken.totalSupply(), PREFERENCE_MAX ); } function _min(uint a, uint b) pure public returns (uint) { if (a > b) return b; return a; } function _max(uint a, uint b) pure public returns (uint) { if (a > b) return a; return b; } function _unsLn(uint x) pure public returns (uint log) { log = 0; // not a true ln function, we can't represent the negatives if (x < 1 ether) return 0; while (x >= 1.5 ether) { log += 0.405465 ether; x = x * 2 / 3; } x = x - 1 ether; uint y = x; uint i = 1; while (i < 10) { log += (y / i); i = i + 1; y = y * x / 1 ether; log -= (y / i); i = i + 1; y = y * x / 1 ether; } return(log); } }