Skip to content

Commit

Permalink
🚀 Mykonos Upgrade (v2.1.0)
Browse files Browse the repository at this point in the history
  • Loading branch information
JaredBorders authored Aug 9, 2023
2 parents 36b0491 + 4d43b53 commit d69a51e
Show file tree
Hide file tree
Showing 59 changed files with 4,365 additions and 1,200 deletions.
325 changes: 182 additions & 143 deletions .gas-snapshot

Large diffs are not rendered by default.

27 changes: 14 additions & 13 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,18 @@ jobs:

- name: Run forge test
run: |
forge test --fork-url ${{ secrets.ARCHIVE_NODE_URL_GOERLI_L2 }} --etherscan-api-key ${{ secrets.ETHERSCAN_API_KEY }} -vvv
forge test --fork-url ${{ secrets.ARCHIVE_NODE_URL_L2 }} --etherscan-api-key ${{ secrets.ETHERSCAN_API_KEY }} -vvv
id: test

- name: Run forge coverage
run: |
forge coverage --fork-url ${{ secrets.ARCHIVE_NODE_URL_GOERLI_L2 }} --report lcov
- name: Upload Coverage Report
uses: codecov/codecov-action@v3
with:
files: ./lcov.info
name: codecov-unit
fail_ci_if_error: true
verbose: true

# FIXME: Coverage is broken due to stack too deep error
# - name: Run forge coverage
# run: |
# forge coverage --fork-url ${{ secrets.ARCHIVE_NODE_URL_L2 }} --report lcov

