diff --git a/.gas-snapshot b/.gas-snapshot index 881bab8a..2a22fe7a 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,196 +1,203 @@ AccountTest:test_Abs(int256) (runs: 256, μ: 5754, ~: 5755) -AccountTest:test_AddDelegatedTrader() (gas: 44916) -AccountTest:test_AddDelegatedTrader_AlreadyDelegated() (gas: 48399) -AccountTest:test_AddDelegatedTrader_Event() (gas: 46688) -AccountTest:test_AddDelegatedTrader_OnlyOwner() (gas: 78145) -AccountTest:test_AddDelegatedTrader_ZeroAddress() (gas: 21753) +AccountTest:test_AddDelegatedTrader() (gas: 51289) +AccountTest:test_AddDelegatedTrader_AlreadyDelegated() (gas: 54790) +AccountTest:test_AddDelegatedTrader_Event() (gas: 53039) +AccountTest:test_AddDelegatedTrader_OnlyOwner() (gas: 82496) +AccountTest:test_AddDelegatedTrader_ZeroAddress() (gas: 21771) AccountTest:test_Checker() (gas: 34868) -AccountTest:test_DelegatedTrader_Execute_ACCOUNT_MODIFY_MARGIN() (gas: 77970) -AccountTest:test_DelegatedTrader_Execute_ACCOUNT_WITHDRAW_ETH() (gas: 77969) -AccountTest:test_DelegatedTrader_Execute_GELATO_CANCEL_CONDITIONAL_ORDER() (gas: 137400) -AccountTest:test_DelegatedTrader_Execute_GELATO_PLACE_CONDITIONAL_ORDER() (gas: 79300) -AccountTest:test_DelegatedTrader_Execute_PERPS_V2_CANCEL_DELAYED_ORDER() (gas: 151504) -AccountTest:test_DelegatedTrader_Execute_PERPS_V2_CANCEL_OFFCHAIN_DELAYED_ORDER() (gas: 151594) -AccountTest:test_DelegatedTrader_Execute_PERPS_V2_CLOSE_POSITION() (gas: 169170) -AccountTest:test_DelegatedTrader_Execute_PERPS_V2_MODIFY_MARGIN() (gas: 116305) -AccountTest:test_DelegatedTrader_Execute_PERPS_V2_SET_MIN_KEEPER_FEE() (gas: 272103) -AccountTest:test_DelegatedTrader_Execute_PERPS_V2_SUBMIT_ATOMIC_ORDER() (gas: 379053) -AccountTest:test_DelegatedTrader_Execute_PERPS_V2_SUBMIT_CLOSE_DELAYED_ORDER() (gas: 147791) -AccountTest:test_DelegatedTrader_Execute_PERPS_V2_SUBMIT_CLOSE_OFFCHAIN_DELAYED_ORDER() (gas: 147569) -AccountTest:test_DelegatedTrader_Execute_PERPS_V2_SUBMIT_DELAYED_ORDER() (gas: 389990) -AccountTest:test_DelegatedTrader_Execute_PERPS_V2_SUBMIT_OFFCHAIN_DELAYED_ORDER() (gas: 399645) -AccountTest:test_DelegatedTrader_Execute_PERPS_V2_WITHDRAW_ALL_MARGIN() (gas: 366045) -AccountTest:test_DelegatedTrader_Execute_UNISWAP_V3_SWAP() (gas: 77697) -AccountTest:test_DelegatedTrader_TransferAccountOwnership() (gas: 48330) -AccountTest:test_Deposit_ETH_AnyCaller() (gas: 81013) -AccountTest:test_Deposit_Margin_OnlyOwner() (gas: 107745) +AccountTest:test_DelegatedTrader_Execute_ACCOUNT_MODIFY_MARGIN() (gas: 84321) +AccountTest:test_DelegatedTrader_Execute_ACCOUNT_WITHDRAW_ETH() (gas: 84320) +AccountTest:test_DelegatedTrader_Execute_GELATO_CANCEL_CONDITIONAL_ORDER() (gas: 143751) +AccountTest:test_DelegatedTrader_Execute_GELATO_PLACE_CONDITIONAL_ORDER() (gas: 85607) +AccountTest:test_DelegatedTrader_Execute_PERPS_V2_CANCEL_DELAYED_ORDER() (gas: 157855) +AccountTest:test_DelegatedTrader_Execute_PERPS_V2_CANCEL_OFFCHAIN_DELAYED_ORDER() (gas: 157901) +AccountTest:test_DelegatedTrader_Execute_PERPS_V2_CLOSE_POSITION() (gas: 175543) +AccountTest:test_DelegatedTrader_Execute_PERPS_V2_MODIFY_MARGIN() (gas: 122678) +AccountTest:test_DelegatedTrader_Execute_PERPS_V2_SET_MIN_KEEPER_FEE() (gas: 278454) +AccountTest:test_DelegatedTrader_Execute_PERPS_V2_SUBMIT_ATOMIC_ORDER() (gas: 385404) +AccountTest:test_DelegatedTrader_Execute_PERPS_V2_SUBMIT_CLOSE_DELAYED_ORDER() (gas: 154101) +AccountTest:test_DelegatedTrader_Execute_PERPS_V2_SUBMIT_CLOSE_OFFCHAIN_DELAYED_ORDER() (gas: 153942) +AccountTest:test_DelegatedTrader_Execute_PERPS_V2_SUBMIT_DELAYED_ORDER() (gas: 396341) +AccountTest:test_DelegatedTrader_Execute_PERPS_V2_SUBMIT_OFFCHAIN_DELAYED_ORDER() (gas: 405996) +AccountTest:test_DelegatedTrader_Execute_PERPS_V2_WITHDRAW_ALL_MARGIN() (gas: 372418) +AccountTest:test_DelegatedTrader_Execute_UNISWAP_V3_SWAP() (gas: 84048) +AccountTest:test_DelegatedTrader_TransferAccountOwnership() (gas: 54681) +AccountTest:test_Deposit_ETH_AnyCaller() (gas: 85324) +AccountTest:test_Deposit_Margin_OnlyOwner() (gas: 112056) AccountTest:test_Dispatch_InvalidCommand() (gas: 20279) AccountTest:test_Dispatch_ValidCommand_InvalidInput() (gas: 53633) AccountTest:test_ExecuteConditionalOrder_Locked() (gas: 54074) -AccountTest:test_Execute_CanUnlock() (gas: 50995) +AccountTest:test_Execute_CanUnlock() (gas: 50950) AccountTest:test_Execute_Locked() (gas: 54603) AccountTest:test_GetCommittedMargin() (gas: 18186) -AccountTest:test_GetConditionalOrder() (gas: 34820) -AccountTest:test_GetConditionalOrderId() (gas: 18164) +AccountTest:test_GetConditionalOrder() (gas: 34797) +AccountTest:test_GetConditionalOrderId() (gas: 18186) AccountTest:test_GetDelayedOrder_EthMarket() (gas: 74378) -AccountTest:test_GetDelayedOrder_InvalidMarket() (gas: 26635) +AccountTest:test_GetDelayedOrder_InvalidMarket() (gas: 26592) AccountTest:test_GetEvents() (gas: 7658) AccountTest:test_GetFactory() (gas: 7703) AccountTest:test_GetFreeMargin() (gas: 34453) AccountTest:test_GetFuturesMarketManager() (gas: 5574) -AccountTest:test_GetGelato() (gas: 5605) +AccountTest:test_GetGelato() (gas: 5627) AccountTest:test_GetMarginAsset() (gas: 5606) AccountTest:test_GetOps() (gas: 5584) -AccountTest:test_GetPosition_EthMarket() (gas: 62247) +AccountTest:test_GetPosition_EthMarket() (gas: 62269) AccountTest:test_GetPosition_InvalidMarket() (gas: 26315) -AccountTest:test_GetSystemStatus() (gas: 5584) +AccountTest:test_GetSystemStatus() (gas: 5606) AccountTest:test_GetTokenInTokenOut_Invalid_Fee() (gas: 10261) AccountTest:test_GetTokenInTokenOut_Invalid_Pools_No_Revert(bytes) (runs: 256, μ: 11371, ~: 11131) -AccountTest:test_GetTokenInTokenOut_Invalid_TokenIn() (gas: 10182) +AccountTest:test_GetTokenInTokenOut_Invalid_TokenIn() (gas: 10204) AccountTest:test_GetTokenInTokenOut_Invalid_TokenOut() (gas: 10235) AccountTest:test_GetTokenInTokenOut_Multi_Pool() (gas: 11453) AccountTest:test_GetTokenInTokenOut_Single_Pool() (gas: 7468) -AccountTest:test_GetTrackingCode() (gas: 5554) +AccountTest:test_GetTrackingCode() (gas: 5512) AccountTest:test_GetVerison() (gas: 16130) -AccountTest:test_IsSameSign(int256,int256) (runs: 256, μ: 6232, ~: 6081) +AccountTest:test_IsSameSign(int256,int256) (runs: 256, μ: 6231, ~: 6081) AccountTest:test_NonReentrant_Locked() (gas: 31079) AccountTest:test_NonReentrant_Unlocked() (gas: 460695) -AccountTest:test_Ownership_Transfer() (gas: 96233) -AccountTest:test_Ownership_Transfer_Event() (gas: 76810) -AccountTest:test_Ownership_setInitialOwnership_OnlyFactory() (gas: 19402) -AccountTest:test_PerpsV2ExchangeRate() (gas: 74513) -AccountTest:test_RemoveDelegatedTrader() (gas: 35396) -AccountTest:test_RemoveDelegatedTrader_Event() (gas: 36776) -AccountTest:test_RemoveDelegatedTrader_NotDelegated() (gas: 23908) -AccountTest:test_RemoveDelegatedTrader_OnlyOwner() (gas: 104584) -AccountTest:test_RemoveDelegatedTrader_ZeroAddress() (gas: 48201) -AccountTest:test_Withdraw_ETH_OnlyOwner() (gas: 107713) -AccountTest:test_Withdraw_Margin_OnlyOwner() (gas: 107799) +AccountTest:test_Ownership_Transfer() (gas: 100578) +AccountTest:test_Ownership_Transfer_Event() (gas: 81121) +AccountTest:test_Ownership_Transfer_Twice() (gas: 123115) +AccountTest:test_Ownership_setInitialOwnership_OnlyFactory() (gas: 19424) +AccountTest:test_PerpsV2ExchangeRate() (gas: 74468) +AccountTest:test_RemoveDelegatedTrader() (gas: 41967) +AccountTest:test_RemoveDelegatedTrader_Event() (gas: 43364) +AccountTest:test_RemoveDelegatedTrader_NotDelegated() (gas: 23937) +AccountTest:test_RemoveDelegatedTrader_OnlyOwner() (gas: 110786) +AccountTest:test_RemoveDelegatedTrader_ZeroAddress() (gas: 54603) +AccountTest:test_Withdraw_ETH_OnlyOwner() (gas: 112046) +AccountTest:test_Withdraw_Margin_OnlyOwner() (gas: 112132) AccountTest:test_getPerpsV2Market_Invalid_Key() (gas: 14269) AccountTest:test_getPerpsV2Market_Valid_Key() (gas: 11480) -AccountTest:test_sUSDRate_Chainlink_Price() (gas: 137167) +AccountTest:test_sUSDRate_Chainlink_Price() (gas: 137189) AccountTest:test_sUSDRate_Invalid_Chainlink_Price() (gas: 54955) -AccountTest:test_sUSDRate_Invalid_Market() (gas: 11365) -AccountTest:test_sUSDRate_Pyth_Price() (gas: 52801) +AccountTest:test_sUSDRate_Invalid_Market() (gas: 11342) +AccountTest:test_sUSDRate_Pyth_Price() (gas: 52757) AccountTest:test_sUSDRate_Valid_Market() (gas: 52828) -AuthTest:test_addDelegate() (gas: 33557) +AuthTest:test_addDelegate() (gas: 32035) AuthTest:test_isAuth() (gas: 13625) AuthTest:test_isOwner() (gas: 11275) -AuthTest:test_removeDelegate() (gas: 24160) -AuthTest:test_transferOwnership() (gas: 13950) -EventsTest:test_Constructor_FactorySet() (gas: 7619) -EventsTest:test_EmitConditionalOrderCancelled_Event() (gas: 21231) -EventsTest:test_EmitConditionalOrderCancelled_OnlyAccounts() (gas: 14427) +AuthTest:test_removeDelegate() (gas: 21724) +AuthTest:test_transferOwnership() (gas: 12431) +EventsTest:test_Constructor_FactorySet() (gas: 7686) +EventsTest:test_EmitConditionalOrderCancelled_Event() (gas: 21297) +EventsTest:test_EmitConditionalOrderCancelled_OnlyAccounts() (gas: 14494) EventsTest:test_EmitConditionalOrderFilled_Event() (gas: 22518) -EventsTest:test_EmitConditionalOrderFilled_OnlyAccounts() (gas: 14579) -EventsTest:test_EmitConditionalOrderPlaced_Event() (gas: 24959) -EventsTest:test_EmitConditionalOrderPlaced_OnlyAccounts() (gas: 14733) -EventsTest:test_EmitDeposit_Event() (gas: 20141) -EventsTest:test_EmitDeposit_OnlyAccounts() (gas: 14269) -EventsTest:test_EmitEthWithdraw_Event() (gas: 20110) -EventsTest:test_EmitEthWithdraw_OnlyAccounts() (gas: 14236) +EventsTest:test_EmitConditionalOrderFilled_OnlyAccounts() (gas: 14601) +EventsTest:test_EmitConditionalOrderPlaced_Event() (gas: 25004) +EventsTest:test_EmitConditionalOrderPlaced_OnlyAccounts() (gas: 14756) +EventsTest:test_EmitDelegatedAccountAdded_Event() (gas: 19564) +EventsTest:test_EmitDelegatedAccountAdded_OnlyAccounts() (gas: 14338) +EventsTest:test_EmitDelegatedAccountRemoved_Event() (gas: 19532) +EventsTest:test_EmitDelegatedAccountRemoved_OnlyAccounts() (gas: 14338) +EventsTest:test_EmitDeposit_Event() (gas: 20142) +EventsTest:test_EmitDeposit_OnlyAccounts() (gas: 14314) +EventsTest:test_EmitEthWithdraw_Event() (gas: 20166) +EventsTest:test_EmitEthWithdraw_OnlyAccounts() (gas: 14292) +EventsTest:test_EmitOwnershipTransferred_Event() (gas: 19499) +EventsTest:test_EmitOwnershipTransferred_OnlyAccounts() (gas: 14325) EventsTest:test_EmitUniswapV3Swap_Event() (gas: 20986) -EventsTest:test_EmitUniswapV3Swap_OnlyAccounts() (gas: 14518) -EventsTest:test_EmitWithdraw_Event() (gas: 20119) -EventsTest:test_EmitWithdraw_OnlyAccounts() (gas: 14289) -FactoryBehaviorTest:test_Account_OwnerSet() (gas: 324700) +EventsTest:test_EmitUniswapV3Swap_OnlyAccounts() (gas: 14540) +EventsTest:test_EmitWithdraw_Event() (gas: 20186) +EventsTest:test_EmitWithdraw_OnlyAccounts() (gas: 14334) +FactoryBehaviorTest:test_Account_OwnerSet() (gas: 329023) FactoryBehaviorTest:test_Implementation_Owner() (gas: 7680) FactoryTest:test_Constructor_Accounts(address) (runs: 256, μ: 7968, ~: 7968) FactoryTest:test_Constructor_CanUpgrade() (gas: 7682) FactoryTest:test_Constructor_Implementation() (gas: 9773) FactoryTest:test_Constructor_Owner() (gas: 7667) -FactoryTest:test_NewAccount_Address() (gas: 322337) +FactoryTest:test_NewAccount_Address() (gas: 326660) FactoryTest:test_NewAccount_CannotBeInitialized() (gas: 347037) FactoryTest:test_NewAccount_CannotFetchVersion() (gas: 371612) -FactoryTest:test_NewAccount_Event() (gas: 327009) -FactoryTest:test_NewAccount_MultiplePerAddress() (gas: 623354) -FactoryTest:test_NewAccount_State() (gas: 323401) +FactoryTest:test_NewAccount_Event() (gas: 331332) +FactoryTest:test_NewAccount_MultiplePerAddress() (gas: 629500) +FactoryTest:test_NewAccount_State() (gas: 327724) 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, μ: 492146, ~: 347341) -FactoryTest:test_UpdateAccountOwnership_OldOwner_MultipleAccount(uint256) (runs: 256, μ: 795114, ~: 659300) -FactoryTest:test_UpdateAccountOwnership_OldOwner_SingleAccount() (gas: 344143) -FactoryTest:test_Upgrade_Implementation() (gas: 1034722) +FactoryTest:test_UpdateAccountOwnership_NewOwner_MultipleAccount(uint256) (runs: 256, μ: 505465, ~: 353475) +FactoryTest:test_UpdateAccountOwnership_OldOwner_MultipleAccount(uint256) (runs: 256, μ: 812070, ~: 667257) +FactoryTest:test_UpdateAccountOwnership_OldOwner_SingleAccount() (gas: 350277) +FactoryTest:test_Upgrade_Implementation() (gas: 1040856) FactoryTest:test_Upgrade_Implementation_Event() (gas: 13041) 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: 1516118) -MarginBehaviorTest:test_Commands_CancelDelayedOrder_NoneExists() (gas: 291337) -MarginBehaviorTest:test_Commands_CancelOffchainDelayedOrder() (gas: 1525694) -MarginBehaviorTest:test_Commands_CancelOffchainDelayedOrder_NoneExists() (gas: 291382) -MarginBehaviorTest:test_Commands_ClosePosition() (gas: 1673609) -MarginBehaviorTest:test_Commands_ClosePositionWhen_NoneExists() (gas: 390785) -MarginBehaviorTest:test_Commands_ModifyMarginInMarket_ExistingMarginInMarket(int256) (runs: 256, μ: 777290, ~: 715482) -MarginBehaviorTest:test_Commands_ModifyMarginInMarket_NoExistingMarginInMarket(int256) (runs: 256, μ: 329037, ~: 242803) +MarginBehaviorTest:test_Commands_CancelDelayedOrder() (gas: 1516141) +MarginBehaviorTest:test_Commands_CancelDelayedOrder_NoneExists() (gas: 291360) +MarginBehaviorTest:test_Commands_CancelOffchainDelayedOrder() (gas: 1525717) +MarginBehaviorTest:test_Commands_CancelOffchainDelayedOrder_NoneExists() (gas: 291405) +MarginBehaviorTest:test_Commands_ClosePosition() (gas: 1673632) +MarginBehaviorTest:test_Commands_ClosePositionWhen_NoneExists() (gas: 390808) +MarginBehaviorTest:test_Commands_ModifyMarginInMarket_ExistingMarginInMarket(int256) (runs: 256, μ: 777313, ~: 715505) +MarginBehaviorTest:test_Commands_ModifyMarginInMarket_NoExistingMarginInMarket(int256) (runs: 256, μ: 326522, ~: 242826) MarginBehaviorTest:test_Commands_SetMinKeeperFee() (gas: 242574) MarginBehaviorTest:test_Commands_SetMinKeeperFee_Fails() (gas: 52897) -MarginBehaviorTest:test_Commands_SubmitAtomicOrder() (gas: 1303439) -MarginBehaviorTest:test_Commands_SubmitCloseDelayedOrder() (gas: 2046385) +MarginBehaviorTest:test_Commands_SubmitAtomicOrder() (gas: 1303462) +MarginBehaviorTest:test_Commands_SubmitCloseDelayedOrder() (gas: 2046408) MarginBehaviorTest:test_Commands_SubmitCloseDelayedOrder_NoneExists() (gas: 120693) -MarginBehaviorTest:test_Commands_SubmitCloseOffchainDelayedOrder() (gas: 2036294) +MarginBehaviorTest:test_Commands_SubmitCloseOffchainDelayedOrder() (gas: 2036317) MarginBehaviorTest:test_Commands_SubmitCloseOffchainDelayedOrder_NoneExists() (gas: 120448) -MarginBehaviorTest:test_Commands_SubmitDelayedOrder() (gas: 1478227) -MarginBehaviorTest:test_Commands_SubmitOffchainDelayedOrder() (gas: 1467970) -MarginBehaviorTest:test_Commands_WithdrawAllMarginFromMarket_ExistingMarginInMarket() (gas: 923020) -MarginBehaviorTest:test_Commands_WithdrawAllMarginFromMarket_NoExistingMarginInMarket() (gas: 501516) +MarginBehaviorTest:test_Commands_SubmitDelayedOrder() (gas: 1478250) +MarginBehaviorTest:test_Commands_SubmitOffchainDelayedOrder() (gas: 1467993) +MarginBehaviorTest:test_Commands_WithdrawAllMarginFromMarket_ExistingMarginInMarket() (gas: 923043) +MarginBehaviorTest:test_Commands_WithdrawAllMarginFromMarket_NoExistingMarginInMarket() (gas: 501539) MarginBehaviorTest:test_Deposit_ETH() (gas: 22856) -MarginBehaviorTest:test_Deposit_Margin(uint256) (runs: 256, μ: 229860, ~: 248181) -MarginBehaviorTest:test_Execute_InputCommandDifferingLengths() (gas: 224292) -MarginBehaviorTest:test_Scenario_1() (gas: 1487208) -MarginBehaviorTest:test_Withdraw_Eth(uint256) (runs: 256, μ: 60938, ~: 66431) -MarginBehaviorTest:test_Withdraw_Margin(int256) (runs: 256, μ: 266462, ~: 241968) -OrderGelatoBehaviorTest:test_CancelConditionalOrder_Event() (gas: 325505) +MarginBehaviorTest:test_Deposit_Margin(uint256) (runs: 256, μ: 230109, ~: 248204) +MarginBehaviorTest:test_Execute_InputCommandDifferingLengths() (gas: 224315) +MarginBehaviorTest:test_Scenario_1() (gas: 1487231) +MarginBehaviorTest:test_Withdraw_Eth(uint256) (runs: 256, μ: 60992, ~: 66454) +MarginBehaviorTest:test_Withdraw_Margin(int256) (runs: 256, μ: 266502, ~: 241991) +OrderGelatoBehaviorTest:test_CancelConditionalOrder_Event() (gas: 325524) OrderGelatoBehaviorTest:test_CancelConditionalOrder_Invalid_NotOwner() (gas: 36633) -OrderGelatoBehaviorTest:test_CancelConditionalOrder_Margin() (gas: 323844) +OrderGelatoBehaviorTest:test_CancelConditionalOrder_Margin() (gas: 323862) OrderGelatoBehaviorTest:test_CancelConditionalOrder_Nonexistent(uint256) (runs: 256, μ: 93339, ~: 93339) -OrderGelatoBehaviorTest:test_CancelConditionalOrder_State() (gas: 321444) -OrderGelatoBehaviorTest:test_ConditionalOrder_Limit_Valid_Execute_Cancel() (gas: 1612464) -OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_AfterUnlock() (gas: 1529590) -OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_InvalidAtExecutionTime() (gas: 489102) -OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_Limit_Valid_Margin() (gas: 1530797) -OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_Limit_Valid_State() (gas: 1527903) -OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_Limit_Valid_Synthetix() (gas: 1543050) -OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_MarketIsPaused() (gas: 415615) -OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_Stop_Valid_Margin() (gas: 1530804) -OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_Stop_Valid_State() (gas: 1527953) -OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_Stop_Valid_Synthetix() (gas: 1543055) -OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_Valid_FeeTransfer() (gas: 1523506) -OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_Valid_GelatoFee() (gas: 1525899) -OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_Valid_InsufficientEth() (gas: 555877) -OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_Valid_InvalidReduceOnly_InactiveMarket_FeeTransfer() (gas: 485354) -OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_Valid_InvalidReduceOnly_SameSign_FeeTransfer() (gas: 1448836) -OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_Valid_TaskCancelled() (gas: 1533867) -OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_Valid_TaskRemovedFromGelato() (gas: 1534572) -OrderGelatoBehaviorTest:test_PlaceConditionalOrder_CommittingMargin_Deposit() (gas: 366048) -OrderGelatoBehaviorTest:test_PlaceConditionalOrder_CommittingMargin_Withdraw(uint256,int256) (runs: 256, μ: 421975, ~: 386611) -OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Invalid_InsufficientFreeMargin() (gas: 382834) -OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Invalid_InsufficientMargin() (gas: 386614) +OrderGelatoBehaviorTest:test_CancelConditionalOrder_State() (gas: 321462) +OrderGelatoBehaviorTest:test_ConditionalOrder_Limit_Valid_Execute_Cancel() (gas: 1612487) +OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_AfterUnlock() (gas: 1529613) +OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_InvalidAtExecutionTime() (gas: 489114) +OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_Limit_Valid_Margin() (gas: 1530820) +OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_Limit_Valid_State() (gas: 1527926) +OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_Limit_Valid_Synthetix() (gas: 1543073) +OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_MarketIsPaused() (gas: 415638) +OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_Stop_Valid_Margin() (gas: 1530827) +OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_Stop_Valid_State() (gas: 1527976) +OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_Stop_Valid_Synthetix() (gas: 1543078) +OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_Valid_FeeTransfer() (gas: 1523529) +OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_Valid_GelatoFee() (gas: 1525922) +OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_Valid_InsufficientEth() (gas: 555923) +OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_Valid_InvalidReduceOnly_InactiveMarket_FeeTransfer() (gas: 485372) +OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_Valid_InvalidReduceOnly_SameSign_FeeTransfer() (gas: 1448859) +OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_Valid_TaskCancelled() (gas: 1533890) +OrderGelatoBehaviorTest:test_ExecuteConditionalOrder_Valid_TaskRemovedFromGelato() (gas: 1534595) +OrderGelatoBehaviorTest:test_PlaceConditionalOrder_CommittingMargin_Deposit() (gas: 366071) +OrderGelatoBehaviorTest:test_PlaceConditionalOrder_CommittingMargin_Withdraw(uint256,int256) (runs: 256, μ: 423262, ~: 386634) +OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Invalid_InsufficientFreeMargin() (gas: 382857) +OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Invalid_InsufficientMargin() (gas: 390937) OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Invalid_NotOwner() (gas: 37357) OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Invalid_OrderType() (gas: 8581) OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Invalid_ZeroSizeDelta() (gas: 35252) -OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Limit_Invalid_Long(uint256) (runs: 256, μ: 438304, ~: 440015) -OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Limit_Invalid_Short(uint256) (runs: 256, μ: 440008, ~: 440008) -OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Limit_Valid_Event(int256) (runs: 256, μ: 380208, ~: 380208) -OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Limit_Valid_Long(uint256) (runs: 256, μ: 439987, ~: 439987) -OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Limit_Valid_Short(uint256) (runs: 256, μ: 438348, ~: 439981) -OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Limit_Valid_State(int256) (runs: 256, μ: 374312, ~: 374312) -OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Stop_Invalid_Long(uint256) (runs: 256, μ: 459953, ~: 459953) -OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Stop_Invalid_Short(uint256) (runs: 256, μ: 458355, ~: 459988) -OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Stop_Valid_Long(uint256) (runs: 256, μ: 458212, ~: 459923) -OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Stop_Valid_Short(uint256) (runs: 256, μ: 459981, ~: 459981) -OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Valid_GelatoTaskId() (gas: 374904) -OrderGelatoBehaviorTest:test_ReduceOnlyOrder_Valid_Long(int256) (runs: 256, μ: 1677446, ~: 1412659) -OrderGelatoBehaviorTest:test_ReduceOnlyOrder_Valid_Short(int256) (runs: 256, μ: 1988908, ~: 2244992) -OrderGelatoBehaviorTest:test_ReduceOnlyOrder_Valid_State() (gas: 2271305) -OrderGelatoBehaviorTest:test_ReduceOnlyOrder_Valid_Synthetix() (gas: 2286447) +OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Limit_Invalid_Long(uint256) (runs: 256, μ: 438405, ~: 440038) +OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Limit_Invalid_Short(uint256) (runs: 256, μ: 440031, ~: 440031) +OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Limit_Valid_Event(int256) (runs: 256, μ: 380231, ~: 380231) +OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Limit_Valid_Long(uint256) (runs: 256, μ: 440010, ~: 440010) +OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Limit_Valid_Short(uint256) (runs: 256, μ: 438293, ~: 440004) +OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Limit_Valid_State(int256) (runs: 256, μ: 374335, ~: 374335) +OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Stop_Invalid_Long(uint256) (runs: 256, μ: 459976, ~: 459976) +OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Stop_Invalid_Short(uint256) (runs: 256, μ: 458300, ~: 460011) +OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Stop_Valid_Long(uint256) (runs: 256, μ: 458313, ~: 459946) +OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Stop_Valid_Short(uint256) (runs: 256, μ: 460004, ~: 460004) +OrderGelatoBehaviorTest:test_PlaceConditionalOrder_Valid_GelatoTaskId() (gas: 374927) +OrderGelatoBehaviorTest:test_ReduceOnlyOrder_Valid_Long(int256) (runs: 256, μ: 1670962, ~: 1412682) +OrderGelatoBehaviorTest:test_ReduceOnlyOrder_Valid_Short(int256) (runs: 256, μ: 1982278, ~: 2245015) +OrderGelatoBehaviorTest:test_ReduceOnlyOrder_Valid_State() (gas: 2271328) +OrderGelatoBehaviorTest:test_ReduceOnlyOrder_Valid_Synthetix() (gas: 2286470) OrderPublicBehaviorTest:test_ExecuteConditionalOrder_Public() (gas: 1405271) -OrderPublicBehaviorTest:test_ExecuteConditionalOrder_Public_Cannot_PayExecutorFee() (gas: 233066) -OrderPublicBehaviorTest:test_ExecuteConditionalOrder_Public_Invalid_ConditionalOrder() (gas: 96675) +OrderPublicBehaviorTest:test_ExecuteConditionalOrder_Public_Cannot_PayExecutorFee() (gas: 233089) +OrderPublicBehaviorTest:test_ExecuteConditionalOrder_Public_Invalid_ConditionalOrder() (gas: 96664) OrderPublicBehaviorTest:test_ExecuteConditionalOrder_Public_PayExecutorFee() (gas: 1400926) -OrderPublicBehaviorTest:test_ExecuteConditionalOrder_Public_Replay_Prevented() (gas: 1409794) +OrderPublicBehaviorTest:test_ExecuteConditionalOrder_Public_Replay_Prevented() (gas: 1409783) PermitBehaviorTest:test_Permit() (gas: 100512) PermitBehaviorTest:test_Permit_UniswapV3Swap() (gas: 357327) PermitBehaviorTest:test_Permit_UniswapV3Swap_Cant_Replay() (gas: 111991) @@ -210,16 +217,18 @@ SwapBehaviorTest:test_UniswapV3Swap_DAI_SUSD_Event() (gas: 339404) SwapBehaviorTest:test_UniswapV3Swap_DAI_USDC_SUSD() (gas: 1545395) SwapBehaviorTest:test_UniswapV3Swap_Either_SUSD_In_Out() (gas: 82878) SwapBehaviorTest:test_UniswapV3Swap_Insufficient_Margin() (gas: 107157) -SwapBehaviorTest:test_UniswapV3Swap_Insufficient_TokenIn_Balance() (gas: 205088) +SwapBehaviorTest:test_UniswapV3Swap_Insufficient_TokenIn_Balance() (gas: 209399) SwapBehaviorTest:test_UniswapV3Swap_Invalid_AmountIn() (gas: 120037) SwapBehaviorTest:test_UniswapV3Swap_Invalid_AmountOutMin() (gas: 350743) -SwapBehaviorTest:test_UniswapV3Swap_Invalid_Path() (gas: 342000) +SwapBehaviorTest:test_UniswapV3Swap_Invalid_Path() (gas: 342023) SwapBehaviorTest:test_UniswapV3Swap_Only_Whitelisted_TokenIn() (gas: 61267) -SwapBehaviorTest:test_UniswapV3Swap_Only_Whitelisted_TokenOut() (gas: 235127) -SwapBehaviorTest:test_UniswapV3Swap_SUSD_DAI() (gas: 515632) -SwapBehaviorTest:test_UniswapV3Swap_SUSD_DAI_Event() (gas: 510651) -SwapBehaviorTest:test_UniswapV3Swap_SUSD_DAI_USDC() (gas: 603817) +SwapBehaviorTest:test_UniswapV3Swap_Only_Whitelisted_TokenOut() (gas: 235150) +SwapBehaviorTest:test_UniswapV3Swap_SUSD_DAI() (gas: 515655) +SwapBehaviorTest:test_UniswapV3Swap_SUSD_DAI_Event() (gas: 510674) +SwapBehaviorTest:test_UniswapV3Swap_SUSD_DAI_USDC() (gas: 603840) +UpgradeTest:test_Deployed_Account_Version() (gas: 16468) UpgradeTest:test_Deployed_Account_Version() (gas: 16468) UpgradeTest:test_Deployed_Account_Version() (gas: 16468) UpgradeTest:test_Upgrade() (gas: 86517) -UpgradeTest:test_Upgrade() (gas: 89278) \ No newline at end of file +UpgradeTest:test_Upgrade() (gas: 89186) +UpgradeTest:test_Upgrade() (gas: 89186) \ No newline at end of file diff --git a/deploy-addresses/optimism-goerli.json b/deploy-addresses/optimism-goerli.json index 6270b1a2..864479dc 100644 --- a/deploy-addresses/optimism-goerli.json +++ b/deploy-addresses/optimism-goerli.json @@ -1,6 +1,6 @@ { - "Account": "0x5f78a5EdF0B356F50886a6829Fc02B16532e8735", - "Events": "0xe32F27B27F4ea5f10f269b52223910bA83e2933C", + "Account": "0x62b26650A52E1B6a669C21a9d43aFD78AC0Ef84F", + "Events": "0x4533D9eAC2eb200B98fD9BaA306221ad2528e52F", "Factory": "0x30582eeE34719fe22b1B6c3b607636A3ab94522E", "Settings": "0x8B9CbD3da94c637c0652c680Abd3CF7f787aBAF4" } \ No newline at end of file diff --git a/deploy-addresses/optimism.json b/deploy-addresses/optimism.json index 898218e9..0edb9670 100644 --- a/deploy-addresses/optimism.json +++ b/deploy-addresses/optimism.json @@ -1,6 +1,6 @@ { - "Account": "0x6B86c1A6878940666489780871E1C98B366d0aFF", - "Events": "0xB753d2EE5dcA1fF39A83CA3Ec500656c31Be940b", + "Account": "0x9AcbDBABafC7E47dE0769C352b725Fa9fd443Bc2", + "Events": "0x349142D51afCFbB3E9ef70DB1F7dB8437aABC5F7", "Factory": "0x8234F990b149Ae59416dc260305E565e5DAfEb54", "Settings": "0x865dA103d126b3Be3599D84caB57109A861F5631" } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 4c2d5e98..3e606613 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@kwenta/cross-margin-manager", - "version": "2.1.1", + "version": "2.1.2", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 33ac1f11..c255e311 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kwenta/cross-margin-manager", - "version": "2.1.1", + "version": "2.1.2", "scripts": { "compile": "forge build", "test": "forge test --fork-url $(grep ARCHIVE_NODE_URL_L2 .env | cut -d '=' -f2) --etherscan-api-key $(grep ETHERSCAN_API_KEY .env | cut -d '=' -f2) --gas-report -vvv", diff --git a/script/upgrades/v2.1.2/Upgrade.s.sol b/script/upgrades/v2.1.2/Upgrade.s.sol new file mode 100644 index 00000000..9e68cb08 --- /dev/null +++ b/script/upgrades/v2.1.2/Upgrade.s.sol @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.18; + +import {Script} from "lib/forge-std/src/Script.sol"; + +import {IAddressResolver} from "script/utils/interfaces/IAddressResolver.sol"; + +import {Account} from "src/Account.sol"; +import {Events} from "src/Events.sol"; +import {Settings} from "src/Settings.sol"; +import {IAccount} from "src/interfaces/IAccount.sol"; + +import { + OPTIMISM_PDAO, + OPTIMISM_GELATO, + OPTIMISM_OPS, + FUTURES_MARKET_MANAGER, + OPTIMISM_FACTORY, + OPTIMISM_EVENTS, + OPTIMISM_SETTINGS, + OPTIMISM_SYNTHETIX_ADDRESS_RESOLVER, + OPTIMISM_UNISWAP_PERMIT2, + OPTIMISM_UNISWAP_UNIVERSAL_ROUTER, + PERPS_V2_EXCHANGE_RATE, + PROXY_SUSD, + SYSTEM_STATUS +} from "script/utils/parameters/OptimismParameters.sol"; +import { + OPTIMISM_GOERLI_DEPLOYER, + OPTIMISM_GOERLI_EVENTS, + OPTIMISM_GOERLI_SETTINGS, + OPTIMISM_GOERLI_FACTORY, + OPTIMISM_GOERLI_GELATO, + OPTIMISM_GOERLI_OPS, + OPTIMISM_GOERLI_SYNTHETIX_ADDRESS_RESOLVER, + OPTIMISM_GOERLI_UNISWAP_PERMIT2, + OPTIMISM_GOERLI_UNISWAP_UNIVERSAL_ROUTER +} from "script/utils/parameters/OptimismGoerliParameters.sol"; + +/// @title Script to upgrade the Account implementation v2.1.1 -> v2.1.2 +/// @author JaredBorders (jaredborders@pm.me) + +/// @dev steps to deploy and verify on Optimism: +/// (1) load the variables in the .env file via `source .env` +/// (2) run `forge script script/upgrades/v2.1.2/Upgrade.s.sol:UpgradeAccountOptimism --rpc-url $ARCHIVE_NODE_URL_L2 --broadcast --verify -vvvv` +/// (3) Smart Margin Account Factory owner (i.e. Kwenta pDAO) will need to call `upgradeAccountImplementation` on the Factory with the address of the new Account implementation +contract UpgradeAccountOptimism is Script { + function run() public { + uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); + vm.startBroadcast(deployerPrivateKey); + + upgrade(); + + vm.stopBroadcast(); + } + + function upgrade() public returns (address implementation) { + IAddressResolver addressResolver = + IAddressResolver(OPTIMISM_SYNTHETIX_ADDRESS_RESOLVER); + + // deploy events + address events = address(new Events({_factory: OPTIMISM_FACTORY})); + + address marginAsset = addressResolver.getAddress({name: PROXY_SUSD}); + address perpsV2ExchangeRate = + addressResolver.getAddress({name: PERPS_V2_EXCHANGE_RATE}); + address futuresMarketManager = + addressResolver.getAddress({name: FUTURES_MARKET_MANAGER}); + address systemStatus = addressResolver.getAddress({name: SYSTEM_STATUS}); + + IAccount.AccountConstructorParams memory params = IAccount + .AccountConstructorParams({ + factory: OPTIMISM_FACTORY, + events: events, + marginAsset: marginAsset, + perpsV2ExchangeRate: perpsV2ExchangeRate, + futuresMarketManager: futuresMarketManager, + systemStatus: systemStatus, + gelato: OPTIMISM_GELATO, + ops: OPTIMISM_OPS, + settings: OPTIMISM_SETTINGS, + universalRouter: OPTIMISM_UNISWAP_UNIVERSAL_ROUTER, + permit2: OPTIMISM_UNISWAP_PERMIT2 + }); + + implementation = address(new Account(params)); + } +} + +/// @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/upgrades/v2.1.2/Upgrade.s.sol:UpgradeAccountOptimismGoerli --rpc-url $ARCHIVE_NODE_URL_GOERLI_L2 --broadcast --verify -vvvv` +/// (3) Smart Margin Account Factory owner (i.e. Kwenta pDAO) will need to call `upgradeAccountImplementation` on the Factory with the address of the new Account implementation +contract UpgradeAccountOptimismGoerli is Script { + function run() public { + uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); + vm.startBroadcast(deployerPrivateKey); + + upgrade(); + + vm.stopBroadcast(); + } + + function upgrade() public returns (address implementation) { + IAddressResolver addressResolver = + IAddressResolver(OPTIMISM_GOERLI_SYNTHETIX_ADDRESS_RESOLVER); + + // deploy events + address events = + address(new Events({_factory: OPTIMISM_GOERLI_FACTORY})); + + address marginAsset = addressResolver.getAddress({name: PROXY_SUSD}); + address perpsV2ExchangeRate = + addressResolver.getAddress({name: PERPS_V2_EXCHANGE_RATE}); + address futuresMarketManager = + addressResolver.getAddress({name: FUTURES_MARKET_MANAGER}); + address systemStatus = addressResolver.getAddress({name: SYSTEM_STATUS}); + + IAccount.AccountConstructorParams memory params = IAccount + .AccountConstructorParams({ + factory: OPTIMISM_GOERLI_FACTORY, + events: events, + marginAsset: marginAsset, + perpsV2ExchangeRate: perpsV2ExchangeRate, + futuresMarketManager: futuresMarketManager, + systemStatus: systemStatus, + gelato: OPTIMISM_GOERLI_GELATO, + ops: OPTIMISM_GOERLI_OPS, + settings: OPTIMISM_GOERLI_SETTINGS, + universalRouter: OPTIMISM_GOERLI_UNISWAP_UNIVERSAL_ROUTER, + permit2: OPTIMISM_GOERLI_UNISWAP_PERMIT2 + }); + + implementation = address(new Account(params)); + } +} diff --git a/script/utils/parameters/OptimismGoerliParameters.sol b/script/utils/parameters/OptimismGoerliParameters.sol index 14530c53..f5139180 100644 --- a/script/utils/parameters/OptimismGoerliParameters.sol +++ b/script/utils/parameters/OptimismGoerliParameters.sol @@ -19,15 +19,15 @@ address constant OPTIMISM_GOERLI_GELATO = address constant OPTIMISM_GOERLI_OPS = 0x255F82563b5973264e89526345EcEa766DB3baB2; -// v2.1.1 +// v2.1.2 address constant OPTIMISM_GOERLI_IMPLEMENTATION = - 0x5f78a5EdF0B356F50886a6829Fc02B16532e8735; + 0x62b26650A52E1B6a669C21a9d43aFD78AC0Ef84F; -// released with v2.1.0 implementation (used by v2.1.*) +// released with v2.1.2 implementation (used by v2.1.*) address constant OPTIMISM_GOERLI_EVENTS = - 0xe32F27B27F4ea5f10f269b52223910bA83e2933C; + 0x4533D9eAC2eb200B98fD9BaA306221ad2528e52F; -// updated with v2.1.1 implementation +// updated with v2.1.2 implementation address constant OPTIMISM_GOERLI_FACTORY = 0x30582eeE34719fe22b1B6c3b607636A3ab94522E; diff --git a/script/utils/parameters/OptimismParameters.sol b/script/utils/parameters/OptimismParameters.sol index 111bb300..6bf0e447 100644 --- a/script/utils/parameters/OptimismParameters.sol +++ b/script/utils/parameters/OptimismParameters.sol @@ -16,14 +16,14 @@ address constant OPTIMISM_GELATO = 0x01051113D81D7d6DA508462F2ad6d7fD96cF42Ef; address constant OPTIMISM_OPS = 0x340759c8346A1E6Ed92035FB8B6ec57cE1D82c2c; -// v2.1.1 +// v2.1.2 address constant OPTIMISM_IMPLEMENTATION = - 0x6B86c1A6878940666489780871E1C98B366d0aFF; + 0x9AcbDBABafC7E47dE0769C352b725Fa9fd443Bc2; -// released with v2.1.0 implementation (used by v2.1.*) -address constant OPTIMISM_EVENTS = 0xB753d2EE5dcA1fF39A83CA3Ec500656c31Be940b; +// released with v2.1.2 implementation (used by v2.1.*) +address constant OPTIMISM_EVENTS = 0x349142D51afCFbB3E9ef70DB1F7dB8437aABC5F7; -// updated with v2.1.1 implementation +// updated with v2.1.2 implementation address constant OPTIMISM_FACTORY = 0x8234F990b149Ae59416dc260305E565e5DAfEb54; // released with v2.1.0 implementation (used by v2.1.*) diff --git a/src/Account.sol b/src/Account.sol index 693d0ae7..8cb7bfb9 100644 --- a/src/Account.sol +++ b/src/Account.sol @@ -37,7 +37,7 @@ contract Account is IAccount, Auth, OpsReady { //////////////////////////////////////////////////////////////*/ /// @inheritdoc IAccount - bytes32 public constant VERSION = "2.1.1"; + bytes32 public constant VERSION = "2.1.2"; /// @notice tracking code used when modifying positions bytes32 internal constant TRACKING_CODE = "KWENTA"; @@ -218,13 +218,14 @@ contract Account is IAccount, Auth, OpsReady { /// @inheritdoc IAccount function setInitialOwnership(address _owner) external override { if (msg.sender != address(FACTORY)) revert Unauthorized(); + + /// @dev set owner directly owner = _owner; - emit OwnershipTransferred(address(0), _owner); + + EVENTS.emitOwnershipTransferred({caller: address(0), newOwner: _owner}); } - /// @notice transfer ownership of account to new address - /// @dev update factory's record of account ownership - /// @param _newOwner: new account owner + /// @inheritdoc Auth function transferOwnership(address _newOwner) public override { // will revert if msg.sender is *NOT* owner super.transferOwnership(_newOwner); @@ -234,6 +235,38 @@ contract Account is IAccount, Auth, OpsReady { _newOwner: _newOwner, _oldOwner: msg.sender // verified to be old owner }); + + /// @notice previous owner was verified to be msg.sender + EVENTS.emitOwnershipTransferred({ + caller: msg.sender, + newOwner: _newOwner + }); + } + + /*////////////////////////////////////////////////////////////// + DELEGATION + //////////////////////////////////////////////////////////////*/ + + /// @inheritdoc Auth + function addDelegate(address _delegate) public override { + // will revert if msg.sender is *NOT* owner + super.addDelegate(_delegate); + + EVENTS.emitDelegatedAccountAdded({ + caller: msg.sender, + delegate: _delegate + }); + } + + /// @inheritdoc Auth + function removeDelegate(address _delegate) public override { + // will revert if msg.sender is *NOT* owner + super.removeDelegate(_delegate); + + EVENTS.emitDelegatedAccountRemoved({ + caller: msg.sender, + delegate: _delegate + }); } /*////////////////////////////////////////////////////////////// diff --git a/src/Events.sol b/src/Events.sol index 78d79a1e..1d1c64a8 100644 --- a/src/Events.sol +++ b/src/Events.sol @@ -143,4 +143,31 @@ contract Events is IEvents { priceOracle: priceOracle }); } + + /// @inheritdoc IEvents + function emitOwnershipTransferred(address caller, address newOwner) + external + override + onlyAccounts + { + emit OwnershipTransferred({caller: caller, newOwner: newOwner}); + } + + /// @inheritdoc IEvents + function emitDelegatedAccountAdded(address caller, address delegate) + external + override + onlyAccounts + { + emit DelegatedAccountAdded({caller: caller, delegate: delegate}); + } + + /// @inheritdoc IEvents + function emitDelegatedAccountRemoved(address caller, address delegate) + external + override + onlyAccounts + { + emit DelegatedAccountRemoved({caller: caller, delegate: delegate}); + } } diff --git a/src/interfaces/IAccount.sol b/src/interfaces/IAccount.sol index 31f15c77..5beb4695 100644 --- a/src/interfaces/IAccount.sol +++ b/src/interfaces/IAccount.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.18; import {IPerpsV2MarketConsolidated} from "src/interfaces/synthetix/IPerpsV2MarketConsolidated.sol"; -/// @title Kwenta Smart Margin Account v2.1.1 Implementation Interface +/// @title Kwenta Smart Margin Account v2.1.2 Implementation Interface /// @author JaredBorders (jaredborders@pm.me), JChiaramonte7 (jeremy@bytecode.llc) interface IAccount { /*/////////////////////////////////////////////////////////////// diff --git a/src/interfaces/IEvents.sol b/src/interfaces/IEvents.sol index ded3db16..f5dd11c1 100644 --- a/src/interfaces/IEvents.sol +++ b/src/interfaces/IEvents.sol @@ -147,4 +147,34 @@ interface IEvents { uint256 keeperFee, IAccount.PriceOracleUsed priceOracle ); + + /// @notice emitted after ownership transfer + /// @param caller: previous owner + /// @param newOwner: new owner + function emitOwnershipTransferred(address caller, address newOwner) + external; + + event OwnershipTransferred( + address indexed caller, address indexed newOwner + ); + + /// @notice emitted after a delegate is added + /// @param caller: owner of the account + /// @param delegate: address of the delegate being added + function emitDelegatedAccountAdded(address caller, address delegate) + external; + + event DelegatedAccountAdded( + address indexed caller, address indexed delegate + ); + + /// @notice emitted after a delegate is removed + /// @param caller: owner of the account + /// @param delegate: address of the delegate being removed + function emitDelegatedAccountRemoved(address caller, address delegate) + external; + + event DelegatedAccountRemoved( + address indexed caller, address indexed delegate + ); } diff --git a/src/utils/Auth.sol b/src/utils/Auth.sol index 99e88f9a..f563d6b0 100644 --- a/src/utils/Auth.sol +++ b/src/utils/Auth.sol @@ -31,31 +31,6 @@ abstract contract Auth { /// @param delegateAddress: address of the delegate attempting to be added error InvalidDelegateAddress(address delegateAddress); - /*////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - /// @notice emitted after ownership transfer - /// @param caller: previous owner - /// @param newOwner: new owner - event OwnershipTransferred( - address indexed caller, address indexed newOwner - ); - - /// @notice emitted after a delegate is added - /// @param caller: owner of the account - /// @param delegate: address of the delegate being added - event DelegatedAccountAdded( - address indexed caller, address indexed delegate - ); - - /// @notice emitted after a delegate is removed - /// @param caller: owner of the account - /// @param delegate: address of the delegate being removed - event DelegatedAccountRemoved( - address indexed caller, address indexed delegate - ); - /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ @@ -64,8 +39,6 @@ abstract contract Auth { /// @param _owner The address of the owner constructor(address _owner) { owner = _owner; - - emit OwnershipTransferred(address(0), _owner); } /*////////////////////////////////////////////////////////////// @@ -93,8 +66,6 @@ abstract contract Auth { if (!isOwner()) revert Unauthorized(); owner = _newOwner; - - emit OwnershipTransferred(msg.sender, _newOwner); } /// @notice Add a delegate to the account @@ -108,8 +79,6 @@ abstract contract Auth { } delegates[_delegate] = true; - - emit DelegatedAccountAdded({caller: msg.sender, delegate: _delegate}); } /// @notice Remove a delegate from the account @@ -123,7 +92,5 @@ abstract contract Auth { } delete delegates[_delegate]; - - emit DelegatedAccountRemoved({caller: msg.sender, delegate: _delegate}); } } diff --git a/test/unit/Account.t.sol b/test/unit/Account.t.sol index c685fc7d..fdd26045 100644 --- a/test/unit/Account.t.sol +++ b/test/unit/Account.t.sol @@ -41,7 +41,8 @@ import { sETHPERP, SYSTEM_STATUS, UNISWAP_PERMIT2, - UNISWAP_UNIVERSAL_ROUTER + UNISWAP_UNIVERSAL_ROUTER, + USER } from "test/utils/Constants.sol"; contract AccountTest is Test, ConsolidatedEvents { @@ -118,7 +119,7 @@ contract AccountTest is Test, ConsolidatedEvents { //////////////////////////////////////////////////////////////*/ function test_GetVerison() public view { - assert(account.VERSION() == "2.1.1"); + assert(account.VERSION() == "2.1.2"); } function test_GetTrackingCode() public view { @@ -253,6 +254,20 @@ contract AccountTest is Test, ConsolidatedEvents { account.transferOwnership(KWENTA_TREASURY); } + function test_Ownership_Transfer_Twice() public { + // ensure factory and account state align + address originalOwner = factory.getAccountOwner(address(account)); + + account.transferOwnership(KWENTA_TREASURY); + vm.prank(KWENTA_TREASURY); + account.transferOwnership(USER); + + address newOwner = factory.getAccountOwner(address(account)); + assert(newOwner == USER && newOwner == account.owner()); + assert(factory.getAccountsOwnedBy(USER)[0] == address(account)); + assert(factory.getAccountsOwnedBy(originalOwner).length == 0); + } + function test_Ownership_setInitialOwnership_OnlyFactory() public { vm.expectRevert(abi.encodeWithSelector(Auth.Unauthorized.selector)); account.setInitialOwnership(KWENTA_TREASURY); diff --git a/test/unit/Events.t.sol b/test/unit/Events.t.sol index 9a23b1a4..8301c14d 100644 --- a/test/unit/Events.t.sol +++ b/test/unit/Events.t.sol @@ -6,6 +6,7 @@ import {Test} from "lib/forge-std/src/Test.sol"; import {Setup} from "script/Deploy.s.sol"; import {Account} from "src/Account.sol"; +import {Auth} from "src/utils/Auth.sol"; import {Events} from "src/Events.sol"; import {Factory} from "src/Factory.sol"; import {IAccount} from "src/interfaces/IAccount.sol"; @@ -249,4 +250,58 @@ contract EventsTest is Test, ConsolidatedEvents { amountOutMinimum: 2 }); } + + function test_EmitOwnershipTransferred_Event() public { + vm.expectEmit(true, true, true, true); + emit OwnershipTransferred(address(0xA), address(0xB)); + vm.prank(account); + events.emitOwnershipTransferred({ + caller: address(0xA), + newOwner: address(0xB) + }); + } + + function test_EmitOwnershipTransferred_OnlyAccounts() public { + vm.expectRevert(abi.encodeWithSelector(IEvents.OnlyAccounts.selector)); + events.emitOwnershipTransferred({ + caller: address(0xA), + newOwner: address(0xB) + }); + } + + function test_EmitDelegatedAccountAdded_Event() public { + vm.expectEmit(true, true, true, true); + emit DelegatedAccountAdded(address(0xA), address(0xB)); + vm.prank(account); + events.emitDelegatedAccountAdded({ + caller: address(0xA), + delegate: address(0xB) + }); + } + + function test_EmitDelegatedAccountAdded_OnlyAccounts() public { + vm.expectRevert(abi.encodeWithSelector(IEvents.OnlyAccounts.selector)); + events.emitDelegatedAccountAdded({ + caller: address(0xA), + delegate: address(0xB) + }); + } + + function test_EmitDelegatedAccountRemoved_Event() public { + vm.expectEmit(true, true, true, true); + emit DelegatedAccountRemoved(address(0xA), address(0xB)); + vm.prank(account); + events.emitDelegatedAccountRemoved({ + caller: address(0xA), + delegate: address(0xB) + }); + } + + function test_EmitDelegatedAccountRemoved_OnlyAccounts() public { + vm.expectRevert(abi.encodeWithSelector(IEvents.OnlyAccounts.selector)); + events.emitDelegatedAccountRemoved({ + caller: address(0xA), + delegate: address(0xB) + }); + } } diff --git a/test/upgrades/README.md b/test/upgrades/README.md new file mode 100644 index 00000000..a7db4f82 --- /dev/null +++ b/test/upgrades/README.md @@ -0,0 +1,18 @@ +# Upgrade Testing + +## Methodology + +- Define the *new* interface which will be used in the upgrade +> see `v2.1.1/interfaces/IAccount` +- Import the *old* being used at the specific block number that is used for forking the environment +- Modify all possible state in an existing account (i.e. deploy one and then modify it in every way possible) +> this is to sanity check no storage slots are overwritten +- Deploy a new contract that uses the new interface +- Upgrade the SMv2 system via a call to the Factory to upgrade the implementation +- Check all state is still correct + +## Compatability + +- If written properly, all older tests for upgrades should work +- State should **ONLY** be introduced and **NEVER** rearranged or removed. +- Even with introducing state, it should be done in a way that is backwards compatible with the old state (i.e. appended) \ No newline at end of file diff --git a/test/upgrades/v2.1.1/Upgrade.t.sol b/test/upgrades/v2.1.1/Upgrade.t.sol index d40898fd..642fb47a 100644 --- a/test/upgrades/v2.1.1/Upgrade.t.sol +++ b/test/upgrades/v2.1.1/Upgrade.t.sol @@ -42,7 +42,7 @@ contract UpgradeTest is Test { address private NEW_IMPLEMENTATION; /*////////////////////////////////////////////////////////////// - V2.1.1 ACTIVE ACCOUNT + V2.1.0 ACTIVE ACCOUNT //////////////////////////////////////////////////////////////*/ address private activeAccount; @@ -133,7 +133,7 @@ contract UpgradeTest is Test { (, response) = activeAccount.call(abi.encodeWithSignature("VERSION()")); (bytes32 version) = abi.decode(response, (bytes32)); - assertEq(version, "2.1.1", "wrong version"); + assert(version != "2.1.0"); /** * CHECK STATE DID NOT CHANGE diff --git a/test/upgrades/v2.1.2/Upgrade.t.sol b/test/upgrades/v2.1.2/Upgrade.t.sol new file mode 100644 index 00000000..ba087f89 --- /dev/null +++ b/test/upgrades/v2.1.2/Upgrade.t.sol @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.18; + +import {Test} from "lib/forge-std/src/Test.sol"; + +import {UpgradeAccountOptimism} from "script/upgrades/v2.1.2/Upgrade.s.sol"; +import { + OPTIMISM_FACTORY, + OPTIMISM_PDAO, + OPTIMISM_UNISWAP_PERMIT2 +} from "script/utils/parameters/OptimismParameters.sol"; + +import {Factory} from "src/Factory.sol"; +import {IAccount as OldAccount} from + "test/upgrades/v2.1.1/interfaces/IAccount.sol"; +import {IAccount as NewAccount} from + "test/upgrades/v2.1.2/interfaces/IAccount.sol"; +import {IERC20} from "src/interfaces/token/IERC20.sol"; +import {ISynth} from "test/utils/interfaces/ISynth.sol"; + +import {IAddressResolver} from "test/utils/interfaces/IAddressResolver.sol"; +import {ADDRESS_RESOLVER, PROXY_SUSD} from "test/utils/Constants.sol"; + +contract UpgradeTest is Test { + // BLOCK_NUMBER_UPGRADE corresponds to Optimism network state @ Oct-24-2023 05:53:13 PM +UTC + // hard coded addresses are only guaranteed for this block + uint256 private constant BLOCK_NUMBER_UPGRADE = 111_285_608; + + address private constant DELEGATE = address(0xDE1A6A7E); + + /*////////////////////////////////////////////////////////////// + V2.1.1 IMPLEMENTATION + //////////////////////////////////////////////////////////////*/ + + address private constant OLD_IMPLEMENTATION = + 0x6B86c1A6878940666489780871E1C98B366d0aFF; + + /*////////////////////////////////////////////////////////////// + V2.1.2 IMPLEMENTATION + //////////////////////////////////////////////////////////////*/ + + address private NEW_IMPLEMENTATION; + + /*////////////////////////////////////////////////////////////// + V2.1.1 ACTIVE ACCOUNT + //////////////////////////////////////////////////////////////*/ + + address private activeAccount; + + /*////////////////////////////////////////////////////////////// + SETUP + //////////////////////////////////////////////////////////////*/ + + function setUp() public { + vm.rollFork(BLOCK_NUMBER_UPGRADE); + + // create active v2.1.1 account + activeAccount = initAccountForStateTesting(); + + // define Setup contract used for upgrades + UpgradeAccountOptimism upgradeAccountOptimism = + new UpgradeAccountOptimism(); + + // deploy v2.1.2 implementation + address implementationAddr = upgradeAccountOptimism.upgrade(); + NEW_IMPLEMENTATION = payable(implementationAddr); + } + + /*////////////////////////////////////////////////////////////// + TESTS + //////////////////////////////////////////////////////////////*/ + + function test_Deployed_Account_Version() public { + (, bytes memory response) = + activeAccount.call(abi.encodeWithSignature("VERSION()")); + (bytes32 version) = abi.decode(response, (bytes32)); + assertEq(version, "2.1.1", "wrong version"); + } + + function test_Upgrade() public { + /** + * RECORD ALL STATE PRIOR TO UPGRADE + */ + + // fetch commited margin from Active Account + (, bytes memory response) = + activeAccount.call(abi.encodeWithSignature("committedMargin()")); + (uint256 commitedMargin) = abi.decode(response, (uint256)); + assertGt(commitedMargin, 0, "commitedMargin is zero"); + + // fetch current conditional order id from Active Account + (, response) = + activeAccount.call(abi.encodeWithSignature("conditionalOrderId()")); + (uint256 conditionalOrderId) = abi.decode(response, (uint256)); + assertGt(conditionalOrderId, 0, "conditionalOrderId is zero"); + + // fetch current conditional orders from Active Account + OldAccount.ConditionalOrder[] memory orders = + new OldAccount.ConditionalOrder[](conditionalOrderId); + for (uint256 index = 0; index < conditionalOrderId; index++) { + (, response) = activeAccount.call( + abi.encodeWithSignature("getConditionalOrder(uint256)", index) + ); + (OldAccount.ConditionalOrder memory order) = + abi.decode(response, (OldAccount.ConditionalOrder)); + orders[index] = order; + } + + // fetch owner from Active Account + (, response) = activeAccount.call(abi.encodeWithSignature("owner()")); + (address owner) = abi.decode(response, (address)); + assert(owner != address(0)); + + // fetch delegate from Active Account + (, response) = activeAccount.call( + abi.encodeWithSignature("delegates(address)", DELEGATE) + ); + assertEq(true, abi.decode(response, (bool)), "delegate missmatch"); + + /** + * EXECUTE UPGRADE + */ + + // upgrade Active Account to v2.1.2 + vm.prank(OPTIMISM_PDAO); + Factory(OPTIMISM_FACTORY).upgradeAccountImplementation( + address(NEW_IMPLEMENTATION) + ); + + /** + * VERIFY VERSION DID CHANGE + */ + + (, response) = activeAccount.call(abi.encodeWithSignature("VERSION()")); + (bytes32 version) = abi.decode(response, (bytes32)); + assert(version != "2.1.1"); + + /** + * CHECK STATE DID NOT CHANGE + */ + + (, response) = + activeAccount.call(abi.encodeWithSignature("committedMargin()")); + assertEq( + commitedMargin, + abi.decode(response, (uint256)), + "commitedMargin missmatch" + ); + + // fetch current conditional order id from Active Account + (, response) = + activeAccount.call(abi.encodeWithSignature("conditionalOrderId()")); + assertEq( + conditionalOrderId, + abi.decode(response, (uint256)), + "conditionalOrderId missmatch" + ); + + // fetch current conditional orders from Active Account + for (uint256 index = 0; index < conditionalOrderId; index++) { + (, response) = activeAccount.call( + abi.encodeWithSignature("getConditionalOrder(uint256)", index) + ); + assertEq( + orders[index].marketKey, + abi.decode(response, (NewAccount.ConditionalOrder)).marketKey, + "conditionalOrder missmatch" + ); + } + + // fetch owner from Active Account + (, response) = activeAccount.call(abi.encodeWithSignature("owner()")); + assertEq(owner, abi.decode(response, (address)), "owner missmatch"); + + // fetch delegate from Active Account + (, response) = activeAccount.call( + abi.encodeWithSignature("delegates(address)", DELEGATE) + ); + assertEq(true, abi.decode(response, (bool)), "delegate missmatch"); + } + + /*////////////////////////////////////////////////////////////// + UTILITIES + //////////////////////////////////////////////////////////////*/ + + function initAccountForStateTesting() internal returns (address) { + uint256 amount = 10_000 ether; + + /// @notice create account + address payable accountAddress = Factory(OPTIMISM_FACTORY).newAccount(); + + /// @notice mint sUSD to this contract + address issuer = IAddressResolver(ADDRESS_RESOLVER).getAddress("Issuer"); + ISynth synthsUSD = + ISynth(IAddressResolver(ADDRESS_RESOLVER).getAddress("SynthsUSD")); + vm.prank(issuer); + synthsUSD.issue(address(this), amount); + + /// @notice fund SM account with eth and sUSD (i.e. margin) + vm.deal(accountAddress, 1 ether); + IERC20(IAddressResolver(ADDRESS_RESOLVER).getAddress(PROXY_SUSD)) + .approve(address(accountAddress), type(uint256).max); + OldAccount.Command[] memory commands = new OldAccount.Command[](1); + commands[0] = OldAccount.Command.ACCOUNT_MODIFY_MARGIN; + bytes[] memory inputs = new bytes[](1); + inputs[0] = abi.encode(amount); + OldAccount(accountAddress).execute(commands, inputs); + + /// @notice create/submit conditional order which lock up margin + commands[0] = OldAccount.Command.GELATO_PLACE_CONDITIONAL_ORDER; + bytes32 marketKey = bytes32("sETHPERP"); + inputs[0] = abi.encode( + marketKey, + int256(amount / 2), + int256(1 ether), + 10_000 ether, + OldAccount.ConditionalOrderTypes.LIMIT, + 1000 ether, + true + ); + OldAccount(accountAddress).execute(commands, inputs); + + /// @notice add delegate + (bool s,) = accountAddress.call( + abi.encodeWithSignature("addDelegate(address)", DELEGATE) + ); + assertEq(s, true, "addDelegate failed"); + + return accountAddress; + } +} diff --git a/test/upgrades/v2.1.2/interfaces/IAccount.sol b/test/upgrades/v2.1.2/interfaces/IAccount.sol new file mode 100644 index 00000000..5beb4695 --- /dev/null +++ b/test/upgrades/v2.1.2/interfaces/IAccount.sol @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.18; + +import {IPerpsV2MarketConsolidated} from + "src/interfaces/synthetix/IPerpsV2MarketConsolidated.sol"; + +/// @title Kwenta Smart Margin Account v2.1.2 Implementation Interface +/// @author JaredBorders (jaredborders@pm.me), JChiaramonte7 (jeremy@bytecode.llc) +interface IAccount { + /*/////////////////////////////////////////////////////////////// + Types + ///////////////////////////////////////////////////////////////*/ + + /// @notice Command Flags used to decode commands to execute + /// @dev under the hood ACCOUNT_MODIFY_MARGIN = 0, ACCOUNT_WITHDRAW_ETH = 1 + enum Command { + ACCOUNT_MODIFY_MARGIN, // 0 + ACCOUNT_WITHDRAW_ETH, + PERPS_V2_MODIFY_MARGIN, + PERPS_V2_WITHDRAW_ALL_MARGIN, + PERPS_V2_SUBMIT_ATOMIC_ORDER, + PERPS_V2_SUBMIT_DELAYED_ORDER, // 5 + PERPS_V2_SUBMIT_OFFCHAIN_DELAYED_ORDER, + PERPS_V2_CLOSE_POSITION, + PERPS_V2_SUBMIT_CLOSE_DELAYED_ORDER, + PERPS_V2_SUBMIT_CLOSE_OFFCHAIN_DELAYED_ORDER, + PERPS_V2_CANCEL_DELAYED_ORDER, // 10 + PERPS_V2_CANCEL_OFFCHAIN_DELAYED_ORDER, + GELATO_PLACE_CONDITIONAL_ORDER, + GELATO_CANCEL_CONDITIONAL_ORDER, + UNISWAP_V3_SWAP, + PERMIT2_PERMIT, // 15 + PERPS_V2_SET_MIN_KEEPER_FEE + } + + /// @notice denotes conditional order types for code clarity + /// @dev under the hood LIMIT = 0, STOP = 1 + enum ConditionalOrderTypes { + LIMIT, + STOP + } + + /// @notice denotes conditional order cancelled reasons for code clarity + /// @dev under the hood CONDITIONAL_ORDER_CANCELLED_BY_USER = 0, CONDITIONAL_ORDER_CANCELLED_NOT_REDUCE_ONLY = 1 + enum ConditionalOrderCancelledReason { + CONDITIONAL_ORDER_CANCELLED_BY_USER, + CONDITIONAL_ORDER_CANCELLED_NOT_REDUCE_ONLY + } + + /// @notice denotes what oracle is used for price when executing conditional orders + /// @dev under the hood PYTH = 0, CHAINLINK = 1 + enum PriceOracleUsed { + PYTH, + CHAINLINK + } + + /// @param factory: address of the Smart Margin Account Factory + /// @param events: address of the contract used by all accounts for emitting events + /// @param marginAsset: address of the Synthetix ProxyERC20sUSD contract used as the margin asset + /// @param perpsV2ExchangeRate: address of the Synthetix PerpsV2ExchangeRate + /// @param futuresMarketManager: address of the Synthetix FuturesMarketManager + /// @param systemStatus: address of the Synthetix SystemStatus + /// @param gelato: address of Gelato + /// @param ops: address of Ops + /// @param settings: address of contract used to store global settings + /// @param universalRouter: address of Uniswap's Universal Router + /// @param permit2: address of Uniswap's Permit2 + struct AccountConstructorParams { + address factory; + address events; + address marginAsset; + address perpsV2ExchangeRate; + address futuresMarketManager; + address systemStatus; + address gelato; + address ops; + address settings; + address universalRouter; + address permit2; + } + + /// marketKey: Synthetix PerpsV2 Market id/key + /// marginDelta: amount of margin to deposit or withdraw; positive indicates deposit, negative withdraw + /// sizeDelta: denoted in market currency (i.e. ETH, BTC, etc), size of Synthetix PerpsV2 position + /// targetPrice: limit or stop price target needing to be met to submit Synthetix PerpsV2 order + /// gelatoTaskId: unqiue taskId from gelato necessary for cancelling conditional orders + /// conditionalOrderType: conditional order type to determine conditional order fill logic + /// desiredFillPrice: desired price to fill Synthetix PerpsV2 order at execution time + /// reduceOnly: if true, only allows position's absolute size to decrease + struct ConditionalOrder { + bytes32 marketKey; + int256 marginDelta; + int256 sizeDelta; + uint256 targetPrice; + bytes32 gelatoTaskId; + ConditionalOrderTypes conditionalOrderType; + uint256 desiredFillPrice; + bool reduceOnly; + } + /// @dev see example below elucidating targtPrice vs desiredFillPrice: + /// 1. targetPrice met (ex: targetPrice = X) + /// 2. account submits delayed order to Synthetix PerpsV2 with desiredFillPrice = Y + /// 3. keeper executes Synthetix PerpsV2 order after delay period + /// 4. if current market price defined by Synthetix PerpsV2 + /// after delay period satisfies desiredFillPrice order is filled + + /*////////////////////////////////////////////////////////////// + ERRORS + //////////////////////////////////////////////////////////////*/ + + /// @notice thrown when commands length does not equal inputs length + error LengthMismatch(); + + /// @notice thrown when Command given is not valid + error InvalidCommandType(uint256 commandType); + + /// @notice thrown when conditional order type given is not valid due to zero sizeDelta + error ZeroSizeDelta(); + + /// @notice exceeds useable margin + /// @param available: amount of useable margin asset + /// @param required: amount of margin asset required + error InsufficientFreeMargin(uint256 available, uint256 required); + + /// @notice call to transfer ETH on withdrawal fails + error EthWithdrawalFailed(); + + /// @notice base price from the oracle was invalid + /// @dev Rate can be invalid either due to: + /// 1. Returned as invalid from ExchangeRates - due to being stale or flagged by oracle + /// 2. Out of deviation bounds w.r.t. to previously stored rate + /// 3. if there is no valid stored rate, w.r.t. to previous 3 oracle rates + /// 4. Price is zero + error InvalidPrice(); + + /// @notice thrown when account execution has been disabled in the settings contract + error AccountExecutionDisabled(); + + /// @notice thrown when a call attempts to reenter the protected function + error Reentrancy(); + + /// @notice thrown when token swap attempted with invalid token (i.e. token that is not whitelisted) + /// @param tokenIn: token attempting to swap from + /// @param tokenOut: token attempting to swap to + error TokenSwapNotAllowed(address tokenIn, address tokenOut); + + /// @notice thrown when a conditional order is attempted to be executed during invalid market conditions + /// @param conditionalOrderId: conditional order id + /// @param executor: address of executor + error CannotExecuteConditionalOrder( + uint256 conditionalOrderId, address executor + ); + + /// @notice thrown when a conditional order is attempted to be executed but SM account cannot pay fee + /// @param executorFee: fee required to execute conditional order + error CannotPayExecutorFee(uint256 executorFee, address executor); + + /// @notice thrown when call to set/updates the min keeper fee fails + error SetMinKeeperFeeFailed(); + + /*////////////////////////////////////////////////////////////// + VIEWS + //////////////////////////////////////////////////////////////*/ + + /// @notice returns the version of the Account + function VERSION() external view returns (bytes32); + + /// @return returns the amount of margin locked for future events (i.e. conditional orders) + function committedMargin() external view returns (uint256); + + /// @return returns current conditional order id + function conditionalOrderId() external view returns (uint256); + + /// @notice get delayed order data from Synthetix PerpsV2 + /// @dev call reverts if _marketKey is invalid + /// @param _marketKey: key for Synthetix PerpsV2 Market + /// @return delayed order struct defining delayed order (will return empty struct if no delayed order exists) + function getDelayedOrder(bytes32 _marketKey) + external + returns (IPerpsV2MarketConsolidated.DelayedOrder memory); + + /// @notice checker() is the Resolver for Gelato + /// (see https://docs.gelato.network/developer-services/automate/guides/custom-logic-triggers/smart-contract-resolvers) + /// @notice signal to a keeper that a conditional order is valid/invalid for execution + /// @dev call reverts if conditional order Id does not map to a valid conditional order; + /// ConditionalOrder.marketKey would be invalid + /// @param _conditionalOrderId: key for an active conditional order + /// @return canExec boolean that signals to keeper a conditional order can be executed by Gelato + /// @return execPayload calldata for executing a conditional order + function checker(uint256 _conditionalOrderId) + external + view + returns (bool canExec, bytes memory execPayload); + + /// @notice the current withdrawable or usable balance + /// @return free margin amount + function freeMargin() external view returns (uint256); + + /// @notice get up-to-date position data from Synthetix PerpsV2 + /// @param _marketKey: key for Synthetix PerpsV2 Market + /// @return position struct defining current position + function getPosition(bytes32 _marketKey) + external + returns (IPerpsV2MarketConsolidated.Position memory); + + /// @notice conditional order id mapped to conditional order + /// @param _conditionalOrderId: id of conditional order + /// @return conditional order + function getConditionalOrder(uint256 _conditionalOrderId) + external + view + returns (ConditionalOrder memory); + + /*////////////////////////////////////////////////////////////// + MUTATIVE + //////////////////////////////////////////////////////////////*/ + + /// @notice sets the initial owner of the account + /// @dev only called once by the factory on account creation + /// @param _owner: address of the owner + function setInitialOwnership(address _owner) external; + + /// @notice executes commands along with provided inputs + /// @param _commands: array of commands, each represented as an enum + /// @param _inputs: array of byte strings containing abi encoded inputs for each command + function execute(Command[] calldata _commands, bytes[] calldata _inputs) + external + payable; + + /// @notice execute queued conditional order + /// @dev currently only supports conditional order submission via PERPS_V2_SUBMIT_OFFCHAIN_DELAYED_ORDER COMMAND + /// @param _conditionalOrderId: key for an active conditional order + function executeConditionalOrder(uint256 _conditionalOrderId) external; +}