Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions contracts/EmptyFuturesMarketManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ contract EmptyFuturesMarketManager is IFuturesMarketManager {
return _markets;
}

function marketForAsset(bytes32 asset) external view returns (address) {
asset;
function marketForKey(bytes32 marketKey) external view returns (address) {
marketKey;
return address(0);
}

function marketsForAssets(bytes32[] calldata assets) external view returns (address[] memory) {
assets;
function marketsForKeys(bytes32[] calldata marketKeys) external view returns (address[] memory) {
marketKeys;
address[] memory _markets;
return _markets;
}
Expand Down
2 changes: 1 addition & 1 deletion contracts/Exchanger.sol
Original file line number Diff line number Diff line change
Expand Up @@ -951,7 +951,7 @@ contract Exchanger is Owned, MixinSystemSettings, IExchanger {
(, bool srcInvalid) = exchangeCircuitBreaker().rateWithInvalid(sourceCurrencyKey);
(, bool dstInvalid) = exchangeCircuitBreaker().rateWithInvalid(destinationCurrencyKey);
require(!srcInvalid, "source synth rate invalid");
require(!dstInvalid, "destinatio synth rate invalid");
require(!dstInvalid, "destination synth rate invalid");

// check rates not stale or flagged
_ensureCanExchange(sourceCurrencyKey, sourceAmount, destinationCurrencyKey);
Expand Down
6 changes: 5 additions & 1 deletion contracts/FuturesMarket.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,9 @@ import "./interfaces/IFuturesMarket.sol";

// https://docs.synthetix.io/contracts/source/contracts/FuturesMarket
contract FuturesMarket is IFuturesMarket, FuturesMarketBase, MixinFuturesNextPriceOrders, MixinFuturesViews {
constructor(address _resolver, bytes32 _baseAsset) public FuturesMarketBase(_resolver, _baseAsset) {}
constructor(
address _resolver,
bytes32 _baseAsset,
bytes32 _marketKey
) public FuturesMarketBase(_resolver, _baseAsset, _marketKey) {}
}
29 changes: 19 additions & 10 deletions contracts/FuturesMarketBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ contract FuturesMarketBase is MixinFuturesMarketSettings, IFuturesMarketBaseType

/* ========== STATE VARIABLES ========== */

// The market identifier in the futures system (manager + settings). Multiple markets can co-exist
// for the same asset in order to allow migrations.
bytes32 public marketKey;

// The asset being traded in this market. This should be a valid key into the ExchangeRates contract.
bytes32 public baseAsset;

Expand Down Expand Up @@ -157,8 +161,13 @@ contract FuturesMarketBase is MixinFuturesMarketSettings, IFuturesMarketBaseType

/* ========== CONSTRUCTOR ========== */

constructor(address _resolver, bytes32 _baseAsset) public MixinFuturesMarketSettings(_resolver) {
constructor(
address _resolver,
bytes32 _baseAsset,
bytes32 _marketKey
) public MixinFuturesMarketSettings(_resolver) {
baseAsset = _baseAsset;
marketKey = _marketKey;

// Initialise the funding sequence with 0 initially accrued, so that the first usable funding index is 1.
fundingSequence.push(0);
Expand Down Expand Up @@ -222,13 +231,13 @@ contract FuturesMarketBase is MixinFuturesMarketSettings, IFuturesMarketBaseType
function _proportionalSkew(uint price) internal view returns (int) {
// marketSize is in baseAsset units so we need to convert from USD units
require(price > 0, "price can't be zero");
uint skewScaleBaseAsset = _skewScaleUSD(baseAsset).divideDecimal(price);
uint skewScaleBaseAsset = _skewScaleUSD(marketKey).divideDecimal(price);
require(skewScaleBaseAsset != 0, "skewScale is zero"); // don't divide by zero
return int(marketSkew).divideDecimal(int(skewScaleBaseAsset));
}

function _currentFundingRate(uint price) internal view returns (int) {
int maxFundingRate = int(_maxFundingRate(baseAsset));
int maxFundingRate = int(_maxFundingRate(marketKey));
// Note the minus sign: funding flows in the opposite direction to the skew.
return _min(_max(-_UNIT, -_proportionalSkew(price)), _UNIT).multiplyDecimal(maxFundingRate);
}
Expand Down Expand Up @@ -359,7 +368,7 @@ contract FuturesMarketBase is MixinFuturesMarketSettings, IFuturesMarketBaseType
// This should guarantee that the value returned here can always been withdrawn, but there may be
// a little extra actually-accessible value left over, depending on the position size and margin.
uint milli = uint(_UNIT / 1000);
int maxLeverage = int(_maxLeverage(baseAsset).sub(milli));
int maxLeverage = int(_maxLeverage(marketKey).sub(milli));
uint inaccessible = _abs(_notionalValue(position.size, price).divideDecimal(maxLeverage));

// If the user has a position open, we'll enforce a min initial margin requirement.
Expand Down Expand Up @@ -532,7 +541,7 @@ contract FuturesMarketBase is MixinFuturesMarketSettings, IFuturesMarketBaseType
{
// stack too deep
int leverage = int(newPos.size).multiplyDecimal(int(params.price)).divideDecimal(int(newMargin.add(fee)));
if (_maxLeverage(baseAsset).add(uint(_UNIT) / 100) < _abs(leverage)) {
if (_maxLeverage(marketKey).add(uint(_UNIT) / 100) < _abs(leverage)) {
return (oldPos, 0, Status.MaxLeverageExceeded);
}
}
Expand All @@ -541,7 +550,7 @@ contract FuturesMarketBase is MixinFuturesMarketSettings, IFuturesMarketBaseType
// Allow a bit of extra value in case of rounding errors.
if (
_orderSizeTooLarge(
uint(int(_maxMarketValueUSD(baseAsset).add(100 * uint(_UNIT))).divideDecimal(int(params.price))),
uint(int(_maxMarketValueUSD(marketKey).add(100 * uint(_UNIT))).divideDecimal(int(params.price))),
oldPos.size,
newPos.size
)
Expand Down Expand Up @@ -628,7 +637,7 @@ contract FuturesMarketBase is MixinFuturesMarketSettings, IFuturesMarketBaseType
*/
function _assetPriceRequireSystemChecks() internal returns (uint) {
// check that futures market isn't suspended, revert with appropriate message
_systemStatus().requireFuturesMarketActive(baseAsset); // asset and market may be different
_systemStatus().requireFuturesMarketActive(marketKey); // asset and market may be different
// check that synth is active, and wasn't suspended, revert with appropriate message
_systemStatus().requireSynthActive(baseAsset);
// check if circuit breaker if price is within deviation tolerance and system & synth is active
Expand Down Expand Up @@ -802,7 +811,7 @@ contract FuturesMarketBase is MixinFuturesMarketSettings, IFuturesMarketBaseType
_revertIfError(
(margin < _minInitialMargin()) ||
(margin <= _liquidationMargin(position.size, price)) ||
(_maxLeverage(baseAsset) < _abs(_currentLeverage(position, price, margin))),
(_maxLeverage(marketKey) < _abs(_currentLeverage(position, price, margin))),
Status.InsufficientMargin
);
}
Expand Down Expand Up @@ -896,7 +905,7 @@ contract FuturesMarketBase is MixinFuturesMarketSettings, IFuturesMarketBaseType
_recomputeFunding(price);
_modifyPosition(
msg.sender,
TradeParams({sizeDelta: sizeDelta, price: price, takerFee: _takerFee(baseAsset), makerFee: _makerFee(baseAsset)})
TradeParams({sizeDelta: sizeDelta, price: price, takerFee: _takerFee(marketKey), makerFee: _makerFee(marketKey)})
);
}

Expand All @@ -910,7 +919,7 @@ contract FuturesMarketBase is MixinFuturesMarketSettings, IFuturesMarketBaseType
_recomputeFunding(price);
_modifyPosition(
msg.sender,
TradeParams({sizeDelta: -size, price: price, takerFee: _takerFee(baseAsset), makerFee: _makerFee(baseAsset)})
TradeParams({sizeDelta: -size, price: price, takerFee: _takerFee(marketKey), makerFee: _makerFee(marketKey)})
);
}

Expand Down
30 changes: 18 additions & 12 deletions contracts/FuturesMarketData.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ contract FuturesMarketData {
struct MarketSummary {
address market;
bytes32 asset;
bytes32 key;
uint maxLeverage;
uint price;
uint marketSize;
Expand Down Expand Up @@ -75,6 +76,7 @@ contract FuturesMarketData {
struct MarketData {
address market;
bytes32 baseAsset;
bytes32 marketKey;
FuturesMarketData.FeeRates feeRates;
FuturesMarketData.MarketLimits limits;
FuturesMarketData.FundingParameters fundingParameters;
Expand Down Expand Up @@ -130,11 +132,11 @@ contract FuturesMarketData {
});
}

function parameters(bytes32 baseAsset) external view returns (IFuturesMarketSettings.Parameters memory) {
return _parameters(baseAsset);
function parameters(bytes32 marketKey) external view returns (IFuturesMarketSettings.Parameters memory) {
return _parameters(marketKey);
}

function _parameters(bytes32 baseAsset) internal view returns (IFuturesMarketSettings.Parameters memory) {
function _parameters(bytes32 marketKey) internal view returns (IFuturesMarketSettings.Parameters memory) {
(
uint takerFee,
uint makerFee,
Expand All @@ -145,7 +147,7 @@ contract FuturesMarketData {
uint maxMarketValueUSD,
uint maxFundingRate,
uint skewScaleUSD
) = _futuresMarketSettings().parameters(baseAsset);
) = _futuresMarketSettings().parameters(marketKey);
return
IFuturesMarketSettings.Parameters(
takerFee,
Expand All @@ -165,15 +167,17 @@ contract FuturesMarketData {
MarketSummary[] memory summaries = new MarketSummary[](numMarkets);
for (uint i; i < numMarkets; i++) {
IFuturesMarket market = IFuturesMarket(markets[i]);
bytes32 marketKey = market.marketKey();
bytes32 baseAsset = market.baseAsset();
IFuturesMarketSettings.Parameters memory params = _parameters(baseAsset);
IFuturesMarketSettings.Parameters memory params = _parameters(marketKey);

(uint price, ) = market.assetPrice();
(uint debt, ) = market.marketDebt();

summaries[i] = MarketSummary(
address(market),
baseAsset,
marketKey,
params.maxLeverage,
price,
market.marketSize(),
Expand All @@ -191,8 +195,8 @@ contract FuturesMarketData {
return _marketSummaries(markets);
}

function marketSummariesForAssets(bytes32[] calldata assets) external view returns (MarketSummary[] memory) {
return _marketSummaries(_futuresMarketManager().marketsForAssets(assets));
function marketSummariesForKeys(bytes32[] calldata marketKeys) external view returns (MarketSummary[] memory) {
return _marketSummaries(_futuresMarketManager().marketsForKeys(marketKeys));
}

function allMarketSummaries() external view returns (MarketSummary[] memory) {
Expand All @@ -216,13 +220,15 @@ contract FuturesMarketData {
(uint price, bool invalid) = market.assetPrice();
(uint marketDebt, ) = market.marketDebt();
bytes32 baseAsset = market.baseAsset();
bytes32 marketKey = market.marketKey();

IFuturesMarketSettings.Parameters memory params = _parameters(baseAsset);
IFuturesMarketSettings.Parameters memory params = _parameters(marketKey);

return
MarketData(
address(market),
baseAsset,
marketKey,
FeeRates(params.takerFee, params.makerFee, params.takerFeeNextPrice, params.makerFeeNextPrice),
MarketLimits(params.maxLeverage, params.maxMarketValueUSD),
_fundingParameters(params),
Expand All @@ -235,8 +241,8 @@ contract FuturesMarketData {
return _marketDetails(market);
}

function marketDetailsForAsset(bytes32 asset) external view returns (MarketData memory) {
return _marketDetails(IFuturesMarket(_futuresMarketManager().marketForAsset(asset)));
function marketDetailsForKey(bytes32 marketKey) external view returns (MarketData memory) {
return _marketDetails(IFuturesMarket(_futuresMarketManager().marketForKey(marketKey)));
}

function _position(IFuturesMarket market, address account)
Expand Down Expand Up @@ -309,7 +315,7 @@ contract FuturesMarketData {
return _positionDetails(market, account);
}

function positionDetailsForAsset(bytes32 asset, address account) external view returns (PositionData memory) {
return _positionDetails(IFuturesMarket(_futuresMarketManager().marketForAsset(asset)), account);
function positionDetailsForMarketKey(bytes32 marketKey, address account) external view returns (PositionData memory) {
return _positionDetails(IFuturesMarket(_futuresMarketManager().marketForKey(marketKey)), account);
}
}
50 changes: 27 additions & 23 deletions contracts/FuturesMarketManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ contract FuturesMarketManager is Owned, MixinResolver, IFuturesMarketManager {
/* ========== STATE VARIABLES ========== */

AddressSetLib.AddressSet internal _markets;
mapping(bytes32 => address) public marketForAsset;
mapping(bytes32 => address) public marketForKey;

/* ========== ADDRESS RESOLVER CONFIGURATION ========== */

Expand Down Expand Up @@ -80,20 +80,20 @@ contract FuturesMarketManager is Owned, MixinResolver, IFuturesMarketManager {
return _markets.getPage(0, _markets.elements.length);
}

function _marketsForAssets(bytes32[] memory assets) internal view returns (address[] memory) {
uint numAssets = assets.length;
address[] memory results = new address[](numAssets);
for (uint i; i < numAssets; i++) {
results[i] = marketForAsset[assets[i]];
function _marketsForKeys(bytes32[] memory marketKeys) internal view returns (address[] memory) {
uint mMarkets = marketKeys.length;
address[] memory results = new address[](mMarkets);
for (uint i; i < mMarkets; i++) {
results[i] = marketForKey[marketKeys[i]];
}
return results;
}

/*
* The market addresses for a given set of asset strings.
* The market addresses for a given set of market key strings.
*/
function marketsForAssets(bytes32[] calldata assets) external view returns (address[] memory) {
return _marketsForAssets(assets);
function marketsForKeys(bytes32[] calldata marketKeys) external view returns (address[] memory) {
return _marketsForKeys(marketKeys);
}

/*
Expand All @@ -114,19 +114,21 @@ contract FuturesMarketManager is Owned, MixinResolver, IFuturesMarketManager {
/* ========== MUTATIVE FUNCTIONS ========== */

/*
* Add a set of new markets. Reverts if some market's asset already has a market.
* Add a set of new markets. Reverts if some market key already has a market.
*/
function addMarkets(address[] calldata marketsToAdd) external onlyOwner {
uint numOfMarkets = marketsToAdd.length;
for (uint i; i < numOfMarkets; i++) {
address market = marketsToAdd[i];
require(!_markets.contains(market), "Market already exists");

bytes32 key = IFuturesMarket(market).baseAsset();
require(marketForAsset[key] == address(0), "Market already exists for asset");
marketForAsset[key] = market;
bytes32 key = IFuturesMarket(market).marketKey();
bytes32 baseAsset = IFuturesMarket(market).baseAsset();

require(marketForKey[key] == address(0), "Market already exists for key");
marketForKey[key] = market;
_markets.add(market);
emit MarketAdded(market, key);
emit MarketAdded(market, baseAsset, key);
}
}

Expand All @@ -136,11 +138,13 @@ contract FuturesMarketManager is Owned, MixinResolver, IFuturesMarketManager {
address market = marketsToRemove[i];
require(market != address(0), "Unknown market");

bytes32 key = IFuturesMarket(market).baseAsset();
require(marketForAsset[key] != address(0), "Unknown market");
delete marketForAsset[key];
bytes32 key = IFuturesMarket(market).marketKey();
bytes32 baseAsset = IFuturesMarket(market).baseAsset();

require(marketForKey[key] != address(0), "Unknown market");
delete marketForKey[key];
_markets.remove(market);
emit MarketRemoved(market, key);
emit MarketRemoved(market, baseAsset, key);
}
}

Expand All @@ -152,10 +156,10 @@ contract FuturesMarketManager is Owned, MixinResolver, IFuturesMarketManager {
}

/*
* Remove the markets for a given set of assets. Reverts if any asset has no associated market.
* Remove the markets for a given set of market keys. Reverts if any key has no associated market.
*/
function removeMarketsByAsset(bytes32[] calldata assetsToRemove) external onlyOwner {
_removeMarkets(_marketsForAssets(assetsToRemove));
function removeMarketsByKey(bytes32[] calldata marketKeysToRemove) external onlyOwner {
_removeMarkets(_marketsForKeys(marketKeysToRemove));
}

/*
Expand Down Expand Up @@ -219,7 +223,7 @@ contract FuturesMarketManager is Owned, MixinResolver, IFuturesMarketManager {

/* ========== EVENTS ========== */

event MarketAdded(address market, bytes32 indexed asset);
event MarketAdded(address market, bytes32 indexed asset, bytes32 indexed marketKey);

event MarketRemoved(address market, bytes32 indexed asset);
event MarketRemoved(address market, bytes32 indexed asset, bytes32 indexed marketKey);
}
Loading