# - name: Upload Coverage Report
# uses: codecov/codecov-action@v3
# with:
# files: ./lcov.info
# name: codecov-unit
# fail_ci_if_error: true
# verbose: true
80 changes: 40 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Contracts to manage account abstractions and features on top of [Synthetix Perps

> See ./deploy-addresses/ for deployed contract addresses
The Margin Manager codebase consists of the `Factory` and `Account` contracts, and all of the associated dependencies. The purpose of the `Factory` is to create/deploy trading accounts (`Account` contracts) for users that support features ranging from cross-margin, conditional orders, copy trading, etc.. Once a smart margin account has been created, the main point of entry is the `Account.execute` function. `Account.execute` allows users to execute a set of commands describing the actions/trades they want executed by their account.
The Smart Margin codebase consists of the `Factory` and `Account` contracts, and all of the associated dependencies. The purpose of the `Factory` is to create/deploy trading accounts (`Account` contracts) for users that support features ranging from cross-margin, conditional orders, copy trading, etc.. Once a smart margin account has been created, the main point of entry is the `Account.execute` function. `Account.execute` allows users to execute a set of commands describing the actions/trades they want executed by their account.

### User Entry: MarginBase Command Execution

Expand All @@ -36,24 +36,7 @@ Calls to `Account.execute`, the entrypoint to the smart margin account, require

`commands[i]` is the command that will use `inputs[i]` as its encoded input parameters.

The supported commands can be found below (ordering may _not_ match what is defined in `IAccount.sol`):

```
ACCOUNT_MODIFY_MARGIN
ACCOUNT_WITHDRAW_ETH
PERPS_V2_MODIFY_MARGIN
PERPS_V2_WITHDRAW_ALL_MARGIN
PERPS_V2_SUBMIT_ATOMIC_ORDER
PERPS_V2_SUBMIT_DELAYED_ORDER
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
PERPS_V2_CANCEL_OFFCHAIN_DELAYED_ORDER
GELATO_PLACE_CONDITIONAL_ORDER
GELATO_CANCEL_CONDITIONAL_ORDER
```
The supported commands can be found in the [wiki](https://github.com/Kwenta/smart-margin/wiki/Commands) and [code](https://github.com/Kwenta/smart-margin/blob/main/src/interfaces/IAccount.sol).

#### How the input bytes are structured

Expand All @@ -73,7 +56,7 @@ Encoding parameters in a bytes string in this way gives us maximum flexiblity to

For a more detailed breakdown of which parameters you should provide for each command take a look at the `Account.dispatch` function.

Developer documentation to give a detailed explanation of the inputs for every command can be found in the [wiki](https://github.com/Kwenta/margin-manager/wiki/Commands)
Developer documentation to give a detailed explanation of the inputs for every command can also be found in the [wiki](https://github.com/Kwenta/margin-manager/wiki/Commands)

#### Diagram

Expand All @@ -100,8 +83,10 @@ Smart margin accounts are upgradable. This is achieved by using a proxy pattern,
Finally, all associated functionality related to upgradability can be disabled by the `Factory` contract owner.

## Folder Structure
> to run: `tree src/`
```
src
src/
├── Account.sol
├── AccountProxy.sol
├── Events.sol
Expand All @@ -110,31 +95,36 @@ src
├── interfaces
│ ├── IAccount.sol
│ ├── IAccountProxy.sol
│ ├── IERC20.sol
│ ├── IEvents.sol
│ ├── IFactory.sol
│ ├── IOps.sol
│ ├── ISettings.sol
│ └── synthetix
│ ├── IFuturesMarketManager.sol
│ ├── IPerpsV2MarketConsolidated.sol
│ └── ISystemStatus.sol
│ ├── gelato
│ │ └── IOps.sol
│ ├── synthetix
│ │ ├── IFuturesMarketManager.sol
│ │ ├── IPerpsV2ExchangeRate.sol
│ │ ├── IPerpsV2MarketConsolidated.sol
│ │ └── ISystemStatus.sol
│ ├── token
│ │ └── IERC20.sol
│ └── uniswap
│ ├── IPermit2.sol
│ └── IUniversalRouter.sol
└── utils
├── Auth.sol
└── OpsReady.sol
├── Owned.sol
├── executors
│ └── OrderExecution.sol
├── gelato
│ └── OpsReady.sol
└── uniswap
├── BytesLib.sol
├── Constants.sol
├── SafeCast160.sol
└── V3Path.sol
```

## Test Coverage

| File | % Lines | % Statements | % Branches | % Funcs |
|--------------------------------------|------------------|------------------|------------------|-----------------|
| src/Account.sol | 99.05% (209/211) | 99.10% (221/223) | 93.59% (73/78) | 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) |

## Usage

### Setup
Expand Down Expand Up @@ -164,7 +154,7 @@ npm run test
4. Run specific test

```
forge test --fork-url $(grep ARCHIVE_NODE_URL_GOERLI_L2 .env | cut -d '=' -f2) --match-test TEST_NAME -vvv
forge test --fork-url $(grep ARCHIVE_NODE_URL_L2 .env | cut -d '=' -f2) --match-test TEST_NAME -vvv
```

> tests will fail if you have not set up your .env (see .env.example)
Expand Down Expand Up @@ -195,6 +185,16 @@ forge test --fork-url $(grep ARCHIVE_NODE_URL_GOERLI_L2 .env | cut -d '=' -f2) -
13. Update `./deploy-addresses/optimism.json` with new `Account` address
14. Ensure mainnet accounts are updated and functional (ensure state is correct)

## External Conditional Order Executors
> As of SM v2.1.0, public actors can execute conditional orders and receive a fee for doing so
1. Navigate to `src/utils/executors/OrderExecution.sol`
2. `OrderExecution` is a *simplified* contract which defines: (1) basic batch conditional order execution functionality, (2) a method to update onchain Pyth oracle price feed(s), (3) **combined** price feed update(s) and then conditional order execution functionality
> `OrderExecution` is meant to serve as a starting point for developers to build their own conditional order executors and IS NOT production ready nor has it been audited
3. See https://docs.pyth.network/evm/update-price-feeds for more information on updating Pyth oracle price feeds
4. See `IAccount.executeConditionalOrder` for more information on conditional order execution
5. Currently there are no scripts in this repository which deploy the `OrderExecution` contract
6. See https://github.com/JaredBorders/KwentaOrderExecutor for a conditional order executor that includes deployment scripts

## Project Tools

### Static Analysis
Expand Down
Binary file added audits/v1.0.0/Kwenta_A-3.pdf
Binary file not shown.
Binary file not shown.
Binary file added audits/v2.0.0/Kwenta_A-4.pdf
Binary file not shown.
Binary file added audits/v2.0.2/Kwenta_A-5.pdf
Binary file not shown.
Binary file added audits/v2.1.0/Kwenta_A-6.pdf
Binary file not shown.
6 changes: 3 additions & 3 deletions deploy-addresses/optimism-goerli.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"Account": "0xf9CC1eD5EC9b8DFb9273fdDABD07C7bB37938285",
"Events": "0xa7AE3969A128048290968b41865Eaa53B20FA69e",
"Account": "0x0fCeF9608B9F7a8C57D53B98E78fA64b4D786a9F",
"Events": "0xe32F27B27F4ea5f10f269b52223910bA83e2933C",
"Factory": "0x30582eeE34719fe22b1B6c3b607636A3ab94522E",
"Settings": "0xd2f3c4D549EF6AB572dB6512AB0e33f709E7caE1"
"Settings": "0x8B9CbD3da94c637c0652c680Abd3CF7f787aBAF4"
}
6 changes: 3 additions & 3 deletions deploy-addresses/optimism.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"Account": "0x3BC8D34314E77c2E36948Fd8F4B353f1baDc3F6C",
"Events": "0x11193470df30B37Af9fc5Ec696c240D878bdfb42",
"Account": "0x83E13069aA457778ca349E0128927B417A2c2B3f",
"Events": "0xB753d2EE5dcA1fF39A83CA3Ec500656c31Be940b",
"Factory": "0x8234F990b149Ae59416dc260305E565e5DAfEb54",
"Settings": "0xD02813baF080d06FC6F706cF93F5DaA96D6edB17"
"Settings": "0x865dA103d126b3Be3599D84caB57109A861F5631"
}
4 changes: 2 additions & 2 deletions package-lock.json

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

10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
{
"name": "@kwenta/cross-margin-manager",
"version": "2.0.2",
"version": "2.1.0",
"scripts": {
"compile": "forge build",
"test": "forge test --fork-url $(grep ARCHIVE_NODE_URL_GOERLI_L2 .env | cut -d '=' -f2) --etherscan-api-key $(grep ETHERSCAN_API_KEY .env | cut -d '=' -f2) --gas-report -vvv",
"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",
"format": "forge fmt",
"coverage": "forge coverage --fork-url $(grep ARCHIVE_NODE_URL_GOERLI_L2 .env | cut -d '=' -f2)",
"coverage:generate-lcov": "forge coverage --fork-url $(grep ARCHIVE_NODE_URL_GOERLI_L2 .env | cut -d '=' -f2) --report lcov",
"coverage": "forge coverage --fork-url $(grep ARCHIVE_NODE_URL_L2 .env | cut -d '=' -f2)",
"coverage:generate-lcov": "forge coverage --fork-url $(grep ARCHIVE_NODE_URL_L2 .env | cut -d '=' -f2) --report lcov",
"check:upgradeability": "slither-check-upgradeability . Account --proxy-name AccountProxy",
"analysis:solsat": "solstat --path ./src",
"analysis:slither": "slither .",
"gas-snapshot": "forge snapshot --fork-url $(grep ARCHIVE_NODE_URL_GOERLI_L2 .env | cut -d '=' -f2)",
"gas-snapshot": "forge snapshot --fork-url $(grep ARCHIVE_NODE_URL_L2 .env | cut -d '=' -f2)",
"view-storage-layout": "slither --print variable-order ."
},
"repository": {
Expand Down
93 changes: 67 additions & 26 deletions script/Deploy.s.sol
Original file line number Diff line number Diff line change
@@ -1,15 +1,39 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

import "forge-std/Script.sol";
import "./utils/parameters/OptimismGoerliParameters.sol";
import "./utils/parameters/OptimismParameters.sol";
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 {Factory} from "src/Factory.sol";
import {IAddressResolver} from "./utils/interfaces/IAddressResolver.sol";
import {IAccount} from "src/interfaces/IAccount.sol";
import {Settings} from "src/Settings.sol";

import {
OPTIMISM_DEPLOYER,
OPTIMISM_PDAO,
OPTIMISM_SYNTHETIX_ADDRESS_RESOLVER,
OPTIMISM_GELATO,
OPTIMISM_OPS,
OPTIMISM_UNISWAP_UNIVERSAL_ROUTER,
OPTIMISM_UNISWAP_PERMIT2,
PROXY_SUSD,
PERPS_V2_EXCHANGE_RATE,
FUTURES_MARKET_MANAGER,
SYSTEM_STATUS
} from "script/utils/parameters/OptimismParameters.sol";
import {
OPTIMISM_GOERLI_DEPLOYER,
OPTIMISM_GOERLI_KWENTA_ADMIN_DAO_MULTI_SIG,
OPTIMISM_GOERLI_SYNTHETIX_ADDRESS_RESOLVER,
OPTIMISM_GOERLI_GELATO,
OPTIMISM_GOERLI_OPS,
OPTIMISM_GOERLI_UNISWAP_UNIVERSAL_ROUTER,
OPTIMISM_GOERLI_UNISWAP_PERMIT2
} from "script/utils/parameters/OptimismGoerliParameters.sol";

/// @title Script to deploy Kwenta's Smart Margin Account Factory
/// @author JaredBorders ([email protected])
contract Setup {
Expand All @@ -18,7 +42,9 @@ contract Setup {
address _owner,
address _addressResolver,
address _gelato,
address _ops
address _ops,
address _universalRouter,
address _permit2
)
public
returns (
Expand All @@ -28,40 +54,51 @@ contract Setup {
Account implementation
)
{
IAddressResolver addressResolver;

// define *initial* factory owner
address temporaryOwner =
_deployer == address(0) ? address(this) : _deployer;

// deploy the factory
factory = new Factory({
_owner: temporaryOwner
});
_owner: temporaryOwner
});

// deploy the events contract and set the factory
events = new Events({
_factory: address(factory)
});
_factory: address(factory)
});

// deploy the settings contract
settings = new Settings({
_owner: _owner
});
_owner: _owner
});

