From 8195bf728658bc0a13bf08a4d63f968e5b2d471d Mon Sep 17 00:00:00 2001 From: JaredBorders Date: Thu, 20 Apr 2023 13:51:10 -0400 Subject: [PATCH 01/16] =?UTF-8?q?=E2=9C=85=20Add=20initial=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/unit/Account.t.sol | 6 ++++++ test/unit/Factory.t.sol | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/test/unit/Account.t.sol b/test/unit/Account.t.sol index 171195e6..52dd59d8 100644 --- a/test/unit/Account.t.sol +++ b/test/unit/Account.t.sol @@ -632,6 +632,12 @@ contract AccountTest is Test, ConsolidatedEvents { account.execute(commands, inputs); } + /*////////////////////////////////////////////////////////////// + EXECUTION LOCK + //////////////////////////////////////////////////////////////*/ + + function test_Execute_Locked() public {} + /*////////////////////////////////////////////////////////////// MATH UTILITIES //////////////////////////////////////////////////////////////*/ diff --git a/test/unit/Factory.t.sol b/test/unit/Factory.t.sol index b8b195bc..9dbcaf4f 100644 --- a/test/unit/Factory.t.sol +++ b/test/unit/Factory.t.sol @@ -209,6 +209,16 @@ contract FactoryTest is Test, ConsolidatedEvents { factory.newAccount(); } + /*////////////////////////////////////////////////////////////// + EXECUTION LOCK + //////////////////////////////////////////////////////////////*/ + + function test_Lock_OnlyOwner() public {} + function test_Lock_AllAccounts() public {} + function test_Lock_AllAccounts_Event() public {} + function test_Lock_SingleAccount() public {} + function test_Lock_SingleAccount_Event() public {} + /*////////////////////////////////////////////////////////////// UPGRADABILITY //////////////////////////////////////////////////////////////*/ From aeb6d6d38026234b0a42af77573aa08ecaaf9b39 Mon Sep 17 00:00:00 2001 From: JaredBorders Date: Thu, 20 Apr 2023 14:18:28 -0400 Subject: [PATCH 02/16] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20Add=20Settings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Settings.sol | 16 ++++++++++++++++ src/interfaces/ISettings.sol | 9 +++++++++ 2 files changed, 25 insertions(+) create mode 100644 src/Settings.sol create mode 100644 src/interfaces/ISettings.sol diff --git a/src/Settings.sol b/src/Settings.sol new file mode 100644 index 00000000..56c1c1f6 --- /dev/null +++ b/src/Settings.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.18; + +import {ISettings} from "./interfaces/ISettings.sol"; +import {Owned} from "@solmate/auth/Owned.sol"; + +contract Settings is ISettings, Owned { + bool public accountExecutionEnabled = true; + + constructor(address _owner) Owned(_owner) {} + + function setAccountExecutionEnabled(bool _enabled) external onlyOwner { + accountExecutionEnabled = _enabled; + emit AccountExecutionEnabledSet(_enabled); + } +} diff --git a/src/interfaces/ISettings.sol b/src/interfaces/ISettings.sol new file mode 100644 index 00000000..06bb6e05 --- /dev/null +++ b/src/interfaces/ISettings.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.18; + +interface ISettings { + event AccountExecutionEnabledSet(bool enabled); + + function accountExecutionEnabled() external view returns (bool); + function setAccountExecutionEnabled(bool _enabled) external; +} From 57a5ec7116895fc6694808731f8347ca398af79f Mon Sep 17 00:00:00 2001 From: JaredBorders Date: Thu, 20 Apr 2023 14:19:02 -0400 Subject: [PATCH 03/16] =?UTF-8?q?=F0=9F=91=B7=F0=9F=8F=BB=E2=80=8D?= =?UTF-8?q?=E2=99=82=EF=B8=8F=20Introduce=20lock=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Account.sol | 13 ++++++++++++- src/interfaces/IAccount.sol | 4 ++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Account.sol b/src/Account.sol index b7e3fb2c..9dba9772 100644 --- a/src/Account.sol +++ b/src/Account.sol @@ -8,6 +8,7 @@ import { IFactory, IFuturesMarketManager, IPerpsV2MarketConsolidated, + ISettings, ISystemStatus } from "./interfaces/IAccount.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; @@ -53,6 +54,9 @@ contract Account is IAccount, Auth, OpsReady { /// @dev the system status contract is used to check if the system is operational ISystemStatus internal immutable SYSTEM_STATUS; + /// @notice address of contract used to store global settings + ISettings internal immutable SETTINGS; + /*////////////////////////////////////////////////////////////// STATE //////////////////////////////////////////////////////////////*/ @@ -78,6 +82,7 @@ contract Account is IAccount, Auth, OpsReady { /// @param _futuresMarketManager: address of the Synthetix FuturesMarketManager /// @param _gelato: address of Gelato /// @param _ops: address of Ops + /// @param _settings: address of contract used to store global settings constructor( address _factory, address _events, @@ -85,13 +90,15 @@ contract Account is IAccount, Auth, OpsReady { address _futuresMarketManager, address _systemStatus, address _gelato, - address _ops + address _ops, + address _settings ) Auth(address(0)) OpsReady(_gelato, _ops) { FACTORY = IFactory(_factory); EVENTS = IEvents(_events); MARGIN_ASSET = IERC20(_marginAsset); FUTURES_MARKET_MANAGER = IFuturesMarketManager(_futuresMarketManager); SYSTEM_STATUS = ISystemStatus(_systemStatus); + SETTINGS = ISettings(_settings); } /*////////////////////////////////////////////////////////////// @@ -183,6 +190,10 @@ contract Account is IAccount, Auth, OpsReady { payable override { + if (!SETTINGS.accountExecutionEnabled()) { + revert AccountExecutionDisabled(); + } + uint256 numCommands = _commands.length; if (_inputs.length != numCommands) { revert LengthMismatch(); diff --git a/src/interfaces/IAccount.sol b/src/interfaces/IAccount.sol index c94f4669..b5ce7d71 100644 --- a/src/interfaces/IAccount.sol +++ b/src/interfaces/IAccount.sol @@ -6,6 +6,7 @@ import {IFactory} from "./IFactory.sol"; import {IFuturesMarketManager} from "@synthetix/IFuturesMarketManager.sol"; import {IPerpsV2MarketConsolidated} from "@synthetix/IPerpsV2MarketConsolidated.sol"; +import {ISettings} from "./ISettings.sol"; import {ISystemStatus} from "@synthetix/ISystemStatus.sol"; /// @title Kwenta Smart Margin Account Implementation Interface @@ -102,6 +103,9 @@ interface IAccount { /// 4. Price is zero error InvalidPrice(); + /// @notice thrown when account execution has been disabled in the settings contract + error AccountExecutionDisabled(); + /*////////////////////////////////////////////////////////////// VIEWS //////////////////////////////////////////////////////////////*/ From 5390fd09784a4bad8099feee2e2c96f1782de5b6 Mon Sep 17 00:00:00 2001 From: JaredBorders Date: Thu, 20 Apr 2023 14:49:18 -0400 Subject: [PATCH 04/16] =?UTF-8?q?=F0=9F=9A=80=20Update=20deployment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/Deploy.s.sol | 106 +++++++------------ script/utils/deploy-parameters.sol | 29 +++++ script/utils/interfaces/IAddressResolver.sol | 6 ++ 3 files changed, 71 insertions(+), 70 deletions(-) create mode 100644 script/utils/deploy-parameters.sol create mode 100644 script/utils/interfaces/IAddressResolver.sol diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol index 0815b705..892f1cdb 100644 --- a/script/Deploy.s.sol +++ b/script/Deploy.s.sol @@ -2,13 +2,12 @@ pragma solidity 0.8.18; import "forge-std/Script.sol"; +import "./utils/deploy-parameters.sol"; import {Account} from "src/Account.sol"; import {Events} from "src/Events.sol"; import {Factory} from "src/Factory.sol"; - -interface IAddressResolver { - function getAddress(bytes32 name) external view returns (address); -} +import {IAddressResolver} from "./utils/interfaces/IAddressResolver.sol"; +import {Settings} from "src/Settings.sol"; /// @title Script to deploy Kwenta's Smart Margin Account Factory /// @author JaredBorders (jaredborders@pm.me) @@ -19,16 +18,33 @@ contract Setup { address _addressResolver, address _gelato, address _ops - ) public returns (Factory factory, Events events, Account implementation) { + ) + public + returns ( + Factory factory, + Events events, + Settings settings, + Account implementation + ) + { // define *initial* factory owner address temporaryOwner = _deployer == address(0) ? address(this) : _deployer; // deploy the factory - factory = deploySmartMarginFactory({_owner: temporaryOwner}); + factory = new Factory({ + _owner: temporaryOwner + }); // deploy the events contract and set the factory - events = deployEvents({_factory: address(factory)}); + events = new Events({ + _factory: address(factory) + }); + + // deploy the settings contract + settings = new Settings({ + _owner: _owner + }); // resolve necessary addresses via the Synthetix Address Resolver IAddressResolver addressResolver = IAddressResolver(_addressResolver); @@ -39,15 +55,15 @@ contract Setup { address systemStatus = addressResolver.getAddress({name: bytes32("SystemStatus")}); - // deploy the account implementation - implementation = deployAccountImplementation({ + implementation = new Account({ _factory: address(factory), _events: address(events), _marginAsset: marginAsset, _futuresMarketManager: futuresMarketManager, _systemStatus: systemStatus, _gelato: _gelato, - _ops: _ops + _ops: _ops, + _settings: address(settings) }); // update the factory with the new account implementation @@ -58,64 +74,22 @@ contract Setup { // transfer ownership of the factory to the owner factory.transferOwnership({newOwner: _owner}); } - - function deploySmartMarginFactory(address _owner) - internal - returns (Factory factory) - { - factory = new Factory({ - _owner: _owner - }); - } - - function deployEvents(address _factory) internal returns (Events events) { - events = new Events({ - _factory: _factory - }); - } - - function deployAccountImplementation( - address _factory, - address _events, - address _marginAsset, - address _futuresMarketManager, - address _systemStatus, - address _gelato, - address _ops - ) internal returns (Account implementation) { - implementation = new Account({ - _factory: _factory, - _events: _events, - _marginAsset: _marginAsset, - _futuresMarketManager: _futuresMarketManager, - _systemStatus: _systemStatus, - _gelato: _gelato, - _ops: _ops - }); - } } /// @dev steps to deploy and verify on Optimism: /// (1) load the variables in the .env file via `source .env` /// (2) run `forge script script/Deploy.s.sol:DeployOptimism --rpc-url $ARCHIVE_NODE_URL_L2 --broadcast --verify -vvvv` contract DeployOptimism is Script, Setup { - address private constant KWENTA_ADMIN_DAO_MULTI_SIG = - 0xF510a2Ff7e9DD7e18629137adA4eb56B9c13E885; - address private constant SYNTHETIX_ADDRESS_RESOLVER = - 0x1Cb059b7e74fD21665968C908806143E744D5F30; - address private constant GELATO = 0x01051113D81D7d6DA508462F2ad6d7fD96cF42Ef; - address private constant OPS = 0x340759c8346A1E6Ed92035FB8B6ec57cE1D82c2c; - function run() public { uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); vm.startBroadcast(deployerPrivateKey); Setup.deploySystem({ - _deployer: 0x39CFcA7b389529ac861CbB05aDD802e5B06E5101, - _owner: KWENTA_ADMIN_DAO_MULTI_SIG, - _addressResolver: SYNTHETIX_ADDRESS_RESOLVER, - _gelato: GELATO, - _ops: OPS + _deployer: OPTIMISM_DEPLOYER, + _owner: OPTIMISM_KWENTA_ADMIN_DAO_MULTI_SIG, + _addressResolver: OPTIMISM_SYNTHETIX_ADDRESS_RESOLVER, + _gelato: OPTIMISM_GELATO, + _ops: OPTIMISM_OPS }); vm.stopBroadcast(); @@ -125,25 +99,17 @@ contract DeployOptimism is Script, Setup { /// @dev steps to deploy and verify on Optimism Goerli: /// (1) load the variables in the .env file via `source .env` /// (2) run `forge script script/Deploy.s.sol:DeployOptimismGoerli --rpc-url $ARCHIVE_NODE_URL_GOERLI_L2 --broadcast --verify -vvvv` -/// @dev here the KWENTA_ADMIN_DAO_MULTI_SIG is the deployer address contract DeployOptimismGoerli is Script, Setup { - address private constant KWENTA_ADMIN_DAO_MULTI_SIG = - 0xc625F59d51ecDff57FEFE535C80d318CA42A0Ec4; - address private constant SYNTHETIX_ADDRESS_RESOLVER = - 0x9Fc84992dF5496797784374B810E04238728743d; - address private constant GELATO = 0xF82D64357D9120a760e1E4C75f646C0618eFc2F3; - address private constant OPS = 0x255F82563b5973264e89526345EcEa766DB3baB2; - function run() public { uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); vm.startBroadcast(deployerPrivateKey); Setup.deploySystem({ - _deployer: 0xc625F59d51ecDff57FEFE535C80d318CA42A0Ec4, - _owner: KWENTA_ADMIN_DAO_MULTI_SIG, - _addressResolver: SYNTHETIX_ADDRESS_RESOLVER, - _gelato: GELATO, - _ops: OPS + _deployer: OPTIMISM_GOERLI_DEPLOYER, + _owner: OPTIMISM_GOERLI_KWENTA_ADMIN_DAO_MULTI_SIG, + _addressResolver: OPTIMISM_GOERLI_SYNTHETIX_ADDRESS_RESOLVER, + _gelato: OPTIMISM_GOERLI_GELATO, + _ops: OPTIMISM_GOERLI_OPS }); vm.stopBroadcast(); diff --git a/script/utils/deploy-parameters.sol b/script/utils/deploy-parameters.sol new file mode 100644 index 00000000..dc2dc510 --- /dev/null +++ b/script/utils/deploy-parameters.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.18; + +/*////////////////////////////////////////////////////////////// + OPTIMISM +//////////////////////////////////////////////////////////////*/ + +address constant OPTIMISM_DEPLOYER = 0x39CFcA7b389529ac861CbB05aDD802e5B06E5101; +address constant OPTIMISM_KWENTA_ADMIN_DAO_MULTI_SIG = + 0xF510a2Ff7e9DD7e18629137adA4eb56B9c13E885; +address constant OPTIMISM_SYNTHETIX_ADDRESS_RESOLVER = + 0x1Cb059b7e74fD21665968C908806143E744D5F30; +address constant OPTIMISM_GELATO = 0x01051113D81D7d6DA508462F2ad6d7fD96cF42Ef; +address constant OPTIMISM_OPS = 0x340759c8346A1E6Ed92035FB8B6ec57cE1D82c2c; + +/*////////////////////////////////////////////////////////////// + OPTIMISM GOERLI +//////////////////////////////////////////////////////////////*/ + +address constant OPTIMISM_GOERLI_DEPLOYER = + 0xc625F59d51ecDff57FEFE535C80d318CA42A0Ec4; +address constant OPTIMISM_GOERLI_KWENTA_ADMIN_DAO_MULTI_SIG = + 0xc625F59d51ecDff57FEFE535C80d318CA42A0Ec4; +address constant OPTIMISM_GOERLI_SYNTHETIX_ADDRESS_RESOLVER = + 0x9Fc84992dF5496797784374B810E04238728743d; +address constant OPTIMISM_GOERLI_GELATO = + 0xF82D64357D9120a760e1E4C75f646C0618eFc2F3; +address constant OPTIMISM_GOERLI_OPS = + 0x255F82563b5973264e89526345EcEa766DB3baB2; diff --git a/script/utils/interfaces/IAddressResolver.sol b/script/utils/interfaces/IAddressResolver.sol new file mode 100644 index 00000000..7ace47ad --- /dev/null +++ b/script/utils/interfaces/IAddressResolver.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.18; + +interface IAddressResolver { + function getAddress(bytes32 name) external view returns (address); +} From f7d1975824be15d4ee146fef0a28e0f477bb4c83 Mon Sep 17 00:00:00 2001 From: JaredBorders Date: Thu, 20 Apr 2023 14:49:41 -0400 Subject: [PATCH 05/16] =?UTF-8?q?=E2=9C=85=20Update=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/integration/factory.behavior.t.sol | 4 +--- test/integration/margin.behavior.t.sol | 5 +++-- test/integration/order.behavior.t.sol | 5 +++-- test/unit/Account.t.sol | 7 +++++-- test/unit/Auth.t.sol | 2 +- test/unit/Events.t.sol | 2 +- test/unit/Factory.t.sol | 12 +----------- test/utils/AccountExposed.sol | 6 ++++-- 8 files changed, 19 insertions(+), 24 deletions(-) diff --git a/test/integration/factory.behavior.t.sol b/test/integration/factory.behavior.t.sol index 8cda3f18..86148c1d 100644 --- a/test/integration/factory.behavior.t.sol +++ b/test/integration/factory.behavior.t.sol @@ -4,7 +4,6 @@ pragma solidity 0.8.18; import "forge-std/Test.sol"; import "../utils/Constants.sol"; import {Account} from "../../src/Account.sol"; -import {Events} from "../../src/Events.sol"; import {Factory} from "../../src/Factory.sol"; import {Setup} from "../../script/Deploy.s.sol"; @@ -15,7 +14,6 @@ contract FactoryBehaviorTest is Test { // main contracts Factory private factory; - Events private events; Account private implementation; /*////////////////////////////////////////////////////////////// @@ -29,7 +27,7 @@ contract FactoryBehaviorTest is Test { Setup setup = new Setup(); // deploy system contracts - (factory, events, implementation) = setup.deploySystem({ + (factory,,, implementation) = setup.deploySystem({ _deployer: address(0), _owner: address(this), _addressResolver: ADDRESS_RESOLVER, diff --git a/test/integration/margin.behavior.t.sol b/test/integration/margin.behavior.t.sol index edc69067..19b409e4 100644 --- a/test/integration/margin.behavior.t.sol +++ b/test/integration/margin.behavior.t.sol @@ -43,7 +43,7 @@ contract MarginBehaviorTest is Test, ConsolidatedEvents { Setup setup = new Setup(); // deploy system contracts - (factory, events,) = setup.deploySystem({ + (factory, events,,) = setup.deploySystem({ _deployer: address(0), _owner: address(this), _addressResolver: ADDRESS_RESOLVER, @@ -69,7 +69,8 @@ contract MarginBehaviorTest is Test, ConsolidatedEvents { futuresMarketManager, systemStatus, GELATO, - OPS + OPS, + address(0) ); } diff --git a/test/integration/order.behavior.t.sol b/test/integration/order.behavior.t.sol index 9a51753d..a4f8a6f1 100644 --- a/test/integration/order.behavior.t.sol +++ b/test/integration/order.behavior.t.sol @@ -53,7 +53,7 @@ contract OrderBehaviorTest is Test, ConsolidatedEvents { Setup setup = new Setup(); // deploy system contracts - (factory, events,) = setup.deploySystem({ + (factory, events,,) = setup.deploySystem({ _deployer: address(0), _owner: address(this), _addressResolver: ADDRESS_RESOLVER, @@ -76,7 +76,8 @@ contract OrderBehaviorTest is Test, ConsolidatedEvents { futuresMarketManager, address(systemStatus), GELATO, - OPS + OPS, + address(0) ); // deploy an Account contract and fund it diff --git a/test/unit/Account.t.sol b/test/unit/Account.t.sol index 52dd59d8..8d18acf9 100644 --- a/test/unit/Account.t.sol +++ b/test/unit/Account.t.sol @@ -13,6 +13,7 @@ import {IAccount} from "../../src/interfaces/IAccount.sol"; import {IAddressResolver} from "../utils/interfaces/IAddressResolver.sol"; import {IFuturesMarketManager} from "../../src/interfaces/IAccount.sol"; import {IPerpsV2MarketConsolidated} from "../../src/interfaces/IAccount.sol"; +import {Settings} from "../../src/Settings.sol"; import {Setup} from "../../script/Deploy.s.sol"; contract AccountTest is Test, ConsolidatedEvents { @@ -23,6 +24,7 @@ contract AccountTest is Test, ConsolidatedEvents { // main contracts Factory private factory; Events private events; + Settings private settings; Account private account; // helper contracts for testing @@ -38,7 +40,7 @@ contract AccountTest is Test, ConsolidatedEvents { Setup setup = new Setup(); // deploy system contracts - (factory, events,) = setup.deploySystem({ + (factory, events, settings,) = setup.deploySystem({ _deployer: address(0), _owner: address(this), _addressResolver: ADDRESS_RESOLVER, @@ -64,7 +66,8 @@ contract AccountTest is Test, ConsolidatedEvents { futuresMarketManager, systemStatus, GELATO, - OPS + OPS, + address(settings) ); } diff --git a/test/unit/Auth.t.sol b/test/unit/Auth.t.sol index e17daab0..94a2a234 100644 --- a/test/unit/Auth.t.sol +++ b/test/unit/Auth.t.sol @@ -75,4 +75,4 @@ contract AuthTest is Test { assertEq(auth.delegates(DELEGATE), false); } -} \ No newline at end of file +} diff --git a/test/unit/Events.t.sol b/test/unit/Events.t.sol index 25fbf165..c181b320 100644 --- a/test/unit/Events.t.sol +++ b/test/unit/Events.t.sol @@ -32,7 +32,7 @@ contract EventsTest is Test, ConsolidatedEvents { Setup setup = new Setup(); // deploy system contracts - (factory, events,) = setup.deploySystem({ + (factory, events,,) = setup.deploySystem({ _deployer: address(0), _owner: address(this), _addressResolver: ADDRESS_RESOLVER, diff --git a/test/unit/Factory.t.sol b/test/unit/Factory.t.sol index 9dbcaf4f..aeac8e10 100644 --- a/test/unit/Factory.t.sol +++ b/test/unit/Factory.t.sol @@ -32,7 +32,7 @@ contract FactoryTest is Test, ConsolidatedEvents { Setup setup = new Setup(); // deploy system contracts - (factory,, implementation) = setup.deploySystem({ + (factory,,, implementation) = setup.deploySystem({ _deployer: address(0), _owner: address(this), _addressResolver: ADDRESS_RESOLVER, @@ -209,16 +209,6 @@ contract FactoryTest is Test, ConsolidatedEvents { factory.newAccount(); } - /*////////////////////////////////////////////////////////////// - EXECUTION LOCK - //////////////////////////////////////////////////////////////*/ - - function test_Lock_OnlyOwner() public {} - function test_Lock_AllAccounts() public {} - function test_Lock_AllAccounts_Event() public {} - function test_Lock_SingleAccount() public {} - function test_Lock_SingleAccount_Event() public {} - /*////////////////////////////////////////////////////////////// UPGRADABILITY //////////////////////////////////////////////////////////////*/ diff --git a/test/utils/AccountExposed.sol b/test/utils/AccountExposed.sol index 31648a1e..00b3209b 100644 --- a/test/utils/AccountExposed.sol +++ b/test/utils/AccountExposed.sol @@ -19,7 +19,8 @@ contract AccountExposed is Account { address _futuresMarketManager, address _systemStatus, address _gelato, - address _ops + address _ops, + address _settings ) Account( _factory, @@ -28,7 +29,8 @@ contract AccountExposed is Account { _futuresMarketManager, _systemStatus, _gelato, - _ops + _ops, + _settings ) {} From c80378d1ec9a85b07640759c941d348131fe4dfc Mon Sep 17 00:00:00 2001 From: JaredBorders Date: Thu, 20 Apr 2023 15:10:20 -0400 Subject: [PATCH 06/16] =?UTF-8?q?=E2=9C=85=20Test=20lock?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/unit/Account.t.sol | 23 +++++++++++++- test/unit/Settings.t.sol | 50 +++++++++++++++++++++++++++++++ test/utils/ConsolidatedEvents.sol | 6 ++++ 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 test/unit/Settings.t.sol diff --git a/test/unit/Account.t.sol b/test/unit/Account.t.sol index 8d18acf9..5bf6ca2f 100644 --- a/test/unit/Account.t.sol +++ b/test/unit/Account.t.sol @@ -33,6 +33,7 @@ contract AccountTest is Test, ConsolidatedEvents { /*////////////////////////////////////////////////////////////// SETUP //////////////////////////////////////////////////////////////*/ + function setUp() public { vm.rollFork(BLOCK_NUMBER); @@ -639,7 +640,27 @@ contract AccountTest is Test, ConsolidatedEvents { EXECUTION LOCK //////////////////////////////////////////////////////////////*/ - function test_Execute_Locked() public {} + function test_Execute_Locked() public { + // lock accounts as settings owner (which is this address) + settings.setAccountExecutionEnabled(false); + + // expect revert when calling execute + vm.expectRevert( + abi.encodeWithSelector(IAccount.AccountExecutionDisabled.selector) + ); + account.execute(new IAccount.Command[](0), new bytes[](0)); + } + + function test_Execute_CanUnlock() public { + // lock accounts as settings owner (which is this address) + settings.setAccountExecutionEnabled(false); + + // unlock accounts as settings owner (which is this address) + settings.setAccountExecutionEnabled(true); + + // no-op that proves execute is not locked + account.execute(new IAccount.Command[](0), new bytes[](0)); + } /*////////////////////////////////////////////////////////////// MATH UTILITIES diff --git a/test/unit/Settings.t.sol b/test/unit/Settings.t.sol new file mode 100644 index 00000000..c2995712 --- /dev/null +++ b/test/unit/Settings.t.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.18; + +import "forge-std/Test.sol"; +import "../utils/Constants.sol"; +import {ConsolidatedEvents} from "../utils/ConsolidatedEvents.sol"; +import {Settings} from "../../src/Settings.sol"; + +contract SettingsTest is Test, ConsolidatedEvents { + /*////////////////////////////////////////////////////////////// + STATE + //////////////////////////////////////////////////////////////*/ + + // main contracts + Settings private settings; + + /*////////////////////////////////////////////////////////////// + SETUP + //////////////////////////////////////////////////////////////*/ + + function setUp() public { + settings = new Settings(address(this)); + } + + /*////////////////////////////////////////////////////////////// + TESTS + //////////////////////////////////////////////////////////////*/ + + function test_Constructor_Owner() public { + assertEq(settings.owner(), address(this)); + } + + function test_setAccountExecutionEnabled() public { + assertEq(settings.accountExecutionEnabled(), true); + settings.setAccountExecutionEnabled(false); + assertEq(settings.accountExecutionEnabled(), false); + } + + function test_setAccountExecutionEnabled_OnlyOwner() public { + vm.expectRevert("UNAUTHORIZED"); + vm.prank(USER); + settings.setAccountExecutionEnabled(false); + } + + function test_setAccountExecutionEnabled_Event() public { + vm.expectEmit(true, true, true, true); + emit AccountExecutionEnabledSet(false); + settings.setAccountExecutionEnabled(false); + } +} diff --git a/test/utils/ConsolidatedEvents.sol b/test/utils/ConsolidatedEvents.sol index 7ffdcb14..791150bc 100644 --- a/test/utils/ConsolidatedEvents.sol +++ b/test/utils/ConsolidatedEvents.sol @@ -72,4 +72,10 @@ contract ConsolidatedEvents { uint256 fillPrice, uint256 keeperFee ); + + /*////////////////////////////////////////////////////////////// + ISETTINGS + //////////////////////////////////////////////////////////////*/ + + event AccountExecutionEnabledSet(bool enabled); } From 7e86a78dad0b07191e78f14fc029fc592c1dced0 Mon Sep 17 00:00:00 2001 From: JaredBorders Date: Thu, 20 Apr 2023 15:10:35 -0400 Subject: [PATCH 07/16] =?UTF-8?q?=E2=9C=85=20Prep=20new=20class=20of=20tes?= =?UTF-8?q?ts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/integration/upgrade.behavior.t.sol | 43 +++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 test/integration/upgrade.behavior.t.sol diff --git a/test/integration/upgrade.behavior.t.sol b/test/integration/upgrade.behavior.t.sol new file mode 100644 index 00000000..a3ecb945 --- /dev/null +++ b/test/integration/upgrade.behavior.t.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.18; + +import "forge-std/Test.sol"; +import "../utils/Constants.sol"; +import {Account} from "../../src/Account.sol"; +import {Events} from "../../src/Events.sol"; +import {Factory} from "../../src/Factory.sol"; +import {IAccount} from "../../src/interfaces/IAccount.sol"; +import {Settings} from "../../src/Settings.sol"; +import {Setup} from "../../script/Deploy.s.sol"; + +contract UpgradeBehaviorTest is Test { + /*////////////////////////////////////////////////////////////// + STATE + //////////////////////////////////////////////////////////////*/ + + // main contracts + Factory private factory; + Events private events; + Settings private settings; + Account private account; + + /*////////////////////////////////////////////////////////////// + SETUP + //////////////////////////////////////////////////////////////*/ + + function setUp() public { + vm.rollFork(BLOCK_NUMBER); + + // define Setup contract used for deployments + Setup setup = new Setup(); + + // deploy system contracts + (factory, events, settings,) = setup.deploySystem({ + _deployer: address(0), + _owner: address(this), + _addressResolver: ADDRESS_RESOLVER, + _gelato: GELATO, + _ops: OPS + }); + } +} From 6ec0dac87d707cd74399cc9ca47b9457c7172d3d Mon Sep 17 00:00:00 2001 From: JaredBorders Date: Fri, 21 Apr 2023 12:57:42 -0400 Subject: [PATCH 08/16] =?UTF-8?q?=F0=9F=91=B7=F0=9F=8F=BB=E2=80=8D?= =?UTF-8?q?=E2=99=82=EF=B8=8F=20Add=20modifier?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Account.sol | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/Account.sol b/src/Account.sol index 9dba9772..16f064b4 100644 --- a/src/Account.sol +++ b/src/Account.sol @@ -70,6 +70,22 @@ contract Account is IAccount, Auth, OpsReady { /// @notice track conditional orders by id mapping(uint256 id => ConditionalOrder order) internal conditionalOrders; + /*////////////////////////////////////////////////////////////// + MODIFIERS + //////////////////////////////////////////////////////////////*/ + + modifier isAccountExecutionEnabled() { + _isAccountExecutionEnabled(); + + _; + } + + function _isAccountExecutionEnabled() internal view { + if (!SETTINGS.accountExecutionEnabled()) { + revert AccountExecutionDisabled(); + } + } + /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ @@ -189,11 +205,8 @@ contract Account is IAccount, Auth, OpsReady { external payable override + isAccountExecutionEnabled { - if (!SETTINGS.accountExecutionEnabled()) { - revert AccountExecutionDisabled(); - } - uint256 numCommands = _commands.length; if (_inputs.length != numCommands) { revert LengthMismatch(); @@ -727,6 +740,7 @@ contract Account is IAccount, Auth, OpsReady { function executeConditionalOrder(uint256 _conditionalOrderId) external override + isAccountExecutionEnabled onlyOps { ConditionalOrder memory conditionalOrder = From 1e5c1ad3ca48afc919508acdd08d126e962db83e Mon Sep 17 00:00:00 2001 From: JaredBorders Date: Fri, 21 Apr 2023 12:58:02 -0400 Subject: [PATCH 09/16] =?UTF-8?q?=E2=9C=85=20Test=20cond.=20order=20cannot?= =?UTF-8?q?=20be=20executed=20during=20lock?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/integration/order.behavior.t.sol | 35 ++++++++++++++++++++++++++- test/unit/Account.t.sol | 12 +++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/test/integration/order.behavior.t.sol b/test/integration/order.behavior.t.sol index a4f8a6f1..3ee5aa67 100644 --- a/test/integration/order.behavior.t.sol +++ b/test/integration/order.behavior.t.sol @@ -18,6 +18,7 @@ import {IPerpsV2MarketConsolidated} from "../../src/interfaces/IAccount.sol"; import {ISynth} from "../utils/interfaces/ISynth.sol"; import {ISystemStatus} from "../utils/interfaces/ISystemStatus.sol"; import {OpsReady} from "../../src/utils/OpsReady.sol"; +import {Settings} from "../../src/Settings.sol"; import {Setup} from "../../script/Deploy.s.sol"; // functions tagged with @HELPER are helper functions and not tests @@ -32,6 +33,7 @@ contract OrderBehaviorTest is Test, ConsolidatedEvents { // main contracts Factory private factory; Events private events; + Settings private settings; Account private account; // helper contracts for testing @@ -53,7 +55,7 @@ contract OrderBehaviorTest is Test, ConsolidatedEvents { Setup setup = new Setup(); // deploy system contracts - (factory, events,,) = setup.deploySystem({ + (factory, events, settings,) = setup.deploySystem({ _deployer: address(0), _owner: address(this), _addressResolver: ADDRESS_RESOLVER, @@ -605,6 +607,37 @@ contract OrderBehaviorTest is Test, ConsolidatedEvents { assert(!canExecute); } + function test_ExecuteConditionalOrder_AfterUnlock() public { + // lock accounts as settings owner (which is this address) + settings.setAccountExecutionEnabled(false); + + // unlock accounts as settings owner (which is this address) + settings.setAccountExecutionEnabled(true); + + uint256 conditionalOrderId = placeConditionalOrder({ + marketKey: sETHPERP, + marginDelta: int256(currentEthPriceInUSD), + sizeDelta: 1 ether, + targetPrice: currentEthPriceInUSD, + conditionalOrderType: IAccount.ConditionalOrderTypes.LIMIT, + desiredFillPrice: DESIRED_FILL_PRICE, + reduceOnly: false + }); + (bytes memory executionData, IOps.ModuleData memory moduleData) = + generateGelatoModuleData(conditionalOrderId); + vm.prank(GELATO); + IOps(OPS).exec({ + taskCreator: address(account), + execAddress: address(account), + execData: executionData, + moduleData: moduleData, + txFee: GELATO_FEE, + feeToken: ETH, + useTaskTreasuryFunds: false, + revertOnFailure: true + }); + } + // assert successful execution frees committed margin function test_ExecuteConditionalOrder_Valid_GelatoFee() public { uint256 existingGelatoBalance = GELATO.balance; diff --git a/test/unit/Account.t.sol b/test/unit/Account.t.sol index 5bf6ca2f..1e31abaf 100644 --- a/test/unit/Account.t.sol +++ b/test/unit/Account.t.sol @@ -651,6 +651,18 @@ contract AccountTest is Test, ConsolidatedEvents { account.execute(new IAccount.Command[](0), new bytes[](0)); } + function test_ExecuteConditionalOrder_Locked() public { + // lock accounts as settings owner (which is this address) + settings.setAccountExecutionEnabled(false); + + // expect revert when calling execute + vm.expectRevert( + abi.encodeWithSelector(IAccount.AccountExecutionDisabled.selector) + ); + vm.prank(GELATO); + account.executeConditionalOrder(1); + } + function test_Execute_CanUnlock() public { // lock accounts as settings owner (which is this address) settings.setAccountExecutionEnabled(false); From 72fe65ea1e33f7e2c2155c7b6c664165bb57bf7b Mon Sep 17 00:00:00 2001 From: JaredBorders Date: Fri, 21 Apr 2023 16:41:16 -0400 Subject: [PATCH 10/16] =?UTF-8?q?=E2=9C=85=20Update=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/integration/upgrade.behavior.t.sol | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/test/integration/upgrade.behavior.t.sol b/test/integration/upgrade.behavior.t.sol index a3ecb945..db3ab71e 100644 --- a/test/integration/upgrade.behavior.t.sol +++ b/test/integration/upgrade.behavior.t.sol @@ -8,7 +8,6 @@ import {Events} from "../../src/Events.sol"; import {Factory} from "../../src/Factory.sol"; import {IAccount} from "../../src/interfaces/IAccount.sol"; import {Settings} from "../../src/Settings.sol"; -import {Setup} from "../../script/Deploy.s.sol"; contract UpgradeBehaviorTest is Test { /*////////////////////////////////////////////////////////////// @@ -27,17 +26,5 @@ contract UpgradeBehaviorTest is Test { function setUp() public { vm.rollFork(BLOCK_NUMBER); - - // define Setup contract used for deployments - Setup setup = new Setup(); - - // deploy system contracts - (factory, events, settings,) = setup.deploySystem({ - _deployer: address(0), - _owner: address(this), - _addressResolver: ADDRESS_RESOLVER, - _gelato: GELATO, - _ops: OPS - }); } } From d3ece918e70c171e7281d9dbbca4870e8deb9c32 Mon Sep 17 00:00:00 2001 From: JaredBorders Date: Fri, 21 Apr 2023 17:54:44 -0400 Subject: [PATCH 11/16] =?UTF-8?q?=E2=9C=85=20Test=20upgrade?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/integration/order.behavior.t.sol | 4 +- test/integration/upgrade.behavior.t.sol | 122 +++++++++++++++++++++++- test/unit/Account.t.sol | 2 +- test/utils/Constants.sol | 4 +- 4 files changed, 122 insertions(+), 10 deletions(-) diff --git a/test/integration/order.behavior.t.sol b/test/integration/order.behavior.t.sol index 3ee5aa67..e9579b86 100644 --- a/test/integration/order.behavior.t.sol +++ b/test/integration/order.behavior.t.sol @@ -607,7 +607,7 @@ contract OrderBehaviorTest is Test, ConsolidatedEvents { assert(!canExecute); } - function test_ExecuteConditionalOrder_AfterUnlock() public { + function test_ExecuteConditionalOrder_AfterUnlock() public { // lock accounts as settings owner (which is this address) settings.setAccountExecutionEnabled(false); @@ -636,7 +636,7 @@ contract OrderBehaviorTest is Test, ConsolidatedEvents { useTaskTreasuryFunds: false, revertOnFailure: true }); - } + } // assert successful execution frees committed margin function test_ExecuteConditionalOrder_Valid_GelatoFee() public { diff --git a/test/integration/upgrade.behavior.t.sol b/test/integration/upgrade.behavior.t.sol index db3ab71e..e4260626 100644 --- a/test/integration/upgrade.behavior.t.sol +++ b/test/integration/upgrade.behavior.t.sol @@ -4,21 +4,31 @@ pragma solidity 0.8.18; import "forge-std/Test.sol"; import "../utils/Constants.sol"; import {Account} from "../../src/Account.sol"; +import {ERC20} from "@solmate/tokens/ERC20.sol"; import {Events} from "../../src/Events.sol"; import {Factory} from "../../src/Factory.sol"; import {IAccount} from "../../src/interfaces/IAccount.sol"; +import {IAddressResolver} from "../utils/interfaces/IAddressResolver.sol"; +import {ISynth} from "../utils/interfaces/ISynth.sol"; import {Settings} from "../../src/Settings.sol"; contract UpgradeBehaviorTest is Test { + /*////////////////////////////////////////////////////////////// + DEPLOYED CONTRACTS + //////////////////////////////////////////////////////////////*/ + + Factory private constant DEPLOYED_FACTORY = + Factory(0x30582eeE34719fe22b1B6c3b607636A3ab94522E); + Events private constant DEPLOYED_EVENTS = + Events(0x8c3E12418d9327FAb68D6873FF274f181Cba99da); + Account private constant DEPLOYED_ACCOUNT = + Account(payable(0x3D0157ed46F43909425777084DB1e3CEeC55E781)); + /*////////////////////////////////////////////////////////////// STATE //////////////////////////////////////////////////////////////*/ - // main contracts - Factory private factory; - Events private events; - Settings private settings; - Account private account; + Account private implementation; /*////////////////////////////////////////////////////////////// SETUP @@ -26,5 +36,107 @@ contract UpgradeBehaviorTest is Test { function setUp() public { vm.rollFork(BLOCK_NUMBER); + + // deploy the settings contract + Settings settings = new Settings({ + _owner: KWENTA_TREASURY + }); + + // resolve necessary addresses via the Synthetix Address Resolver + IAddressResolver addressResolver = IAddressResolver(ADDRESS_RESOLVER); + address futuresMarketManager = + addressResolver.getAddress({name: bytes32("FuturesMarketManager")}); + address systemStatus = + addressResolver.getAddress({name: bytes32("SystemStatus")}); + + // deploy new account + implementation = new Account({ + _factory: address(DEPLOYED_FACTORY), + _events: address(DEPLOYED_EVENTS), + _marginAsset: MARGIN_ASSET, + _futuresMarketManager: futuresMarketManager, + _systemStatus: systemStatus, + _gelato: GELATO, + _ops: OPS, + _settings: address(settings) + }); + } + + /*////////////////////////////////////////////////////////////// + TESTS + //////////////////////////////////////////////////////////////*/ + + function test_Deployed_Account_Version() public view { + assert(DEPLOYED_ACCOUNT.VERSION() == "2.0.0"); + } + + function test_Upgrade_Implementation() public { + // create smart margin account (v2.0.0) + /// @dev DEPLOYED_FACTORY.newAccount() creates a v2.0.0 account + /// but we use the new implementation Account type (i.e. v2.0.1). + /// This is fine because the Account type is just a "wrapper" in this + /// case and if a function is called which does not exist + /// in the v2.0.0 account the call will revert. + Account account = Account(DEPLOYED_FACTORY.newAccount()); + + // mint sUSD to this address + mintSUSD(address(this), AMOUNT); + + // approve account to spend sUSD + ERC20(MARGIN_ASSET).approve(address(account), AMOUNT); + + // deposit sUSD into account + IAccount.Command[] memory commands = new IAccount.Command[](1); + commands[0] = IAccount.Command.ACCOUNT_MODIFY_MARGIN; + bytes[] memory inputs = new bytes[](1); + inputs[0] = abi.encode(AMOUNT); + account.execute(commands, inputs); + + // submit condition order + commands[0] = IAccount.Command.GELATO_PLACE_CONDITIONAL_ORDER; + inputs[0] = abi.encode( + sETHPERP, + int256(AMOUNT), + int256(AMOUNT), + 1000 ether, + IAccount.ConditionalOrderTypes.STOP, + 1000 ether, + true + ); + account.execute(commands, inputs); + + // add a delegate account just because.. + account.addDelegate(KWENTA_TREASURY); + + // check account is NOT 2.0.1 + assert(account.VERSION() != "2.0.1"); + + // upgrade account implementation + vm.prank(KWENTA_TREASURY); + DEPLOYED_FACTORY.upgradeAccountImplementation(address(implementation)); + + // check account was updated to 2.0.1 + assert(account.VERSION() == "2.0.1"); + + // check state of account after upgrade did not change + assert(account.owner() == address(this)); + assert(account.committedMargin() == AMOUNT); + assert(account.conditionalOrderId() == 1); + assert(account.getConditionalOrder(0).marketKey == sETHPERP); + assert(account.getConditionalOrder(0).targetPrice == 1000 ether); + assert(account.freeMargin() == 0); + assert(account.delegates(KWENTA_TREASURY)); + } + + /*////////////////////////////////////////////////////////////// + HELPERS + //////////////////////////////////////////////////////////////*/ + + function mintSUSD(address to, uint256 amount) private { + address issuer = IAddressResolver(ADDRESS_RESOLVER).getAddress("Issuer"); + ISynth synthsUSD = + ISynth(IAddressResolver(ADDRESS_RESOLVER).getAddress("SynthsUSD")); + vm.prank(issuer); + synthsUSD.issue(to, amount); } } diff --git a/test/unit/Account.t.sol b/test/unit/Account.t.sol index 1e31abaf..108baa75 100644 --- a/test/unit/Account.t.sol +++ b/test/unit/Account.t.sol @@ -81,7 +81,7 @@ contract AccountTest is Test, ConsolidatedEvents { //////////////////////////////////////////////////////////////*/ function test_GetVerison() public view { - assert(account.VERSION() == "2.0.0"); + assert(account.VERSION() == "2.0.1"); } function test_GetTrackingCode() public view { diff --git a/test/utils/Constants.sol b/test/utils/Constants.sol index 121da5be..11950993 100644 --- a/test/utils/Constants.sol +++ b/test/utils/Constants.sol @@ -5,10 +5,10 @@ pragma solidity 0.8.18; TEST VALUES //////////////////////////////////////////////////////////////*/ -// BLOCK_NUMBER corresponds to Optimism Goerli network state @ Apr-05-2023 07:46:26 PM +UTC +// BLOCK_NUMBER corresponds to Optimism Goerli network state @ Apr-21-2023 08:49:56 PM +UTC // hard coded addresses are only guaranteed for this block // used to create a consistent and realistic test environment exposing Synthetix PerpsV2 -uint256 constant BLOCK_NUMBER = 7_642_959; +uint256 constant BLOCK_NUMBER = 8_341_064; // test deployer address with ETH address constant DEPLOYER = 0xc625F59d51ecDff57FEFE535C80d318CA42A0Ec4; From c9d35c1de9f3954248e50a4556bfd0c61aed4550 Mon Sep 17 00:00:00 2001 From: JaredBorders Date: Fri, 21 Apr 2023 17:55:03 -0400 Subject: [PATCH 12/16] =?UTF-8?q?=F0=9F=91=B7=F0=9F=8F=BB=E2=80=8D?= =?UTF-8?q?=E2=99=82=EF=B8=8F=20Update=20version=20(2.0.1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Account.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Account.sol b/src/Account.sol index 16f064b4..0a1fc228 100644 --- a/src/Account.sol +++ b/src/Account.sol @@ -23,7 +23,7 @@ contract Account is IAccount, Auth, OpsReady { //////////////////////////////////////////////////////////////*/ /// @inheritdoc IAccount - bytes32 public constant VERSION = "2.0.0"; + bytes32 public constant VERSION = "2.0.1"; /// @notice tracking code used when modifying positions bytes32 internal constant TRACKING_CODE = "KWENTA"; From 0937c3ccd59a4e5d4f28905865b8244ca56bd433 Mon Sep 17 00:00:00 2001 From: JaredBorders Date: Fri, 21 Apr 2023 17:59:14 -0400 Subject: [PATCH 13/16] =?UTF-8?q?=E2=9C=85=20Update=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/integration/upgrade.behavior.t.sol | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/test/integration/upgrade.behavior.t.sol b/test/integration/upgrade.behavior.t.sol index e4260626..793be0d8 100644 --- a/test/integration/upgrade.behavior.t.sol +++ b/test/integration/upgrade.behavior.t.sol @@ -28,6 +28,7 @@ contract UpgradeBehaviorTest is Test { STATE //////////////////////////////////////////////////////////////*/ + Settings private settings; Account private implementation; /*////////////////////////////////////////////////////////////// @@ -38,7 +39,7 @@ contract UpgradeBehaviorTest is Test { vm.rollFork(BLOCK_NUMBER); // deploy the settings contract - Settings settings = new Settings({ + settings = new Settings({ _owner: KWENTA_TREASURY }); @@ -126,6 +127,16 @@ contract UpgradeBehaviorTest is Test { assert(account.getConditionalOrder(0).targetPrice == 1000 ether); assert(account.freeMargin() == 0); assert(account.delegates(KWENTA_TREASURY)); + + // check execute() can be locked + vm.prank(KWENTA_TREASURY); + settings.setAccountExecutionEnabled(false); + + // try to execute a command + vm.expectRevert( + abi.encodeWithSelector(IAccount.AccountExecutionDisabled.selector) + ); + account.execute(new IAccount.Command[](0), new bytes[](0)); } /*////////////////////////////////////////////////////////////// From 4abde6f2985ac3aaab13e64091209627ee785fdd Mon Sep 17 00:00:00 2001 From: JaredBorders Date: Fri, 21 Apr 2023 18:06:23 -0400 Subject: [PATCH 14/16] =?UTF-8?q?=F0=9F=93=9A=20Update=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 25 +++++++++++++----------- diagrams/Abstract-System-Diagram.png | Bin 30626 -> 39369 bytes test/integration/upgrade.behavior.t.sol | 2 +- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index b4ea75f4..8fbfb61d 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Contracts to manage account abstractions and features on top of [Synthetix Perps ### System Diagram

