Skip to content

Commit

Permalink
Sip 71 binary options improvements (#599)
Browse files Browse the repository at this point in the history
  • Loading branch information
zyzek authored Jul 20, 2020
1 parent f52cff4 commit bf1c38f
Show file tree
Hide file tree
Showing 18 changed files with 1,163 additions and 151 deletions.
17 changes: 12 additions & 5 deletions contracts/BinaryOption.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,21 @@ contract BinaryOption is IERC20, IBinaryOption {

/* The last claimant might be owed slightly more or less than the actual remaining deposit
based on rounding errors with the price.
Therefore if the user's bid is the entire rest of the pot, just give them everything that's left. */
if (_bid == totalBids && _bid != 0) {
Therefore if the user's bid is the entire rest of the pot, just give them everything that's left.
If there is no supply, then this option lost, and we'll return 0.
*/
if ((_bid == totalBids && _bid != 0) || supply == 0) {
return supply;
}

/* If somehow a user who is not the last bidder is owed more than what's available,
/* Note that option supply on the losing side and deposits can become decoupled,
but losing options are not claimable, therefore we only need to worry about
the situation where supply < owed on the winning side.
If somehow a user who is not the last bidder is owed more than what's available,
subsequent bidders will be disadvantaged. Given that the minimum bid is 10^16 wei,
this should never occur in reality. */
assert(owed <= supply);
require(owed <= supply, "supply < claimable");
return owed;
}

Expand All @@ -85,7 +91,8 @@ contract BinaryOption is IERC20, IBinaryOption {
}

function totalClaimableSupply() external view returns (uint) {
return _totalClaimableSupply(market.exercisableDeposits());
(, uint exercisableDeposits) = market.senderPriceAndExercisableDeposits();
return _totalClaimableSupply(exercisableDeposits);
}

/* ========== MUTATIVE FUNCTIONS ========== */
Expand Down
50 changes: 42 additions & 8 deletions contracts/BinaryOptionMarket.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ contract BinaryOptionMarket is Owned, MixinResolver, IBinaryOptionMarket {
uint public deposited;
address public creator;
bool public resolved;
bool public refundsEnabled;

uint internal _feeMultiplier;

Expand All @@ -80,6 +81,7 @@ contract BinaryOptionMarket is Owned, MixinResolver, IBinaryOptionMarket {
uint[2] memory _creatorLimits, // [capitalRequirement, skewLimit]
bytes32 _oracleKey,
uint _strikePrice,
bool _refundsEnabled,
uint[3] memory _times, // [biddingEnd, maturity, expiry]
uint[2] memory _bids, // [longBid, shortBid]
uint[3] memory _fees // [poolFee, creatorFee, refundFee]
Expand All @@ -94,8 +96,12 @@ contract BinaryOptionMarket is Owned, MixinResolver, IBinaryOptionMarket {
oracleDetails = OracleDetails(_oracleKey, _strikePrice, 0);
times = Times(_times[0], _times[1], _times[2]);

refundsEnabled = _refundsEnabled;

(uint longBid, uint shortBid) = (_bids[0], _bids[1]);
_checkCreatorLimits(longBid, shortBid);
emit Bid(Side.Long, _creator, longBid);
emit Bid(Side.Short, _creator, shortBid);

// Note that the initial deposit of synths must be made by the manager, otherwise the contract's assumed
// deposits will fall out of sync with its actual balance. Similarly the total system deposits must be updated in the manager.
Expand Down Expand Up @@ -217,7 +223,14 @@ contract BinaryOptionMarket is Owned, MixinResolver, IBinaryOptionMarket {
}

function senderPriceAndExercisableDeposits() external view returns (uint price, uint exercisable) {
exercisable = _exercisableDeposits(deposited);
// When the market is not yet resolved, both sides might be able to exercise all the options.
// On the other hand, if the market has resolved, then only the winning side may exercise.
exercisable = 0;
if (!resolved || address(_option(_result())) == msg.sender) {
exercisable = _exercisableDeposits(deposited);
}

// Send the correct price for each side of the market.
if (msg.sender == address(options.long)) {
price = prices.long;
} else if (msg.sender == address(options.short)) {
Expand Down Expand Up @@ -423,6 +436,7 @@ contract BinaryOptionMarket is Owned, MixinResolver, IBinaryOptionMarket {
}

function refund(Side side, uint value) external duringBidding returns (uint refundMinusFee) {
require(refundsEnabled, "Refunds disabled");
if (value == 0) {
return 0;
}
Expand Down Expand Up @@ -488,8 +502,18 @@ contract BinaryOptionMarket is Owned, MixinResolver, IBinaryOptionMarket {
returns (uint longClaimed, uint shortClaimed)
{
uint exercisable = _exercisableDeposits(deposited);
uint longOptions = options.long.claim(msg.sender, prices.long, exercisable);
uint shortOptions = options.short.claim(msg.sender, prices.short, exercisable);
Side outcome = _result();
bool _resolved = resolved;

// Only claim options if we aren't resolved, and only claim the winning side.
uint longOptions;
uint shortOptions;
if (!_resolved || outcome == Side.Long) {
longOptions = options.long.claim(msg.sender, prices.long, exercisable);
}
if (!_resolved || outcome == Side.Short) {
shortOptions = options.short.claim(msg.sender, prices.short, exercisable);
}

require(longOptions != 0 || shortOptions != 0, "Nothing to claim");
emit OptionsClaimed(msg.sender, longOptions, shortOptions);
Expand Down Expand Up @@ -536,13 +560,12 @@ contract BinaryOptionMarket is Owned, MixinResolver, IBinaryOptionMarket {

/* ---------- Market Expiry ---------- */

function expire(address payable beneficiary) external onlyOwner {
require(_expired(), "Unexpired options remaining");

function _selfDestruct(address payable beneficiary) internal {
uint _deposited = deposited;
if (_deposited != 0) {
_decrementDeposited(_deposited);
}

// Transfer the balance rather than the deposit value in case there are any synths left over
// from direct transfers.
IERC20 sUSD = _sUSD();
Expand All @@ -554,11 +577,22 @@ contract BinaryOptionMarket is Owned, MixinResolver, IBinaryOptionMarket {
// Destroy the option tokens before destroying the market itself.
options.long.expire(beneficiary);
options.short.expire(beneficiary);

// Good night
selfdestruct(beneficiary);
}

function cancel(address payable beneficiary) external onlyOwner duringBidding {
(uint longTotalBids, uint shortTotalBids) = _totalBids();
(uint creatorLongBids, uint creatorShortBids) = _bidsOf(creator);
bool cancellable = longTotalBids == creatorLongBids && shortTotalBids == creatorShortBids;
require(cancellable, "Not cancellable");
_selfDestruct(beneficiary);
}

function expire(address payable beneficiary) external onlyOwner {
require(_expired(), "Unexpired options remaining");
_selfDestruct(beneficiary);
}

/* ========== MODIFIERS ========== */

modifier duringBidding() {
Expand Down
3 changes: 2 additions & 1 deletion contracts/BinaryOptionMarketFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,14 @@ contract BinaryOptionMarketFactory is Owned, SelfDestructible, MixinResolver {
uint[2] calldata creatorLimits,
bytes32 oracleKey,
uint strikePrice,
bool refundsEnabled,
uint[3] calldata times, // [biddingEnd, maturity, expiry]
uint[2] calldata bids, // [longBid, shortBid]
uint[3] calldata fees // [poolFee, creatorFee, refundFee]
) external returns (BinaryOptionMarket) {
address manager = _manager();
require(address(manager) == msg.sender, "Only permitted by the manager.");

return new BinaryOptionMarket(manager, creator, creatorLimits, oracleKey, strikePrice, times, bids, fees);
return new BinaryOptionMarket(manager, creator, creatorLimits, oracleKey, strikePrice, refundsEnabled, times, bids, fees);
}
}
14 changes: 12 additions & 2 deletions contracts/BinaryOptionMarketManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ contract BinaryOptionMarketManager is Owned, Pausable, SelfDestructible, MixinRe
function createMarket(
bytes32 oracleKey,
uint strikePrice,
bool refundsEnabled,
uint[2] calldata times, // [biddingEnd, maturity]
uint[2] calldata bids // [longBid, shortBid]
)
Expand Down Expand Up @@ -268,6 +269,7 @@ contract BinaryOptionMarketManager is Owned, Pausable, SelfDestructible, MixinRe
[creatorLimits.capitalRequirement, creatorLimits.skewLimit],
oracleKey,
strikePrice,
refundsEnabled,
[biddingEnd, maturity, expiry],
bids,
[fees.poolFee, fees.creatorFee, fees.refundFee]
Expand All @@ -291,9 +293,16 @@ contract BinaryOptionMarketManager is Owned, Pausable, SelfDestructible, MixinRe
_maturedMarkets.push(market);
}

function expireMarkets(address[] calldata markets) external notPaused {
_systemStatus().requireSystemActive();
function cancelMarket(address market) external notPaused {
require(_activeMarkets.contains(market), "Not an active market");
address creator = BinaryOptionMarket(market).creator();
require(msg.sender == creator, "Sender not market creator");
BinaryOptionMarket(market).cancel(msg.sender);
_activeMarkets.remove(market);
emit MarketCancelled(market);
}

function expireMarkets(address[] calldata markets) external notPaused {
for (uint i = 0; i < markets.length; i++) {
address market = markets[i];

Expand Down Expand Up @@ -406,6 +415,7 @@ contract BinaryOptionMarketManager is Owned, Pausable, SelfDestructible, MixinRe
uint expiryDate
);
event MarketExpired(address market);
event MarketCancelled(address market);
event MarketsMigrated(BinaryOptionMarketManager receivingManager, BinaryOptionMarket[] markets);
event MarketsReceived(BinaryOptionMarketManager migratingManager, BinaryOptionMarket[] markets);
event MarketCreationEnabledUpdated(bool enabled);
Expand Down
1 change: 1 addition & 0 deletions contracts/interfaces/IBinaryOptionMarket.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ interface IBinaryOptionMarket {
function deposited() external view returns (uint);
function creator() external view returns (address);
function resolved() external view returns (bool);
function refundsEnabled() external view returns (bool);

function phase() external view returns (Phase);
function oraclePriceAndTimestamp() external view returns (uint price, uint updatedAt);
Expand Down
3 changes: 2 additions & 1 deletion contracts/interfaces/IBinaryOptionMarketManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ interface IBinaryOptionMarketManager {
/* ========== MUTATIVE FUNCTIONS ========== */

function createMarket(
bytes32 oracleKey, uint strikePrice,
bytes32 oracleKey, uint strikePrice, bool refundsEnabled,
uint[2] calldata times, // [biddingEnd, maturity]
uint[2] calldata bids // [longBid, shortBid]
) external returns (IBinaryOptionMarket);
function resolveMarket(address market) external;
function cancelMarket(address market) external;
function expireMarkets(address[] calldata market) external;
}
3 changes: 2 additions & 1 deletion contracts/test-helpers/MockBinaryOptionMarketManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ contract MockBinaryOptionMarketManager {
uint[2] calldata creatorLimits,
bytes32 oracleKey,
uint strikePrice,
bool refundsEnabled,
uint[3] calldata times, // [biddingEnd, maturity, expiry]
uint[2] calldata bids, // [longBid, shortBid]
uint[3] calldata fees // [poolFee, creatorFee, refundFee]
) external {
market = new BinaryOptionMarket(address(this), creator, creatorLimits, oracleKey, strikePrice, times, bids, fees);
market = new BinaryOptionMarket(address(this), creator, creatorLimits, oracleKey, strikePrice, refundsEnabled, times, bids, fees);
market.setResolverAndSyncCache(resolver);
}

Expand Down
2 changes: 2 additions & 0 deletions contracts/test-helpers/TestableBinaryOptionMarket.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ contract TestableBinaryOptionMarket is BinaryOptionMarket {
address _owner, address _creator,
uint[2] memory _creatorLimits,
bytes32 _oracleKey, uint256 _strikePrice,
bool _refundsEnabled,
uint[3] memory _times,
uint[2] memory _bids,
uint[3] memory _fees
Expand All @@ -16,6 +17,7 @@ contract TestableBinaryOptionMarket is BinaryOptionMarket {
_owner, _creator,
_creatorLimits,
_oracleKey, _strikePrice,
_refundsEnabled,
_times,
_bids,
_fees)
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"name": "synthetix",
"version": "2.24.1",
"version": "2.25.0-alpha",
"license": "MIT",
"author": "Synthetix",
"description": "The smart contracts which make up the Synthetix system. (synthetix.io)",
"scripts": {
"clean-install": "rm -rf build && rm ./package-lock.json && rm -rf node_modules/* && npm install",
"compile:legacy": "buidler compile --config legacy/buidler.legacy.js",
"compile": "npm run compile:legacy && buidler compile",
"coverage": "buidler coverage --network coverage",
"coverage": "NODE_OPTIONS=\"--max-old-space-size=4096\" buidler coverage --network coverage",
"generate-asts": "buidler compile",
"format": "prettier --write \"contracts/**/*.sol\" \"**/*.js\"",
"lint": "solhint \"contracts/*.sol\" && solhint \"contracts/test-helpers/*.sol\" && solhint --config contracts/interfaces/.solhint.json \"contracts/interfaces/*.sol\" && eslint \"**/*.js\"",
Expand Down
64 changes: 52 additions & 12 deletions publish/deployed/kovan/deployment.json

Large diffs are not rendered by default.

24 changes: 22 additions & 2 deletions publish/deployed/kovan/versions.json
Original file line number Diff line number Diff line change
Expand Up @@ -5448,7 +5448,8 @@
},
"BinaryOptionMarketManager": {
"address": "0x9492eac3c8c6F1E768C71Fa2eAf04FB2F42104eC",
"status": "current"
"status": "replaced",
"replaced_in": "v2.25.0-alpha"
}
}
},
Expand All @@ -5462,7 +5463,8 @@
"contracts": {
"BinaryOptionMarketFactory": {
"address": "0x14c049BDf3bcf46bc884e26ceBC130560C60D549",
"status": "current"
"status": "replaced",
"replaced_in": "v2.25.0-alpha"
}
}
},
Expand All @@ -5479,5 +5481,23 @@
"status": "current"
}
}
},
"v2.25.0-alpha": {
"tag": "v2.25.0-alpha",
"fulltag": "v2.25.0-alpha",
"release": "Antares",
"network": "kovan",
"date": "2020-07-17T14:31:25+10:00",
"commit": "370731b3f0e5732616a80e38d4bc8cb07febc9d6",
"contracts": {
"BinaryOptionMarketFactory": {
"address": "0xdA78C7356D6E087fb0e1F62365C9259DA1a6b298",
"status": "current"
},
"BinaryOptionMarketManager": {
"address": "0x4176483150F667B2303C1eeF295431F0Cc593783",
"status": "current"
}
}
}
}
1 change: 1 addition & 0 deletions publish/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require('./src/commands/build').cmd(program);
require('./src/commands/deploy').cmd(program);
require('./src/commands/generate-token-list').cmd(program);
require('./src/commands/import-fee-periods').cmd(program);
require('./src/commands/migrate-binary-option-markets').cmd(program);
require('./src/commands/nominate').cmd(program);
require('./src/commands/owner').cmd(program);
require('./src/commands/purge-synths').cmd(program);
Expand Down
Loading

0 comments on commit bf1c38f

Please sign in to comment.