Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disable dutch auctions per-collateral #873

Merged
merged 18 commits into from
Aug 3, 2023
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
24 changes: 17 additions & 7 deletions contracts/facade/FacadeTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ contract FacadeTest is IFacadeTest {
/// Prompt all traders to run auctions
/// Relatively gas-inefficient, shouldn't be used in production. Use multicall instead
function runAuctionsForAllTraders(IRToken rToken) external {
runAuctionsForAllTradersForKind(rToken, TradeKind.BATCH_AUCTION);
}

// Prompt all traders to run auctions of a specific kind
function runAuctionsForAllTradersForKind(IRToken rToken, TradeKind kind) public {
IMain main = rToken.main();
IBackingManager backingManager = main.backingManager();
IRevenueTrader rsrTrader = main.rsrTrader();
Expand Down Expand Up @@ -55,12 +60,17 @@ contract FacadeTest is IFacadeTest {
try main.backingManager().forwardRevenue(erc20s) {} catch {}

// Start exact RSR auctions
(IERC20[] memory rsrERC20s, TradeKind[] memory rsrKinds) = traderERC20s(rsrTrader, erc20s);
(IERC20[] memory rsrERC20s, TradeKind[] memory rsrKinds) = traderERC20s(
rsrTrader,
kind,
erc20s
);
try main.rsrTrader().manageTokens(rsrERC20s, rsrKinds) {} catch {}

// Start exact RToken auctions
(IERC20[] memory rTokenERC20s, TradeKind[] memory rTokenKinds) = traderERC20s(
rTokenTrader,
kind,
erc20s
);
try main.rTokenTrader().manageTokens(rTokenERC20s, rTokenKinds) {} catch {}
Expand Down Expand Up @@ -115,11 +125,11 @@ contract FacadeTest is IFacadeTest {

// === Private ===

function traderERC20s(IRevenueTrader trader, IERC20[] memory erc20sAll)
private
view
returns (IERC20[] memory erc20s, TradeKind[] memory kinds)
{
function traderERC20s(
IRevenueTrader trader,
TradeKind kind,
IERC20[] memory erc20sAll
) private view returns (IERC20[] memory erc20s, TradeKind[] memory kinds) {
uint256 len;
IERC20[] memory traderERC20sAll = new IERC20[](erc20sAll.length);
for (uint256 i = 0; i < erc20sAll.length; ++i) {
Expand All @@ -136,7 +146,7 @@ contract FacadeTest is IFacadeTest {
kinds = new TradeKind[](len);
for (uint256 i = 0; i < len; ++i) {
erc20s[i] = traderERC20sAll[i];
kinds[i] = TradeKind.BATCH_AUCTION;
kinds[i] = kind;
}
}
}
18 changes: 15 additions & 3 deletions contracts/interfaces/IBroker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ interface IBroker is IComponent {
event DutchTradeImplementationSet(ITrade indexed oldVal, ITrade indexed newVal);
event BatchAuctionLengthSet(uint48 indexed oldVal, uint48 indexed newVal);
event DutchAuctionLengthSet(uint48 indexed oldVal, uint48 indexed newVal);
event DisabledSet(bool indexed prevVal, bool indexed newVal);
event BatchTradeDisabledSet(bool indexed prevVal, bool indexed newVal);
event DutchTradeDisabledSet(
IERC20Metadata indexed erc20,
bool indexed prevVal,
bool indexed newVal
);

// Initialization
function init(
Expand All @@ -62,7 +67,9 @@ interface IBroker is IComponent {
/// Only callable by one of the trading contracts the broker deploys
function reportViolation() external;

function disabled() external view returns (bool);
function batchTradeDisabled() external view returns (bool);

function dutchTradeDisabled(IERC20Metadata erc20) external view returns (bool);
}

interface TestIBroker is IBroker {
Expand All @@ -86,5 +93,10 @@ interface TestIBroker is IBroker {

function setDutchAuctionLength(uint48 newAuctionLength) external;

function setDisabled(bool disabled_) external;
function setBatchTradeDisabled(bool disabled) external;

function setDutchTradeDisabled(IERC20Metadata erc20, bool disabled) external;

// only present on pre-3.0.0 Brokers; used by EasyAuction regression test
function disabled() external view returns (bool);
}
45 changes: 36 additions & 9 deletions contracts/p0/Broker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ contract BrokerP0 is ComponentP0, IBroker {

uint48 public constant MAX_AUCTION_LENGTH = 604800; // {s} max valid duration -1 week
// solhint-disable-next-line var-name-mixedcase
uint48 public immutable MIN_AUCTION_LENGTH; // {s} 2 blocks based on network
uint48 public immutable MIN_AUCTION_LENGTH; // {s} 20 blocks, based on network

// Added for interface compatibility with P1
ITrade public batchTradeImplementation;
Expand All @@ -37,10 +37,12 @@ contract BrokerP0 is ComponentP0, IBroker {
uint48 public batchAuctionLength; // {s} the length of a Gnosis EasyAuction
uint48 public dutchAuctionLength; // {s} the length of a Dutch Auction

bool public disabled;
bool public batchTradeDisabled;

mapping(IERC20Metadata => bool) public dutchTradeDisabled;

constructor() {
MIN_AUCTION_LENGTH = NetworkConfigLib.blocktime() * 2;
MIN_AUCTION_LENGTH = NetworkConfigLib.blocktime() * 20;
}

function init(
Expand Down Expand Up @@ -68,7 +70,6 @@ contract BrokerP0 is ComponentP0, IBroker {
TradeRequest memory req,
TradePrices memory prices
) external returns (ITrade) {
require(!disabled, "broker disabled");
assert(req.sellAmount > 0);

address caller = _msgSender();
Expand All @@ -92,8 +93,23 @@ contract BrokerP0 is ComponentP0, IBroker {
/// @custom:protected
function reportViolation() external notTradingPausedOrFrozen {
require(trades[_msgSender()], "unrecognized trade contract");
emit DisabledSet(disabled, true);
disabled = true;
ITrade trade = ITrade(_msgSender());
TradeKind kind = trade.KIND();

if (kind == TradeKind.BATCH_AUCTION) {
emit BatchTradeDisabledSet(batchTradeDisabled, true);
batchTradeDisabled = true;
} else if (kind == TradeKind.DUTCH_AUCTION) {
IERC20Metadata sell = trade.sell();
emit DutchTradeDisabledSet(sell, dutchTradeDisabled[sell], true);
dutchTradeDisabled[sell] = true;

IERC20Metadata buy = trade.buy();
emit DutchTradeDisabledSet(buy, dutchTradeDisabled[buy], true);
dutchTradeDisabled[buy] = true;
} else {
revert("unrecognized trade kind");
}
}

/// @param maxTokensAllowed {qTok} The max number of sell tokens allowed by the trading platform
Expand Down Expand Up @@ -173,6 +189,7 @@ contract BrokerP0 is ComponentP0, IBroker {
// === Private ===

function newBatchAuction(TradeRequest memory req, address caller) private returns (ITrade) {
require(!batchTradeDisabled, "batch auctions disabled");
require(batchAuctionLength > 0, "batch auctions not enabled");
GnosisTrade trade = new GnosisTrade();
trades[address(trade)] = true;
Expand Down Expand Up @@ -201,6 +218,10 @@ contract BrokerP0 is ComponentP0, IBroker {
TradePrices memory prices,
ITrading caller
) private returns (ITrade) {
require(
!dutchTradeDisabled[req.sell.erc20()] && !dutchTradeDisabled[req.buy.erc20()],
"dutch auctions disabled for token pair"
);
require(dutchAuctionLength > 0, "dutch auctions not enabled");
DutchTrade trade = new DutchTrade();
trades[address(trade)] = true;
Expand All @@ -216,8 +237,14 @@ contract BrokerP0 is ComponentP0, IBroker {
}

/// @custom:governance
function setDisabled(bool disabled_) external governance {
emit DisabledSet(disabled, disabled_);
disabled = disabled_;
function setBatchTradeDisabled(bool disabled) external governance {
emit BatchTradeDisabledSet(batchTradeDisabled, disabled);
batchTradeDisabled = disabled;
}

/// @custom:governance
function setDutchTradeDisabled(IERC20Metadata erc20, bool disabled) external governance {
emit DutchTradeDisabledSet(erc20, dutchTradeDisabled[erc20], disabled);
dutchTradeDisabled[erc20] = disabled;
}
}
1 change: 0 additions & 1 deletion contracts/p0/mixins/Trading.sol
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ abstract contract TradingP0 is RewardableP0, ITrading {
) internal returns (ITrade trade) {
IBroker broker = main.broker();
assert(address(trades[req.sell.erc20()]) == address(0));
require(!broker.disabled(), "broker disabled");

req.sell.erc20().safeApprove(address(broker), 0);
req.sell.erc20().safeApprove(address(broker), req.sellAmount);
Expand Down
55 changes: 41 additions & 14 deletions contracts/p1/Broker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ contract BrokerP1 is ComponentP1, IBroker {
uint48 public constant MAX_AUCTION_LENGTH = 604800; // {s} max valid duration - 1 week
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
// solhint-disable-next-line var-name-mixedcase
uint48 public immutable MIN_AUCTION_LENGTH; // {s} 2 blocks based on network
uint48 public immutable MIN_AUCTION_LENGTH; // {s} 20 blocks, based on network

IBackingManager private backingManager;
IRevenueTrader private rsrTrader;
Expand All @@ -43,9 +43,10 @@ contract BrokerP1 is ComponentP1, IBroker {
// {s} the length of a Gnosis EasyAuction. Governance parameter.
uint48 public batchAuctionLength;

// Whether trading is disabled.
// Initially false. Settable by OWNER. A trade clone can set it to true via reportViolation()
bool public disabled;
// Whether Batch Auctions are disabled.
// Initially false. Settable by OWNER.
// A GnosisTrade clone can set it to true via reportViolation()
bool public batchTradeDisabled;

// The set of ITrade (clone) addresses this contract has created
mapping(address => bool) private trades;
Expand All @@ -58,12 +59,15 @@ contract BrokerP1 is ComponentP1, IBroker {
// {s} the length of a Dutch Auction. Governance parameter.
uint48 public dutchAuctionLength;

// Whether Dutch Auctions are currently disabled, per ERC20
mapping(IERC20Metadata => bool) public dutchTradeDisabled;

// ==== Invariant ====
// (trades[addr] == true) iff this contract has created an ITrade clone at addr

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
MIN_AUCTION_LENGTH = NetworkConfigLib.blocktime() * 2;
MIN_AUCTION_LENGTH = NetworkConfigLib.blocktime() * 20;
}

// effects: initial parameters are set
Expand Down Expand Up @@ -93,7 +97,6 @@ contract BrokerP1 is ComponentP1, IBroker {
/// @dev Requires setting an allowance in advance
/// @custom:protected and @custom:interaction CEI
// checks:
// not disabled, paused (trading), or frozen
// caller is a system Trader
// effects:
// Deploys a new trade clone, `trade`
Expand All @@ -106,8 +109,6 @@ contract BrokerP1 is ComponentP1, IBroker {
TradeRequest memory req,
TradePrices memory prices
) external returns (ITrade) {
require(!disabled, "broker disabled");

address caller = _msgSender();
require(
caller == address(backingManager) ||
Expand All @@ -129,8 +130,23 @@ contract BrokerP1 is ComponentP1, IBroker {
// effects: disabled' = true
function reportViolation() external notTradingPausedOrFrozen {
require(trades[_msgSender()], "unrecognized trade contract");
emit DisabledSet(disabled, true);
disabled = true;
ITrade trade = ITrade(_msgSender());
TradeKind kind = trade.KIND();

if (kind == TradeKind.BATCH_AUCTION) {
emit BatchTradeDisabledSet(batchTradeDisabled, true);
batchTradeDisabled = true;
} else if (kind == TradeKind.DUTCH_AUCTION) {
IERC20Metadata sell = trade.sell();
emit DutchTradeDisabledSet(sell, dutchTradeDisabled[sell], true);
dutchTradeDisabled[sell] = true;

IERC20Metadata buy = trade.buy();
emit DutchTradeDisabledSet(buy, dutchTradeDisabled[buy], true);
dutchTradeDisabled[buy] = true;
} else {
revert("unrecognized trade kind");
}
}

// === Setters ===
Expand Down Expand Up @@ -188,14 +204,21 @@ contract BrokerP1 is ComponentP1, IBroker {
}

/// @custom:governance
function setDisabled(bool disabled_) external governance {
emit DisabledSet(disabled, disabled_);
disabled = disabled_;
function setBatchTradeDisabled(bool disabled) external governance {
emit BatchTradeDisabledSet(batchTradeDisabled, disabled);
batchTradeDisabled = disabled;
}

/// @custom:governance
function setDutchTradeDisabled(IERC20Metadata erc20, bool disabled) external governance {
emit DutchTradeDisabledSet(erc20, dutchTradeDisabled[erc20], disabled);
dutchTradeDisabled[erc20] = disabled;
}

// === Private ===

function newBatchAuction(TradeRequest memory req, address caller) private returns (ITrade) {
require(!batchTradeDisabled, "batch auctions disabled");
require(batchAuctionLength > 0, "batch auctions not enabled");
GnosisTrade trade = GnosisTrade(address(batchTradeImplementation).clone());
trades[address(trade)] = true;
Expand Down Expand Up @@ -224,6 +247,10 @@ contract BrokerP1 is ComponentP1, IBroker {
TradePrices memory prices,
ITrading caller
) private returns (ITrade) {
require(
!dutchTradeDisabled[req.sell.erc20()] && !dutchTradeDisabled[req.buy.erc20()],
"dutch auctions disabled for token pair"
);
require(dutchAuctionLength > 0, "dutch auctions not enabled");
DutchTrade trade = DutchTrade(address(dutchTradeImplementation).clone());
trades[address(trade)] = true;
Expand All @@ -244,5 +271,5 @@ contract BrokerP1 is ComponentP1, IBroker {
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[43] private __gap;
uint256[42] private __gap;
}
12 changes: 8 additions & 4 deletions contracts/plugins/mocks/InvalidBrokerMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ contract InvalidBrokerMock is ComponentP0, IBroker {
uint48 public batchAuctionLength; // {s} the length of a batch auction
uint48 public dutchAuctionLength; // {s} the length of a dutch auction

bool public disabled = false;
bool public batchTradeDisabled = false;

mapping(IERC20Metadata => bool) public dutchTradeDisabled;

function init(
IMain main_,
Expand All @@ -44,8 +46,6 @@ contract InvalidBrokerMock is ComponentP0, IBroker {
TradeRequest memory,
TradePrices memory
) external view notTradingPausedOrFrozen returns (ITrade) {
require(!disabled, "broker disabled");

// Revert when opening trades
revert("Failure opening trade");
}
Expand All @@ -64,5 +64,9 @@ contract InvalidBrokerMock is ComponentP0, IBroker {

/// Dummy implementation
/* solhint-disable no-empty-blocks */
function setDisabled(bool disabled_) external governance {}
function setBatchTradeDisabled(bool disabled) external governance {}

/// Dummy implementation
/* solhint-disable no-empty-blocks */
function setDutchTradeDisabled(IERC20Metadata erc20, bool disabled) external governance {}
}
Loading