- System-Diagram + System-Diagram

## Contracts Overview @@ -106,16 +106,18 @@ src ├── AccountProxy.sol ├── Events.sol ├── Factory.sol +├── Settings.sol ├── interfaces -│   ├── IAccount.sol -│   ├── IAccountProxy.sol -│   ├── IEvents.sol -│   ├── IFactory.sol -│   ├── IOps.sol -│   └── synthetix -│   ├── IFuturesMarketManager.sol -│   ├── IPerpsV2MarketConsolidated.sol -│   └── ISystemStatus.sol +│ ├── IAccount.sol +│ ├── IAccountProxy.sol +│ ├── IEvents.sol +│ ├── IFactory.sol +│ ├── IOps.sol +│ ├── ISettings.sol +│ └── synthetix +│ ├── IFuturesMarketManager.sol +│ ├── IPerpsV2MarketConsolidated.sol +│ └── ISystemStatus.sol └── utils ├── Auth.sol └── OpsReady.sol @@ -125,10 +127,11 @@ src | File | % Lines | % Statements | % Branches | % Funcs | |--------------------------------|------------------|------------------|-----------------|-----------------| -| src/Account.sol | 98.51% (199/202) | 98.60% (211/214) | 91.89% (68/74) | 100.00% (34/34) | +| src/Account.sol | 98.53% (201/204) | 98.61% (213/216) | 92.11% (70/76) | 100.00% (35/35) | | src/AccountProxy.sol | 100.00% (10/10) | 76.92% (10/13) | 50.00% (3/6) | 100.00% (6/6) | | src/Events.sol | 100.00% (6/6) | 100.00% (6/6) | 100.00% (0/0) | 100.00% (6/6) | | src/Factory.sol | 100.00% (27/27) | 100.00% (34/34) | 85.71% (12/14) | 100.00% (6/6) | +| src/Settings.sol | 100.00% (2/2) | 100.00% (2/2) | 100.00% (0/0) | 100.00% (1/1) | | src/utils/Auth.sol | 100.00% (15/15) | 100.00% (18/18) | 100.00% (10/10) | 100.00% (5/5) | | src/utils/OpsReady.sol | 100.00% (3/3) | 100.00% (4/4) | 75.00% (3/4) | 100.00% (1/1) | diff --git a/diagrams/Abstract-System-Diagram.png b/diagrams/Abstract-System-Diagram.png index 22fdd32a73945671ab2c348cd5bedaf8932341fd..89b986f925fd188e09b66596ca12cfec176bc84b 100644 GIT binary patch literal 39369 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 XE-7s3$@aBv@K5E;dBvR5SML2E!*$}) literal 30626 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
h>HH!?qb`bF}| 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 H#y Date: Fri, 21 Apr 2023 18:06:41 -0400 Subject: [PATCH 15/16] =?UTF-8?q?=F0=9F=93=B8=20Update=20gas-snapshot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gas-snapshot | 218 ++++++++++++++++++++++++++------------------------ 1 file changed, 114 insertions(+), 104 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index e2424be1..c00b5a3f 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,57 +1,60 @@ AccountTest:test_Abs(int256) (runs: 256, μ: 5754, ~: 5755) AccountTest:test_AddDelegatedTrader() (gas: 44915) AccountTest:test_AddDelegatedTrader_AlreadyDelegated() (gas: 48443) -AccountTest:test_AddDelegatedTrader_Event() (gas: 46709) -AccountTest:test_AddDelegatedTrader_OnlyOwner() (gas: 78123) -AccountTest:test_AddDelegatedTrader_ZeroAddress() (gas: 21741) +AccountTest:test_AddDelegatedTrader_Event() (gas: 46687) +AccountTest:test_AddDelegatedTrader_OnlyOwner() (gas: 78145) +AccountTest:test_AddDelegatedTrader_ZeroAddress() (gas: 21774) AccountTest:test_Checker() (gas: 57578) -AccountTest:test_DelegatedTrader_Execute_ACCOUNT_MODIFY_MARGIN() (gas: 50514) -AccountTest:test_DelegatedTrader_Execute_ACCOUNT_WITHDRAW_ETH() (gas: 50515) -AccountTest:test_DelegatedTrader_Execute_GELATO_CANCEL_CONDITIONAL_ORDER() (gas: 109965) -AccountTest:test_DelegatedTrader_Execute_GELATO_PLACE_CONDITIONAL_ORDER() (gas: 51823) -AccountTest:test_DelegatedTrader_Execute_PERPS_V2_CANCEL_DELAYED_ORDER() (gas: 124071) -AccountTest:test_DelegatedTrader_Execute_PERPS_V2_CANCEL_OFFCHAIN_DELAYED_ORDER() (gas: 124095) -AccountTest:test_DelegatedTrader_Execute_PERPS_V2_CLOSE_POSITION() (gas: 116043) -AccountTest:test_DelegatedTrader_Execute_PERPS_V2_MODIFY_MARGIN() (gas: 88805) -AccountTest:test_DelegatedTrader_Execute_PERPS_V2_SUBMIT_ATOMIC_ORDER() (gas: 348080) -AccountTest:test_DelegatedTrader_Execute_PERPS_V2_SUBMIT_CLOSE_DELAYED_ORDER() (gas: 120272) -AccountTest:test_DelegatedTrader_Execute_PERPS_V2_SUBMIT_CLOSE_OFFCHAIN_DELAYED_ORDER() (gas: 120113) -AccountTest:test_DelegatedTrader_Execute_PERPS_V2_SUBMIT_DELAYED_ORDER() (gas: 381612) -AccountTest:test_DelegatedTrader_Execute_PERPS_V2_SUBMIT_OFFCHAIN_DELAYED_ORDER() (gas: 391333) -AccountTest:test_DelegatedTrader_Execute_PERPS_V2_WITHDRAW_ALL_MARGIN() (gas: 334968) -AccountTest:test_DelegatedTrader_TransferAccountOwnership() (gas: 48309) -AccountTest:test_Deposit_ETH_AnyCaller() (gas: 81012) -AccountTest:test_Deposit_Margin_OnlyOwner() (gas: 80290) +AccountTest:test_DelegatedTrader_Execute_ACCOUNT_MODIFY_MARGIN() (gas: 55722) +AccountTest:test_DelegatedTrader_Execute_ACCOUNT_WITHDRAW_ETH() (gas: 55723) +AccountTest:test_DelegatedTrader_Execute_GELATO_CANCEL_CONDITIONAL_ORDER() (gas: 115174) +AccountTest:test_DelegatedTrader_Execute_GELATO_PLACE_CONDITIONAL_ORDER() (gas: 57075) +AccountTest:test_DelegatedTrader_Execute_PERPS_V2_CANCEL_DELAYED_ORDER() (gas: 129301) +AccountTest:test_DelegatedTrader_Execute_PERPS_V2_CANCEL_OFFCHAIN_DELAYED_ORDER() (gas: 129325) +AccountTest:test_DelegatedTrader_Execute_PERPS_V2_CLOSE_POSITION() (gas: 121273) +AccountTest:test_DelegatedTrader_Execute_PERPS_V2_MODIFY_MARGIN() (gas: 94035) +AccountTest:test_DelegatedTrader_Execute_PERPS_V2_SUBMIT_ATOMIC_ORDER() (gas: 356044) +AccountTest:test_DelegatedTrader_Execute_PERPS_V2_SUBMIT_CLOSE_DELAYED_ORDER() (gas: 125502) +AccountTest:test_DelegatedTrader_Execute_PERPS_V2_SUBMIT_CLOSE_OFFCHAIN_DELAYED_ORDER() (gas: 125343) +AccountTest:test_DelegatedTrader_Execute_PERPS_V2_SUBMIT_DELAYED_ORDER() (gas: 389642) +AccountTest:test_DelegatedTrader_Execute_PERPS_V2_SUBMIT_OFFCHAIN_DELAYED_ORDER() (gas: 399341) +AccountTest:test_DelegatedTrader_Execute_PERPS_V2_WITHDRAW_ALL_MARGIN() (gas: 342932) +AccountTest:test_DelegatedTrader_TransferAccountOwnership() (gas: 48374) +AccountTest:test_Deposit_ETH_AnyCaller() (gas: 81035) +AccountTest:test_Deposit_Margin_OnlyOwner() (gas: 85564) AccountTest:test_Dispatch_InvalidCommand() (gas: 20253) -AccountTest:test_Dispatch_ValidCommand_InvalidInput() (gas: 26132) +AccountTest:test_Dispatch_ValidCommand_InvalidInput() (gas: 31429) +AccountTest:test_ExecuteConditionalOrder_Locked() (gas: 31816) +AccountTest:test_Execute_CanUnlock() (gas: 28493) +AccountTest:test_Execute_Locked() (gas: 32301) AccountTest:test_GetCommittedMargin() (gas: 18186) -AccountTest:test_GetConditionalOrder() (gas: 34757) +AccountTest:test_GetConditionalOrder() (gas: 34779) AccountTest:test_GetConditionalOrderId() (gas: 18186) -AccountTest:test_GetDelayedOrder_EthMarket() (gas: 74329) -AccountTest:test_GetDelayedOrder_InvalidMarket() (gas: 28554) -AccountTest:test_GetEvents() (gas: 7657) +AccountTest:test_GetDelayedOrder_EthMarket() (gas: 74307) +AccountTest:test_GetDelayedOrder_InvalidMarket() (gas: 28576) +AccountTest:test_GetEvents() (gas: 7635) AccountTest:test_GetFactory() (gas: 7680) -AccountTest:test_GetFreeMargin() (gas: 34409) +AccountTest:test_GetFreeMargin() (gas: 34431) AccountTest:test_GetFuturesMarketManager() (gas: 5506) -AccountTest:test_GetGelato() (gas: 5560) -AccountTest:test_GetMarginAsset() (gas: 5562) -AccountTest:test_GetOps() (gas: 5562) -AccountTest:test_GetPosition_EthMarket() (gas: 62265) -AccountTest:test_GetPosition_InvalidMarket() (gas: 28246) -AccountTest:test_GetSystemStatus() (gas: 5539) +AccountTest:test_GetGelato() (gas: 5605) +AccountTest:test_GetMarginAsset() (gas: 5584) +AccountTest:test_GetOps() (gas: 5540) +AccountTest:test_GetPosition_EthMarket() (gas: 62221) +AccountTest:test_GetPosition_InvalidMarket() (gas: 28224) +AccountTest:test_GetSystemStatus() (gas: 5561) AccountTest:test_GetTrackingCode() (gas: 5554) -AccountTest:test_GetVerison() (gas: 16063) -AccountTest:test_IsSameSign(int256,int256) (runs: 256, μ: 6163, ~: 6014) -AccountTest:test_Ownership_Transfer() (gas: 96255) -AccountTest:test_Ownership_Transfer_Event() (gas: 76789) +AccountTest:test_GetVerison() (gas: 16086) +AccountTest:test_IsSameSign(int256,int256) (runs: 256, μ: 6233, ~: 6080) +AccountTest:test_Ownership_Transfer() (gas: 96277) +AccountTest:test_Ownership_Transfer_Event() (gas: 76767) AccountTest:test_Ownership_setInitialOwnership_OnlyFactory() (gas: 19424) AccountTest:test_RemoveDelegatedTrader() (gas: 35396) AccountTest:test_RemoveDelegatedTrader_Event() (gas: 36741) AccountTest:test_RemoveDelegatedTrader_NotDelegated() (gas: 23930) AccountTest:test_RemoveDelegatedTrader_OnlyOwner() (gas: 104606) AccountTest:test_RemoveDelegatedTrader_ZeroAddress() (gas: 48223) -AccountTest:test_Withdraw_ETH_OnlyOwner() (gas: 80323) -AccountTest:test_Withdraw_Margin_OnlyOwner() (gas: 80388) +AccountTest:test_Withdraw_ETH_OnlyOwner() (gas: 85553) +AccountTest:test_Withdraw_Margin_OnlyOwner() (gas: 85574) AuthTest:test_addDelegate() (gas: 33557) AuthTest:test_isAuth() (gas: 13625) AuthTest:test_isOwner() (gas: 11275) @@ -85,8 +88,8 @@ FactoryTest:test_NewAccount_State() (gas: 323401) FactoryTest:test_Ownership_NonAccount() (gas: 10980) FactoryTest:test_Ownership_Transfer() (gas: 13152) FactoryTest:test_UpdateAccountOwnership_AccountDoesNotExist() (gas: 11402) -FactoryTest:test_UpdateAccountOwnership_NewOwner_MultipleAccount(uint256) (runs: 256, μ: 511398, ~: 347341) -FactoryTest:test_UpdateAccountOwnership_OldOwner_MultipleAccount(uint256) (runs: 256, μ: 803181, ~: 659300) +FactoryTest:test_UpdateAccountOwnership_NewOwner_MultipleAccount(uint256) (runs: 256, μ: 508982, ~: 347341) +FactoryTest:test_UpdateAccountOwnership_OldOwner_MultipleAccount(uint256) (runs: 256, μ: 804322, ~: 659300) FactoryTest:test_UpdateAccountOwnership_OldOwner_SingleAccount() (gas: 344143) FactoryTest:test_Upgrade_Implementation() (gas: 1034722) FactoryTest:test_Upgrade_Implementation_Event() (gas: 13041) @@ -94,68 +97,75 @@ FactoryTest:test_Upgrade_Implementation_OnlyOwner() (gas: 10957) FactoryTest:test_Upgrade_Implementation_UpgradabilityRemoved() (gas: 14816) FactoryTest:test_Upgrade_Remove() (gas: 11452) FactoryTest:test_Upgrade_Remove_OnlyOwner() (gas: 10761) -MarginBehaviorTest:test_Commands_CancelDelayedOrder() (gas: 1508559) -MarginBehaviorTest:test_Commands_CancelDelayedOrder_NoneExists() (gas: 276134) -MarginBehaviorTest:test_Commands_CancelOffchainDelayedOrder() (gas: 1518158) -MarginBehaviorTest:test_Commands_CancelOffchainDelayedOrder_NoneExists() (gas: 276201) -MarginBehaviorTest:test_Commands_ClosePosition() (gas: 1639761) -MarginBehaviorTest:test_Commands_ClosePositionWhen_NoneExists() (gas: 349933) -MarginBehaviorTest:test_Commands_ModifyMarginInMarket_ExistingMarginInMarket(int256) (runs: 256, μ: 748566, ~: 696205) -MarginBehaviorTest:test_Commands_ModifyMarginInMarket_NoExistingMarginInMarket(int256) (runs: 256, μ: 309616, ~: 227622) -MarginBehaviorTest:test_Commands_SubmitAtomicOrder() (gas: 1256284) -MarginBehaviorTest:test_Commands_SubmitCloseDelayedOrder() (gas: 2035880) -MarginBehaviorTest:test_Commands_SubmitCloseDelayedOrder_NoneExists() (gas: 93238) -MarginBehaviorTest:test_Commands_SubmitCloseOffchainDelayedOrder() (gas: 2025708) -MarginBehaviorTest:test_Commands_SubmitCloseOffchainDelayedOrder_NoneExists() (gas: 93058) -MarginBehaviorTest:test_Commands_SubmitDelayedOrder() (gas: 1454036) -MarginBehaviorTest:test_Commands_SubmitOffchainDelayedOrder() (gas: 1443779) -MarginBehaviorTest:test_Commands_WithdrawAllMarginFromMarket_ExistingMarginInMarket() (gas: 871774) -MarginBehaviorTest:test_Commands_WithdrawAllMarginFromMarket_NoExistingMarginInMarket() (gas: 480671) +MarginBehaviorTest:test_Commands_CancelDelayedOrder() (gas: 1519820) +MarginBehaviorTest:test_Commands_CancelDelayedOrder_NoneExists() (gas: 282094) +MarginBehaviorTest:test_Commands_CancelOffchainDelayedOrder() (gas: 1529419) +MarginBehaviorTest:test_Commands_CancelOffchainDelayedOrder_NoneExists() (gas: 282161) +MarginBehaviorTest:test_Commands_ClosePosition() (gas: 1649251) +MarginBehaviorTest:test_Commands_ClosePositionWhen_NoneExists() (gas: 355893) +MarginBehaviorTest:test_Commands_ModifyMarginInMarket_ExistingMarginInMarket(int256) (runs: 256, μ: 758056, ~: 705695) +MarginBehaviorTest:test_Commands_ModifyMarginInMarket_NoExistingMarginInMarket(int256) (runs: 256, μ: 318555, ~: 233582) +MarginBehaviorTest:test_Commands_SubmitAtomicOrder() (gas: 1265044) +MarginBehaviorTest:test_Commands_SubmitCloseDelayedOrder() (gas: 2050619) +MarginBehaviorTest:test_Commands_SubmitCloseDelayedOrder_NoneExists() (gas: 98468) +MarginBehaviorTest:test_Commands_SubmitCloseOffchainDelayedOrder() (gas: 2040447) +MarginBehaviorTest:test_Commands_SubmitCloseOffchainDelayedOrder_NoneExists() (gas: 98288) +MarginBehaviorTest:test_Commands_SubmitDelayedOrder() (gas: 1468045) +MarginBehaviorTest:test_Commands_SubmitOffchainDelayedOrder() (gas: 1457788) +MarginBehaviorTest:test_Commands_WithdrawAllMarginFromMarket_ExistingMarginInMarket() (gas: 881264) +MarginBehaviorTest:test_Commands_WithdrawAllMarginFromMarket_NoExistingMarginInMarket() (gas: 489431) MarginBehaviorTest:test_Deposit_ETH() (gas: 22833) -MarginBehaviorTest:test_Deposit_Margin(int256) (runs: 256, μ: 229356, ~: 251931) -MarginBehaviorTest:test_Execute_InputCommandDifferingLengths() (gas: 209189) -MarginBehaviorTest:test_Scenario_1() (gas: 1464093) -MarginBehaviorTest:test_Withdraw_Eth(uint256) (runs: 256, μ: 33449, ~: 38896) -MarginBehaviorTest:test_Withdraw_Margin(int256) (runs: 256, μ: 251052, ~: 226740) -OrderBehaviorTest:test_CancelConditionalOrder_Event() (gas: 309080) -OrderBehaviorTest:test_CancelConditionalOrder_Invalid_NotOwner() (gas: 26264) -OrderBehaviorTest:test_CancelConditionalOrder_Margin() (gas: 311404) -OrderBehaviorTest:test_CancelConditionalOrder_Nonexistent(uint256) (runs: 256, μ: 82991, ~: 82991) -OrderBehaviorTest:test_CancelConditionalOrder_State() (gas: 308989) -OrderBehaviorTest:test_ConditionalOrder_Limit_Valid_Execute_Cancel() (gas: 1588517) -OrderBehaviorTest:test_ExecuteConditionalOrder_InvalidAtExecutionTime() (gas: 648685) -OrderBehaviorTest:test_ExecuteConditionalOrder_Invalid_NotOps() (gas: 19637) -OrderBehaviorTest:test_ExecuteConditionalOrder_Limit_Valid_Margin() (gas: 1490235) -OrderBehaviorTest:test_ExecuteConditionalOrder_Limit_Valid_State() (gas: 1487323) -OrderBehaviorTest:test_ExecuteConditionalOrder_Limit_Valid_Synthetix() (gas: 1502462) -OrderBehaviorTest:test_ExecuteConditionalOrder_MarketIsPaused() (gas: 385272) -OrderBehaviorTest:test_ExecuteConditionalOrder_Stop_Valid_Margin() (gas: 1490184) -OrderBehaviorTest:test_ExecuteConditionalOrder_Stop_Valid_State() (gas: 1487315) -OrderBehaviorTest:test_ExecuteConditionalOrder_Stop_Valid_Synthetix() (gas: 1502409) -OrderBehaviorTest:test_ExecuteConditionalOrder_Valid_FeeTransfer() (gas: 1482954) -OrderBehaviorTest:test_ExecuteConditionalOrder_Valid_GelatoFee() (gas: 1485315) -OrderBehaviorTest:test_ExecuteConditionalOrder_Valid_InsufficientEth() (gas: 1730267) -OrderBehaviorTest:test_ExecuteConditionalOrder_Valid_TaskCancelled() (gas: 1493287) -OrderBehaviorTest:test_ExecuteConditionalOrder_Valid_TaskRemovedFromGelato() (gas: 1494006) -OrderBehaviorTest:test_PlaceConditionalOrder_CommittingMargin_Deposit() (gas: 357825) -OrderBehaviorTest:test_PlaceConditionalOrder_CommittingMargin_Withdraw(uint256,int256) (runs: 256, μ: 411344, ~: 374608) -OrderBehaviorTest:test_PlaceConditionalOrder_Invalid_InsufficientFreeMargin() (gas: 370742) -OrderBehaviorTest:test_PlaceConditionalOrder_Invalid_InsufficientMargin() (gas: 359212) -OrderBehaviorTest:test_PlaceConditionalOrder_Invalid_NotOwner() (gas: 27010) -OrderBehaviorTest:test_PlaceConditionalOrder_Invalid_OrderType() (gas: 8534) -OrderBehaviorTest:test_PlaceConditionalOrder_Invalid_ZeroSizeDelta() (gas: 24905) -OrderBehaviorTest:test_PlaceConditionalOrder_Limit_Invalid_Long(uint256) (runs: 256, μ: 470725, ~: 472358) -OrderBehaviorTest:test_PlaceConditionalOrder_Limit_Invalid_Short(uint256) (runs: 256, μ: 472351, ~: 472351) -OrderBehaviorTest:test_PlaceConditionalOrder_Limit_Valid_Event(int256) (runs: 256, μ: 362352, ~: 362352) -OrderBehaviorTest:test_PlaceConditionalOrder_Limit_Valid_Long(uint256) (runs: 256, μ: 472330, ~: 472330) -OrderBehaviorTest:test_PlaceConditionalOrder_Limit_Valid_Short(uint256) (runs: 256, μ: 470691, ~: 472324) -OrderBehaviorTest:test_PlaceConditionalOrder_Limit_Valid_State(int256) (runs: 256, μ: 366049, ~: 366049) -OrderBehaviorTest:test_PlaceConditionalOrder_Stop_Invalid_Long(uint256) (runs: 256, μ: 492296, ~: 492296) -OrderBehaviorTest:test_PlaceConditionalOrder_Stop_Invalid_Short(uint256) (runs: 256, μ: 490676, ~: 492309) -OrderBehaviorTest:test_PlaceConditionalOrder_Stop_Valid_Long(uint256) (runs: 256, μ: 490611, ~: 492244) -OrderBehaviorTest:test_PlaceConditionalOrder_Stop_Valid_Short(uint256) (runs: 256, μ: 492302, ~: 492302) -OrderBehaviorTest:test_PlaceConditionalOrder_Valid_GelatoTaskId() (gas: 366671) -OrderBehaviorTest:test_ReduceOnlyOrder_Valid_Long(int256) (runs: 256, μ: 1632195, ~: 1362516) -OrderBehaviorTest:test_ReduceOnlyOrder_Valid_Short(int256) (runs: 256, μ: 1914980, ~: 2182365) -OrderBehaviorTest:test_ReduceOnlyOrder_Valid_State() (gas: 2206119) -OrderBehaviorTest:test_ReduceOnlyOrder_Valid_Synthetix() (gas: 2221231) \ No newline at end of file +MarginBehaviorTest:test_Deposit_Margin(int256) (runs: 256, μ: 232559, ~: 257161) +MarginBehaviorTest:test_Execute_InputCommandDifferingLengths() (gas: 215149) +MarginBehaviorTest:test_Scenario_1() (gas: 1477372) +MarginBehaviorTest:test_Withdraw_Eth(uint256) (runs: 256, μ: 38598, ~: 44126) +MarginBehaviorTest:test_Withdraw_Margin(int256) (runs: 256, μ: 256476, ~: 232700) +OrderBehaviorTest:test_CancelConditionalOrder_Event() (gas: 313836) +OrderBehaviorTest:test_CancelConditionalOrder_Invalid_NotOwner() (gas: 31486) +OrderBehaviorTest:test_CancelConditionalOrder_Margin() (gas: 316159) +OrderBehaviorTest:test_CancelConditionalOrder_Nonexistent(uint256) (runs: 256, μ: 88214, ~: 88214) +OrderBehaviorTest:test_CancelConditionalOrder_State() (gas: 313744) +OrderBehaviorTest:test_ConditionalOrder_Limit_Valid_Execute_Cancel() (gas: 1599754) +OrderBehaviorTest:test_ExecuteConditionalOrder_AfterUnlock() (gas: 1502906) +OrderBehaviorTest:test_ExecuteConditionalOrder_InvalidAtExecutionTime() (gas: 654635) +OrderBehaviorTest:test_ExecuteConditionalOrder_Invalid_NotOps() (gas: 24867) +OrderBehaviorTest:test_ExecuteConditionalOrder_Limit_Valid_Margin() (gas: 1504228) +OrderBehaviorTest:test_ExecuteConditionalOrder_Limit_Valid_State() (gas: 1501316) +OrderBehaviorTest:test_ExecuteConditionalOrder_Limit_Valid_Synthetix() (gas: 1516455) +OrderBehaviorTest:test_ExecuteConditionalOrder_MarketIsPaused() (gas: 390500) +OrderBehaviorTest:test_ExecuteConditionalOrder_Stop_Valid_Margin() (gas: 1504177) +OrderBehaviorTest:test_ExecuteConditionalOrder_Stop_Valid_State() (gas: 1501308) +OrderBehaviorTest:test_ExecuteConditionalOrder_Stop_Valid_Synthetix() (gas: 1516402) +OrderBehaviorTest:test_ExecuteConditionalOrder_Valid_FeeTransfer() (gas: 1496948) +OrderBehaviorTest:test_ExecuteConditionalOrder_Valid_GelatoFee() (gas: 1499308) +OrderBehaviorTest:test_ExecuteConditionalOrder_Valid_InsufficientEth() (gas: 1744983) +OrderBehaviorTest:test_ExecuteConditionalOrder_Valid_TaskCancelled() (gas: 1507280) +OrderBehaviorTest:test_ExecuteConditionalOrder_Valid_TaskRemovedFromGelato() (gas: 1507992) +OrderBehaviorTest:test_PlaceConditionalOrder_CommittingMargin_Deposit() (gas: 363047) +OrderBehaviorTest:test_PlaceConditionalOrder_CommittingMargin_Withdraw(uint256,int256) (runs: 256, μ: 418524, ~: 380507) +OrderBehaviorTest:test_PlaceConditionalOrder_Invalid_InsufficientFreeMargin() (gas: 376708) +OrderBehaviorTest:test_PlaceConditionalOrder_Invalid_InsufficientMargin() (gas: 364434) +OrderBehaviorTest:test_PlaceConditionalOrder_Invalid_NotOwner() (gas: 32232) +OrderBehaviorTest:test_PlaceConditionalOrder_Invalid_OrderType() (gas: 8537) +OrderBehaviorTest:test_PlaceConditionalOrder_Invalid_ZeroSizeDelta() (gas: 30127) +OrderBehaviorTest:test_PlaceConditionalOrder_Limit_Invalid_Long(uint256) (runs: 256, μ: 475869, ~: 477580) +OrderBehaviorTest:test_PlaceConditionalOrder_Limit_Invalid_Short(uint256) (runs: 256, μ: 477573, ~: 477573) +OrderBehaviorTest:test_PlaceConditionalOrder_Limit_Valid_Event(int256) (runs: 256, μ: 367574, ~: 367574) +OrderBehaviorTest:test_PlaceConditionalOrder_Limit_Valid_Long(uint256) (runs: 256, μ: 477552, ~: 477552) +OrderBehaviorTest:test_PlaceConditionalOrder_Limit_Valid_Short(uint256) (runs: 256, μ: 475835, ~: 477546) +OrderBehaviorTest:test_PlaceConditionalOrder_Limit_Valid_State(int256) (runs: 256, μ: 371293, ~: 371293) +OrderBehaviorTest:test_PlaceConditionalOrder_Stop_Invalid_Long(uint256) (runs: 256, μ: 497518, ~: 497518) +OrderBehaviorTest:test_PlaceConditionalOrder_Stop_Invalid_Short(uint256) (runs: 256, μ: 495820, ~: 497531) +OrderBehaviorTest:test_PlaceConditionalOrder_Stop_Valid_Long(uint256) (runs: 256, μ: 495755, ~: 497466) +OrderBehaviorTest:test_PlaceConditionalOrder_Stop_Valid_Short(uint256) (runs: 256, μ: 497524, ~: 497524) +OrderBehaviorTest:test_PlaceConditionalOrder_Valid_GelatoTaskId() (gas: 371885) +OrderBehaviorTest:test_ReduceOnlyOrder_Valid_Long(int256) (runs: 256, μ: 1639951, ~: 1371982) +OrderBehaviorTest:test_ReduceOnlyOrder_Valid_Short(int256) (runs: 256, μ: 1938114, ~: 2197162) +OrderBehaviorTest:test_ReduceOnlyOrder_Valid_State() (gas: 2220835) +OrderBehaviorTest:test_ReduceOnlyOrder_Valid_Synthetix() (gas: 2235947) +SettingsTest:test_Constructor_Owner() (gas: 7604) +SettingsTest:test_setAccountExecutionEnabled() (gas: 13352) +SettingsTest:test_setAccountExecutionEnabled_Event() (gas: 15640) +SettingsTest:test_setAccountExecutionEnabled_OnlyOwner() (gas: 10851) +UpgradeBehaviorTest:test_Deployed_Account_Version() (gas: 3330) +UpgradeBehaviorTest:test_Upgrade_Implementation() (gas: 942866) \ No newline at end of file From 438caef3810a73d537060c7867a9d1c66ca44748 Mon Sep 17 00:00:00 2001 From: JaredBorders Date: Thu, 27 Apr 2023 10:35:32 +0300 Subject: [PATCH 16/16] =?UTF-8?q?=E2=9C=85=20Update=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/integration/order.behavior.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/order.behavior.t.sol b/test/integration/order.behavior.t.sol index e9579b86..24cfb54e 100644 --- a/test/integration/order.behavior.t.sol +++ b/test/integration/order.behavior.t.sol @@ -79,7 +79,7 @@ contract OrderBehaviorTest is Test, ConsolidatedEvents { address(systemStatus), GELATO, OPS, - address(0) + address(settings) ); // deploy an Account contract and fund it