// resolve necessary addresses via the Synthetix Address Resolver
IAddressResolver addressResolver = IAddressResolver(_addressResolver);

implementation = new Account({
_factory: address(factory),
_events: address(events),
_marginAsset: addressResolver.getAddress({name: PROXY_SUSD}),
_perpsV2ExchangeRate: addressResolver.getAddress({name: PERPS_V2_EXCHANGE_RATE}),
_futuresMarketManager: addressResolver.getAddress({name: FUTURES_MARKET_MANAGER}),
_systemStatus: addressResolver.getAddress({name: SYSTEM_STATUS}),
_gelato: _gelato,
_ops: _ops,
_settings: address(settings)
addressResolver = IAddressResolver(_addressResolver);

IAccount.AccountConstructorParams memory params = IAccount
.AccountConstructorParams({
factory: address(factory),
events: address(events),
marginAsset: addressResolver.getAddress({name: PROXY_SUSD}),
perpsV2ExchangeRate: addressResolver.getAddress({
name: PERPS_V2_EXCHANGE_RATE
}),
futuresMarketManager: addressResolver.getAddress({
name: FUTURES_MARKET_MANAGER
}),
systemStatus: addressResolver.getAddress({name: SYSTEM_STATUS}),
gelato: _gelato,
ops: _ops,
settings: address(settings),
universalRouter: _universalRouter,
permit2: _permit2
});

implementation = new Account(params);

// update the factory with the new account implementation
factory.upgradeAccountImplementation({
_implementation: address(implementation)
Expand All @@ -82,10 +119,12 @@ contract DeployOptimism is Script, Setup {

Setup.deploySystem({
_deployer: OPTIMISM_DEPLOYER,
_owner: OPTIMISM_KWENTA_ADMIN_DAO_MULTI_SIG,
_owner: OPTIMISM_PDAO,
_addressResolver: OPTIMISM_SYNTHETIX_ADDRESS_RESOLVER,
_gelato: OPTIMISM_GELATO,
_ops: OPTIMISM_OPS
_ops: OPTIMISM_OPS,
_universalRouter: OPTIMISM_UNISWAP_UNIVERSAL_ROUTER,
_permit2: OPTIMISM_UNISWAP_PERMIT2
});

vm.stopBroadcast();
Expand All @@ -105,7 +144,9 @@ contract DeployOptimismGoerli is Script, Setup {
_owner: OPTIMISM_GOERLI_KWENTA_ADMIN_DAO_MULTI_SIG,
_addressResolver: OPTIMISM_GOERLI_SYNTHETIX_ADDRESS_RESOLVER,
_gelato: OPTIMISM_GOERLI_GELATO,
_ops: OPTIMISM_GOERLI_OPS
_ops: OPTIMISM_GOERLI_OPS,
_universalRouter: OPTIMISM_GOERLI_UNISWAP_UNIVERSAL_ROUTER,
_permit2: OPTIMISM_GOERLI_UNISWAP_PERMIT2
});

vm.stopBroadcast();
Expand Down
Loading

0 comments on commit d69a51e

Please sign in to comment.