├── .eslintignore ├── .eslintrc.json ├── .gitattributes ├── .github └── workflows │ └── run-test.yml ├── .gitignore ├── .gitmodules ├── .prettierignore ├── .prettierrc ├── .snx ├── .gitignore ├── config.json ├── feeds.json ├── futures-markets.json ├── owner-actions.json ├── params.json ├── rewards.json ├── shorting-rewards.json └── synths.json ├── .solcover.js ├── .solhint.json ├── .solhintignore ├── README.md ├── contracts ├── BaseExchangeAdapter.sol ├── GMXAdapter.sol ├── GMXFuturesPoolHedger.sol ├── LiquidityPool.sol ├── LiquidityToken.sol ├── OptionGreekCache.sol ├── OptionMarket.sol ├── OptionMarketPricer.sol ├── OptionToken.sol ├── SNXPerpV2Adapter.sol ├── SNXPerpsV2PoolHedger.sol ├── ShortCollateral.sol ├── governance-wrapper │ ├── BaseGovernanceWrapper.sol │ ├── GovernanceWrapperViewerGMX.sol │ └── modules │ │ ├── LiquidityPoolGovernanceWrapper.sol │ │ ├── OptionGreekCacheGovernanceWrapper.sol │ │ ├── OptionMarketGovernanceWrapper.sol │ │ ├── OptionMarketPricerGovernanceWrapper.sol │ │ ├── OptionTokenGovernanceWrapper.sol │ │ ├── gmx │ │ ├── GMXAdapterGovernanceWrapper.sol │ │ └── GMXHedgerGovernanceWrapper.sol │ │ └── snx │ │ ├── SNXAdapterGovernanceWrapper.sol │ │ └── SNXHedgerGovernanceWrapper.sol ├── interfaces │ ├── IAddressResolver.sol │ ├── IAggregatorV3.sol │ ├── ICollateralShort.sol │ ├── ICurve.sol │ ├── IDecimals.sol │ ├── IDelegateApprovals.sol │ ├── IERC20Decimals.sol │ ├── IExchangeRates.sol │ ├── IExchanger.sol │ ├── IFeeCounter.sol │ ├── IGWAVOracle.sol │ ├── ILiquidityPool.sol │ ├── ILiquidityTracker.sol │ ├── ILyraRegistry.sol │ ├── IOptionGreekCache.sol │ ├── IOptionMarket.sol │ ├── IOptionMarketPricer.sol │ ├── IOptionToken.sol │ ├── IShortCollateral.sol │ ├── ISwapRouter.sol │ ├── ISynthetix.sol │ ├── ISynthetixAdapter.sol │ ├── IUniswapV3Oracle.sol │ ├── IWETH.sol │ ├── gmx │ │ ├── IPositionRouter.sol │ │ ├── IPositionRouterCallbackReceiver.sol │ │ ├── IRouter.sol │ │ ├── IVault.sol │ │ ├── IVaultPriceFeed.sol │ │ └── IVaultUtils.sol │ └── perpsV2 │ │ ├── IFuturesMarketManager.sol │ │ ├── IFuturesMarketSettings.sol │ │ ├── IPerpsV2MarketBaseTypes.sol │ │ ├── IPerpsV2MarketConsolidated.sol │ │ ├── IPerpsV2MarketSettings.sol │ │ └── ISystemStatus.sol ├── libraries │ ├── BlackScholes.sol │ ├── ConvertDecimals.sol │ ├── FixedPointMathLib.sol │ ├── GWAV.sol │ ├── Math.sol │ ├── PoolHedger.sol │ └── SimpleInitializable.sol ├── periphery │ ├── BasicFeeCounter.sol │ ├── BasicLiquidityCounter.sol │ ├── GWAVOracle.sol │ ├── KeeperHelper.sol │ ├── LyraAdapter.sol │ ├── LyraRegistry.sol │ ├── MultistepSwapper.sol │ ├── OptionMarketViewer.sol │ └── Wrapper │ │ ├── BasicOptionMarketWrapper.sol │ │ ├── OptionMarketWrapper.sol │ │ └── OptionMarketWrapperWithSwaps.sol ├── synthetix │ ├── AbstractOwned.sol │ ├── DecimalMath.sol │ ├── Owned.sol │ ├── OwnedUpgradeable.sol │ └── SignedDecimalMath.sol └── test-helpers │ ├── BytesLib.sol │ ├── ITestERC20.sol │ ├── MathTest.sol │ ├── MockAggregatorV2V3.sol │ ├── OldBlackScholesMath.sol │ ├── Path.sol │ ├── ShortPoolHedger.sol │ ├── SynthetixAdapter.sol │ ├── TestBaseExchangeAdapter.sol │ ├── TestBlackScholes.sol │ ├── TestCurve.sol │ ├── TestERC20.sol │ ├── TestERC20Fail.sol │ ├── TestERC20SetDecimals.sol │ ├── TestERC20SetDecimalsFail.sol │ ├── TestFaucet.sol │ ├── TestGMXPoolHedger.sol │ ├── TestGWAV.sol │ ├── TestInaccurateSynthetixAdapter.sol │ ├── TestLyraAdapter.sol │ ├── TestShortPoolHedger.sol │ ├── TestSynthetixAdapterV2.sol │ ├── TestWETH.sol │ ├── gmx │ ├── GMXCompileImports.sol │ ├── TestGMXPositionRouter.sol │ ├── TestGMXRouter.sol │ └── TestGMXVaultChainlinkPrice.sol │ └── snx │ ├── CompileUniImports.sol │ ├── MockPerpsV2MarketConsolidated.sol │ ├── MockPyth.sol │ ├── MockSystemStatus.sol │ ├── SNXCompileImport.sol │ ├── TestAddressResolver.sol │ ├── TestCollateralShort.sol │ ├── TestDelegateApprovals.sol │ ├── TestExchangeRates.sol │ ├── TestExchanger.sol │ ├── TestFuturesMarketManager.sol │ ├── TestFuturesMarketSettings.sol │ ├── TestPerpsMarket.sol │ ├── TestSynthetix.sol │ └── TestSynthetixReturnZero.sol ├── deployments ├── .env.defaults ├── .env.private.example ├── .gitignore ├── goerli-arbi │ ├── .env.public │ ├── external.json │ ├── external.mocked.realPricingMockGmx.json │ ├── lyra.realPricingMockGmx.json │ ├── params.default.json │ ├── params.json │ └── params.realPricingMockGmx.json ├── goerli-ovm │ ├── .env.public │ ├── external.json │ ├── lyra.realSNX.json │ ├── params.realPricingMockSnx.json │ └── params.realSNX.json ├── local │ ├── .env.public │ ├── params.mockGmx.json │ └── params.mockSnx.json ├── mainnet-arbi │ ├── .env.public │ ├── external.json │ ├── lyra.realGMX.json │ ├── params.default.json │ ├── params.json │ └── params.realGMX.json └── mainnet-ovm │ ├── .env.public │ ├── external.json │ ├── lyra.realSNX.json │ ├── params.realSNX.json │ └── synthetix.json ├── examples ├── deployAndSeedLocal.ts └── goerliInteraction.ts ├── hardhat.config.ts ├── package.json ├── scripts ├── addMarketToRealGMX.ts ├── addMarketToTestGMX.ts ├── deploy │ ├── deployGMXContracts.ts │ ├── deployGMXGovernanceWrappers.ts │ ├── deploySNXContracts.ts │ ├── deployTestUSDC.ts │ ├── deployUniswap.ts │ ├── initGMXContracts.ts │ └── initSNXContracts.ts ├── deployOptionMarketWrapper.ts ├── deployRealGMX.ts ├── deployRealSNX.ts ├── deploySynthetixAdapter.ts ├── deployTestGMX.ts ├── deployTestSNX.ts ├── deployTestWithRealPricingGMX.ts ├── deployTestWithRealPricingSNX.ts ├── events │ └── index.ts ├── getEvents.ts ├── govWrapGMX.ts ├── govWrapTestGMX.ts ├── govWrapTestWithRealPricingGMX.ts ├── random-scripts │ └── realSNXIntegration.ts ├── runIntegrationTest.ts ├── seed │ ├── changeOwners.ts │ ├── createBoards.ts │ ├── exercisableBoardScenario.ts │ ├── hedgeDelta.ts │ ├── seedContracts.ts │ ├── seedDeposit.ts │ ├── seedLiquidations.ts │ ├── seedMint.ts │ ├── seedTrades.ts │ └── updateCaches.ts ├── seedRealGMX.ts ├── seedRealSNX.ts ├── seedTestGMX.ts ├── seedTestSNX.ts ├── seedTestWithRealPricing.ts ├── seedTestWithRealPricingGMX.ts └── util │ ├── index.ts │ ├── integrationFunctions.ts │ ├── maths.ts │ ├── parseFiles.ts │ ├── providers.ts │ ├── transactions.ts │ ├── verification.ts │ ├── web3utils.ts │ └── wrapperPacking.ts ├── slither.config.json ├── slither.db.json ├── test ├── contracts │ ├── BaseExchangeAdapter │ │ └── 1_Misc.ts │ ├── GMXAdapter │ │ └── 1_Misc.ts │ ├── GovernanceWrapper │ │ ├── 1_GMXHedger.ts │ │ ├── 2_GMXAdapter.ts │ │ ├── 3_GreekCache.ts │ │ ├── 4_OptionMarket.ts │ │ ├── 5_Liquiditypool.ts │ │ ├── 6_OptionToken.ts │ │ ├── 7_OptionMarketPricer.ts │ │ ├── 8_BaseGovernanceWrapper.ts │ │ └── utils.ts │ ├── GovernanceWrapperSNX │ │ ├── 1_SNXHedger.ts │ │ └── 2_SNXAdapter.ts │ ├── LiquidityPool │ │ ├── 10_Liquidity.ts │ │ ├── 11_PoolValue.ts │ │ ├── 12_TokenPriceAndSupply.ts │ │ ├── 13_LockCollateral.ts │ │ ├── 14_FreeCollateral.ts │ │ ├── 15_SwapHedger.ts │ │ ├── 16_ExchangeBase.ts │ │ ├── 17_ContractAdjustment.ts │ │ ├── 18_LiquidityPriority.ts │ │ ├── 19_Misc.ts │ │ ├── 1_Admin.ts │ │ ├── 20_EdgeCase.t.ts │ │ ├── 2_InitiateDeposit.ts │ │ ├── 3_ProcessDeposit.ts │ │ ├── 4_InitiateWithdrawal.ts │ │ ├── 5_ProcessWithdrawal.ts │ │ ├── 6_WithdrawalFee.ts │ │ ├── 7_LiquidityCB.ts │ │ ├── 8_VarianceCB.ts │ │ └── 9_OtherCB.ts │ ├── LiquidityTokens │ │ └── 1_LiquidityTokens.ts │ ├── OptionGreekCache │ │ ├── 1_Admin.ts │ │ ├── 2_SyncBoards.ts │ │ ├── 3_ForceClosePricing.ts │ │ ├── 4_MinCollatPricing.ts │ │ ├── 5_UpdateCachedGreeks.ts │ │ ├── 6_StaleCacheChecking.ts │ │ ├── 7_UpdateStrikeExposure.ts │ │ └── 8_NewStrikeGas.ts │ ├── OptionMarket │ │ ├── 10_ShortCallBase.ts │ │ ├── 1_Admin.ts │ │ ├── 2_RevertedOpen.ts │ │ ├── 3_SuccessfulOpen.ts │ │ ├── 4_RevertedClose.ts │ │ ├── 5_SuccessfulClose.ts │ │ ├── 6_ForceClose.ts │ │ ├── 7_SettleBoard.ts │ │ ├── 8_Claim.ts │ │ └── 9_LongSuccessfulOpen.ts │ ├── OptionMarketPricer │ │ ├── 1_Admin.ts │ │ ├── 2_IvImpact.ts │ │ ├── 3_TradeResult.ts │ │ ├── 4_VegaUtil.ts │ │ ├── 5_VarianceFee.ts │ │ ├── 6_Cutoffs.ts │ │ └── 7_TimeWeightFee.ts │ ├── OptionToken │ │ ├── 1_Admin.ts │ │ ├── 2_AdjustingPositions.ts │ │ ├── 3_AddCollateral.ts │ │ ├── 4_Liquidation.ts │ │ ├── 5_MinCollateral.ts │ │ ├── 6_Settle.ts │ │ ├── 7_Split.ts │ │ ├── 8_Merge.ts │ │ └── 9_Misc.ts │ ├── SNXFuturesPoolHedger │ │ ├── 1_Admin.ts │ │ ├── 2_HedgeDelta.ts │ │ ├── 3_Hedging.ts │ │ ├── 4_Collateral.ts │ │ ├── 5_SNXMock.ts │ │ └── 6_Curve.ts │ ├── SNXPerpV2Adapter │ │ ├── 1_Admin.ts │ │ └── 2_Exchange.ts │ ├── ShortCollateral │ │ ├── 1_CollateralTransfer.ts │ │ ├── 2_SettlePosition.ts │ │ ├── 3_Reclaim.ts │ │ └── 4_Excess.ts │ ├── SynthetixAdapter │ │ ├── 1_Admin.ts │ │ ├── 2_Getters.ts │ │ ├── 3_Pause.ts │ │ └── 4_Exchange.ts │ ├── USDC_quoteAsset │ │ ├── LiquidityPool │ │ │ ├── 10_Liquidity.ts │ │ │ ├── 11_PoolValue.ts │ │ │ ├── 12_TokenPriceAndSupply.ts │ │ │ ├── 13_LockCollateral.ts │ │ │ ├── 14_FreeCollateral.ts │ │ │ ├── 15_SwapHedger.ts │ │ │ ├── 16_ExchangeBase.ts │ │ │ ├── 17_ContractAdjustment.ts │ │ │ ├── 18_LiquidityPriority.ts │ │ │ ├── 1_Admin.ts │ │ │ ├── 2_InitiateDeposit.ts │ │ │ ├── 3_ProcessDeposit.ts │ │ │ ├── 4_InitiateWithdrawal.ts │ │ │ ├── 5_ProcessWithdrawal.ts │ │ │ ├── 6_WithdrawalFee.ts │ │ │ ├── 7_LiquidityCB.ts │ │ │ ├── 8_VarianceCB.ts │ │ │ └── 9_OtherCB.ts │ │ └── OptionMarket │ │ │ ├── 2_RevertedOpen.ts │ │ │ ├── 3_SuccessfulOpen.ts │ │ │ ├── 4_RevertedClose.ts │ │ │ ├── 5_SuccessfulClose.ts │ │ │ ├── 6_ForceClose.ts │ │ │ ├── 7_SettleBoard.ts │ │ │ └── 8_Claim.ts │ ├── externalChecks │ │ ├── MechanismChecks.ts │ │ └── mechanismTestResults.ts │ ├── gmx_integration │ │ └── IntegrationTestsGMX.ts │ ├── libraries │ │ ├── BlackScholes.ts │ │ ├── GWAV.ts │ │ └── MathTest.ts │ └── periphery │ │ ├── GWAVOracle.ts │ │ ├── KeeperHelper │ │ ├── 1_packedFunctions.ts │ │ ├── 2_settlePositions.ts │ │ └── 3_updateCache.ts │ │ ├── LyraAdapter.ts │ │ ├── LyraRegistry.ts │ │ ├── MiscPeriphery.ts │ │ ├── MultistepSwapper.ts │ │ ├── OptionMarketViewer.ts │ │ └── OptionMarketWrapper │ │ ├── 1_LongPositions.ts │ │ ├── 2_ShortCallBase.ts │ │ ├── 3_ShortCallQuote.ts │ │ ├── 4_ShortPutQuote.ts │ │ ├── 5_MiscFunctions.ts │ │ ├── 6_EthShortCallBaseGMX.ts │ │ └── 7_EnsureConvertRounding.ts ├── integration │ ├── integrationTests.ts │ ├── integrationTestsSNX.ts │ ├── snxPerpIntegration.ts │ └── util │ │ ├── cannonHelpers.ts │ │ ├── externals.json │ │ └── writeExternals.ts └── utils │ ├── arrayCombiner.ts │ ├── assert.ts │ ├── blackScholes.ts │ ├── contractHelpers │ ├── boards.ts │ ├── fees.ts │ ├── hedging.ts │ ├── index.ts │ ├── keeperHelperPacking.ts │ ├── liquidity.ts │ ├── openClose.ts │ ├── parameters.ts │ ├── synthetix.ts │ └── wrapper.ts │ ├── defaultParams.ts │ ├── deployTestSystem.ts │ ├── deployTestSystemGMX.ts │ ├── evm.ts │ ├── fixture.ts │ ├── gmxHelpers.ts │ ├── package │ ├── index-artifacts.ts │ ├── index-paths.ts │ ├── index.ts │ ├── merge.ts │ ├── parseFiles.ts │ ├── prepare-artifacts.ts │ ├── realGMXUtils.ts │ └── realSynthetixUtils.ts │ ├── seedTestSystem.ts │ ├── seedTestSystemGMX.ts │ └── testSetup.ts ├── tsconfig.json ├── types.d.ts └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | # folders 2 | artifacts/ 3 | build/ 4 | cache/ 5 | coverage/ 6 | dist/ 7 | lib/ 8 | node_modules/ 9 | typechain/ 10 | typechain-types/ 11 | test-old/ 12 | contracts/interfaces/ 13 | contracts/synthetix/ 14 | contracts/test-helpers/ 15 | random-scripts/ 16 | 17 | # files 18 | .solcover.js 19 | coverage.json 20 | package.json 21 | docify.js 22 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "eslint:recommended", 4 | "plugin:@typescript-eslint/eslint-recommended", 5 | "plugin:@typescript-eslint/recommended", 6 | "prettier" 7 | ], 8 | "parser": "@typescript-eslint/parser", 9 | "parserOptions": { 10 | "project": "tsconfig.json" 11 | }, 12 | "plugins": ["@typescript-eslint"], 13 | "root": true, 14 | "rules": { 15 | "@typescript-eslint/no-explicit-any": "off", 16 | "@typescript-eslint/no-floating-promises": [ 17 | "error", 18 | { 19 | "ignoreIIFE": true, 20 | "ignoreVoid": true 21 | } 22 | ], 23 | "@typescript-eslint/no-inferrable-types": "off", 24 | "@typescript-eslint/no-unused-vars": [ 25 | "error", 26 | { 27 | "argsIgnorePattern": "_", 28 | "varsIgnorePattern": "_" 29 | } 30 | ] 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /.github/workflows/run-test.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the main branch 8 | push: 9 | branches: [ master ] 10 | pull_request: 11 | branches: [ master ] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 17 | jobs: 18 | testcoverage: 19 | if: github.event.pull_request.draft == false # Don't run if PR is draft 20 | name: Test and coverage 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@v2 24 | with: 25 | submodules: recursive 26 | - uses: actions/setup-node@v2 27 | with: 28 | node-version: '14' 29 | - run: yarn global add node-gyp-cache 30 | - run: yarn config set node_gyp node-gyp-cache 31 | - run: yarn install --frozen-lockfile 32 | - run: yarn compile 33 | - run: yarn coverage 34 | - name: Upload coverage to Codecov 35 | uses: codecov/codecov-action@v1 36 | with: 37 | token: 177c7477-b961-48a3-811f-49494a3d5a41 38 | directory: ./coverage/ 39 | flags: unittests 40 | name: codecov-umbrella 41 | fail_ci_if_error: false 42 | verbose: true 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | .lyra/ 3 | .idea/ 4 | .openzeppelin 5 | node_modules/ 6 | **/.DS_Store 7 | local/ 8 | #Hardhat files 9 | cache/ 10 | out/ 11 | artifacts/ 12 | typechain/ 13 | artifacts-ovm/ 14 | typechain-types/ 15 | coverage/ 16 | coverage.json 17 | .env.private* 18 | yarn-error.log 19 | .vscode/ 20 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/listing-manager"] 2 | path = lib/listing-manager 3 | url = https://github.com/lyra-finance/listing-manager.git 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # folders 2 | artifacts/ 3 | build/ 4 | cache/ 5 | coverage/ 6 | dist/ 7 | lib/ 8 | node_modules/ 9 | typechain/ 10 | typechain-types/ 11 | contracts/mocks/ 12 | contracts/test/ 13 | random-scripts/ 14 | 15 | # files 16 | coverage.json 17 | package.json 18 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "proseWrap": "preserve", 4 | "singleQuote": true, 5 | "useTabs": false, 6 | "tabWidth": 2, 7 | "arrowParens": "avoid", 8 | "semi": true, 9 | "trailingComma": "all", 10 | "overrides": [ 11 | { 12 | "files": "*.sol", 13 | "options": { 14 | "printWidth": 120, 15 | "tabWidth": 2, 16 | "useTabs": false, 17 | "bracketSpacing": false, 18 | "singleQuote": false 19 | } 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /.snx/.gitignore: -------------------------------------------------------------------------------- 1 | **/compiled/ 2 | **/flattened/ 3 | deployment.json -------------------------------------------------------------------------------- /.snx/feeds.json: -------------------------------------------------------------------------------- 1 | { 2 | "SNX": { "asset": "SNX", "feed": "0x0" }, 3 | "ETH": { "asset": "ETH", "feed": "0x0" }, 4 | "BTC": { "asset": "BTC", "feed": "0x0" } 5 | } 6 | -------------------------------------------------------------------------------- /.snx/futures-markets.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "marketKey": "sBTC", 4 | "asset": "sBTC", 5 | "takerFee": "0.003", 6 | "makerFee": "0.002", 7 | "takerFeeNextPrice": "0.001", 8 | "makerFeeNextPrice": "0.0", 9 | "nextPriceConfirmWindow": "2", 10 | "maxLeverage": "10", 11 | "maxMarketValueUSD": "20000000", 12 | "maxFundingRate": "0.1", 13 | "skewScaleUSD": "300000000" 14 | }, 15 | { 16 | "marketKey": "sETH", 17 | "asset": "sETH", 18 | "takerFee": "0.003", 19 | "makerFee": "0.002", 20 | "takerFeeNextPrice": "0.001", 21 | "makerFeeNextPrice": "0.0", 22 | "nextPriceConfirmWindow": "2", 23 | "maxLeverage": "10", 24 | "maxMarketValueUSD": "20000000", 25 | "maxFundingRate": "0.1", 26 | "skewScaleUSD": "300000000" 27 | } 28 | ] 29 | -------------------------------------------------------------------------------- /.snx/owner-actions.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /.snx/params.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /.snx/rewards.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "sETHUniswapV1", 4 | "stakingToken": "0xe9cf7887b93150d4f2da7dfc6d502b216438f244", 5 | "rewardsToken": "Synthetix" 6 | }, 7 | { 8 | "name": "sXAUUniswapV2", 9 | "stakingToken": "0x34a0216C5057bC18e5d34D4405284564eFd759b2", 10 | "rewardsToken": "Synthetix" 11 | }, 12 | { 13 | "name": "sUSDCurve", 14 | "stakingToken": "0xc25a3a3b969415c80451098fa907ec722572917f", 15 | "rewardsToken": "Synthetix" 16 | }, 17 | { 18 | "name": "SNXBalancer", 19 | "stakingToken": "0x815f8ef4863451f4faf34fbc860034812e7377d9", 20 | "rewardsToken": "Synthetix" 21 | } 22 | ] 23 | -------------------------------------------------------------------------------- /.snx/shorting-rewards.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "sETH", 4 | "rewardsToken": "Synthetix" 5 | }, 6 | { 7 | "name": "sBTC", 8 | "rewardsToken": "Synthetix" 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /.snx/synths.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "asset": "USD", 4 | "category": "forex", 5 | "sign": "$", 6 | "description": "US Dollars", 7 | "name": "sUSD", 8 | "subclass": "MultiCollateralSynth" 9 | }, 10 | { 11 | "feed": "0x0", 12 | "asset": "BTC", 13 | "category": "crypto", 14 | "sign": "₿", 15 | "description": "Bitcoin", 16 | "name": "sBTC", 17 | "subclass": "MultiCollateralSynth" 18 | }, 19 | { 20 | "feed": "0x0", 21 | "asset": "ETH", 22 | "category": "crypto", 23 | "sign": "Ξ", 24 | "description": "Ether", 25 | "name": "sETH", 26 | "subclass": "MultiCollateralSynth" 27 | } 28 | ] 29 | -------------------------------------------------------------------------------- /.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // skipFiles: ['./test-helpers', './periphery', './synthetix', './openzeppelin-l2', 'interfaces'], 3 | skipFiles: [ 4 | './test-helpers', 5 | './synthetix', 6 | './openzeppelin-l2', 7 | './contracts/periphery/Wrapper/BasicOptionMarketWrapper.sol', 8 | './contracts/periphery/BasicFeeCounter.sol', 9 | './contracts/periphery/BasicLiquidityCounter.sol', 10 | 'interfaces', 11 | ], 12 | configureYulOptimizer: true, 13 | }; 14 | -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "plugins": ["prettier"], 4 | "rules": { 5 | "compiler-version": ["error", "^0.8.9"], 6 | "const-name-snakecase": "off", 7 | "constructor-syntax": "error", 8 | "func-visibility": ["error", { "ignoreConstructors": true }], 9 | "no-empty-blocks": "off", 10 | "max-line-length": ["warn", 120], 11 | "max-states-count": "off", 12 | "not-rely-on-time": "off", 13 | "prettier/prettier": [ 14 | "error", 15 | { 16 | "endOfLine": "auto" 17 | } 18 | ], 19 | "reason-string": ["warn", { "maxLength": 64 }], 20 | "event-name-camelcase": "warn", 21 | "var-name-mixedcase": "off" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.solhintignore: -------------------------------------------------------------------------------- 1 | # folders 2 | .yarn/ 3 | build/ 4 | dist/ 5 | typechain-types/ 6 | node_modules/ 7 | contracts/mocks/* 8 | contracts/test/* 9 | -------------------------------------------------------------------------------- /contracts/LiquidityToken.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | // Libraries 5 | import "./synthetix/DecimalMath.sol"; 6 | 7 | // Inherited 8 | import "openzeppelin-contracts-4.4.1/token/ERC20/ERC20.sol"; 9 | import "./synthetix/Owned.sol"; 10 | import "./libraries/SimpleInitializable.sol"; 11 | 12 | // Interfaces 13 | import "./interfaces/ILiquidityTracker.sol"; 14 | 15 | /** 16 | * @title LiquidityToken 17 | * @author Lyra 18 | * @dev An ERC20 token which represents a share of the LiquidityPool. 19 | * It is minted when users deposit, and burned when users withdraw. 20 | */ 21 | contract LiquidityToken is ERC20, Owned, SimpleInitializable { 22 | using DecimalMath for uint; 23 | 24 | /// @dev The liquidityPool for which these tokens represent a share of 25 | address public liquidityPool; 26 | /// @dev Contract to call when liquidity gets updated. Basically a hook for future contracts to use. 27 | ILiquidityTracker public liquidityTracker; 28 | 29 | /////////// 30 | // Setup // 31 | /////////// 32 | 33 | /** 34 | * @param name_ Token collection name 35 | * @param symbol_ Token collection symbol 36 | */ 37 | constructor(string memory name_, string memory symbol_) ERC20(name_, symbol_) Owned() {} 38 | 39 | /** 40 | * @dev Initialize the contract. 41 | * @param _liquidityPool LiquidityPool address 42 | */ 43 | function init(address _liquidityPool) external onlyOwner initializer { 44 | liquidityPool = _liquidityPool; 45 | } 46 | 47 | /////////// 48 | // Admin // 49 | /////////// 50 | 51 | function setLiquidityTracker(ILiquidityTracker _liquidityTracker) external onlyOwner { 52 | liquidityTracker = _liquidityTracker; 53 | emit LiquidityTrackerSet(liquidityTracker); 54 | } 55 | 56 | //////////////////////// 57 | // Only LiquidityPool // 58 | //////////////////////// 59 | 60 | /** 61 | * @dev Mints new tokens and transfers them to `owner`. 62 | */ 63 | function mint(address account, uint tokenAmount) external onlyLiquidityPool { 64 | _mint(account, tokenAmount); 65 | } 66 | 67 | /** 68 | * @dev Burn new tokens and transfers them to `owner`. 69 | */ 70 | function burn(address account, uint tokenAmount) external onlyLiquidityPool { 71 | _burn(account, tokenAmount); 72 | } 73 | 74 | ////////// 75 | // Misc // 76 | ////////// 77 | /** 78 | * @dev Override to track the liquidty of the token. Mint, address(0), burn - to, address(0) 79 | */ 80 | function _afterTokenTransfer(address from, address to, uint amount) internal override { 81 | if (address(liquidityTracker) != address(0)) { 82 | if (from != address(0)) { 83 | liquidityTracker.removeTokens(from, amount); 84 | } 85 | if (to != address(0)) { 86 | liquidityTracker.addTokens(to, amount); 87 | } 88 | } 89 | } 90 | 91 | /////////////// 92 | // Modifiers // 93 | /////////////// 94 | 95 | modifier onlyLiquidityPool() { 96 | if (msg.sender != liquidityPool) { 97 | revert OnlyLiquidityPool(address(this), msg.sender, liquidityPool); 98 | } 99 | _; 100 | } 101 | 102 | //////////// 103 | // Events // 104 | //////////// 105 | event LiquidityTrackerSet(ILiquidityTracker liquidityTracker); 106 | 107 | //////////// 108 | // Errors // 109 | //////////// 110 | // Access 111 | error OnlyLiquidityPool(address thrower, address caller, address liquidityPool); 112 | } 113 | -------------------------------------------------------------------------------- /contracts/governance-wrapper/BaseGovernanceWrapper.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | // Inherited 5 | import "../synthetix/Owned.sol"; 6 | 7 | /** 8 | * @title BaseGovernanceWrapper 9 | * @author Lyra 10 | * @dev Base contract for managing access to exchange functions. 11 | */ 12 | contract BaseGovernanceWrapper is Owned { 13 | address public riskCouncil; 14 | 15 | constructor() Owned() {} 16 | 17 | function setRiskCouncil(address _riskCouncil) external onlyOwner { 18 | if (address(0) == _riskCouncil) { 19 | revert("Zero address"); 20 | } 21 | 22 | riskCouncil = _riskCouncil; 23 | emit BGW_RiskCouncilSet(riskCouncil); 24 | } 25 | 26 | /// @notice Allow owner to replace the owner of any arbitrary contract; a manual override. 27 | function forceChangeOwner(Owned ownedContract, address replacementOwner) external onlyOwner { 28 | ownedContract.nominateNewOwner(replacementOwner); 29 | emit BGW_ForceChangeOwner(address(ownedContract), replacementOwner); 30 | } 31 | 32 | //////////// 33 | // Access // 34 | //////////// 35 | function _onlyRiskCouncilOrOwner() internal view { 36 | if (msg.sender != owner && msg.sender != riskCouncil) { 37 | revert BGW_OnlyOwnerOrRiskCouncil(msg.sender, owner, riskCouncil); 38 | } 39 | } 40 | 41 | modifier onlyRiskCouncilOrOwner() { 42 | _onlyRiskCouncilOrOwner(); 43 | _; 44 | } 45 | 46 | //////////// 47 | // Events // 48 | //////////// 49 | event BGW_RiskCouncilSet(address newRiskCouncil); 50 | event BGW_ForceChangeOwner(address indexed ownedContract, address replcementOwner); 51 | 52 | //////////// 53 | // Errors // 54 | //////////// 55 | 56 | error BGW_OnlyOwnerOrRiskCouncil(address caller, address owner, address riskCouncil); 57 | } 58 | -------------------------------------------------------------------------------- /contracts/governance-wrapper/GovernanceWrapperViewerGMX.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | // Inherited 5 | import "../synthetix/Owned.sol"; 6 | import "./modules/gmx/GMXAdapterGovernanceWrapper.sol"; 7 | import "./modules/gmx/GMXHedgerGovernanceWrapper.sol"; 8 | import "./modules/LiquidityPoolGovernanceWrapper.sol"; 9 | import "./modules/OptionGreekCacheGovernanceWrapper.sol"; 10 | import "./modules/OptionMarketGovernanceWrapper.sol"; 11 | import "./modules/OptionMarketPricerGovernanceWrapper.sol"; 12 | import "./modules/OptionTokenGovernanceWrapper.sol"; 13 | 14 | /** 15 | * @title BaseGovernanceWrapper 16 | * @author Lyra 17 | * @dev Base contract for managing access to exchange functions. 18 | */ 19 | contract GovernanceWrapperViewerGMX is Owned { 20 | struct GMXGovernanceWrappers { 21 | GMXAdapterGovernanceWrapper gmxAdapterGovernanceWrapper; 22 | GMXHedgerGovernanceWrapper gmxHedgerGovernanceWrapper; 23 | LiquidityPoolGovernanceWrapper liquidityPoolGovernanceWrapper; 24 | OptionGreekCacheGovernanceWrapper optionGreekCacheGovernanceWrapper; 25 | OptionMarketGovernanceWrapper optionMarketGovernanceWrapper; 26 | OptionMarketPricerGovernanceWrapper optionMarketPricerGovernanceWrapper; 27 | OptionTokenGovernanceWrapper optionTokenGovernanceWrapper; 28 | } 29 | 30 | mapping(address => GMXGovernanceWrappers) public marketWrappers; 31 | 32 | constructor() Owned() {} 33 | 34 | function addGMXGovernanceWrappers(address optionMarket, GMXGovernanceWrappers memory govWrappers) external onlyOwner { 35 | marketWrappers[optionMarket] = govWrappers; 36 | } 37 | 38 | function getAllBounds( 39 | address optionMarket 40 | ) 41 | external 42 | view 43 | returns ( 44 | GMXGovernanceWrappers memory wrapperAddresses, 45 | GMXAdapterGovernanceWrapper.GMXAdapterBounds memory adapterBounds, 46 | GMXHedgerGovernanceWrapper.HedgerBounds memory hedgerBounds, 47 | LiquidityPoolGovernanceWrapper.LiquidityPoolBounds memory liquidityPoolBounds, 48 | OptionGreekCacheGovernanceWrapper.GreekCacheBounds memory greekCacheBounds, 49 | OptionMarketGovernanceWrapper.OptionMarketBounds memory optionMarketBounds, 50 | OptionMarketPricerGovernanceWrapper.OptionMarketPricerBounds memory optionMarketPricerBounds, 51 | OptionTokenGovernanceWrapper.OptionTokenBounds memory optionTokenBounds, 52 | address boardManager, 53 | address optionMarketRiskCouncil 54 | ) 55 | { 56 | GMXGovernanceWrappers memory wrappers = marketWrappers[optionMarket]; 57 | return ( 58 | wrappers, 59 | wrappers.gmxAdapterGovernanceWrapper.getAdapterBounds(OptionMarket(optionMarket)), 60 | wrappers.gmxHedgerGovernanceWrapper.getHedgerBounds(), 61 | wrappers.liquidityPoolGovernanceWrapper.getLiquidityPoolBounds(), 62 | wrappers.optionGreekCacheGovernanceWrapper.getGreekCacheBounds(), 63 | wrappers.optionMarketGovernanceWrapper.getOptionMarketBounds(), 64 | wrappers.optionMarketPricerGovernanceWrapper.getOptionMarketPricerBounds(), 65 | wrappers.optionTokenGovernanceWrapper.getOptionTokenBounds(), 66 | wrappers.optionMarketGovernanceWrapper.boardManager(), 67 | wrappers.optionMarketGovernanceWrapper.riskCouncil() 68 | ); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /contracts/interfaces/IAddressResolver.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.16; 3 | 4 | // https://docs.synthetix.io/contracts/source/interfaces/iaddressresolver 5 | interface IAddressResolver { 6 | function getAddress(bytes32 name) external view returns (address); 7 | } 8 | -------------------------------------------------------------------------------- /contracts/interfaces/IAggregatorV3.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | interface AggregatorInterface { 5 | function latestAnswer() external view returns (int); 6 | 7 | function latestRound() external view returns (uint); 8 | 9 | function getAnswer(uint roundId) external view returns (int); 10 | 11 | function getTimestamp(uint roundId) external view returns (uint); 12 | } 13 | 14 | interface AggregatorV3Interface { 15 | function decimals() external view returns (uint8); 16 | 17 | function getRoundData( 18 | uint80 _roundId 19 | ) external view returns (uint80 roundId, int answer, uint startedAt, uint updatedAt, uint80 answeredInRound); 20 | 21 | function latestRoundData() 22 | external 23 | view 24 | returns (uint80 roundId, int answer, uint startedAt, uint updatedAt, uint80 answeredInRound); 25 | } 26 | 27 | interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface {} 28 | -------------------------------------------------------------------------------- /contracts/interfaces/ICollateralShort.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | interface ICollateralShort { 5 | struct Loan { 6 | // ID for the loan 7 | uint id; 8 | // Account that created the loan 9 | address account; 10 | // Amount of collateral deposited 11 | uint collateral; 12 | // The synth that was borrowed 13 | bytes32 currency; 14 | // Amount of synths borrowed 15 | uint amount; 16 | // Indicates if the position was short sold 17 | bool short; 18 | // interest amounts accrued 19 | uint accruedInterest; 20 | // last interest index 21 | uint interestIndex; 22 | // time of last interaction. 23 | uint lastInteraction; 24 | } 25 | 26 | function loans(uint id) external returns (uint, address, uint, bytes32, uint, bool, uint, uint, uint); 27 | 28 | function minCratio() external returns (uint); 29 | 30 | function minCollateral() external returns (uint); 31 | 32 | function issueFeeRate() external returns (uint); 33 | 34 | function open(uint collateral, uint amount, bytes32 currency) external returns (uint id); 35 | 36 | function repay(address borrower, uint id, uint amount) external returns (uint short, uint collateral); 37 | 38 | function repayWithCollateral(uint id, uint repayAmount) external returns (uint short, uint collateral); 39 | 40 | function draw(uint id, uint amount) external returns (uint short, uint collateral); 41 | 42 | // Same as before 43 | function deposit(address borrower, uint id, uint amount) external returns (uint short, uint collateral); 44 | 45 | // Same as before 46 | function withdraw(uint id, uint amount) external returns (uint short, uint collateral); 47 | 48 | // function to return the loan details in one call, without needing to know about the collateralstate 49 | function getShortAndCollateral(address account, uint id) external view returns (uint short, uint collateral); 50 | } 51 | -------------------------------------------------------------------------------- /contracts/interfaces/ICurve.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | interface ICurve { 5 | function exchange_with_best_rate( 6 | address _from, 7 | address _to, 8 | uint _amount, 9 | uint _expected, 10 | address _receiver 11 | ) external payable returns (uint amountOut); 12 | 13 | function exchange_underlying( 14 | int128 _from, 15 | int128 _to, 16 | uint _amount, 17 | uint _expected 18 | ) external payable returns (uint amountOut); 19 | 20 | function get_best_rate(address _from, address _to, uint _amount) external view returns (address pool, uint amountOut); 21 | } 22 | -------------------------------------------------------------------------------- /contracts/interfaces/IDecimals.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity >0.7.0; 3 | 4 | /** 5 | * @dev return decimals of a token 6 | */ 7 | interface IDecimals { 8 | /** 9 | * @dev Returns the number of decimals used to get its user representation. 10 | * For example, if `decimals` equals `2`, a balance of `505` tokens should 11 | * be displayed to a user as `5.05` (`505 / 10 ** 2`). 12 | * 13 | * Tokens usually opt for a value of 18, imitating the relationship between 14 | * Ether and Wei. This is the value {ERC20} uses, unless this function is 15 | * overridden; 16 | * 17 | * NOTE: This information is only used for _display_ purposes: it in 18 | * no way affects any of the arithmetic of the contract, including 19 | * {IERC20-balanceOf} and {IERC20-transfer}. 20 | */ 21 | function decimals() external view returns (uint8); 22 | } 23 | -------------------------------------------------------------------------------- /contracts/interfaces/IDelegateApprovals.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.16; 3 | 4 | interface IDelegateApprovals { 5 | function approveExchangeOnBehalf(address delegate) external; 6 | 7 | function canExchangeOnBehalf(address exchanger, address beneficiary) external view returns (bool); 8 | } 9 | -------------------------------------------------------------------------------- /contracts/interfaces/IERC20Decimals.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity ^0.8.0; 3 | 4 | import "openzeppelin-contracts-4.4.1/token/ERC20/IERC20.sol"; 5 | 6 | /** 7 | * @dev Optional functions from the ERC20 standard. 8 | */ 9 | interface IERC20Decimals is IERC20 { 10 | /** 11 | * @dev Returns the name of the token. 12 | */ 13 | function name() external view returns (string memory); 14 | 15 | /** 16 | * @dev Returns the symbol of the token, usually a shorter version of the 17 | * name. 18 | */ 19 | function symbol() external view returns (string memory); 20 | 21 | /** 22 | * @dev Returns the number of decimals used to get its user representation. 23 | * For example, if `decimals` equals `2`, a balance of `505` tokens should 24 | * be displayed to a user as `5.05` (`505 / 10 ** 2`). 25 | * 26 | * Tokens usually opt for a value of 18, imitating the relationship between 27 | * Ether and Wei. This is the value {ERC20} uses, unless this function is 28 | * overridden; 29 | * 30 | * NOTE: This information is only used for _display_ purposes: it in 31 | * no way affects any of the arithmetic of the contract, including 32 | * {IERC20-balanceOf} and {IERC20-transfer}. 33 | */ 34 | function decimals() external view returns (uint8); 35 | } 36 | -------------------------------------------------------------------------------- /contracts/interfaces/IExchangeRates.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier:MIT 2 | pragma solidity 0.8.16; 3 | 4 | // https://docs.synthetix.io/contracts/source/interfaces/iexchangerates 5 | interface IExchangeRates { 6 | function rateAndInvalid(bytes32 currencyKey) external view returns (uint rate, bool isInvalid); 7 | } 8 | -------------------------------------------------------------------------------- /contracts/interfaces/IExchanger.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier:MIT 2 | pragma solidity 0.8.16; 3 | 4 | // https://docs.synthetix.io/contracts/source/interfaces/iexchanger 5 | interface IExchanger { 6 | function feeRateForExchange( 7 | bytes32 sourceCurrencyKey, 8 | bytes32 destinationCurrencyKey 9 | ) external view returns (uint exchangeFeeRate); 10 | } 11 | -------------------------------------------------------------------------------- /contracts/interfaces/IFeeCounter.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | interface IFeeCounter { 5 | function trackFee(address market, address trader, uint amount, uint totalCost, uint totalFee) external; 6 | } 7 | -------------------------------------------------------------------------------- /contracts/interfaces/IGWAVOracle.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier:ISC 2 | 3 | pragma solidity 0.8.16; 4 | 5 | // For full documentation refer to @lyrafinance/protocol/contracts/periphery/GWAVOracle.sol"; 6 | 7 | interface IGWAVOracle { 8 | function ivGWAV(uint boardId, uint secondsAgo) external view returns (uint); 9 | 10 | function skewGWAV(uint strikeId, uint secondsAgo) external view returns (uint); 11 | 12 | function volGWAV(uint strikeId, uint secondsAgo) external view returns (uint); 13 | 14 | function deltaGWAV(uint strikeId, uint secondsAgo) external view returns (int callDelta); 15 | 16 | function vegaGWAV(uint strikeId, uint secondsAgo) external view returns (uint vega); 17 | 18 | function optionPriceGWAV(uint strikeId, uint secondsAgo) external view returns (uint callPrice, uint putPrice); 19 | } 20 | -------------------------------------------------------------------------------- /contracts/interfaces/ILiquidityTracker.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | // https://docs.synthetix.io/contracts/source/interfaces/iaddressresolver 5 | interface ILiquidityTracker { 6 | function addTokens(address trader, uint amount) external; 7 | 8 | function removeTokens(address trader, uint amount) external; 9 | } 10 | -------------------------------------------------------------------------------- /contracts/interfaces/ILyraRegistry.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier:ISC 2 | 3 | pragma solidity 0.8.16; 4 | 5 | import "openzeppelin-contracts-4.4.1/token/ERC20/IERC20.sol"; 6 | 7 | // For full documentation refer to @lyrafinance/protocol/contracts/periphery/LyraRegistry.sol"; 8 | /// @dev inputs/returns that contain Lyra contracts replaced with addresses (as opposed to LyraRegistry.sol) 9 | /// so that interacting contracts are not required to import Lyra contracts 10 | interface ILyraRegistry { 11 | struct OptionMarketAddresses { 12 | address liquidityPool; 13 | address liquidityToken; 14 | address greekCache; 15 | address optionMarket; 16 | address optionMarketPricer; 17 | address optionToken; 18 | address poolHedger; 19 | address shortCollateral; 20 | address gwavOracle; 21 | IERC20 quoteAsset; 22 | IERC20 baseAsset; 23 | } 24 | 25 | function optionMarkets() external view returns (address[] memory); 26 | 27 | function marketAddress(address market) external view returns (OptionMarketAddresses memory); 28 | 29 | function globalAddresses(bytes32 name) external view returns (address); 30 | 31 | function getMarketAddresses(address optionMarket) external view returns (OptionMarketAddresses memory); 32 | 33 | function getGlobalAddress(bytes32 contractName) external view returns (address globalContract); 34 | 35 | event GlobalAddressUpdated(bytes32 indexed name, address addr); 36 | 37 | event MarketUpdated(address indexed optionMarket, OptionMarketAddresses market); 38 | 39 | event MarketRemoved(address indexed market); 40 | 41 | error RemovingInvalidMarket(address thrower, address market); 42 | 43 | error NonExistentMarket(address optionMarket); 44 | 45 | error NonExistentGlobalContract(bytes32 contractName); 46 | } 47 | -------------------------------------------------------------------------------- /contracts/interfaces/IShortCollateral.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | 3 | pragma solidity 0.8.16; 4 | 5 | // Interfaces 6 | import "./IOptionMarket.sol"; 7 | import "./IOptionToken.sol"; 8 | 9 | // For full documentation refer to @lyrafinance/protocol/contracts/ShortCollateral.sol"; 10 | 11 | interface IShortCollateral { 12 | // The amount the SC underpaid the LP due to insolvency. 13 | // The SC will take this much less from the LP when settling insolvent positions. 14 | function LPBaseExcess() external view returns (uint); 15 | 16 | function LPQuoteExcess() external view returns (uint); 17 | 18 | ///////////////////////// 19 | // Position Settlement // 20 | ///////////////////////// 21 | 22 | function settleOptions(uint[] memory positionIds) external; 23 | 24 | //////////// 25 | // Events // 26 | //////////// 27 | 28 | /// @dev Emitted when a board is settled 29 | event BoardSettlementCollateralSent( 30 | uint amountBaseSent, 31 | uint amountQuoteSent, 32 | uint lpBaseInsolvency, 33 | uint lpQuoteInsolvency, 34 | uint LPBaseExcess, 35 | uint LPQuoteExcess 36 | ); 37 | 38 | /** 39 | * @dev Emitted when an Option is settled. 40 | */ 41 | event PositionSettled( 42 | uint indexed positionId, 43 | address indexed settler, 44 | address indexed optionOwner, 45 | uint strikePrice, 46 | uint priceAtExpiry, 47 | IOptionMarket.OptionType optionType, 48 | uint amount, 49 | uint settlementAmount, 50 | uint insolventAmount 51 | ); 52 | 53 | /** 54 | * @dev Emitted when quote is sent to either a user or the LiquidityPool 55 | */ 56 | event QuoteSent(address indexed receiver, uint amount); 57 | /** 58 | * @dev Emitted when base is sent to either a user or the LiquidityPool 59 | */ 60 | event BaseSent(address indexed receiver, uint amount); 61 | 62 | event BaseExchangedAndQuoteSent(address indexed recipient, uint amountBase, uint quoteReceived); 63 | 64 | //////////// 65 | // Errors // 66 | //////////// 67 | 68 | // Collateral transfers 69 | error OutOfQuoteCollateralForTransfer(address thrower, uint balance, uint amount); 70 | error OutOfBaseCollateralForTransfer(address thrower, uint balance, uint amount); 71 | error OutOfBaseCollateralForExchangeAndTransfer(address thrower, uint balance, uint amount); 72 | 73 | // Token transfers 74 | error BaseTransferFailed(address thrower, address from, address to, uint amount); 75 | error QuoteTransferFailed(address thrower, address from, address to, uint amount); 76 | 77 | // Access 78 | error BoardMustBeSettled(address thrower, IOptionToken.PositionWithOwner position); 79 | error OnlyOptionMarket(address thrower, address caller, address optionMarket); 80 | } 81 | -------------------------------------------------------------------------------- /contracts/interfaces/ISwapRouter.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: GPL-2.0-or-later 2 | pragma solidity 0.8.16; 3 | 4 | /// @title Callback for IUniswapV3PoolActions#swap 5 | /// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface 6 | interface IUniswapV3SwapCallback { 7 | /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap. 8 | /// @dev In the implementation you must pay the pool tokens owed for the swap. 9 | /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. 10 | /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped. 11 | /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by 12 | /// the end of the swap. If positive, the callback must send that amount of token0 to the pool. 13 | /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by 14 | /// the end of the swap. If positive, the callback must send that amount of token1 to the pool. 15 | /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call 16 | function uniswapV3SwapCallback(int amount0Delta, int amount1Delta, bytes calldata data) external; 17 | } 18 | 19 | /// @title Router token swapping functionality 20 | /// @notice Functions for swapping tokens via Uniswap V3 21 | interface ISwapRouter is IUniswapV3SwapCallback { 22 | struct ExactInputParams { 23 | bytes path; 24 | address recipient; 25 | uint deadline; 26 | uint amountIn; 27 | uint amountOutMinimum; 28 | } 29 | 30 | /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path 31 | /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata 32 | /// @return amountOut The amount of the received token 33 | function exactInput(ExactInputParams calldata params) external payable returns (uint amountOut); 34 | } 35 | -------------------------------------------------------------------------------- /contracts/interfaces/ISynthetix.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.16; 3 | 4 | interface ISynthetix { 5 | function exchange( 6 | bytes32 sourceCurrencyKey, 7 | uint sourceAmount, 8 | bytes32 destinationCurrencyKey 9 | ) external returns (uint amountReceived); 10 | 11 | function exchangeOnBehalfWithTracking( 12 | address exchangeForAddress, 13 | bytes32 sourceCurrencyKey, 14 | uint sourceAmount, 15 | bytes32 destinationCurrencyKey, 16 | address rewardAddress, 17 | bytes32 trackingCode 18 | ) external returns (uint amountReceived); 19 | } 20 | -------------------------------------------------------------------------------- /contracts/interfaces/IUniswapV3Oracle.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity >0.7.0; 3 | 4 | interface IUniswapV3Oracle { 5 | /** 6 | * @dev return price of a uniswap pair. price is scaled by 1e18 7 | */ 8 | function getTwap(address pool, address base, address quote, uint32 period) external view returns (uint); 9 | } 10 | -------------------------------------------------------------------------------- /contracts/interfaces/IWETH.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | import "./IERC20Decimals.sol"; 3 | 4 | pragma solidity 0.8.16; 5 | 6 | interface IWETH is IERC20Decimals { 7 | receive() external payable; 8 | 9 | function deposit() external payable; 10 | 11 | function withdraw(uint wad) external; 12 | } 13 | -------------------------------------------------------------------------------- /contracts/interfaces/gmx/IPositionRouterCallbackReceiver.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | 3 | pragma solidity 0.8.16; 4 | 5 | interface IPositionRouterCallbackReceiver { 6 | function gmxPositionCallback(bytes32 positionKey, bool isExecuted, bool isIncrease) external; 7 | } 8 | -------------------------------------------------------------------------------- /contracts/interfaces/gmx/IRouter.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | 3 | pragma solidity 0.8.16; 4 | 5 | interface IRouter { 6 | function addPlugin(address _plugin) external; 7 | 8 | function pluginTransfer(address _token, address _account, address _receiver, uint _amount) external; 9 | 10 | function pluginIncreasePosition( 11 | address _account, 12 | address _collateralToken, 13 | address _indexToken, 14 | uint _sizeDelta, 15 | bool _isLong 16 | ) external; 17 | 18 | function pluginDecreasePosition( 19 | address _account, 20 | address _collateralToken, 21 | address _indexToken, 22 | uint _collateralDelta, 23 | uint _sizeDelta, 24 | bool _isLong, 25 | address _receiver 26 | ) external returns (uint); 27 | 28 | function swap(address[] memory _path, uint _amountIn, uint _minOut, address _receiver) external; 29 | 30 | function approvePlugin(address _plugin) external; 31 | } 32 | -------------------------------------------------------------------------------- /contracts/interfaces/gmx/IVaultPriceFeed.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | interface IVaultPriceFeed { 5 | function PRICE_PRECISION() external view returns (uint); 6 | 7 | function adjustmentBasisPoints(address _token) external view returns (uint); 8 | 9 | function isAdjustmentAdditive(address _token) external view returns (bool); 10 | 11 | function setAdjustment(address _token, bool _isAdditive, uint _adjustmentBps) external; 12 | 13 | function setUseV2Pricing(bool _useV2Pricing) external; 14 | 15 | function setIsAmmEnabled(bool _isEnabled) external; 16 | 17 | function setIsSecondaryPriceEnabled(bool _isEnabled) external; 18 | 19 | function setSpreadBasisPoints(address _token, uint _spreadBasisPoints) external; 20 | 21 | function setSpreadThresholdBasisPoints(uint _spreadThresholdBasisPoints) external; 22 | 23 | function setFavorPrimaryPrice(bool _favorPrimaryPrice) external; 24 | 25 | function setPriceSampleSpace(uint _priceSampleSpace) external; 26 | 27 | function setMaxStrictPriceDeviation(uint _maxStrictPriceDeviation) external; 28 | 29 | function getPrice( 30 | address _token, 31 | bool _maximise, 32 | bool _includeAmmPrice, 33 | bool _useSwapPricing 34 | ) external view returns (uint); 35 | 36 | function getAmmPrice(address _token) external view returns (uint); 37 | 38 | function getLatestPrimaryPrice(address _token) external view returns (uint); 39 | 40 | function getPrimaryPrice(address _token, bool _maximise) external view returns (uint); 41 | 42 | function setTokenConfig(address _token, address _priceFeed, uint _priceDecimals, bool _isStrictStable) external; 43 | } 44 | -------------------------------------------------------------------------------- /contracts/interfaces/gmx/IVaultUtils.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | interface IVaultUtils { 5 | function updateCumulativeFundingRate(address _collateralToken, address _indexToken) external returns (bool); 6 | 7 | function validateIncreasePosition( 8 | address _account, 9 | address _collateralToken, 10 | address _indexToken, 11 | uint _sizeDelta, 12 | bool _isLong 13 | ) external view; 14 | 15 | function validateDecreasePosition( 16 | address _account, 17 | address _collateralToken, 18 | address _indexToken, 19 | uint _collateralDelta, 20 | uint _sizeDelta, 21 | bool _isLong, 22 | address _receiver 23 | ) external view; 24 | 25 | function validateLiquidation( 26 | address _account, 27 | address _collateralToken, 28 | address _indexToken, 29 | bool _isLong, 30 | bool _raise 31 | ) external view returns (uint, uint); 32 | 33 | function getEntryFundingRate( 34 | address _collateralToken, 35 | address _indexToken, 36 | bool _isLong 37 | ) external view returns (uint); 38 | 39 | function getPositionFee( 40 | address _account, 41 | address _collateralToken, 42 | address _indexToken, 43 | bool _isLong, 44 | uint _sizeDelta 45 | ) external view returns (uint); 46 | 47 | function getFundingFee( 48 | address _account, 49 | address _collateralToken, 50 | address _indexToken, 51 | bool _isLong, 52 | uint _size, 53 | uint _entryFundingRate 54 | ) external view returns (uint); 55 | 56 | function getBuyUsdgFeeBasisPoints(address _token, uint _usdgAmount) external view returns (uint); 57 | 58 | function getSellUsdgFeeBasisPoints(address _token, uint _usdgAmount) external view returns (uint); 59 | 60 | function getSwapFeeBasisPoints(address _tokenIn, address _tokenOut, uint _usdgAmount) external view returns (uint); 61 | 62 | function getFeeBasisPoints( 63 | address _token, 64 | uint _usdgDelta, 65 | uint _feeBasisPoints, 66 | uint _taxBasisPoints, 67 | bool _increment 68 | ) external view returns (uint); 69 | } 70 | -------------------------------------------------------------------------------- /contracts/interfaces/perpsV2/IFuturesMarketManager.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.16; 2 | 3 | interface IFuturesMarketManager { 4 | function markets(uint index, uint pageSize) external view returns (address[] memory); 5 | 6 | function markets(uint index, uint pageSize, bool proxiedMarkets) external view returns (address[] memory); 7 | 8 | function numMarkets() external view returns (uint); 9 | 10 | function numMarkets(bool proxiedMarkets) external view returns (uint); 11 | 12 | function allMarkets() external view returns (address[] memory); 13 | 14 | function allMarkets(bool proxiedMarkets) external view returns (address[] memory); 15 | 16 | function marketForKey(bytes32 marketKey) external view returns (address); 17 | 18 | function marketsForKeys(bytes32[] calldata marketKeys) external view returns (address[] memory); 19 | 20 | function totalDebt() external view returns (uint debt, bool isInvalid); 21 | } 22 | -------------------------------------------------------------------------------- /contracts/interfaces/perpsV2/IFuturesMarketSettings.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.16; 2 | 3 | interface IFuturesMarketSettings { 4 | struct Parameters { 5 | uint takerFee; 6 | uint makerFee; 7 | uint takerFeeNextPrice; 8 | uint makerFeeNextPrice; 9 | uint nextPriceConfirmWindow; 10 | uint maxLeverage; 11 | uint maxMarketValueUSD; 12 | uint maxFundingRate; 13 | uint skewScaleUSD; 14 | } 15 | 16 | function takerFee(bytes32 _marketKey) external view returns (uint); 17 | 18 | function makerFee(bytes32 _marketKey) external view returns (uint); 19 | 20 | function takerFeeNextPrice(bytes32 _marketKey) external view returns (uint); 21 | 22 | function makerFeeNextPrice(bytes32 _marketKey) external view returns (uint); 23 | 24 | function nextPriceConfirmWindow(bytes32 _marketKey) external view returns (uint); 25 | 26 | function maxLeverage(bytes32 _marketKey) external view returns (uint); 27 | 28 | function maxMarketValueUSD(bytes32 _marketKey) external view returns (uint); 29 | 30 | function maxFundingRate(bytes32 _marketKey) external view returns (uint); 31 | 32 | function skewScaleUSD(bytes32 _marketKey) external view returns (uint); 33 | 34 | function parameters( 35 | bytes32 _marketKey 36 | ) 37 | external 38 | view 39 | returns ( 40 | uint _takerFee, 41 | uint _makerFee, 42 | uint _takerFeeNextPrice, 43 | uint _makerFeeNextPrice, 44 | uint _nextPriceConfirmWindow, 45 | uint _maxLeverage, 46 | uint _maxMarketValueUSD, 47 | uint _maxFundingRate, 48 | uint _skewScaleUSD 49 | ); 50 | 51 | function minKeeperFee() external view returns (uint); 52 | 53 | function liquidationFeeRatio() external view returns (uint); 54 | 55 | function liquidationBufferRatio() external view returns (uint); 56 | 57 | function minInitialMargin() external view returns (uint); 58 | } 59 | -------------------------------------------------------------------------------- /contracts/interfaces/perpsV2/IPerpsV2MarketBaseTypes.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | /** 4 | * @author Synthetix 5 | * @dev copied from https://github.com/Synthetixio/synthetix/blob/develop/contracts/interfaces/IPerpsV2MarketBaseTypes.sol 6 | * and updated the version to 0.8 7 | */ 8 | interface IPerpsV2MarketBaseTypes { 9 | /* ========== TYPES ========== */ 10 | 11 | enum OrderType { 12 | Atomic, 13 | Delayed, 14 | Offchain 15 | } 16 | 17 | enum Status { 18 | Ok, 19 | InvalidPrice, 20 | InvalidOrderType, 21 | PriceOutOfBounds, 22 | CanLiquidate, 23 | CannotLiquidate, 24 | MaxMarketSizeExceeded, 25 | MaxLeverageExceeded, 26 | InsufficientMargin, 27 | NotPermitted, 28 | NilOrder, 29 | NoPositionOpen, 30 | PriceTooVolatile, 31 | PriceImpactToleranceExceeded 32 | } 33 | 34 | // If margin/size are positive, the position is long; if negative then it is short. 35 | struct Position { 36 | uint64 id; 37 | uint64 lastFundingIndex; 38 | uint128 margin; 39 | uint128 lastPrice; 40 | int128 size; 41 | } 42 | 43 | // Delayed order storage 44 | struct DelayedOrder { 45 | bool isOffchain; // flag indicating the delayed order is offchain 46 | int128 sizeDelta; // difference in position to pass to modifyPosition 47 | uint128 priceImpactDelta; // price impact tolerance as a percentage used on fillPrice at execution 48 | uint128 targetRoundId; // price oracle roundId using which price this order needs to executed 49 | uint128 commitDeposit; // the commitDeposit paid upon submitting that needs to be refunded if order succeeds 50 | uint128 keeperDeposit; // the keeperDeposit paid upon submitting that needs to be paid / refunded on tx confirmation 51 | uint256 executableAtTime; // The timestamp at which this order is executable at 52 | uint256 intentionTime; // The block timestamp of submission 53 | bytes32 trackingCode; // tracking code to emit on execution for volume source fee sharing 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /contracts/interfaces/perpsV2/IPerpsV2MarketSettings.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.16; 2 | pragma experimental ABIEncoderV2; 3 | 4 | interface IPerpsV2MarketSettings { 5 | struct Parameters { 6 | uint takerFee; 7 | uint makerFee; 8 | uint overrideCommitFee; 9 | uint takerFeeDelayedOrder; 10 | uint makerFeeDelayedOrder; 11 | uint takerFeeOffchainDelayedOrder; 12 | uint makerFeeOffchainDelayedOrder; 13 | uint maxLeverage; 14 | uint maxMarketValue; 15 | uint maxFundingVelocity; 16 | uint skewScale; 17 | uint nextPriceConfirmWindow; 18 | uint delayedOrderConfirmWindow; 19 | uint minDelayTimeDelta; 20 | uint maxDelayTimeDelta; 21 | uint offchainDelayedOrderMinAge; 22 | uint offchainDelayedOrderMaxAge; 23 | bytes32 offchainMarketKey; 24 | uint offchainPriceDivergence; 25 | uint liquidationPremiumMultiplier; 26 | } 27 | 28 | function takerFee(bytes32 _marketKey) external view returns (uint); 29 | 30 | function makerFee(bytes32 _marketKey) external view returns (uint); 31 | 32 | function takerFeeDelayedOrder(bytes32 _marketKey) external view returns (uint); 33 | 34 | function makerFeeDelayedOrder(bytes32 _marketKey) external view returns (uint); 35 | 36 | function takerFeeOffchainDelayedOrder(bytes32 _marketKey) external view returns (uint); 37 | 38 | function makerFeeOffchainDelayedOrder(bytes32 _marketKey) external view returns (uint); 39 | 40 | function nextPriceConfirmWindow(bytes32 _marketKey) external view returns (uint); 41 | 42 | function delayedOrderConfirmWindow(bytes32 _marketKey) external view returns (uint); 43 | 44 | function offchainDelayedOrderMinAge(bytes32 _marketKey) external view returns (uint); 45 | 46 | function offchainDelayedOrderMaxAge(bytes32 _marketKey) external view returns (uint); 47 | 48 | function maxLeverage(bytes32 _marketKey) external view returns (uint); 49 | 50 | function maxMarketValue(bytes32 _marketKey) external view returns (uint); 51 | 52 | function maxFundingVelocity(bytes32 _marketKey) external view returns (uint); 53 | 54 | function skewScale(bytes32 _marketKey) external view returns (uint); 55 | 56 | function minDelayTimeDelta(bytes32 _marketKey) external view returns (uint); 57 | 58 | function maxDelayTimeDelta(bytes32 _marketKey) external view returns (uint); 59 | 60 | function parameters(bytes32 _marketKey) external view returns (Parameters memory); 61 | 62 | function offchainMarketKey(bytes32 _marketKey) external view returns (bytes32); 63 | 64 | function offchainPriceDivergence(bytes32 _marketKey) external view returns (uint); 65 | 66 | function liquidationPremiumMultiplier(bytes32 _marketKey) external view returns (uint); 67 | 68 | function minKeeperFee() external view returns (uint); 69 | 70 | function maxKeeperFee() external view returns (uint); 71 | 72 | function liquidationFeeRatio() external view returns (uint); 73 | 74 | function liquidationBufferRatio() external view returns (uint); 75 | 76 | function minInitialMargin() external view returns (uint); 77 | } 78 | -------------------------------------------------------------------------------- /contracts/interfaces/perpsV2/ISystemStatus.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: UNLICENSE 2 | pragma solidity ^0.8.0; 3 | 4 | // https://docs.synthetix.io/contracts/source/interfaces/isystemstatus 5 | interface ISystemStatus { 6 | struct Status { 7 | bool canSuspend; 8 | bool canResume; 9 | } 10 | 11 | struct Suspension { 12 | bool suspended; 13 | // reason is an integer code, 14 | // 0 => no reason, 1 => upgrading, 2+ => defined by system usage 15 | uint248 reason; 16 | } 17 | 18 | // Views 19 | function accessControl(bytes32 section, address account) external view returns (bool canSuspend, bool canResume); 20 | 21 | function requireSystemActive() external view; 22 | 23 | function systemSuspended() external view returns (bool); 24 | 25 | function requireIssuanceActive() external view; 26 | 27 | function requireExchangeActive() external view; 28 | 29 | function requireFuturesActive() external view; 30 | 31 | function requireFuturesMarketActive(bytes32 marketKey) external view; 32 | 33 | function requireExchangeBetweenSynthsAllowed(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey) external view; 34 | 35 | function requireSynthActive(bytes32 currencyKey) external view; 36 | 37 | function synthSuspended(bytes32 currencyKey) external view returns (bool); 38 | 39 | function requireSynthsActive(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey) external view; 40 | 41 | function systemSuspension() external view returns (bool suspended, uint248 reason); 42 | 43 | function issuanceSuspension() external view returns (bool suspended, uint248 reason); 44 | 45 | function exchangeSuspension() external view returns (bool suspended, uint248 reason); 46 | 47 | function futuresSuspension() external view returns (bool suspended, uint248 reason); 48 | 49 | function synthExchangeSuspension(bytes32 currencyKey) external view returns (bool suspended, uint248 reason); 50 | 51 | function synthSuspension(bytes32 currencyKey) external view returns (bool suspended, uint248 reason); 52 | 53 | function futuresMarketSuspension(bytes32 marketKey) external view returns (bool suspended, uint248 reason); 54 | 55 | function getSynthExchangeSuspensions( 56 | bytes32[] calldata synths 57 | ) external view returns (bool[] memory exchangeSuspensions, uint256[] memory reasons); 58 | 59 | function getSynthSuspensions( 60 | bytes32[] calldata synths 61 | ) external view returns (bool[] memory suspensions, uint256[] memory reasons); 62 | 63 | function getFuturesMarketSuspensions( 64 | bytes32[] calldata marketKeys 65 | ) external view returns (bool[] memory suspensions, uint256[] memory reasons); 66 | 67 | // Restricted functions 68 | function suspendIssuance(uint256 reason) external; 69 | 70 | function suspendSynth(bytes32 currencyKey, uint256 reason) external; 71 | 72 | function suspendFuturesMarket(bytes32 marketKey, uint256 reason) external; 73 | 74 | function updateAccessControl(bytes32 section, address account, bool canSuspend, bool canResume) external; 75 | } 76 | -------------------------------------------------------------------------------- /contracts/libraries/ConvertDecimals.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | // Libraries 5 | import "./Math.sol"; 6 | 7 | /** 8 | * @title ConvertDecimals 9 | * @author Lyra 10 | * @dev Contract to convert amounts to and from erc20 tokens to 18 dp. 11 | */ 12 | library ConvertDecimals { 13 | /// @dev Converts amount from token native dp to 18 dp. This cuts off precision for decimals > 18. 14 | function convertTo18(uint amount, uint8 decimals) internal pure returns (uint) { 15 | return (amount * 1e18) / (10 ** decimals); 16 | } 17 | 18 | /// @dev Converts amount from 18dp to token.decimals(). This cuts off precision for decimals < 18. 19 | function convertFrom18(uint amount, uint8 decimals) internal pure returns (uint) { 20 | return (amount * (10 ** decimals)) / 1e18; 21 | } 22 | 23 | /// @dev Converts amount from a given precisionFactor to 18 dp. This cuts off precision for decimals > 18. 24 | function normaliseTo18(uint amount, uint precisionFactor) internal pure returns (uint) { 25 | return (amount * 1e18) / precisionFactor; 26 | } 27 | 28 | // Loses precision 29 | /// @dev Converts amount from 18dp to the given precisionFactor. This cuts off precision for decimals < 18. 30 | function normaliseFrom18(uint amount, uint precisionFactor) internal pure returns (uint) { 31 | return (amount * precisionFactor) / 1e18; 32 | } 33 | 34 | /// @dev Ensure a value converted from 18dp is rounded up, to ensure the value requested is covered fully. 35 | function convertFrom18AndRoundUp(uint amount, uint8 assetDecimals) internal pure returns (uint amountConverted) { 36 | // If we lost precision due to conversion we ensure the lost value is rounded up to the lowest precision of the asset 37 | if (assetDecimals < 18) { 38 | // Taking the ceil of 10^(18-decimals) will ensure the first n (asset decimals) have precision when converting 39 | amount = Math.ceil(amount, 10 ** (18 - assetDecimals)); 40 | } 41 | amountConverted = ConvertDecimals.convertFrom18(amount, assetDecimals); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /contracts/libraries/Math.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | /** 5 | * @title Math 6 | * @author Lyra 7 | * @dev Library to unify logic for common shared functions 8 | */ 9 | library Math { 10 | /// @dev Return the minimum value between the two inputs 11 | function min(uint x, uint y) internal pure returns (uint) { 12 | return (x < y) ? x : y; 13 | } 14 | 15 | /// @dev Return the maximum value between the two inputs 16 | function max(uint x, uint y) internal pure returns (uint) { 17 | return (x > y) ? x : y; 18 | } 19 | 20 | /// @dev Compute the absolute value of `val`. 21 | function abs(int val) internal pure returns (uint) { 22 | return uint(val < 0 ? -val : val); 23 | } 24 | 25 | /// @dev Takes ceiling of a to m precision 26 | /// @param m represents 1eX where X is the number of trailing 0's 27 | function ceil(uint a, uint m) internal pure returns (uint) { 28 | return ((a + m - 1) / m) * m; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /contracts/libraries/PoolHedger.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | // Interfaces 5 | import "../LiquidityPool.sol"; 6 | 7 | /** 8 | * @title PoolHedger 9 | * @author Lyra 10 | * @dev Scaffold for using the delta hedging funds from the LiquidityPool to hedge option deltas, so LPs are minimally 11 | * exposed to movements in the underlying asset price. 12 | */ 13 | abstract contract PoolHedger { 14 | struct PoolHedgerParameters { 15 | uint interactionDelay; 16 | uint hedgeCap; 17 | } 18 | 19 | LiquidityPool internal liquidityPool; 20 | PoolHedgerParameters internal poolHedgerParams; 21 | uint public lastInteraction; 22 | 23 | ///////////// 24 | // Only LP // 25 | ///////////// 26 | function resetInteractionDelay() external onlyLiquidityPool { 27 | lastInteraction = 0; 28 | } 29 | 30 | ///////////// 31 | // Getters // 32 | ///////////// 33 | 34 | /** 35 | * @dev Returns the current hedged netDelta position. 36 | */ 37 | function getCurrentHedgedNetDelta() external view virtual returns (int); 38 | 39 | /// @notice Returns pending delta hedge liquidity and used delta hedge liquidity 40 | /// @dev include funds that would need to be transferred to the contract to hedge optimally 41 | function getHedgingLiquidity( 42 | uint spotPrice 43 | ) external view virtual returns (uint pendingDeltaLiquidity, uint usedDeltaLiquidity); 44 | 45 | /** 46 | * @dev Calculates the expected delta hedge that hedger must perform and 47 | * adjusts the result down to the hedgeCap param if needed. 48 | */ 49 | function getCappedExpectedHedge() public view virtual returns (int cappedExpectedHedge); 50 | 51 | ////////////// 52 | // External // 53 | ////////////// 54 | 55 | /// @param increasesPoolDelta Does the trade increase or decrease the pool's net delta position 56 | function canHedge(uint tradeSize, bool increasesPoolDelta, uint strikeId) external view virtual returns (bool); 57 | 58 | /** 59 | * @dev Retrieves the netDelta for the system and hedges appropriately. 60 | */ 61 | function hedgeDelta() external payable virtual; 62 | 63 | function updateCollateral() external payable virtual; 64 | 65 | function getPoolHedgerParams() external view virtual returns (PoolHedgerParameters memory) { 66 | return poolHedgerParams; 67 | } 68 | 69 | ////////////// 70 | // Internal // 71 | ////////////// 72 | 73 | function _setPoolHedgerParams(PoolHedgerParameters memory _poolHedgerParams) internal { 74 | poolHedgerParams = _poolHedgerParams; 75 | emit PoolHedgerParametersSet(poolHedgerParams); 76 | } 77 | 78 | /////////////// 79 | // Modifiers // 80 | /////////////// 81 | 82 | modifier onlyLiquidityPool() { 83 | if (msg.sender != address(liquidityPool)) { 84 | revert OnlyLiquidityPool(address(this), msg.sender, address(liquidityPool)); 85 | } 86 | _; 87 | } 88 | 89 | //////////// 90 | // Events // 91 | //////////// 92 | /** 93 | * @dev Emitted when pool hedger parameters are updated. 94 | */ 95 | event PoolHedgerParametersSet(PoolHedgerParameters poolHedgerParams); 96 | 97 | //////////// 98 | // Errors // 99 | //////////// 100 | 101 | // Access 102 | error OnlyLiquidityPool(address thrower, address caller, address liquidityPool); 103 | } 104 | -------------------------------------------------------------------------------- /contracts/libraries/SimpleInitializable.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | /** 5 | * @title SimpleInitializable 6 | * @author Lyra 7 | * @dev Contract to enable a function to be marked as the initializer 8 | */ 9 | abstract contract SimpleInitializable { 10 | bool internal initialized = false; 11 | 12 | modifier initializer() { 13 | if (initialized) { 14 | revert AlreadyInitialised(address(this)); 15 | } 16 | initialized = true; 17 | _; 18 | } 19 | 20 | //////////// 21 | // Errors // 22 | //////////// 23 | error AlreadyInitialised(address thrower); 24 | } 25 | -------------------------------------------------------------------------------- /contracts/periphery/BasicFeeCounter.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier:ISC 2 | pragma solidity 0.8.16; 3 | 4 | // Inherited 5 | import "../synthetix/Owned.sol"; 6 | import "../interfaces/IFeeCounter.sol"; 7 | 8 | /** 9 | * @title BasicFeeCounter 10 | */ 11 | contract BasicFeeCounter is IFeeCounter, Owned { 12 | mapping(address => bool) public trustedCounter; 13 | mapping(address => mapping(address => uint)) public totalFeesPerMarket; 14 | 15 | constructor() Owned() {} 16 | 17 | function setTrustedCounter(address counter, bool isTrusted) external onlyOwner { 18 | trustedCounter[counter] = isTrusted; 19 | } 20 | 21 | function trackFee(address market, address trader, uint, uint, uint totalFee) external onlyTrustedCounter { 22 | totalFeesPerMarket[market][trader] += totalFee; 23 | } 24 | 25 | modifier onlyTrustedCounter() { 26 | require(trustedCounter[msg.sender], "not trusted counter"); 27 | _; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /contracts/periphery/BasicLiquidityCounter.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier:ISC 2 | pragma solidity 0.8.16; 3 | 4 | // Inherited 5 | import "../synthetix/Owned.sol"; 6 | 7 | /** 8 | * @title BasicFeeCounter 9 | */ 10 | contract BasicLiquidityCounter is Owned { 11 | address public liquidityToken; 12 | mapping(address => uint) public userLiquidity; 13 | 14 | constructor() Owned() {} 15 | 16 | /** 17 | * @dev 18 | * param lpToken liquidity token address 19 | */ 20 | function setLiquidityToken(address lpToken) external onlyOwner { 21 | liquidityToken = lpToken; 22 | } 23 | 24 | /** 25 | * @dev 26 | * param trader the address of the trader that is exchaning liquidity 27 | * param amount the amount of liquidity tokens that are being exchanged 28 | */ 29 | function addTokens(address trader, uint amount) external onlyLiquidityToken { 30 | userLiquidity[trader] += amount; 31 | } 32 | 33 | /** 34 | * @dev 35 | * param trader the address of the trader that is exchaning liquidity 36 | * param amount the amount of liquidity tokens that are being exchanged 37 | */ 38 | function removeTokens(address trader, uint amount) external onlyLiquidityToken { 39 | userLiquidity[trader] -= amount; 40 | } 41 | 42 | modifier onlyLiquidityToken() { 43 | require(liquidityToken == msg.sender, "can only be called by LiquidityToken"); 44 | _; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /contracts/synthetix/AbstractOwned.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | // 3 | //Copyright (c) 2019 Synthetix 4 | // 5 | //Permission is hereby granted, free of charge, to any person obtaining a copy 6 | //of this software and associated documentation files (the "Software"), to deal 7 | //in the Software without restriction, including without limitation the rights 8 | //to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | //copies of the Software, and to permit persons to whom the Software is 10 | //furnished to do so, subject to the following conditions: 11 | // 12 | //The above copyright notice and this permission notice shall be included in all 13 | //copies or substantial portions of the Software. 14 | // 15 | //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | //IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | //AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | //LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | //SOFTWARE. 22 | 23 | pragma solidity 0.8.16; 24 | 25 | /** 26 | * @title Owned 27 | * @author Synthetix 28 | * @dev Synthetix owned contract without constructor and custom errors 29 | * @dev https://docs.synthetix.io/contracts/source/contracts/owned 30 | */ 31 | abstract contract AbstractOwned { 32 | address public owner; 33 | address public nominatedOwner; 34 | uint[48] private __gap; 35 | 36 | function nominateNewOwner(address _owner) external onlyOwner { 37 | nominatedOwner = _owner; 38 | emit OwnerNominated(_owner); 39 | } 40 | 41 | function acceptOwnership() external { 42 | if (msg.sender != nominatedOwner) { 43 | revert OnlyNominatedOwner(address(this), msg.sender, nominatedOwner); 44 | } 45 | emit OwnerChanged(owner, nominatedOwner); 46 | owner = nominatedOwner; 47 | nominatedOwner = address(0); 48 | } 49 | 50 | modifier onlyOwner() { 51 | _onlyOwner(); 52 | _; 53 | } 54 | 55 | function _onlyOwner() private view { 56 | if (msg.sender != owner) { 57 | revert OnlyOwner(address(this), msg.sender, owner); 58 | } 59 | } 60 | 61 | event OwnerNominated(address newOwner); 62 | event OwnerChanged(address oldOwner, address newOwner); 63 | 64 | //////////// 65 | // Errors // 66 | //////////// 67 | error OnlyOwner(address thrower, address caller, address owner); 68 | error OnlyNominatedOwner(address thrower, address caller, address nominatedOwner); 69 | } 70 | -------------------------------------------------------------------------------- /contracts/synthetix/Owned.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | // 3 | //Copyright (c) 2019 Synthetix 4 | // 5 | //Permission is hereby granted, free of charge, to any person obtaining a copy 6 | //of this software and associated documentation files (the "Software"), to deal 7 | //in the Software without restriction, including without limitation the rights 8 | //to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | //copies of the Software, and to permit persons to whom the Software is 10 | //furnished to do so, subject to the following conditions: 11 | // 12 | //The above copyright notice and this permission notice shall be included in all 13 | //copies or substantial portions of the Software. 14 | // 15 | //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | //IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | //AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | //LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | //SOFTWARE. 22 | 23 | pragma solidity 0.8.16; 24 | 25 | import "./AbstractOwned.sol"; 26 | 27 | /** 28 | * @title Owned 29 | * @author Synthetix 30 | * @dev Slightly modified Synthetix owned contract, so that first owner is msg.sender 31 | * @dev https://docs.synthetix.io/contracts/source/contracts/owned 32 | */ 33 | contract Owned is AbstractOwned { 34 | constructor() { 35 | owner = msg.sender; 36 | emit OwnerChanged(address(0), msg.sender); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /contracts/synthetix/OwnedUpgradeable.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | 3 | import "openzeppelin-contracts-upgradeable-4.5.1/proxy/utils/Initializable.sol"; 4 | import "./AbstractOwned.sol"; 5 | 6 | pragma solidity 0.8.16; 7 | 8 | /** 9 | * @title OwnedUpgradeable 10 | * @author Lyra 11 | * @dev Modified owned contract to allow for the owner to be initialised by the calling proxy 12 | * @dev https://docs.synthetix.io/contracts/source/contracts/owned 13 | */ 14 | contract OwnedUpgradeable is AbstractOwned, Initializable { 15 | /** 16 | * @dev Initializes the contract setting the deployer as the initial owner. 17 | */ 18 | function __Ownable_init() internal onlyInitializing { 19 | owner = msg.sender; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /contracts/test-helpers/ITestERC20.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | import "openzeppelin-contracts-4.4.1/token/ERC20/IERC20.sol"; 5 | 6 | interface ITestERC20 is IERC20 { 7 | function mint(address account, uint amount) external; 8 | 9 | function burn(address account, uint amount) external; 10 | } 11 | -------------------------------------------------------------------------------- /contracts/test-helpers/MathTest.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | import "../libraries/FixedPointMathLib.sol"; 5 | import "./OldBlackScholesMath.sol"; 6 | 7 | contract MathTest { 8 | function lnV1(int x) external pure returns (int r) { 9 | return FixedPointMathLib.ln(x); 10 | } 11 | 12 | function expV1(int x) external pure returns (uint r) { 13 | return FixedPointMathLib.exp(x); 14 | } 15 | 16 | function lnV2(uint x) external pure returns (int r) { 17 | return OldBlackScholesMath.ln(x); 18 | } 19 | 20 | function expV2(int x) external pure returns (uint r) { 21 | return OldBlackScholesMath.exp(x); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts/test-helpers/MockAggregatorV2V3.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | import "../interfaces/IAggregatorV3.sol"; 5 | 6 | contract MockAggregatorV2V3 is AggregatorV2V3Interface { 7 | uint80 public roundId = 0; 8 | uint8 public keyDecimals = 0; 9 | 10 | struct Entry { 11 | uint80 roundId; 12 | int answer; 13 | uint startedAt; 14 | uint updatedAt; 15 | uint80 answeredInRound; 16 | } 17 | 18 | mapping(uint => Entry) public entries; 19 | 20 | bool public allRoundDataShouldRevert; 21 | bool public latestRoundDataShouldRevert; 22 | 23 | constructor() {} 24 | 25 | // Mock setup function 26 | function setLatestAnswer(int answer, uint timestamp) external { 27 | roundId++; 28 | entries[roundId] = Entry({ 29 | roundId: roundId, 30 | answer: answer, 31 | startedAt: timestamp, 32 | updatedAt: timestamp, 33 | answeredInRound: roundId 34 | }); 35 | } 36 | 37 | function setLatestAnswerWithRound(int answer, uint timestamp, uint80 _roundId) external { 38 | roundId = _roundId; 39 | entries[roundId] = Entry({ 40 | roundId: roundId, 41 | answer: answer, 42 | startedAt: timestamp, 43 | updatedAt: timestamp, 44 | answeredInRound: roundId 45 | }); 46 | } 47 | 48 | function setAllRoundDataShouldRevert(bool _shouldRevert) external { 49 | allRoundDataShouldRevert = _shouldRevert; 50 | } 51 | 52 | function setLatestRoundDataShouldRevert(bool _shouldRevert) external { 53 | latestRoundDataShouldRevert = _shouldRevert; 54 | } 55 | 56 | function setDecimals(uint8 _decimals) external { 57 | keyDecimals = _decimals; 58 | } 59 | 60 | function latestRoundData() external view returns (uint80, int, uint, uint, uint80) { 61 | if (latestRoundDataShouldRevert) { 62 | revert("latestRoundData reverted"); 63 | } 64 | return getRoundData(uint80(latestRound())); 65 | } 66 | 67 | function latestRound() public view returns (uint) { 68 | return roundId; 69 | } 70 | 71 | function decimals() external view returns (uint8) { 72 | return keyDecimals; 73 | } 74 | 75 | function getAnswer(uint _roundId) external view returns (int) { 76 | Entry memory entry = entries[_roundId]; 77 | return entry.answer; 78 | } 79 | 80 | function latestAnswer() external view returns (int) { 81 | Entry memory entry = entries[roundId]; 82 | return entry.answer; 83 | } 84 | 85 | function getTimestamp(uint _roundId) external view returns (uint) { 86 | Entry memory entry = entries[_roundId]; 87 | return entry.updatedAt; 88 | } 89 | 90 | function getRoundData(uint80 _roundId) public view returns (uint80, int, uint, uint, uint80) { 91 | if (allRoundDataShouldRevert) { 92 | revert("getRoundData reverted"); 93 | } 94 | 95 | Entry memory entry = entries[_roundId]; 96 | // Emulate a Chainlink aggregator 97 | require(entry.updatedAt > 0, "No data present"); 98 | return (entry.roundId, entry.answer, entry.startedAt, entry.updatedAt, entry.answeredInRound); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /contracts/test-helpers/OldBlackScholesMath.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | import "../synthetix/SignedDecimalMath.sol"; 5 | import "../synthetix/DecimalMath.sol"; 6 | 7 | library OldBlackScholesMath { 8 | using DecimalMath for uint; 9 | using SignedDecimalMath for int; 10 | 11 | /// @dev Internally this library uses 18 decimals of precision 12 | uint private constant UNIT = 1e18; 13 | /// @dev Below this value, the result is always 0 14 | int private constant MIN_EXP = -41 * int(UNIT); 15 | /// @dev Above this value precision is lost, and uint256s cannot handle the size 16 | uint private constant MAX_EXP = 100 * UNIT; 17 | uint private constant LN_2 = 693147180559945309; 18 | 19 | /** 20 | * @dev Returns the floor relative to UINT 21 | */ 22 | function floor(uint x) internal pure returns (uint) { 23 | return x - (x % UNIT); 24 | } 25 | 26 | /** 27 | * @dev Returns the natural log of the value using Halley's method. 28 | * 0.000001 -> 1000000+ work fine 29 | * this contract will deal with values between 0.3-10, so very safe for this method 30 | */ 31 | function ln(uint x) internal pure returns (int) { 32 | int res; 33 | int next; 34 | 35 | for (uint i = 0; i < 8; ++i) { 36 | int e = int(exp(res)); 37 | 38 | next = res + ((int(x) - e) * 2).divideDecimalRound(int(x) + e); 39 | 40 | if (next == res) { 41 | break; 42 | } 43 | res = next; 44 | } 45 | 46 | return res; 47 | } 48 | 49 | /** 50 | * @dev Returns the exponent of the value using taylor expansion with range reduction. 51 | */ 52 | function exp(uint x) internal pure returns (uint) { 53 | if (x == 0) { 54 | return UNIT; 55 | } 56 | require(x <= MAX_EXP, "cannot handle exponents greater than 100"); 57 | 58 | uint k = floor(x.divideDecimalRound(LN_2)) / UNIT; 59 | uint p = 2 ** k; 60 | uint r = x - (k * LN_2); 61 | 62 | uint _t = UNIT; 63 | 64 | uint lastT; 65 | for (uint8 i = 16; i > 0; i--) { 66 | _t = _t.multiplyDecimalRound(r / i) + UNIT; 67 | if (_t == lastT) { 68 | break; 69 | } 70 | lastT = _t; 71 | } 72 | 73 | return p * _t; 74 | } 75 | 76 | /** 77 | * @dev Returns the exponent of the value using taylor expansion with range reduction, 78 | * with support for negative numbers. 79 | */ 80 | function exp(int x) internal pure returns (uint) { 81 | if (0 <= x) { 82 | return exp(uint(x)); 83 | } else if (x < MIN_EXP) { 84 | // exp(-63) < 1e-27, so we just return 0 85 | return 0; 86 | } else { 87 | return UNIT.divideDecimalRound(exp(uint(-x))); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /contracts/test-helpers/Path.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | import "./BytesLib.sol"; 5 | 6 | /// @title Functions for manipulating path data for multihop swaps 7 | library Path { 8 | using BytesLib for bytes; 9 | 10 | /// @dev The length of the bytes encoded address 11 | uint private constant ADDR_SIZE = 20; 12 | /// @dev The length of the bytes encoded fee 13 | uint private constant FEE_SIZE = 3; 14 | 15 | /// @dev The offset of a single token address and pool fee 16 | uint private constant NEXT_OFFSET = ADDR_SIZE + FEE_SIZE; 17 | /// @dev The offset of an encoded pool key 18 | uint private constant POP_OFFSET = NEXT_OFFSET + ADDR_SIZE; 19 | /// @dev The minimum length of an encoding that contains 2 or more pools 20 | uint private constant MULTIPLE_POOLS_MIN_LENGTH = POP_OFFSET + NEXT_OFFSET; 21 | 22 | /// @notice Returns true iff the path contains two or more pools 23 | /// @param path The encoded swap path 24 | /// @return True if path contains two or more pools, otherwise false 25 | function hasMultiplePools(bytes memory path) internal pure returns (bool) { 26 | return path.length >= MULTIPLE_POOLS_MIN_LENGTH; 27 | } 28 | 29 | /// @notice Returns the number of pools in the path 30 | /// @param path The encoded swap path 31 | /// @return The number of pools in the path 32 | function numPools(bytes memory path) internal pure returns (uint) { 33 | // Ignore the first token address. From then on every fee and token offset indicates a pool. 34 | return ((path.length - ADDR_SIZE) / NEXT_OFFSET); 35 | } 36 | 37 | /// @notice Decodes the first pool in path 38 | /// @param path The bytes encoded swap path 39 | /// @return tokenA The first token of the given pool 40 | /// @return tokenB The second token of the given pool 41 | /// @return fee The fee level of the pool 42 | function decodeFirstPool(bytes memory path) internal pure returns (address tokenA, address tokenB, uint24 fee) { 43 | tokenA = path.toAddress(0); 44 | fee = path.toUint24(ADDR_SIZE); 45 | tokenB = path.toAddress(NEXT_OFFSET); 46 | } 47 | 48 | /// @notice Gets the segment corresponding to the first pool in the path 49 | /// @param path The bytes encoded swap path 50 | /// @return The segment containing all data necessary to target the first pool in the path 51 | function getFirstPool(bytes memory path) internal pure returns (bytes memory) { 52 | return path.slice(0, POP_OFFSET); 53 | } 54 | 55 | /// @notice Skips a token + fee element from the buffer and returns the remainder 56 | /// @param path The swap path 57 | /// @return The remaining token + fee elements in the path 58 | function skipToken(bytes memory path) internal pure returns (bytes memory) { 59 | return path.slice(NEXT_OFFSET, path.length - NEXT_OFFSET); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /contracts/test-helpers/TestBaseExchangeAdapter.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier:ISC 2 | pragma solidity 0.8.16; 3 | 4 | import "../BaseExchangeAdapter.sol"; 5 | 6 | contract TestBaseExchangeAdapter is BaseExchangeAdapter { 7 | function testTransferAsset(IERC20Decimals asset, address recipient, uint amount) external { 8 | _transferAsset(asset, recipient, amount); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /contracts/test-helpers/TestBlackScholes.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | // Libraries 5 | import "../synthetix/SignedDecimalMath.sol"; 6 | import "../synthetix/DecimalMath.sol"; 7 | import "../libraries/Math.sol"; 8 | import "../libraries/BlackScholes.sol"; 9 | import "../libraries/FixedPointMathLib.sol"; 10 | 11 | contract TestBlackScholes { 12 | using DecimalMath for uint; 13 | using SignedDecimalMath for int; 14 | using BlackScholes for *; 15 | 16 | function expPub(int x) external pure returns (uint) { 17 | return FixedPointMathLib.exp(x); 18 | } 19 | 20 | function lnPub(int x) external pure returns (int) { 21 | return FixedPointMathLib.ln(x); 22 | } 23 | 24 | function sqrt_pub(uint x) external pure returns (uint) { 25 | return (x * DecimalMath.UNIT)._sqrt(); 26 | } 27 | 28 | function abs_pub(int x) external pure returns (uint) { 29 | return Math.abs(x); 30 | } 31 | 32 | function stdNormal_pub(int x) external pure returns (uint) { 33 | return x._stdNormal().preciseDecimalToDecimal(); 34 | } 35 | 36 | function stdNormalCDF_pub(int x) external pure returns (uint) { 37 | return x._stdNormalCDF().preciseDecimalToDecimal(); 38 | } 39 | 40 | function annualise_pub(uint secs) external pure returns (uint yearFraction) { 41 | return secs._annualise().preciseDecimalToDecimal(); 42 | } 43 | 44 | function d1d2_pub( 45 | uint tAnnualised, 46 | uint volatility, 47 | uint spot, 48 | uint strikePrice, 49 | int rate 50 | ) external pure returns (int d1, int d2) { 51 | (d1, d2) = tAnnualised._d1d2(volatility, spot, strikePrice, rate); 52 | return (d1.preciseDecimalToDecimal(), d2.preciseDecimalToDecimal()); 53 | } 54 | 55 | function optionPrices_pub( 56 | BlackScholes.BlackScholesInputs memory bsInput 57 | ) external pure returns (uint call, uint put) { 58 | return bsInput.optionPrices(); 59 | } 60 | 61 | function pricesDeltaStdVega_pub( 62 | BlackScholes.BlackScholesInputs memory bsInput 63 | ) external pure returns (BlackScholes.PricesDeltaStdVega memory) { 64 | return bsInput.pricesDeltaStdVega(); 65 | } 66 | 67 | function delta_pub(BlackScholes.BlackScholesInputs memory bsInput) external pure returns (int, int) { 68 | return bsInput.delta(); 69 | } 70 | 71 | function vega_pub(BlackScholes.BlackScholesInputs memory bsInput) external pure returns (uint) { 72 | return bsInput.vega(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /contracts/test-helpers/TestERC20.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier:ISC 2 | pragma solidity 0.8.16; 3 | 4 | import "openzeppelin-contracts-4.4.1/token/ERC20/ERC20.sol"; 5 | 6 | import "./ITestERC20.sol"; 7 | 8 | contract TestERC20 is ITestERC20, ERC20 { 9 | mapping(address => bool) public permitted; 10 | 11 | constructor(string memory name_, string memory symbol_) ERC20(name_, symbol_) { 12 | permitted[msg.sender] = true; 13 | } 14 | 15 | function decimals() public pure override returns (uint8) { 16 | return 18; 17 | } 18 | 19 | function permitMint(address user, bool permit) external { 20 | require(permitted[msg.sender], "only permitted"); 21 | permitted[user] = permit; 22 | } 23 | 24 | function mint(address account, uint amount) external override { 25 | require(permitted[msg.sender], "only permitted"); 26 | ERC20._mint(account, amount); 27 | } 28 | 29 | function burn(address account, uint amount) external override { 30 | require(permitted[msg.sender], "only permitted"); 31 | ERC20._burn(account, amount); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /contracts/test-helpers/TestERC20Fail.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier:ISC 2 | pragma solidity 0.8.16; 3 | 4 | import "./TestERC20.sol"; 5 | 6 | contract TestERC20Fail is TestERC20 { 7 | bool public forceFail = false; 8 | bool public returnFalseOnNotEnoughBalance = false; 9 | bool public maxApproveFail = false; 10 | 11 | constructor(string memory name_, string memory symbol_) TestERC20(name_, symbol_) {} 12 | 13 | function setForceFail(bool _forceFail) external { 14 | forceFail = _forceFail; 15 | } 16 | 17 | function setReturnFalseOnNotEnoughBalance(bool _returnFalse) external { 18 | returnFalseOnNotEnoughBalance = _returnFalse; 19 | } 20 | 21 | function setMaxApprovalFail(bool _maxApproveFail) external { 22 | maxApproveFail = _maxApproveFail; 23 | } 24 | 25 | // This isn't ideal, it hits the coverage cases, but should only return false if the transfer fails. Would 26 | // require a new contract that doesn't revert on failures. 27 | function transfer(address receiver, uint amount) public override(IERC20, ERC20) returns (bool) { 28 | if (forceFail) { 29 | return false; 30 | } 31 | 32 | if (returnFalseOnNotEnoughBalance) { 33 | if (balanceOf(msg.sender) < amount) { 34 | return false; 35 | } 36 | } 37 | 38 | return super.transfer(receiver, amount); 39 | } 40 | 41 | function transferFrom(address sender, address receiver, uint amount) public override(IERC20, ERC20) returns (bool) { 42 | if (forceFail) { 43 | return false; 44 | } 45 | return super.transferFrom(sender, receiver, amount); 46 | } 47 | 48 | function approve(address spender, uint amount) public override(IERC20, ERC20) returns (bool) { 49 | if (forceFail) { 50 | return false; 51 | } 52 | 53 | if (maxApproveFail && amount == type(uint).max) { 54 | return false; 55 | } 56 | return super.approve(spender, amount); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /contracts/test-helpers/TestERC20SetDecimals.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier:ISC 2 | pragma solidity 0.8.16; 3 | 4 | import "openzeppelin-contracts-4.4.1/token/ERC20/ERC20.sol"; 5 | 6 | import "./ITestERC20.sol"; 7 | 8 | contract TestERC20SetDecimals is ITestERC20, ERC20 { 9 | mapping(address => bool) public permitted; 10 | uint8 private _decimals; 11 | 12 | constructor(string memory name_, string memory symbol_, uint8 decimals_) ERC20(name_, symbol_) { 13 | permitted[msg.sender] = true; 14 | _setupDecimals(decimals_); 15 | } 16 | 17 | // Default setup of decimals in OpenZepellin v4 is done via decimals() override 18 | // For testing purposes, manually implementing v3 style ERC20 storage and _setDecimals 19 | function decimals() public view override returns (uint8) { 20 | return _decimals; 21 | } 22 | 23 | function _setupDecimals(uint8 decimals_) internal { 24 | _decimals = decimals_; 25 | } 26 | 27 | function setDecimals(uint8 newDecimals) external { 28 | require(permitted[msg.sender], "TestERC20SetDecimals: only permitted"); 29 | _decimals = newDecimals; 30 | } 31 | 32 | function permitMint(address user, bool permit) external { 33 | require(permitted[msg.sender], "TestERC20SetDecimals: only permitted"); 34 | permitted[user] = permit; 35 | } 36 | 37 | function mint(address account, uint amount) external override { 38 | require(permitted[msg.sender], "TestERC20SetDecimals: only permitted"); 39 | ERC20._mint(account, amount); 40 | } 41 | 42 | function burn(address account, uint amount) external override { 43 | require(permitted[msg.sender], "TestERC20SetDecimals: only permitted"); 44 | ERC20._burn(account, amount); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /contracts/test-helpers/TestFaucet.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier:ISC 2 | pragma solidity 0.8.16; 3 | 4 | import "./TestERC20.sol"; 5 | 6 | contract TestFaucet { 7 | mapping(uint => uint) dripAmounts; 8 | TestERC20[] public addresses; 9 | 10 | mapping(address => bool) public permitted; 11 | mapping(address => bool) public received; 12 | 13 | constructor() { 14 | permitted[msg.sender] = true; 15 | } 16 | 17 | function permitModify(address user, bool permit) external { 18 | require(permitted[msg.sender], "only permitted"); 19 | permitted[user] = permit; 20 | } 21 | 22 | function setDripAmount(TestERC20 token, uint amount) external { 23 | require(permitted[msg.sender], "only permitted"); 24 | uint index; 25 | bool found = false; 26 | for (index = 0; index < addresses.length; ++index) { 27 | if (addresses[index] == token) { 28 | found = true; 29 | break; 30 | } 31 | } 32 | if (!found) { 33 | // index will be addresses.length here 34 | addresses.push(token); 35 | } 36 | dripAmounts[index] = amount; 37 | } 38 | 39 | function drip() external { 40 | require(!received[msg.sender], "already received"); 41 | 42 | for (uint i = 0; i < addresses.length; ++i) { 43 | addresses[i].mint(msg.sender, dripAmounts[i]); 44 | emit Dripped(addresses[i], dripAmounts[i]); 45 | } 46 | received[msg.sender] = true; 47 | } 48 | 49 | event Dripped(TestERC20 token, uint amount); 50 | } 51 | -------------------------------------------------------------------------------- /contracts/test-helpers/TestGMXPoolHedger.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | import "../GMXFuturesPoolHedger.sol"; 5 | 6 | /** 7 | * @notice only used in tests 8 | * @dev expose testIncreasePosition to let us create state which we have 2 positions opened at the same time 9 | */ 10 | contract TestGMXFuturesPoolHedger is GMXFuturesPoolHedger { 11 | function testIncreasePosition( 12 | PositionDetails memory pos, 13 | bool isLong, 14 | uint sizeDelta, 15 | uint collateralDelta 16 | ) external payable { 17 | uint spot = _getSpotPrice(); 18 | _increasePosition(pos, isLong, sizeDelta, collateralDelta, spot); 19 | } 20 | 21 | function testDecreasePosition( 22 | PositionDetails memory pos, 23 | bool isLong, 24 | uint sizeDelta, 25 | uint collateralDelta, 26 | bool isClose 27 | ) external payable { 28 | uint spot = _getSpotPrice(); 29 | _decreasePosition(pos, isLong, sizeDelta, collateralDelta, spot, isClose); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /contracts/test-helpers/TestGWAV.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | import "../interfaces/ISynthetix.sol"; 5 | // Debug 6 | import "./ITestERC20.sol"; 7 | import "../synthetix/DecimalMath.sol"; 8 | import "../libraries/GWAV.sol"; 9 | 10 | contract TestGWAV { 11 | using GWAV for GWAV.Params; 12 | 13 | GWAV.Params public gwav; 14 | 15 | function initialize(uint currentVal, uint currentTimestamp) public { 16 | gwav._initialize(currentVal, currentTimestamp); 17 | } 18 | 19 | function recordMany(uint[] memory vals, uint[] memory timestamps) public { 20 | for (uint i = 0; i < vals.length; ++i) { 21 | gwav._write(vals[i], timestamps[i]); 22 | } 23 | } 24 | 25 | function recordObservation(uint currentVal, uint currentTimestamp) public { 26 | gwav._write(currentVal, currentTimestamp); 27 | } 28 | 29 | function getGWAVBetween(uint timeA, uint timeB) public view returns (uint) { 30 | return gwav.getGWAVForPeriod(timeA, timeB); 31 | } 32 | 33 | function observe(uint[] memory times) public view returns (int[] memory qCumulatives, uint[] memory timestamps) { 34 | return gwav.observe(block.timestamp, times); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /contracts/test-helpers/TestShortPoolHedger.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | import "./ShortPoolHedger.sol"; 5 | 6 | contract TestShortPoolHedger is ShortPoolHedger { 7 | bool public canPoolHedge = true; 8 | 9 | function setCanHedge(bool _canHedge) public { 10 | canPoolHedge = _canHedge; 11 | } 12 | 13 | function canHedge(uint, bool, uint) external view override returns (bool) { 14 | return canPoolHedge; 15 | } 16 | 17 | function hedgeDeltaExt(int expectedHedge) external { 18 | _hedgeDelta(expectedHedge); 19 | } 20 | 21 | function increaseLongExt(SynthetixAdapter.ExchangeParams memory exchangeParams, uint amount) external { 22 | // Last field is optional, only for event 23 | _increaseLong(exchangeParams, amount, 0); 24 | } 25 | 26 | function decreaseLongExt(uint amount) external { 27 | // Last field is optional, only for event 28 | _decreaseLong(amount, 0); 29 | } 30 | 31 | function callTransferQuoteToHedge(uint amount) external { 32 | liquidityPool.transferQuoteToHedge(amount); 33 | } 34 | 35 | function setShortToExt(uint spotPrice, uint desiredShort, uint currentShort, uint currentCollateral) external { 36 | _setShortTo(spotPrice, desiredShort, currentShort, currentCollateral); 37 | } 38 | 39 | function _sendAllQuoteToLPExt() external { 40 | _sendAllQuoteToLP(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /contracts/test-helpers/TestWETH.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier:ISC 2 | pragma solidity 0.8.16; 3 | 4 | import "./TestERC20SetDecimalsFail.sol"; 5 | 6 | contract TestWETH is TestERC20SetDecimalsFail { 7 | constructor( 8 | string memory name_, 9 | string memory symbol_, 10 | uint8 decimals_ 11 | ) TestERC20SetDecimalsFail(name_, symbol_, decimals_) {} 12 | 13 | // Allow minting for wETH 14 | function deposit() public payable { 15 | _mint(msg.sender, msg.value); 16 | } 17 | 18 | function withdraw(uint amount) public { 19 | require(balanceOf(msg.sender) >= amount, "Token: insufficient balance"); 20 | _burn(msg.sender, amount); 21 | payable(msg.sender).transfer(amount); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts/test-helpers/gmx/GMXCompileImports.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.6.12; 3 | 4 | import "gmx/contracts/tokens/Token.sol"; 5 | import "gmx/contracts/oracle/PriceFeed.sol"; 6 | import "gmx/contracts/core/Vault.sol"; 7 | import "gmx/contracts/peripherals/Timelock.sol"; 8 | import "gmx/contracts/tokens/USDG.sol"; 9 | import "gmx/contracts/core/Router.sol"; 10 | import "gmx/contracts/peripherals/Reader.sol"; 11 | import "gmx/contracts/core/ShortsTracker.sol"; 12 | import "gmx/contracts/core/PositionRouter.sol"; 13 | import "gmx/contracts/referrals/ReferralStorage.sol"; 14 | import "gmx/contracts/tokens/YieldTracker.sol"; 15 | import "gmx/contracts/core/VaultPriceFeed.sol"; 16 | import "gmx/contracts/tokens/TimeDistributor.sol"; 17 | import "gmx/contracts/oracle/FastPriceFeed.sol"; 18 | import "gmx/contracts/oracle/FastPriceEvents.sol"; 19 | import "gmx/contracts/core/VaultErrorController.sol"; 20 | 21 | /** 22 | * @dev Contract is used to import all the contracts that are needed for integration testing the GMX contracts. 23 | */ 24 | contract GMXCompileImports { 25 | constructor() public { 26 | // this is empty 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /contracts/test-helpers/gmx/TestGMXPositionRouter.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | import "../../synthetix/DecimalMath.sol"; 5 | import "../../interfaces/gmx/IVault.sol"; 6 | import "../../synthetix/Owned.sol"; 7 | import "../TestERC20.sol"; 8 | 9 | contract TestGMXPositionRouter is Owned { 10 | using DecimalMath for uint; 11 | 12 | uint public callbackGasLimit = 800000; 13 | address public vault; 14 | 15 | constructor(address _vaultAddr) { 16 | vault = _vaultAddr; 17 | } 18 | 19 | function getPositions( 20 | address, // _vault 21 | address, // _account 22 | address[] memory, // _collateralTokens 23 | address[] memory, // _indexTokens 24 | bool[] memory // _isLong 25 | ) external pure returns (uint[] memory) { 26 | uint[] memory vals = new uint[](8); 27 | return vals; 28 | } 29 | 30 | function maxGlobalLongSizes(address) external pure returns (uint) { 31 | return 0; 32 | } 33 | 34 | function maxGlobalShortSizes(address) external pure returns (uint) { 35 | return 0; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /contracts/test-helpers/gmx/TestGMXRouter.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | import "../../synthetix/DecimalMath.sol"; 5 | import "../../interfaces/gmx/IVault.sol"; 6 | import "../../synthetix/Owned.sol"; 7 | import "../TestERC20.sol"; 8 | 9 | contract TestGMXRouter is Owned { 10 | using DecimalMath for uint; 11 | 12 | constructor() {} 13 | 14 | function approvePlugin(address toApprove) external {} 15 | } 16 | -------------------------------------------------------------------------------- /contracts/test-helpers/snx/CompileUniImports.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity =0.7.6; 3 | 4 | //interface 5 | import {IUniswapV3Pool} from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol"; 6 | import {OracleLibrary} from "@uniswap/v3-periphery/contracts/libraries/OracleLibrary.sol"; 7 | 8 | /** 9 | * @dev Contract is used to import all the contracts that are needed for integration testing the GMX contracts. 10 | */ 11 | contract SnxImports { 12 | constructor() { 13 | // this is empty 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /contracts/test-helpers/snx/MockSystemStatus.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "../../interfaces/perpsV2/ISystemStatus.sol"; 4 | 5 | contract MockSystemStatus is ISystemStatus { 6 | bool private _futuresMarketSuspended; 7 | uint248 private _futuresMarketReason; 8 | 9 | // Setters for `futuresMarketSuspension` function 10 | function setFuturesMarketSuspended(bool suspended) external { 11 | _futuresMarketSuspended = suspended; 12 | } 13 | 14 | function setFuturesMarketReason(uint248 reason) external { 15 | _futuresMarketReason = reason; 16 | } 17 | 18 | function accessControl(bytes32 /*section*/, address /*account*/) external view override returns (bool, bool) {} 19 | 20 | function requireSystemActive() external view override {} 21 | 22 | function systemSuspended() external view override returns (bool) {} 23 | 24 | function requireIssuanceActive() external view override {} 25 | 26 | function requireExchangeActive() external view override {} 27 | 28 | function requireFuturesActive() external view override {} 29 | 30 | function requireFuturesMarketActive(bytes32 /*marketKey*/) external view override {} 31 | 32 | function requireExchangeBetweenSynthsAllowed( 33 | bytes32 /*sourceCurrencyKey*/, 34 | bytes32 /*destinationCurrencyKey*/ 35 | ) external view override {} 36 | 37 | function requireSynthActive(bytes32 /*currencyKey*/) external view override {} 38 | 39 | function synthSuspended(bytes32 /*currencyKey*/) external view override returns (bool) {} 40 | 41 | function requireSynthsActive( 42 | bytes32 /*sourceCurrencyKey*/, 43 | bytes32 /*destinationCurrencyKey*/ 44 | ) external view override {} 45 | 46 | function systemSuspension() external view override returns (bool, uint248) {} 47 | 48 | function issuanceSuspension() external view override returns (bool, uint248) {} 49 | 50 | function exchangeSuspension() external view override returns (bool, uint248) {} 51 | 52 | function futuresSuspension() external view override returns (bool, uint248) {} 53 | 54 | function synthExchangeSuspension(bytes32 /*currencyKey*/) external view override returns (bool, uint248) {} 55 | 56 | function synthSuspension(bytes32 /*currencyKey*/) external view override returns (bool, uint248) {} 57 | 58 | function futuresMarketSuspension( 59 | bytes32 /*marketKey*/ 60 | ) external view override returns (bool suspended, uint248 reason) { 61 | return (_futuresMarketSuspended, _futuresMarketReason); 62 | } 63 | 64 | function getSynthExchangeSuspensions( 65 | bytes32[] calldata /*synths*/ 66 | ) external view override returns (bool[] memory, uint256[] memory) {} 67 | 68 | function getSynthSuspensions( 69 | bytes32[] calldata /*synths*/ 70 | ) external view override returns (bool[] memory, uint256[] memory) {} 71 | 72 | function getFuturesMarketSuspensions( 73 | bytes32[] calldata /*marketKeys*/ 74 | ) external view override returns (bool[] memory, uint256[] memory) {} 75 | 76 | function suspendIssuance(uint256 /*reason*/) external override {} 77 | 78 | function suspendSynth(bytes32 /*currencyKey*/, uint256 /*reason*/) external override {} 79 | 80 | function suspendFuturesMarket(bytes32 /*marketKey*/, uint256 /*reason*/) external override {} 81 | 82 | function updateAccessControl( 83 | bytes32 /*section*/, 84 | address /*account*/, 85 | bool /*canSuspend*/, 86 | bool /*canResume*/ 87 | ) external override {} 88 | } 89 | -------------------------------------------------------------------------------- /contracts/test-helpers/snx/SNXCompileImport.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.5.16; 3 | 4 | import "synthetix/contracts/MixinPerpsV2MarketSettings.sol"; 5 | import "synthetix/contracts/PerpsV2ExchangeRate.sol"; 6 | import "synthetix/contracts/PerpsV2Market.sol"; 7 | import "synthetix/contracts/PerpsV2MarketBase.sol"; 8 | import "synthetix/contracts/PerpsV2MarketData.sol"; 9 | import "synthetix/contracts/PerpsV2MarketDelayedOrders.sol"; 10 | import "synthetix/contracts/PerpsV2MarketDelayedOrdersBase.sol"; 11 | import "synthetix/contracts/PerpsV2MarketDelayedOrdersOffchain.sol"; 12 | import "synthetix/contracts/PerpsV2MarketProxyable.sol"; 13 | import "synthetix/contracts/PerpsV2MarketSettings.sol"; 14 | import "synthetix/contracts/PerpsV2MarketState.sol"; 15 | import "synthetix/contracts/PerpsV2MarketViews.sol"; 16 | import "synthetix/contracts/ProxyPerpsV2.sol"; 17 | import "synthetix/contracts/FuturesMarketManager.sol"; 18 | import "synthetix/contracts/FuturesMarketSettings.sol"; 19 | import "synthetix/contracts/SafeDecimalMath.sol"; 20 | import "openzeppelin-solidity-2.3.0/contracts/math/SafeMath.sol"; 21 | import "synthetix/contracts/SignedSafeMath.sol"; 22 | import "synthetix/contracts/SignedSafeDecimalMath.sol"; 23 | import "synthetix/contracts/SafeDecimalMath.sol"; 24 | 25 | /** 26 | * @dev Contract is used to import all the contracts that are needed for integration testing the GMX contracts. 27 | */ 28 | contract SNXCompileImports { 29 | constructor() public { 30 | // this is empty 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /contracts/test-helpers/snx/TestAddressResolver.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier:ISC 2 | pragma solidity 0.8.16; 3 | 4 | import "../../synthetix/Owned.sol"; 5 | import "../../interfaces/IAddressResolver.sol"; 6 | 7 | contract TestAddressResolver is IAddressResolver, Owned { 8 | mapping(bytes32 => address) public override getAddress; 9 | 10 | constructor() Owned() {} 11 | 12 | function setAddresses(bytes32[] memory names, address[] memory locations) external { 13 | require(names.length == locations.length, "length mismatch"); 14 | 15 | for (uint i = 0; i < names.length; ++i) { 16 | getAddress[names[i]] = locations[i]; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /contracts/test-helpers/snx/TestDelegateApprovals.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | import "../../interfaces/IDelegateApprovals.sol"; 5 | 6 | contract TestDelegateApprovals is IDelegateApprovals { 7 | mapping(address => mapping(address => bool)) public exchangingApproved; 8 | 9 | function approveExchangeOnBehalf(address approvee) external override { 10 | exchangingApproved[msg.sender][approvee] = true; 11 | } 12 | 13 | function canExchangeOnBehalf(address exchanger, address beneficiary) external view returns (bool) { 14 | if (exchanger == beneficiary) { 15 | return true; 16 | } 17 | return exchangingApproved[beneficiary][exchanger]; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /contracts/test-helpers/snx/TestExchangeRates.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | import "../../synthetix/Owned.sol"; 5 | 6 | import "../../interfaces/IExchangeRates.sol"; 7 | 8 | contract TestExchangeRates is IExchangeRates, Owned { 9 | mapping(bytes32 => uint) public rates; 10 | mapping(bytes32 => bool) public isInvalid; 11 | 12 | constructor() Owned() {} 13 | 14 | function rateAndInvalid(bytes32 currencyKey) external view override returns (uint rate, bool invalid) { 15 | rate = rates[currencyKey]; 16 | invalid = isInvalid[currencyKey]; 17 | } 18 | 19 | function setRateAndInvalid(bytes32 currencyKey, uint rate, bool invalid) external onlyOwner { 20 | rates[currencyKey] = rate; 21 | isInvalid[currencyKey] = invalid; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts/test-helpers/snx/TestExchanger.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | import "../../synthetix/Owned.sol"; 5 | 6 | import "../../interfaces/IExchanger.sol"; 7 | 8 | contract TestExchanger is IExchanger, Owned { 9 | mapping(bytes32 => mapping(bytes32 => uint)) fee; 10 | 11 | constructor() Owned() {} 12 | 13 | function feeRateForExchange( 14 | bytes32 sourceCurrencyKey, 15 | bytes32 destinationCurrencyKey 16 | ) external view override returns (uint exchangeFeeRate) { 17 | return fee[sourceCurrencyKey][destinationCurrencyKey]; 18 | } 19 | 20 | function setFeeRateForExchange( 21 | bytes32 sourceCurrencyKey, 22 | bytes32 destinationCurrencyKey, 23 | uint newFee 24 | ) external onlyOwner { 25 | fee[sourceCurrencyKey][destinationCurrencyKey] = newFee; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /contracts/test-helpers/snx/TestFuturesMarketManager.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | // Test contract to mimic the funcitonality of snx futures market manager 3 | 4 | pragma solidity 0.8.16; 5 | 6 | import "../../synthetix/Owned.sol"; 7 | import "../../libraries/SimpleInitializable.sol"; 8 | import "../../interfaces/perpsV2/IPerpsV2MarketConsolidated.sol"; 9 | import "../../interfaces/perpsV2/IFuturesMarketManager.sol"; 10 | 11 | import "hardhat/console.sol"; 12 | 13 | contract TestFuturesMarketManager is Owned, SimpleInitializable { 14 | mapping(bytes32 => address) public marketForKey; 15 | 16 | constructor() Owned() {} 17 | 18 | function init() external onlyOwner initializer {} 19 | 20 | function addMarkets(address[] memory marketsToAdd) external onlyOwner { 21 | address marketAddress = marketsToAdd[0]; 22 | bytes32 marketKey = IPerpsV2MarketConsolidated(marketAddress).marketKey(); 23 | 24 | if (marketForKey[marketKey] != address(0)) { 25 | revert MarketAlreadyExists(marketForKey[marketKey], marketAddress, marketKey); 26 | } 27 | 28 | console.log("adding market", marketAddress); 29 | 30 | marketForKey[marketKey] = marketAddress; 31 | emit MarketAdded(marketAddress, marketKey); 32 | } 33 | 34 | function _marketsForKeys(bytes32[] memory marketKeys) internal view returns (address[] memory) { 35 | address[] memory markets = new address[](marketKeys.length); 36 | for (uint i = 0; i < marketKeys.length; i++) { 37 | markets[i] = marketForKey[marketKeys[i]]; 38 | } 39 | console.log("returning market keys", markets[0]); 40 | return markets; 41 | } 42 | 43 | function marketsForKeys(bytes32[] memory marketKeys) external view returns (address[] memory) { 44 | return _marketsForKeys(marketKeys); 45 | } 46 | 47 | error MarketAlreadyExists(address existing, address proposed, bytes32 key); 48 | 49 | event MarketAdded(address market, bytes32 marketKey); 50 | } 51 | -------------------------------------------------------------------------------- /contracts/test-helpers/snx/TestSynthetixReturnZero.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: ISC 2 | pragma solidity 0.8.16; 3 | 4 | import "./TestSynthetix.sol"; 5 | 6 | contract TestSynthetixReturnZero is TestSynthetix { 7 | bool public returnZero = false; 8 | 9 | constructor() {} 10 | 11 | function setReturnZero(bool _returnZero) external { 12 | returnZero = _returnZero; 13 | } 14 | 15 | function exchange( 16 | bytes32 sourceCurrencyKey, 17 | uint sourceAmount, 18 | bytes32 destinationCurrencyKey 19 | ) public override returns (uint amountReceived) { 20 | if (returnZero) { 21 | return 0; 22 | } else { 23 | return super.exchange(sourceCurrencyKey, sourceAmount, destinationCurrencyKey); 24 | } 25 | } 26 | 27 | function exchangeOnBehalfWithTracking( 28 | address exchangeForAddress, 29 | bytes32 sourceCurrencyKey, 30 | uint sourceAmount, 31 | bytes32 destinationCurrencyKey, 32 | address, 33 | bytes32 34 | ) public override returns (uint amountReceived) { 35 | if (returnZero) { 36 | return 0; 37 | } else { 38 | emit Exchange(msg.sender, sourceCurrencyKey, sourceAmount, destinationCurrencyKey); 39 | return super.exchangeOnBehalf(exchangeForAddress, sourceCurrencyKey, sourceAmount, destinationCurrencyKey); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /deployments/.env.defaults: -------------------------------------------------------------------------------- 1 | # Order of precedence is Private > Public > Default. 2 | # Any default/public vars can be overwritten in private env file 3 | 4 | SYNTHETIX_LOCATION=~/Synthetix 5 | # default deploy private key for optimism 6 | PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 7 | -------------------------------------------------------------------------------- /deployments/.env.private.example: -------------------------------------------------------------------------------- 1 | # Order of precedence is Private > Default Private > Public > Default. 2 | # Any default/public vars can be overwritten in private env 3 | # Add a .env.private to each deployment folder to set these variables 4 | 5 | SYNTHETIX_LOCATION=~/Synthetix 6 | PRIVATE_KEY=0123456789012345678901234567890123456789012345678901234567890123 7 | ETHERSCAN_KEY=xxxxxxxxxxxxxxxxxx 8 | -------------------------------------------------------------------------------- /deployments/.gitignore: -------------------------------------------------------------------------------- 1 | */.env.private 2 | local*/lyra.* 3 | local*/external.* 4 | 5 | -------------------------------------------------------------------------------- /deployments/goerli-arbi/.env.public: -------------------------------------------------------------------------------- 1 | RPC_URL=https://arbitrum-goerli.io/ 2 | -------------------------------------------------------------------------------- /deployments/goerli-arbi/params.default.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /deployments/goerli-arbi/params.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /deployments/goerli-ovm/.env.public: -------------------------------------------------------------------------------- 1 | RPC_URL=https://optimism-goerli.io/ 2 | -------------------------------------------------------------------------------- /deployments/local/.env.public: -------------------------------------------------------------------------------- 1 | RPC_URL=http://localhost:8545 2 | GAS_PRICE=0 3 | -------------------------------------------------------------------------------- /deployments/mainnet-arbi/.env.public: -------------------------------------------------------------------------------- 1 | RPC_URL=https://arbitrum-mainnet.io 2 | -------------------------------------------------------------------------------- /deployments/mainnet-arbi/params.default.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /deployments/mainnet-arbi/params.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /deployments/mainnet-ovm/.env.public: -------------------------------------------------------------------------------- 1 | RPC_URL=https://optimism-mainnet.infura.io/ 2 | -------------------------------------------------------------------------------- /examples/deployAndSeedLocal.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from 'ethers'; 2 | import { toBN } from '../scripts/util/web3utils'; 3 | import { DEFAULT_OPTION_MARKET_PARAMS } from '../test/utils/defaultParams'; 4 | import { deployTestSystem } from '../test/utils/deployTestSystem'; 5 | import { getGlobalDeploys, getMarketDeploys, LyraGlobal, LyraMarket } from '../test/utils/package/parseFiles'; 6 | import { seedTestSystem } from '../test/utils/seedTestSystem'; 7 | 8 | // run this script using `yarn hardhat run --network local` if running directly from repo (not @lyrafinance/protocol) 9 | // otherwise OZ will think it's deploying to hardhat network and not local 10 | async function main() { 11 | // 1. get deployer and network 12 | const provider = new ethers.providers.JsonRpcProvider('http://localhost:8545'); 13 | 14 | const privateKey = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'; // enter address with ETH 15 | provider.getGasPrice = async () => { 16 | return ethers.BigNumber.from('0'); 17 | }; 18 | provider.estimateGas = async () => { 19 | return ethers.BigNumber.from(15000000); 20 | }; // max limit to prevent run out of gas errors 21 | const deployer = new ethers.Wallet(privateKey, provider); 22 | 23 | // 2. deploy and seed market 24 | const exportAddresses = true; 25 | let localTestSystem = await deployTestSystem(deployer, false, exportAddresses, { 26 | mockSNX: true, 27 | compileSNX: false, 28 | optionMarketParams: { ...DEFAULT_OPTION_MARKET_PARAMS, feePortionReserved: toBN('0.05') }, 29 | }); 30 | 31 | await seedTestSystem(deployer, localTestSystem); 32 | 33 | // // 3. add new BTC market 34 | // let newMarketSystem = await addNewMarketSystem(deployer, localTestSystem, 'sBTC', exportAddresses) 35 | // await seedNewMarketSystem(deployer, localTestSystem, newMarketSystem) 36 | 37 | // 4. get global contracts 38 | let lyraGlobal: LyraGlobal = getGlobalDeploys('local'); 39 | console.log('contract name:', lyraGlobal.SynthetixAdapter.contractName); 40 | console.log('address:', lyraGlobal.SynthetixAdapter.address); 41 | // console.log("abi:", lyraGlobal.SynthetixAdapter.abi) 42 | console.log('bytecode:', lyraGlobal.SynthetixAdapter.bytecode.slice(0, 20) + '...'); 43 | 44 | // 5. get market contracts 45 | let lyraMarket: LyraMarket = getMarketDeploys('local', 'sETH'); 46 | console.log('contract name:', lyraMarket.OptionMarket.contractName); 47 | console.log('address:', lyraMarket.OptionMarket.address); 48 | // console.log("abi:", lyraMarket.OptionMarket.abi) 49 | console.log('bytecode:', lyraMarket.OptionMarket.bytecode.slice(0, 20) + '...'); 50 | } 51 | 52 | main() 53 | .then(() => process.exit(0)) 54 | .catch(error => { 55 | console.error(error); 56 | process.exit(1); 57 | }); 58 | -------------------------------------------------------------------------------- /examples/goerliInteraction.ts: -------------------------------------------------------------------------------- 1 | import { JsonRpcProvider } from '@ethersproject/providers'; 2 | import chalk from 'chalk'; 3 | import { Contract, ethers } from 'ethers'; 4 | import { MAX_UINT, OptionType, toBN } from '../scripts/util/web3utils'; 5 | import { getGlobalDeploys, getMarketDeploys } from '../test/utils/package/parseFiles'; 6 | 7 | async function main() { 8 | // 1. get deployer and network 9 | const provider = new ethers.providers.JsonRpcProvider( 10 | 'https://optimism-kovan.infura.io/v3/561ebceae957407ea6699a474aa4f7b0', 11 | ); 12 | 13 | const privateKey = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'; // enter address with ETH 14 | const deployer = new ethers.Wallet(privateKey, provider); 15 | 16 | // 2. get lyra contracts 17 | let lyraGlobal = getGlobalDeploys('goerli-arbi'); 18 | let lyraMarket = getMarketDeploys('goerli-arbi', 'sETH'); 19 | 20 | const testFaucet = new Contract(lyraGlobal.TestFaucet.address, lyraGlobal.TestFaucet.abi, deployer); 21 | const sUSD = new Contract(lyraGlobal.QuoteAsset.address, lyraGlobal.QuoteAsset.abi, deployer); 22 | const optionMarket = new Contract(lyraMarket.OptionMarket.address, lyraMarket.OptionMarket.abi, deployer); 23 | 24 | // 3. call lyra 25 | // await execute(testFaucet, 'drip', [] as any, provider); 26 | await execute(sUSD, 'approve', [optionMarket.address, MAX_UINT], provider); 27 | 28 | const tradeParams = { 29 | strikeId: 1, 30 | positionId: 0, 31 | iterations: 5, 32 | optionType: OptionType.LONG_CALL, 33 | amount: toBN('1'), 34 | setCollateralTo: toBN('0'), 35 | minTotalCost: toBN('0'), 36 | maxTotalCost: MAX_UINT, 37 | }; 38 | await execute(optionMarket, 'openPosition', [tradeParams], provider); 39 | } 40 | 41 | export function sleep(ms: number) { 42 | return new Promise(resolve => setTimeout(resolve, ms)); 43 | } 44 | 45 | export async function execute(contract: Contract, func: string, args: any[], provider: JsonRpcProvider) { 46 | while (true) { 47 | try { 48 | console.log(chalk.grey(`Executing ${contract.address}`)); 49 | const overrides: any = { gasLimit: 15000000 }; 50 | const tx = await contract[func](...args, overrides); 51 | while ((await provider.getTransactionReceipt(tx.hash)) == null) { 52 | await sleep(100); 53 | } 54 | const receipt = await tx.wait(); 55 | console.log(`Gas used for tx ${chalk.blueBright(receipt.transactionHash)}:`, receipt.gasUsed.toNumber()); 56 | return tx; 57 | } catch (e) { 58 | if (e instanceof Error) { 59 | console.log(e.message.slice(0, 27)); 60 | if (e.message.slice(0, 27) == 'nonce has already been used') { 61 | continue; 62 | } 63 | throw e; 64 | } 65 | } 66 | } 67 | } 68 | 69 | main() 70 | .then(() => process.exit(0)) 71 | .catch(error => { 72 | console.error(error); 73 | process.exit(1); 74 | }); 75 | -------------------------------------------------------------------------------- /scripts/addMarketToRealGMX.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import { addGMXMarket } from './deploy/deployGMXContracts'; 3 | import { DeploymentType, getSelectedNetwork } from './util'; 4 | import { loadEnv, loadParams } from './util/parseFiles'; 5 | import { getDeployer } from './util/providers'; 6 | 7 | async function main() { 8 | const network = getSelectedNetwork(); 9 | const envVars = loadEnv(network); 10 | const deployer = await getDeployer(envVars); 11 | const deploymentParams = { network, deploymentType: DeploymentType.GMX, deployer }; 12 | const params = loadParams(deploymentParams); 13 | 14 | const marketTicker = 'SOL'; 15 | const marketId = 2; 16 | 17 | console.log(`Adding market ${marketTicker} on ${network}`); 18 | await addGMXMarket(deploymentParams, params, marketTicker, marketId); 19 | 20 | console.log(chalk.greenBright('\n=== Successfully deployed! ===\n')); 21 | } 22 | 23 | main() 24 | .then(() => process.exit(0)) 25 | .catch(error => { 26 | console.error(error); 27 | process.exit(1); 28 | }); 29 | -------------------------------------------------------------------------------- /scripts/addMarketToTestGMX.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import { addGMXMarket } from './deploy/deployGMXContracts'; 3 | import { DeploymentType, getSelectedNetwork } from './util'; 4 | import { loadEnv, loadParams } from './util/parseFiles'; 5 | import { getDeployer } from './util/providers'; 6 | 7 | async function main() { 8 | const network = getSelectedNetwork(); 9 | const envVars = loadEnv(network); 10 | const deployer = await getDeployer(envVars); 11 | const deploymentParams = { network, deployer, deploymentType: DeploymentType.MockGmxMockPricing }; 12 | const params = loadParams(deploymentParams); 13 | 14 | const marketTicker = 'BTC'; 15 | const marketId = 1; 16 | 17 | console.log(`Adding market ${marketTicker} on ${network}`); 18 | await addGMXMarket(deploymentParams, params, marketTicker, marketId); 19 | 20 | console.log(chalk.greenBright('\n=== Successfully deployed! ===\n')); 21 | } 22 | 23 | main() 24 | .then(() => process.exit(0)) 25 | .catch(error => { 26 | console.error(error); 27 | process.exit(1); 28 | }); 29 | -------------------------------------------------------------------------------- /scripts/deploy/deployTestUSDC.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from 'hardhat'; 2 | 3 | export async function deployTestUSDC() { 4 | const usdc = await ethers.getContractFactory('TestERC20SetDecimals'); 5 | 6 | await usdc.deploy('USDC', 'USDC', 6); 7 | } 8 | -------------------------------------------------------------------------------- /scripts/deploy/deployUniswap.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from 'hardhat'; 2 | import { BigNumber, Contract } from 'ethers'; 3 | import { 4 | abi as SWAP_ROUTER_ABI, 5 | bytecode as SWAP_ROUTER_BYTECODE, 6 | } from '@uniswap/v3-periphery/artifacts/contracts/SwapRouter.sol/SwapRouter.json'; 7 | import { 8 | abi as POSITION_MANAGER_ABI, 9 | bytecode as POSITION_MANAGER_BYTECODE, 10 | } from '@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json'; 11 | import { 12 | abi as FACTORY_ABI, 13 | bytecode as FACTORY_BYTECODE, 14 | } from '@uniswap/v3-core/artifacts/contracts/UniswapV3Factory.sol/UniswapV3Factory.json'; 15 | import { ISwapRouter, IUniswapV3Pool, TestWETH } from '../../typechain-types'; 16 | import { toBN, ZERO_ADDRESS } from '../util/web3utils'; 17 | import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; 18 | import { convertPriceE18ToSqrtX96 } from '../util/maths'; 19 | 20 | const ONE = BigNumber.from('1000000000000000000'); 21 | 22 | export async function deployUniswap(deployer: SignerWithAddress): Promise<{ 23 | factory: Contract; 24 | weth: TestWETH; 25 | swapRouter: ISwapRouter; 26 | positionManager: Contract; 27 | }> { 28 | const factory = await (await ethers.getContractFactory(FACTORY_ABI, FACTORY_BYTECODE)).connect(deployer).deploy(); 29 | 30 | const weth = await (await ethers.getContractFactory('TestWETH')).connect(deployer).deploy('WETH', 'WETH', 18); 31 | 32 | const swapRouter = (await (await ethers.getContractFactory(SWAP_ROUTER_ABI, SWAP_ROUTER_BYTECODE)) 33 | .connect(deployer) 34 | .deploy(factory.address, weth.address)) as ISwapRouter; 35 | 36 | // tokenDescriptor is set to address(0) 37 | const positionManager = await (await ethers.getContractFactory(POSITION_MANAGER_ABI, POSITION_MANAGER_BYTECODE)) 38 | .connect(deployer) 39 | .deploy(factory.address, weth.address, ZERO_ADDRESS); 40 | 41 | return { factory, weth, swapRouter, positionManager }; 42 | } 43 | 44 | /** 45 | * 46 | * @param factory 47 | * @param base 48 | * @param quote 49 | * @param initPrice 50 | */ 51 | export async function deployUniswapPool( 52 | factory: Contract, 53 | positionManager: Contract, 54 | base: Contract, 55 | quote: Contract, 56 | initPrice: string, 57 | ) { 58 | const isBaseToken0 = base.address < quote.address; 59 | 60 | // weth/usdt is 2000 61 | const price = toBN(initPrice); 62 | const sqrtX96Price = isBaseToken0 63 | ? convertPriceE18ToSqrtX96(price) 64 | : convertPriceE18ToSqrtX96(ONE.mul(ONE).div(price)); 65 | 66 | const token0 = isBaseToken0 ? base.address : quote.address; 67 | const token1 = isBaseToken0 ? quote.address : base.address; 68 | 69 | // // https://docs.uniswap.org/protocol/reference/periphery/base/PoolInitializer 70 | await positionManager.createAndInitializePoolIfNecessary( 71 | token0, 72 | token1, 73 | 3000, // fee = 0.3% 74 | sqrtX96Price, 75 | ); 76 | 77 | const wethPoolAddr = await factory.getPool(token0, token1, 3000); 78 | const pool = (await ethers.getContractAt('IUniswapV3Pool', wethPoolAddr)) as IUniswapV3Pool; 79 | 80 | await pool.increaseObservationCardinalityNext(128); 81 | 82 | return { pool, isBaseToken0 }; 83 | } 84 | -------------------------------------------------------------------------------- /scripts/deployOptionMarketWrapper.ts: -------------------------------------------------------------------------------- 1 | import { DeploymentType, getSelectedNetwork } from './util'; 2 | import { loadEnv } from './util/parseFiles'; 3 | import { 4 | deployLyraContract, 5 | execute, 6 | executeLyraFunction, 7 | getLyraContract, 8 | getExternalContract, 9 | } from './util/transactions'; 10 | import { toBN, toBytes32, ZERO_ADDRESS } from './util/web3utils'; 11 | import { getDeployer } from './util/providers'; 12 | 13 | async function main() { 14 | const network = getSelectedNetwork(); 15 | const envVars = loadEnv(network); 16 | const deployer = await getDeployer(envVars); 17 | // const deployerAddr = await deployer.getAddress(); 18 | const deploymentParams = { network, deployer, deploymentType: DeploymentType.MockGmxMockPricing }; 19 | const market = 'sETH'; 20 | const marketId = 0; 21 | 22 | console.log(`Deploying OptionMarketWrapper to ${network}`); 23 | 24 | const wrapper = await deployLyraContract(deploymentParams, 'OptionMarketWrapper'); 25 | 26 | await executeLyraFunction(deploymentParams, 'LyraRegistry', 'updateGlobalAddresses', [ 27 | [toBytes32('MARKET_WRAPPER')], 28 | [wrapper.address], 29 | ]); 30 | 31 | await execute(wrapper, 'updateContractParams', [ 32 | ZERO_ADDRESS, 33 | getExternalContract(deploymentParams, 'CurvePool').address, 34 | ZERO_ADDRESS, 35 | toBN('0.1'), 36 | ]); 37 | 38 | await execute(wrapper, 'addCurveStable', [getExternalContract(deploymentParams, 'ProxyERC20sUSD').address, 0]); 39 | await execute(wrapper, 'addCurveStable', [getExternalContract(deploymentParams, 'DAI').address, 1]); 40 | await execute(wrapper, 'addCurveStable', [getExternalContract(deploymentParams, 'USDC').address, 2]); 41 | 42 | await executeLyraFunction(deploymentParams, 'OptionMarketWrapper', 'addMarket', [ 43 | getLyraContract(deploymentParams, 'OptionMarket', market).address, 44 | marketId, 45 | { 46 | optionToken: getLyraContract(deploymentParams, 'OptionToken', market).address, 47 | shortCollateral: getLyraContract(deploymentParams, 'ShortCollateral', market).address, 48 | baseAsset: getExternalContract(deploymentParams, 'Proxy' + market).address, 49 | quoteAsset: getExternalContract(deploymentParams, 'ProxyERC20sUSD').address, 50 | }, 51 | ]); 52 | } 53 | 54 | main() 55 | .then(() => process.exit(0)) 56 | .catch(error => { 57 | console.error(error); 58 | process.exit(1); 59 | }); 60 | -------------------------------------------------------------------------------- /scripts/deployRealGMX.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import { deployGMXContracts } from './deploy/deployGMXContracts'; 3 | import { DeploymentType, getSelectedNetwork } from './util'; 4 | import { loadEnv, loadParams } from './util/parseFiles'; 5 | import { getDeployer } from './util/providers'; 6 | 7 | async function main() { 8 | const network = getSelectedNetwork(); 9 | const envVars = loadEnv(network); 10 | const deployer = await getDeployer(envVars); 11 | const deploymentParams = { network, deploymentType: DeploymentType.GMX, deployer }; 12 | const params = loadParams(deploymentParams); 13 | 14 | console.log(`Deploying contracts linked to snx to ${network}`); 15 | 16 | await deployGMXContracts(deploymentParams, params); 17 | 18 | console.log(chalk.greenBright('\n=== Successfully deployed! ===\n')); 19 | } 20 | 21 | main() 22 | .then(() => process.exit(0)) 23 | .catch(error => { 24 | console.error(error); 25 | process.exit(1); 26 | }); 27 | -------------------------------------------------------------------------------- /scripts/deployRealSNX.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import { deploySNXContracts } from './deploy/deploySNXContracts'; 3 | import { DeploymentType, getSelectedNetwork } from './util'; 4 | import { loadEnv, loadParams } from './util/parseFiles'; 5 | import { getDeployer } from './util/providers'; 6 | 7 | async function main() { 8 | const network = getSelectedNetwork(); 9 | const envVars = loadEnv(network); 10 | const deployer = await getDeployer(envVars); 11 | const deploymentParams = { network, deploymentType: DeploymentType.SNX, deployer }; 12 | const params = loadParams(deploymentParams); 13 | 14 | console.log(`Deploying contracts linked to snx to ${network}`); 15 | 16 | await deploySNXContracts(deploymentParams, params); 17 | 18 | console.log(chalk.greenBright('\n=== Successfully deployed! ===\n')); 19 | } 20 | 21 | main() 22 | .then(() => process.exit(0)) 23 | .catch(error => { 24 | console.error(error); 25 | process.exit(1); 26 | }); 27 | -------------------------------------------------------------------------------- /scripts/deploySynthetixAdapter.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import { DeploymentType, getSelectedNetwork } from './util'; 3 | import { loadEnv } from './util/parseFiles'; 4 | import { deployProxyWithLibraries } from './util/transactions'; 5 | import { getDeployer } from './util/providers'; 6 | 7 | async function main() { 8 | const network = getSelectedNetwork(); 9 | const envVars = loadEnv(network); 10 | const deployer = await getDeployer(envVars); 11 | // const deploymentParams = { network, mockSnx: false, realPricing: false, deployer }; 12 | const deploymentParams = { network, deployer, deploymentType: DeploymentType.SNX }; 13 | 14 | console.log(`Deploying SynthetixAdapter linked to snx to ${network}`); 15 | 16 | await deployProxyWithLibraries(deploymentParams, 'SynthetixAdapter', 'SynthetixAdapter', undefined, undefined, []); 17 | 18 | console.log(chalk.greenBright('\n=== Successfully deployed! ===\n')); 19 | } 20 | 21 | main() 22 | .then(() => process.exit(0)) 23 | .catch(error => { 24 | console.error(error); 25 | process.exit(1); 26 | }); 27 | -------------------------------------------------------------------------------- /scripts/deployTestGMX.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import { deployGMXContracts } from './deploy/deployGMXContracts'; 3 | import { DeploymentType, getSelectedNetwork } from './util'; 4 | import { loadEnv, loadParams } from './util/parseFiles'; 5 | import { getDeployer } from './util/providers'; 6 | 7 | async function main() { 8 | const network = getSelectedNetwork(); 9 | const envVars = loadEnv(network); 10 | const deployer = await getDeployer(envVars); 11 | const deploymentParams = { network, deployer, deploymentType: DeploymentType.MockGmxMockPricing }; 12 | const params = loadParams(deploymentParams); 13 | 14 | console.log(`Deploying contracts with mocked snx to ${network}`); 15 | 16 | await deployGMXContracts(deploymentParams, params); 17 | 18 | console.log(chalk.greenBright('\n=== Successfully deployed! ===\n')); 19 | } 20 | 21 | main() 22 | .then(() => process.exit(0)) 23 | .catch(error => { 24 | console.error(error); 25 | process.exit(1); 26 | }); 27 | -------------------------------------------------------------------------------- /scripts/deployTestSNX.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import { deploySNXContracts } from './deploy/deploySNXContracts'; 3 | import { DeploymentType, getSelectedNetwork } from './util'; 4 | import { loadEnv, loadParams } from './util/parseFiles'; 5 | import { getDeployer } from './util/providers'; 6 | 7 | async function main() { 8 | const network = getSelectedNetwork(); 9 | const envVars = loadEnv(network); 10 | const deployer = await getDeployer(envVars); 11 | const deploymentParams = { network, deployer, deploymentType: DeploymentType.MockSnxMockPricing }; 12 | const params = loadParams(deploymentParams); 13 | 14 | console.log(`Deploying contracts with mocked snx to ${network}`); 15 | 16 | await deploySNXContracts(deploymentParams, params); 17 | 18 | console.log(chalk.greenBright('\n=== Successfully deployed! ===\n')); 19 | } 20 | 21 | main() 22 | .then(() => process.exit(0)) 23 | .catch(error => { 24 | console.error(error); 25 | process.exit(1); 26 | }); 27 | -------------------------------------------------------------------------------- /scripts/deployTestWithRealPricingGMX.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import { deployGMXContracts } from './deploy/deployGMXContracts'; 3 | import { DeploymentType, getSelectedNetwork } from './util'; 4 | import { loadEnv, loadParams } from './util/parseFiles'; 5 | import { getDeployer } from './util/providers'; 6 | 7 | async function main() { 8 | const network = getSelectedNetwork(); 9 | const envVars = loadEnv(network); 10 | const deployer = await getDeployer(envVars); 11 | const deploymentParams = { network, deploymentType: DeploymentType.MockGmxRealPricing, deployer }; 12 | const params = loadParams(deploymentParams); 13 | 14 | console.log(`Deploying contracts with mocked snx, but real pricing, to ${network}`); 15 | 16 | await deployGMXContracts(deploymentParams, params); 17 | 18 | console.log(chalk.greenBright('\n=== Successfully deployed! ===\n')); 19 | } 20 | 21 | main() 22 | .then(() => process.exit(0)) 23 | .catch(error => { 24 | console.error(error); 25 | process.exit(1); 26 | }); 27 | -------------------------------------------------------------------------------- /scripts/deployTestWithRealPricingSNX.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import { deploySNXContracts } from './deploy/deploySNXContracts'; 3 | import { DeploymentType, getSelectedNetwork } from './util'; 4 | import { loadEnv, loadParams } from './util/parseFiles'; 5 | import { getDeployer } from './util/providers'; 6 | 7 | async function main() { 8 | const network = getSelectedNetwork(); 9 | const envVars = loadEnv(network); 10 | const deployer = await getDeployer(envVars); 11 | const deploymentParams = { network, deploymentType: DeploymentType.MockSnxRealPricing, deployer }; 12 | const params = loadParams(deploymentParams); 13 | 14 | console.log(`Deploying contracts with mocked snx, but real pricing, to ${network}`); 15 | 16 | await deploySNXContracts(deploymentParams, params); 17 | 18 | console.log(chalk.greenBright('\n=== Successfully deployed! ===\n')); 19 | } 20 | 21 | main() 22 | .then(() => process.exit(0)) 23 | .catch(error => { 24 | console.error(error); 25 | process.exit(1); 26 | }); 27 | -------------------------------------------------------------------------------- /scripts/getEvents.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import { getTradeVolume } from './events'; 3 | import { DeploymentType, getSelectedNetwork } from './util'; 4 | import { loadEnv } from './util/parseFiles'; 5 | import { getDeployer } from './util/providers'; 6 | 7 | async function main() { 8 | const network = getSelectedNetwork(); 9 | const envVars = loadEnv(network); 10 | const deployer = await getDeployer(envVars); 11 | // const deploymentParams = { network, deployer, mockSnx: false, realPricing: true }; 12 | const deploymentParams = { network, deployer, deploymentType: DeploymentType.GMX }; 13 | // const params = loadParams(deploymentParams); 14 | 15 | const x = await getTradeVolume(deploymentParams, ['sETH']); 16 | 17 | console.log({ x }); 18 | 19 | console.log(chalk.greenBright('\n=== Successfully seeded! ===\n')); 20 | } 21 | 22 | main() 23 | .then(() => process.exit(0)) 24 | .catch(error => { 25 | console.error(error); 26 | process.exit(1); 27 | }); 28 | -------------------------------------------------------------------------------- /scripts/govWrapGMX.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import { DeploymentType, getSelectedNetwork } from './util'; 3 | import { loadEnv, loadParams } from './util/parseFiles'; 4 | import { getDeployer } from './util/providers'; 5 | import { deployGMXGovernanceWrapper, ownershipTransferWrappers } from './deploy/deployGMXGovernanceWrappers'; 6 | 7 | async function main() { 8 | const network = getSelectedNetwork(); 9 | const envVars = loadEnv(network); 10 | const deployer = await getDeployer(envVars); 11 | const deploymentParams = { network, deployer, deploymentType: DeploymentType.GMX }; 12 | const params = loadParams(deploymentParams); 13 | 14 | await deployGMXGovernanceWrapper(deploymentParams, params); 15 | await ownershipTransferWrappers(deploymentParams, params); 16 | 17 | console.log(chalk.greenBright('\n=== Successfully seeded! ===\n')); 18 | } 19 | 20 | main() 21 | .then(() => process.exit(0)) 22 | .catch(error => { 23 | console.error(error); 24 | process.exit(1); 25 | }); 26 | -------------------------------------------------------------------------------- /scripts/govWrapTestGMX.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import { DeploymentType, getSelectedNetwork } from './util'; 3 | import { loadEnv, loadParams } from './util/parseFiles'; 4 | import { getDeployer } from './util/providers'; 5 | import { deployGMXGovernanceWrapper, ownershipTransferWrappers } from './deploy/deployGMXGovernanceWrappers'; 6 | 7 | async function main() { 8 | const network = getSelectedNetwork(); 9 | const envVars = loadEnv(network); 10 | const deployer = await getDeployer(envVars); 11 | const deploymentParams = { network, deployer, deploymentType: DeploymentType.MockGmxMockPricing }; 12 | const params = loadParams(deploymentParams); 13 | 14 | await deployGMXGovernanceWrapper(deploymentParams, params); 15 | await ownershipTransferWrappers(deploymentParams, params); 16 | 17 | console.log(chalk.greenBright('\n=== Successfully seeded! ===\n')); 18 | } 19 | 20 | main() 21 | .then(() => process.exit(0)) 22 | .catch(error => { 23 | console.error(error); 24 | process.exit(1); 25 | }); 26 | -------------------------------------------------------------------------------- /scripts/govWrapTestWithRealPricingGMX.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import { DeploymentType, getSelectedNetwork } from './util'; 3 | import { loadEnv, loadParams } from './util/parseFiles'; 4 | import { getDeployer } from './util/providers'; 5 | import { deployGMXGovernanceWrapper, ownershipTransferWrappers } from './deploy/deployGMXGovernanceWrappers'; 6 | 7 | async function main() { 8 | const network = getSelectedNetwork(); 9 | const envVars = loadEnv(network); 10 | const deployer = await getDeployer(envVars); 11 | const deploymentParams = { network, deploymentType: DeploymentType.MockGmxRealPricing, deployer }; 12 | const params = loadParams(deploymentParams); 13 | 14 | console.log(`Deploying contracts with mocked snx, but real pricing, to ${network}`); 15 | 16 | await deployGMXGovernanceWrapper(deploymentParams, params); 17 | await ownershipTransferWrappers(deploymentParams, params); 18 | 19 | console.log(chalk.greenBright('\n=== Successfully deployed! ===\n')); 20 | } 21 | 22 | main() 23 | .then(() => process.exit(0)) 24 | .catch(error => { 25 | console.error(error); 26 | process.exit(1); 27 | }); 28 | -------------------------------------------------------------------------------- /scripts/runIntegrationTest.ts: -------------------------------------------------------------------------------- 1 | // import { ethers } from 'ethers'; 2 | // import { toBN } from './util/web3utils'; 3 | // import { deployTestSystem } from '../test/utils/deployTestSystem'; 4 | // import { DEFAULT_OPTION_MARKET_PARAMS } from '../test/utils/defaultParams'; 5 | // import { seedTestSystem } from '../test/utils/seedTestSystem'; 6 | // import { integrationTests } from './integration'; 7 | // 8 | // // run this script using `yarn hardhat run --network local` if running directly from repo (not @lyrafinance/core) 9 | // export async function setupIntegration() { 10 | // // 1. get deployer and network 11 | // const provider = new ethers.providers.JsonRpcProvider('http://localhost:8545'); 12 | // 13 | // const privateKey = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'; // enter address with ETH 14 | // provider.getGasPrice = async () => { 15 | // return ethers.BigNumber.from('0'); 16 | // }; 17 | // provider.estimateGas = async () => { 18 | // return ethers.BigNumber.from(15000000); 19 | // }; 20 | // const deployer = new ethers.Wallet(privateKey, provider); 21 | // 22 | // // 2. deploy and seed market 23 | // const exportAddresses = true; 24 | // const localTestSystem = await deployTestSystem( 25 | // deployer, false, exportAddresses, 26 | // { mockSNX: false, compileSNX: false, optionMarketParams: { ...DEFAULT_OPTION_MARKET_PARAMS, feePortionReserved: toBN('0.05') } }); 27 | // 28 | // await seedTestSystem(deployer, localTestSystem); 29 | // return localTestSystem; 30 | // // Run integration tests 31 | // // console.log(`Running test`) 32 | // // await integrationTests(localTestSystem, deployer, 'sETH'); 33 | // } 34 | // 35 | // 36 | // async function main() { 37 | // // 1. get deployer and network 38 | // const provider = new ethers.providers.JsonRpcProvider('http://localhost:8545'); 39 | // 40 | // const privateKey = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'; // enter address with ETH 41 | // provider.getGasPrice = async () => { 42 | // return ethers.BigNumber.from('0'); 43 | // }; 44 | // provider.estimateGas = async () => { 45 | // return ethers.BigNumber.from(15000000); 46 | // }; 47 | // const deployer = new ethers.Wallet(privateKey, provider); 48 | // 49 | // // 2. deploy and seed market 50 | // const exportAddresses = true; 51 | // const localTestSystem = await deployTestSystem( 52 | // deployer, false, exportAddresses, 53 | // { mockSNX: false, compileSNX: false, optionMarketParams: { ...DEFAULT_OPTION_MARKET_PARAMS, feePortionReserved: toBN('0.05') } }); 54 | // 55 | // await seedTestSystem(deployer, localTestSystem); 56 | // 57 | // // Run integration tests 58 | // console.log(`Running test`) 59 | // await integrationTests(localTestSystem, deployer, 'sETH'); 60 | // } 61 | // 62 | // main() 63 | // .then(() => process.exit(0)) 64 | // .catch(error => { 65 | // console.error(error); 66 | // process.exit(1); 67 | // }); 68 | -------------------------------------------------------------------------------- /scripts/seed/changeOwners.ts: -------------------------------------------------------------------------------- 1 | import { DeploymentParams } from '../util'; 2 | import { executeLyraFunction } from '../util/transactions'; 3 | 4 | export async function updateMarketOwners(deploymentParams: DeploymentParams, market: string, newOwner: string) { 5 | console.log(`Updating owner for market ${market} to ${newOwner}`); 6 | await executeLyraFunction(deploymentParams, 'LiquidityPool', 'nominateNewOwner', [newOwner], market); 7 | await executeLyraFunction(deploymentParams, 'LiquidityToken', 'nominateNewOwner', [newOwner], market); 8 | await executeLyraFunction(deploymentParams, 'OptionGreekCache', 'nominateNewOwner', [newOwner], market); 9 | await executeLyraFunction(deploymentParams, 'OptionMarket', 'nominateNewOwner', [newOwner], market); 10 | await executeLyraFunction(deploymentParams, 'OptionMarketPricer', 'nominateNewOwner', [newOwner], market); 11 | await executeLyraFunction(deploymentParams, 'OptionToken', 'nominateNewOwner', [newOwner], market); 12 | await executeLyraFunction(deploymentParams, 'GMXFuturesPoolHedger', 'nominateNewOwner', [newOwner], market); 13 | await executeLyraFunction(deploymentParams, 'ShortCollateral', 'nominateNewOwner', [newOwner], market); 14 | } 15 | 16 | export async function updateGlobalOwners(deploymentParams: DeploymentParams, newOwner: string) { 17 | console.log(`Updating SynthetixAdapter and LyraRegistry owner to ${newOwner}`); 18 | 19 | await executeLyraFunction(deploymentParams, 'LyraRegistry', 'nominateNewOwner', [newOwner]); 20 | await executeLyraFunction(deploymentParams, 'SynthetixAdapter', 'nominateNewOwner', [newOwner]); 21 | await executeLyraFunction(deploymentParams, 'OptionMarketWrapper', 'nominateNewOwner', [newOwner]); 22 | await executeLyraFunction(deploymentParams, 'OptionMarketViewer', 'nominateNewOwner', [newOwner]); 23 | } 24 | -------------------------------------------------------------------------------- /scripts/seed/hedgeDelta.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from 'ethers'; 2 | import { GlobalCacheStruct } from '../../typechain-types/OptionGreekCache'; 3 | import { DeploymentParams } from '../util'; 4 | import { callLyraFunction } from '../util/transactions'; 5 | import { fromBN } from '../util/web3utils'; 6 | 7 | export async function hedgeDelta(deploymentParams: DeploymentParams, market: string) { 8 | console.log(`\n= Hedging market ${market}`); 9 | 10 | const globalCache = (await callLyraFunction( 11 | deploymentParams, 12 | 'OptionGreekCache', 13 | 'getGlobalCache', 14 | [], 15 | market, 16 | )) as GlobalCacheStruct; 17 | 18 | console.log(`Current netDelta: ${fromBN(globalCache.netGreeks.netDelta)}`); 19 | console.log(`Current netStdVega: ${fromBN(globalCache.netGreeks.netStdVega)}`); 20 | // 21 | // console.log('Opening short account...'); 22 | // try { 23 | // await executeLyraFunction(deploymentParams, 'ShortPoolHedger', 'openShortAccount', [], market); 24 | // } catch (e) { 25 | // console.log('Not able to openShortAccount'); 26 | // console.log(e); 27 | // } 28 | 29 | // await executeLyraFunction(deploymentParams, 'GMXFuturesPoolHedger', 'hedgeDelta', [], market, deploymentParams.deployer, {value: toBN('0.005')}); 30 | const currentHedgedNetDelta = (await callLyraFunction( 31 | deploymentParams, 32 | 'GMXFuturesPoolHedger', 33 | 'getCurrentHedgedNetDelta', 34 | [], 35 | market, 36 | )) as BigNumber; 37 | console.log(`Current netDelta: ${fromBN(BigNumber.from(globalCache.netGreeks.netDelta))}`); 38 | console.log(`Current netStdVega: ${fromBN(BigNumber.from(globalCache.netGreeks.netStdVega))}`); 39 | 40 | console.log('Current poolHedger position:', fromBN(currentHedgedNetDelta)); 41 | 42 | console.log('= Hedging complete'); 43 | } 44 | -------------------------------------------------------------------------------- /scripts/seed/seedDeposit.ts: -------------------------------------------------------------------------------- 1 | import { DeploymentParams } from '../util'; 2 | import { executeLyraFunction, executeExternalFunction, getLyraContract } from '../util/transactions'; 3 | import { fromBN, toBN } from '../util/web3utils'; 4 | import { ParamHandler } from '../util/parseFiles'; 5 | 6 | export async function seedDeposit(deploymentParams: DeploymentParams, params: ParamHandler, market: string) { 7 | console.log(`\n= Depositing to LP for market ${market}`); 8 | 9 | const quoteTicker = params.get('QuoteAsset'); 10 | 11 | const depositParams = params.get('Seed', 'deposit'); 12 | const quoteDecimals = params.get('QuoteDecimals'); 13 | 14 | const amount = toBN(depositParams.markets[market].quoteAmount, quoteDecimals); 15 | 16 | console.log('amount', amount); 17 | console.log(`Approving LP for ${fromBN(amount)} sUSD`); 18 | await executeExternalFunction(deploymentParams, quoteTicker, 'approve', [ 19 | getLyraContract(deploymentParams, 'LiquidityPool', market).address, 20 | amount, 21 | ]); 22 | 23 | console.log(`Fund LP with ${amount} sUSD`); 24 | 25 | await executeLyraFunction( 26 | deploymentParams, 27 | 'LiquidityPool', 28 | 'initiateDeposit', 29 | [deploymentParams.deployer.address, amount], 30 | market, 31 | ); 32 | 33 | await executeLyraFunction(deploymentParams, 'LiquidityPool', 'processDepositQueue', [10], market); 34 | 35 | console.log(`= Depositing for market ${market} done`); 36 | } 37 | -------------------------------------------------------------------------------- /scripts/seed/seedMint.ts: -------------------------------------------------------------------------------- 1 | import { DeploymentParams } from '../util'; 2 | import { executeExternalFunction } from '../util/transactions'; 3 | import { ParamHandler } from '../util/parseFiles'; 4 | import { toBN } from '../util/web3utils'; 5 | 6 | export async function seedMint(deploymentParams: DeploymentParams, params: ParamHandler, market: string) { 7 | const quoteDecimals = params.get('QuoteDecimals'); 8 | const baseDecimals = params.get('Markets', market, 'BaseDecimals'); 9 | const mintParams = params.getObj('Seed', 'mintFunds'); 10 | 11 | console.log('market', market); 12 | console.log('params.get("Markets", market, "BaseAsset")', params.get('Markets', market, 'BaseAsset')); 13 | console.log('just get markets', params.get('Markets')); 14 | 15 | const quoteTicker = params.get('QuoteAsset'); 16 | const baseTicker = params.get('Markets', market, 'BaseAsset'); 17 | 18 | //// 19 | // Setup balances and approvals for opening positions 20 | //// 21 | console.log(`\n= Minting balances for market ${market}`); 22 | 23 | const quoteAmount = mintParams.markets[market].quoteAmount; 24 | const baseAmount = mintParams.markets[market].baseAmount; 25 | // Mint tokens 26 | await executeExternalFunction(deploymentParams, quoteTicker, 'mint', [ 27 | deploymentParams.deployer.address, 28 | toBN(quoteAmount, quoteDecimals), 29 | ]); 30 | 31 | console.log(`Minting ${baseAmount} ${market}`); 32 | // await executeExternalFunction(deploymentParams, baseTicker, 'mint', [ 33 | // deploymentParams.deployer.address, 34 | // BigNumber.from('10').pow(baseDecimals).mul(baseAmount), 35 | // ]); 36 | console.log('Base ticker', baseTicker); 37 | await executeExternalFunction(deploymentParams, baseTicker, 'mint', [ 38 | deploymentParams.deployer.address, 39 | toBN(baseAmount, baseDecimals), 40 | ]); 41 | 42 | console.log('= Seeding balances done'); 43 | } 44 | -------------------------------------------------------------------------------- /scripts/seed/updateCaches.ts: -------------------------------------------------------------------------------- 1 | import { BigNumberish } from 'ethers'; 2 | import { DeploymentParams } from '../util'; 3 | import { callLyraFunction, executeLyraFunction } from '../util/transactions'; 4 | 5 | export async function updateCaches(deploymentParams: DeploymentParams, market: string) { 6 | console.log(`Updating greeks per board for market ${market}`); 7 | 8 | const liveBoards = (await callLyraFunction( 9 | deploymentParams, 10 | 'OptionMarket', 11 | 'getLiveBoards', 12 | [], 13 | market, 14 | )) as BigNumberish[]; 15 | 16 | for (const boardId of liveBoards) { 17 | await executeLyraFunction(deploymentParams, 'OptionGreekCache', 'updateBoardCachedGreeks', [boardId], market); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /scripts/seedRealGMX.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import { seedContracts } from './seed/seedContracts'; 3 | import { DeploymentType, getSelectedNetwork } from './util'; 4 | import { loadEnv, loadParams } from './util/parseFiles'; 5 | import { getDeployer } from './util/providers'; 6 | 7 | async function main() { 8 | const network = getSelectedNetwork(); 9 | const envVars = loadEnv(network); 10 | const deployer = await getDeployer(envVars); 11 | // const deploymentParams = { network, deployer, mockSnx: false, realPricing: false }; 12 | const deploymentParams = { network, deployer, deploymentType: DeploymentType.GMX }; 13 | const params = loadParams(deploymentParams); 14 | 15 | await seedContracts(deploymentParams, params); 16 | 17 | console.log(chalk.greenBright('\n=== Successfully seeded! ===\n')); 18 | } 19 | 20 | main() 21 | .then(() => process.exit(0)) 22 | .catch(error => { 23 | console.error(error); 24 | process.exit(1); 25 | }); 26 | -------------------------------------------------------------------------------- /scripts/seedRealSNX.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import { seedContracts } from './seed/seedContracts'; 3 | import { DeploymentType, getSelectedNetwork } from './util'; 4 | import { loadEnv, loadParams } from './util/parseFiles'; 5 | import { getDeployer } from './util/providers'; 6 | 7 | async function main() { 8 | const network = getSelectedNetwork(); 9 | const envVars = loadEnv(network); 10 | const deployer = await getDeployer(envVars); 11 | // const deploymentParams = { network, deployer, mockSnx: false, realPricing: false }; 12 | const deploymentParams = { network, deployer, deploymentType: DeploymentType.SNX }; 13 | const params = loadParams(deploymentParams); 14 | 15 | await seedContracts(deploymentParams, params); 16 | 17 | console.log(chalk.greenBright('\n=== Successfully seeded! ===\n')); 18 | } 19 | 20 | main() 21 | .then(() => process.exit(0)) 22 | .catch(error => { 23 | console.error(error); 24 | process.exit(1); 25 | }); 26 | -------------------------------------------------------------------------------- /scripts/seedTestGMX.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import { seedContracts } from './seed/seedContracts'; 3 | import { DeploymentType, getSelectedNetwork } from './util'; 4 | import { loadEnv, loadParams } from './util/parseFiles'; 5 | import { getDeployer } from './util/providers'; 6 | 7 | async function main() { 8 | const network = getSelectedNetwork(); 9 | const envVars = loadEnv(network); 10 | const deployer = await getDeployer(envVars); 11 | const deploymentParams = { network, deployer, deploymentType: DeploymentType.MockGmxMockPricing }; 12 | const params = loadParams(deploymentParams); 13 | 14 | await seedContracts(deploymentParams, params); 15 | 16 | console.log(chalk.greenBright('\n=== Successfully seeded! ===\n')); 17 | } 18 | 19 | main() 20 | .then(() => process.exit(0)) 21 | .catch(error => { 22 | console.error(error); 23 | process.exit(1); 24 | }); 25 | -------------------------------------------------------------------------------- /scripts/seedTestSNX.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import { seedContracts } from './seed/seedContracts'; 3 | import { DeploymentType, getSelectedNetwork } from './util'; 4 | import { loadEnv, loadParams } from './util/parseFiles'; 5 | import { getDeployer } from './util/providers'; 6 | 7 | async function main() { 8 | const network = getSelectedNetwork(); 9 | const envVars = loadEnv(network); 10 | const deployer = await getDeployer(envVars); 11 | const deploymentParams = { network, deployer, deploymentType: DeploymentType.MockSnxMockPricing }; 12 | const params = loadParams(deploymentParams); 13 | 14 | await seedContracts(deploymentParams, params); 15 | 16 | console.log(chalk.greenBright('\n=== Successfully seeded! ===\n')); 17 | } 18 | 19 | main() 20 | .then(() => process.exit(0)) 21 | .catch(error => { 22 | console.error(error); 23 | process.exit(1); 24 | }); 25 | -------------------------------------------------------------------------------- /scripts/seedTestWithRealPricing.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import { seedContracts } from './seed/seedContracts'; 3 | import { DeploymentType, getSelectedNetwork } from './util'; 4 | import { loadEnv, loadParams } from './util/parseFiles'; 5 | import { getDeployer } from './util/providers'; 6 | 7 | async function main() { 8 | const network = getSelectedNetwork(); 9 | const envVars = loadEnv(network); 10 | const deployer = await getDeployer(envVars); 11 | // const deploymentParams = { network, deployer, mockSnx: true, realPricing: true }; 12 | const deploymentParams = { network, deployer, deploymentType: DeploymentType.MockGmxRealPricing }; 13 | const params = loadParams(deploymentParams); 14 | 15 | await seedContracts(deploymentParams, params); 16 | 17 | console.log(chalk.greenBright('\n=== Successfully seeded! ===\n')); 18 | } 19 | 20 | main() 21 | .then(() => process.exit(0)) 22 | .catch(error => { 23 | console.error(error); 24 | process.exit(1); 25 | }); 26 | -------------------------------------------------------------------------------- /scripts/seedTestWithRealPricingGMX.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import { seedContracts } from './seed/seedContracts'; 3 | import { DeploymentType, getSelectedNetwork } from './util'; 4 | import { loadEnv, loadParams } from './util/parseFiles'; 5 | import { getDeployer } from './util/providers'; 6 | 7 | async function main() { 8 | const network = getSelectedNetwork(); 9 | const envVars = loadEnv(network); 10 | const deployer = await getDeployer(envVars); 11 | const deploymentParams = { network, deployer, deploymentType: DeploymentType.MockGmxRealPricing }; 12 | const params = loadParams(deploymentParams); 13 | 14 | await seedContracts(deploymentParams, params); 15 | 16 | console.log(chalk.greenBright('\n=== Successfully seeded! ===\n')); 17 | } 18 | 19 | main() 20 | .then(() => process.exit(0)) 21 | .catch(error => { 22 | console.error(error); 23 | process.exit(1); 24 | }); 25 | -------------------------------------------------------------------------------- /scripts/util/maths.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from "ethers"; 2 | import { toBN } from "./web3utils"; 3 | 4 | export function convertPriceE18ToSqrtX96(rawPrice: BigNumber) { 5 | const sqrtX96Price = sqrt(rawPrice.mul(BigNumber.from(2).pow(96 * 2)).div(toBN('1'))); 6 | return sqrtX96Price; 7 | } 8 | 9 | function sqrt(x: BigNumber) { 10 | let z = x.add(1).div(2); 11 | let y = x; 12 | while (z.sub(y).isNegative()) { 13 | y = z; 14 | z = x.div(z).add(z).div(2); 15 | } 16 | 17 | return y; 18 | } -------------------------------------------------------------------------------- /scripts/util/providers.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from 'hardhat'; 2 | import { EnvVars } from './index'; 3 | 4 | export async function getDeployer(envVars: EnvVars) { 5 | const provider = new ethers.providers.JsonRpcProvider(envVars.RPC_URL); 6 | 7 | if (envVars.GAS_PRICE) { 8 | provider.getGasPrice = async () => { 9 | return ethers.BigNumber.from(envVars.GAS_PRICE); 10 | }; 11 | } 12 | if (envVars.GAS_LIMIT) { 13 | provider.estimateGas = async () => { 14 | return ethers.BigNumber.from(envVars.GAS_LIMIT); 15 | }; 16 | } 17 | 18 | return new ethers.Wallet(envVars.PRIVATE_KEY, provider); 19 | } 20 | 21 | export async function getAltSigner(envVars: any) { 22 | const provider = new ethers.providers.JsonRpcProvider(envVars.RPC_URL); 23 | return new ethers.Wallet(envVars.ALT_SIGNER_KEY, provider); 24 | } 25 | -------------------------------------------------------------------------------- /scripts/util/verification.ts: -------------------------------------------------------------------------------- 1 | import hre from 'hardhat'; 2 | 3 | export const etherscanVerification = (contractAddress: string, args: (string | string[])[]) => { 4 | if (hre.network.name === 'local') { 5 | return; 6 | } 7 | console.log('Attempting to verify contract on etherscan'); 8 | 9 | return runTaskWithRetry( 10 | 'verify:verify', 11 | { 12 | address: contractAddress, 13 | constructorArguments: args, 14 | }, 15 | 4, 16 | 5000, 17 | ); 18 | }; 19 | 20 | function delay(ms: number) { 21 | return new Promise(resolve => setTimeout(resolve, ms)); 22 | } 23 | 24 | // Retry is needed because the contract was recently deployed and it hasn't propagated to the explorer backend yet 25 | export const runTaskWithRetry = async (task: string, params: any, times: number, msDelay: number) => { 26 | let counter = times; 27 | await delay(msDelay); 28 | 29 | try { 30 | await hre.run(task, params); 31 | } catch (error: any) { 32 | if (error.message.includes('Reason: Already Verified')) { 33 | console.log('Exiting verification, already verified'); 34 | return; 35 | } 36 | if (error instanceof Error) { 37 | console.error('[ETHERSCAN][ERROR]', 'unable to verify', error.message); 38 | 39 | if (error.message.includes('Reason: Already Verified')) { 40 | console.log('Exiting, already verified'); 41 | return; 42 | } 43 | counter--; 44 | 45 | if (counter > 0) { 46 | console.log('Retrying...'); 47 | await runTaskWithRetry(task, params, counter, msDelay); 48 | } 49 | } 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /slither.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "exclude_informational": true, 3 | "exclude_low": true, 4 | "exclude_medium": false, 5 | "exclude_high": false, 6 | "filter_paths": "(contracts/interfaces/|contracts/test-helpers/|contracts/periphery/|node_modules/gmx|node_modules/openzeppelin|SynthetixAdapter.sol|SNXFuturesPoolHedger.sol|ShortPoolHedger.sol)", 7 | "legacy_ast": false 8 | } -------------------------------------------------------------------------------- /test/contracts/BaseExchangeAdapter/1_Misc.ts: -------------------------------------------------------------------------------- 1 | import { toBN, ZERO_ADDRESS } from '../../../scripts/util/web3utils'; 2 | import { expect } from '../../utils/testSetup'; 3 | import { ethers } from 'hardhat'; 4 | import { TestBaseExchangeAdapter } from '../../../typechain-types'; 5 | import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; 6 | 7 | describe('BaseExchangeAdapter', async () => { 8 | let exchangeAdapter: TestBaseExchangeAdapter; 9 | let deployer: SignerWithAddress; 10 | before(async () => { 11 | [deployer] = await ethers.getSigners(); 12 | exchangeAdapter = await (await ethers.getContractFactory('TestBaseExchangeAdapter', deployer)).deploy(); 13 | }); 14 | 15 | // Integration test 16 | it('reverts on non implemented functions', async () => { 17 | await expect(exchangeAdapter.estimateExchangeToExactQuote(ZERO_ADDRESS, 0)).revertedWith('NotImplemented'); 18 | await expect(exchangeAdapter.estimateExchangeToExactBase(ZERO_ADDRESS, 0)).revertedWith('NotImplemented'); 19 | await expect(exchangeAdapter.exchangeFromExactBase(ZERO_ADDRESS, 0)).revertedWith('NotImplemented'); 20 | await expect(exchangeAdapter.exchangeFromExactQuote(ZERO_ADDRESS, 0)).revertedWith('NotImplemented'); 21 | await expect(exchangeAdapter.exchangeToExactBaseWithLimit(ZERO_ADDRESS, 0, 0)).revertedWith('NotImplemented'); 22 | await expect(exchangeAdapter.exchangeToExactBase(ZERO_ADDRESS, 0)).revertedWith('NotImplemented'); 23 | await expect(exchangeAdapter.exchangeToExactQuoteWithLimit(ZERO_ADDRESS, 0, 0)).revertedWith('NotImplemented'); 24 | await expect(exchangeAdapter.exchangeToExactQuote(ZERO_ADDRESS, 0)).revertedWith('NotImplemented'); 25 | await expect(exchangeAdapter.rateAndCarry(ZERO_ADDRESS)).revertedWith('NotImplemented'); 26 | await expect(exchangeAdapter.getSpotPriceForMarket(ZERO_ADDRESS, 0)).revertedWith('NotImplemented'); 27 | await expect(exchangeAdapter.getSettlementPriceForMarket(ZERO_ADDRESS, 0)).revertedWith('NotImplemented'); 28 | }); 29 | 30 | it('reverts on transfer', async () => { 31 | const tokenForceFailQuote = await (await ethers.getContractFactory('TestERC20Fail')).deploy('t', 't'); 32 | await tokenForceFailQuote.mint(exchangeAdapter.address, toBN('2')); 33 | await exchangeAdapter.testTransferAsset(tokenForceFailQuote.address, deployer.address, toBN('1')); 34 | await tokenForceFailQuote.setForceFail(true); 35 | await expect( 36 | exchangeAdapter.testTransferAsset(tokenForceFailQuote.address, deployer.address, toBN('1')), 37 | ).revertedWith(''); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /test/contracts/GovernanceWrapper/8_BaseGovernanceWrapper.ts: -------------------------------------------------------------------------------- 1 | import { DEFAULT_GOV_GMX_ADAPTER_BOUNDS } from '../../utils/defaultParams'; 2 | import { allCurrenciesFixtureGMX } from '../../utils/fixture'; 3 | import { deployGovernanceWrappers, GovernanceWrappersTypeGMX } from './utils'; 4 | import { expect, hre } from '../../utils/testSetup'; 5 | import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; 6 | import { ZERO_ADDRESS } from '../../../scripts/util/web3utils'; 7 | 8 | describe('GMXHedgedGovernanceWrapper - GMX adapter', () => { 9 | let govWrap: GovernanceWrappersTypeGMX; 10 | let RC: SignerWithAddress; 11 | 12 | beforeEach(async () => { 13 | await allCurrenciesFixtureGMX(); 14 | govWrap = await deployGovernanceWrappers(hre.f.gc, hre.f.deployer); 15 | RC = hre.f.alice; 16 | await govWrap.gmxAdapterGov.setRiskCouncil(RC.address); 17 | await govWrap.gmxAdapterGov.setGMXAdapterBounds(hre.f.gc.optionMarket.address, DEFAULT_GOV_GMX_ADAPTER_BOUNDS); 18 | }); 19 | 20 | ///////////// 21 | // Reverts // 22 | ///////////// 23 | 24 | it('cannot set risk council if not owner', async () => { 25 | await expect(govWrap.gmxAdapterGov.connect(RC).setRiskCouncil(RC.address)).to.be.revertedWith('OnlyOwner'); 26 | }); 27 | 28 | it('cannot set risk council to zero address', async () => { 29 | await expect(govWrap.gmxAdapterGov.setRiskCouncil(ZERO_ADDRESS)).to.be.revertedWith('Zero address'); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /test/contracts/LiquidityPool/13_LockCollateral.ts: -------------------------------------------------------------------------------- 1 | import { toBN } from '../../../scripts/util/web3utils'; 2 | import { 3 | fillLiquidityWithLongCall, 4 | fillLiquidityWithLongPut, 5 | getLiquidity, 6 | openDefaultLongCall, 7 | openDefaultLongPut, 8 | } from '../../utils/contractHelpers'; 9 | import { seedFixture } from '../../utils/fixture'; 10 | import { expect, hre } from '../../utils/testSetup'; 11 | 12 | // OptionMarket tests check for correct transfer amounts, so can do unit tests here 13 | 14 | // unit tests 15 | describe('Lock Collateral', async () => { 16 | beforeEach(seedFixture); 17 | describe('lock quote', async () => { 18 | it('locked quote is updated correctly given amount < freeVolLiq', async () => { 19 | await openDefaultLongPut(); 20 | const lockedCollateral = await hre.f.c.liquidityPool.lockedCollateral(); 21 | const strike = await hre.f.c.optionMarket.getStrike(hre.f.strike.strikeId); 22 | expect(lockedCollateral.quote).to.eq(strike.strikePrice); 23 | expect(lockedCollateral.base).to.eq(toBN('0')); 24 | }); 25 | 26 | it('reverts if amount > freeVolLiq', async () => { 27 | await fillLiquidityWithLongCall(); 28 | expect((await getLiquidity()).freeLiquidity).to.eq(toBN('0')); 29 | await expect(openDefaultLongPut()).to.revertedWith('LockingMoreQuoteThanIsFree'); 30 | }); 31 | }); 32 | 33 | describe('lock base', async () => { 34 | it('Locks the correct amount of base', async () => { 35 | // await hre.f.c.snx.exchanger.setFeeRateForExchange( 36 | // toBytes32("sUSD"), toBytes32("sETH"), toBN("0")) 37 | await openDefaultLongCall(); 38 | const lockedCollateral = await hre.f.c.liquidityPool.lockedCollateral(); 39 | expect(lockedCollateral.quote).to.eq(toBN('0')); 40 | expect(lockedCollateral.base).to.eq(toBN('1')); 41 | }); 42 | 43 | it('Reverts if not enough freeLiq to exchange for base', async () => { 44 | await fillLiquidityWithLongPut(); 45 | expect((await getLiquidity()).freeLiquidity).to.eq(toBN('0')); 46 | await expect(openDefaultLongCall()).to.revertedWith('LockingMoreQuoteThanIsFree'); 47 | }); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /test/contracts/OptionGreekCache/8_NewStrikeGas.ts: -------------------------------------------------------------------------------- 1 | import { currentTime, DAY_SEC, toBN, toBN18, WEEK_SEC } from '../../../scripts/util/web3utils'; 2 | import { seedFixture } from '../../utils/fixture'; 3 | import { hre } from '../../utils/testSetup'; 4 | 5 | describe('OptionGreekCache - SyncBoards', () => { 6 | beforeEach(seedFixture); 7 | it('Adding board with 1 strike', async () => { 8 | await hre.f.c.optionMarket.createOptionBoard( 9 | (await currentTime()) + DAY_SEC, 10 | toBN('1'), 11 | new Array(1).fill('1000').map(toBN18), 12 | new Array(1).fill('1').map(toBN18), 13 | false, 14 | ); 15 | }); 16 | 17 | // ~14.6mln gas 18 | it('Adding board with 25 strikes', async () => { 19 | const tx = await hre.f.c.optionMarket.createOptionBoard( 20 | (await currentTime()) + WEEK_SEC, 21 | toBN('1'), 22 | new Array(25).fill('1000').map(toBN18), 23 | new Array(25).fill('1').map(toBN18), 24 | false, 25 | ); 26 | console.log((await tx.wait()).gasUsed.toString()); 27 | }); 28 | 29 | // about 10.4mln to 17.4mln 30 | // it('Adding board with 30 strikes', async () => { 31 | // await hre.f.c.optionMarket.createOptionBoard( 32 | // (await currentTime()) + WEEK_SEC, 33 | // toBN("1"), 34 | // new Array(30).fill('1000').map(toBN18), 35 | // new Array(30).fill('1').map(toBN18), 36 | // false, 37 | // ); 38 | // }); 39 | }); 40 | -------------------------------------------------------------------------------- /test/contracts/OptionMarket/6_ForceClose.ts: -------------------------------------------------------------------------------- 1 | import { BigNumberish } from 'ethers'; 2 | import { HOUR_SEC, MONTH_SEC, OptionType, toBN } from '../../../scripts/util/web3utils'; 3 | import { forceClosePositionWithOverrides, openPositionWithOverrides } from '../../utils/contractHelpers'; 4 | import { fastForward } from '../../utils/evm'; 5 | import { seedFixture } from '../../utils/fixture'; 6 | import { hre } from '../../utils/testSetup'; 7 | 8 | describe('ForceClose', () => { 9 | // exact limits and cutoffs tested in 6_Cutoffs 10 | 11 | let strikeId: BigNumberish; 12 | beforeEach(async () => { 13 | await seedFixture(); 14 | strikeId = hre.f.market.liveBoards[0].strikes[1].strikeId; 15 | }); 16 | 17 | it('long call', async () => { 18 | const [, positionId] = await openPositionWithOverrides(hre.f.c, { 19 | amount: toBN('2'), 20 | optionType: OptionType.LONG_CALL, 21 | strikeId, 22 | }); 23 | await fastForward(MONTH_SEC - 3 * HOUR_SEC); 24 | await forceClosePositionWithOverrides(hre.f.c, { 25 | amount: toBN('2'), 26 | optionType: OptionType.LONG_CALL, 27 | strikeId, 28 | positionId, 29 | }); 30 | }); 31 | it('long put', async () => { 32 | const [, positionId] = await openPositionWithOverrides(hre.f.c, { 33 | amount: toBN('2'), 34 | optionType: OptionType.LONG_PUT, 35 | strikeId, 36 | }); 37 | await fastForward(MONTH_SEC - 3 * HOUR_SEC); 38 | await forceClosePositionWithOverrides(hre.f.c, { 39 | amount: toBN('2'), 40 | optionType: OptionType.LONG_PUT, 41 | strikeId, 42 | positionId, 43 | }); 44 | }); 45 | it('short call base', async () => { 46 | const [, positionId] = await openPositionWithOverrides(hre.f.c, { 47 | amount: toBN('2'), 48 | optionType: OptionType.SHORT_CALL_BASE, 49 | setCollateralTo: toBN('1'), 50 | strikeId, 51 | }); 52 | await fastForward(MONTH_SEC - 3 * HOUR_SEC); 53 | await forceClosePositionWithOverrides(hre.f.c, { 54 | amount: toBN('2'), 55 | optionType: OptionType.SHORT_CALL_BASE, 56 | strikeId, 57 | positionId, 58 | }); 59 | }); 60 | it('short call quote', async () => { 61 | const [, positionId] = await openPositionWithOverrides(hre.f.c, { 62 | amount: toBN('2'), 63 | optionType: OptionType.SHORT_CALL_QUOTE, 64 | setCollateralTo: toBN('3000'), 65 | strikeId, 66 | }); 67 | await fastForward(MONTH_SEC - 3 * HOUR_SEC); 68 | await forceClosePositionWithOverrides(hre.f.c, { 69 | amount: toBN('2'), 70 | optionType: OptionType.SHORT_CALL_QUOTE, 71 | strikeId, 72 | positionId, 73 | }); 74 | }); 75 | it('short put quote', async () => { 76 | const [, positionId] = await openPositionWithOverrides(hre.f.c, { 77 | amount: toBN('2'), 78 | optionType: OptionType.SHORT_PUT_QUOTE, 79 | setCollateralTo: toBN('3000'), 80 | strikeId, 81 | }); 82 | await fastForward(MONTH_SEC - 3 * HOUR_SEC); 83 | await forceClosePositionWithOverrides(hre.f.c, { 84 | amount: toBN('2'), 85 | optionType: OptionType.SHORT_PUT_QUOTE, 86 | strikeId, 87 | positionId, 88 | }); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /test/contracts/OptionMarket/8_Claim.ts: -------------------------------------------------------------------------------- 1 | import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; 2 | import { toBN } from '../../../scripts/util/web3utils'; 3 | import { openAllTrades } from '../../utils/contractHelpers'; 4 | import { DEFAULT_OPTION_MARKET_PARAMS } from '../../utils/defaultParams'; 5 | import { seedFixture } from '../../utils/fixture'; 6 | import { expect, hre } from '../../utils/testSetup'; 7 | 8 | describe('OptionMarket - SM Claim', () => { 9 | let sm: SignerWithAddress; 10 | beforeEach(async () => { 11 | await seedFixture(); 12 | sm = hre.f.alice; 13 | await hre.f.c.optionMarket.setOptionMarketParams({ 14 | ...DEFAULT_OPTION_MARKET_PARAMS, 15 | securityModule: sm.address, 16 | }); 17 | }); 18 | 19 | it('reverts if called by non-SM', async () => { 20 | await expect(hre.f.c.optionMarket.connect(hre.f.deployer).smClaim()).to.revertedWith('OnlySecurityModule'); 21 | }); 22 | it('can harvest all quote', async () => { 23 | await hre.f.c.snx.quoteAsset.mint(hre.f.c.optionMarket.address, toBN('1000')); 24 | const oldSMBal = await hre.f.c.snx.quoteAsset.balanceOf(sm.address); 25 | expect(await hre.f.c.snx.quoteAsset.balanceOf(hre.f.c.optionMarket.address)).eq(toBN('1000')); 26 | 27 | await hre.f.c.snx.quoteAsset.setForceFail(true); 28 | await expect(hre.f.c.optionMarket.connect(sm).smClaim()).revertedWith('QuoteTransferFailed'); 29 | 30 | await hre.f.c.snx.quoteAsset.setForceFail(false); 31 | await hre.f.c.optionMarket.connect(sm).smClaim(); 32 | 33 | expect(await hre.f.c.snx.quoteAsset.balanceOf(hre.f.c.optionMarket.address)).to.eq(0); 34 | expect(oldSMBal.add(toBN('1000'))).to.eq(await hre.f.c.snx.quoteAsset.balanceOf(sm.address)); 35 | }); 36 | 37 | it('claim 0 amounts', async () => { 38 | await openAllTrades(); 39 | await hre.f.c.snx.baseAsset.transfer(hre.f.c.optionMarket.address, toBN('1')); 40 | await hre.f.c.optionMarket.connect(sm).smClaim(); 41 | expect(await hre.f.c.snx.quoteAsset.balanceOf(hre.f.c.optionMarket.address)).to.eq(toBN('0')); 42 | 43 | const oldSMQuoteBal = await hre.f.c.snx.quoteAsset.balanceOf(sm.address); 44 | const oldSMBaseBal = await hre.f.c.snx.baseAsset.balanceOf(sm.address); 45 | 46 | await hre.f.c.optionMarket.connect(sm).smClaim(); 47 | 48 | expect(oldSMQuoteBal).to.eq(await hre.f.c.snx.quoteAsset.balanceOf(sm.address)); 49 | // Base isn't claimed 50 | expect(oldSMBaseBal).to.eq(await hre.f.c.snx.baseAsset.balanceOf(sm.address)); 51 | }); 52 | 53 | it('claims both balances', async () => { 54 | await hre.f.c.snx.baseAsset.mint(hre.f.c.optionMarket.address, toBN('1')); 55 | await hre.f.c.snx.quoteAsset.mint(hre.f.c.optionMarket.address, toBN('1000')); 56 | const oldSMQuoteBal = await hre.f.c.snx.quoteAsset.balanceOf(sm.address); 57 | 58 | await hre.f.c.optionMarket.connect(sm).smClaim(); 59 | 60 | expect(oldSMQuoteBal.add(toBN('1000'))).to.eq(await hre.f.c.snx.quoteAsset.balanceOf(sm.address)); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /test/contracts/OptionMarketPricer/4_VegaUtil.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { toBN } from '../../../scripts/util/web3utils'; 3 | import { TradeParametersStruct, TradePricingStruct } from '../../../typechain-types/OptionGreekCache'; 4 | import { seedFixture } from '../../utils/fixture'; 5 | import { hre } from '../../utils/testSetup'; 6 | 7 | export const defaultTradePricingStruct: TradePricingStruct = { 8 | optionPrice: toBN('1'), 9 | preTradeAmmNetStdVega: toBN('0.1'), 10 | postTradeAmmNetStdVega: toBN('80'), 11 | callDelta: toBN('1'), 12 | volTraded: toBN('1'), 13 | vega: toBN('10'), 14 | ivVariance: toBN('0'), 15 | }; 16 | 17 | async function getVegaUtilFee( 18 | tradeStructOverrides: Partial = {}, 19 | pricingStructOverrides: Partial = {}, 20 | ) { 21 | return ( 22 | await hre.f.c.optionMarketPricer.getVegaUtilFee( 23 | { 24 | ...hre.f.defaultTradeParametersStruct, 25 | ...tradeStructOverrides, 26 | }, 27 | { 28 | ...defaultTradePricingStruct, 29 | ...pricingStructOverrides, 30 | }, 31 | ) 32 | ).vegaUtilFee; 33 | } 34 | 35 | describe('getVegaUtil', async () => { 36 | // As this is a view function, we don't need to reset the state between each test 37 | before(seedFixture); 38 | 39 | it('correctly computes the fee', async () => { 40 | // if this is wrong check pricing calcs were not changed. 41 | expect(await getVegaUtilFee()).eq(toBN('0.016')); 42 | }); 43 | 44 | it('amount is equal to zero', async () => { 45 | expect(await getVegaUtilFee({ amount: 0 })).eq(0); 46 | }); 47 | 48 | it('free liquidity 0 but optionPrice greater than 1', async () => { 49 | await expect( 50 | getVegaUtilFee({ 51 | liquidity: { 52 | ...hre.f.defaultTradeParametersStruct.liquidity, 53 | NAV: 0, 54 | }, 55 | }), 56 | ).to.be.revertedWith('reverted with panic code 18'); 57 | }); 58 | 59 | it('pricing.vol = 0', async () => { 60 | expect(await getVegaUtilFee({}, { volTraded: 0 })).eq(0); 61 | }); 62 | 63 | it('returns 0 if preTrade netStdVega > postTrade', async () => { 64 | expect( 65 | await getVegaUtilFee( 66 | {}, 67 | { 68 | preTradeAmmNetStdVega: 1000, 69 | postTradeAmmNetStdVega: 999, 70 | }, 71 | ), 72 | ).eq(0); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /test/contracts/SNXFuturesPoolHedger/5_SNXMock.ts: -------------------------------------------------------------------------------- 1 | // testing for cases where the changes in snx system should stop opening of new positions 2 | 3 | import { BigNumber, BigNumberish } from "ethers"; 4 | import { OptionType, toBN, toBytes32 } from "../../../scripts/util/web3utils"; 5 | import { MarketViewStruct } from "../../../typechain-types/OptionMarketViewer"; 6 | import { openPosition } from "../../utils/contractHelpers"; 7 | import { deployFixturePerpsAdapter } from "../../utils/fixture"; 8 | import { seedTestSystem } from "../../utils/seedTestSystem"; 9 | import { expect, hre } from "../../utils/testSetup"; 10 | 11 | describe('SNXFuturesPoolHedger - SNX stopping trading testing', async () => { 12 | let market: MarketViewStruct; 13 | let strikeId: BigNumberish; 14 | beforeEach(async () => { 15 | await deployFixturePerpsAdapter(); 16 | await seedTestSystem(hre.f.deployer, hre.f.c, { noHedger: true }); 17 | market = await hre.f.c.optionMarketViewer.getMarket(hre.f.c.optionMarket.address); 18 | strikeId = market.liveBoards[0].strikes[2].strikeId; 19 | }); 20 | 21 | it('System Status for market is suspended', async () => { 22 | await hre.f.pc.systemStatus.setFuturesMarketSuspended(true); 23 | 24 | await expect(openPosition( 25 | { 26 | strikeId: strikeId, 27 | setCollateralTo: 0, 28 | optionType: OptionType.LONG_CALL, 29 | amount: toBN('10'), 30 | } 31 | )).to.be.revertedWith('SNXPerpV2MarketSuspended'); 32 | }); 33 | 34 | it('set funding rate to be higher than permitted', async () => { 35 | await hre.f.pc.perpMarket.setFundingRate(toBN('0.2')); 36 | await expect(openPosition( 37 | { 38 | strikeId: strikeId, 39 | setCollateralTo: 0, 40 | optionType: OptionType.LONG_CALL, 41 | amount: toBN('10'), 42 | } 43 | )).to.be.revertedWith('UnableToHedgeDelta'); 44 | }); 45 | 46 | it('snx market is not deep enough to support hedge', async () => { 47 | await hre.f.pc.perpMarketSettings.setMaxMarketValue(toBytes32('sETHPERP'), toBN('10')); 48 | 49 | await expect(openPosition( 50 | { 51 | strikeId: strikeId, 52 | setCollateralTo: 0, 53 | optionType: OptionType.LONG_CALL, 54 | amount: toBN('100'), 55 | } 56 | )).to.be.revertedWith('UnableToHedgeDelta'); 57 | }); 58 | 59 | it('snx market is not deep enough to support hedge after position is already open', async () => { 60 | 61 | await openPosition( 62 | { 63 | strikeId: strikeId, 64 | setCollateralTo: 0, 65 | optionType: OptionType.LONG_CALL, 66 | amount: toBN('100'), 67 | } 68 | ); 69 | 70 | await hre.f.pc.perpHedger.hedgeDelta(); 71 | 72 | await hre.f.pc.perpMarket.executeOffchainDelayedOrder(hre.f.pc.perpHedger.address, [toBytes32('0x0')]); 73 | 74 | await openPosition( 75 | { 76 | strikeId: strikeId, 77 | setCollateralTo: 0, 78 | optionType: OptionType.LONG_CALL, 79 | amount: toBN('100'), 80 | } 81 | ); 82 | 83 | expect(await hre.f.pc.perpHedger.canHedge(0, false, 0)).to.be.eq(true); 84 | 85 | await hre.f.pc.perpMarketSettings.setMaxMarketValue(toBytes32('sETHPERP'), toBN('10')); 86 | 87 | expect(await hre.f.pc.perpHedger.canHedge(0, false, 0)).to.be.eq(false); 88 | }); 89 | }) -------------------------------------------------------------------------------- /test/contracts/SNXFuturesPoolHedger/6_Curve.ts: -------------------------------------------------------------------------------- 1 | // testing for curve related exchange rate changes 2 | 3 | import { BigNumber, BigNumberish } from "ethers"; 4 | import { OptionType, toBN } from "../../../scripts/util/web3utils" 5 | import { MarketViewStruct } from "../../../typechain-types/OptionMarketViewer"; 6 | import { openPosition } from "../../utils/contractHelpers"; 7 | import { deployFixturePerpsAdapter } from "../../utils/fixture"; 8 | import { seedTestSystem } from "../../utils/seedTestSystem"; 9 | import { expect, hre } from "../../utils/testSetup" 10 | 11 | describe('SNXFuturesPoolHedger - Curve exchange rate changes', async () => { 12 | let market: MarketViewStruct; 13 | let strikeId: BigNumberish; 14 | beforeEach(async () => { 15 | await deployFixturePerpsAdapter(); 16 | await seedTestSystem(hre.f.deployer, hre.f.c, { noHedger: true }); 17 | market = await hre.f.c.optionMarketViewer.getMarket(hre.f.c.optionMarket.address); 18 | strikeId = market.liveBoards[0].strikes[2].strikeId; 19 | }); 20 | 21 | 22 | it('curve exchange rate is out of bounds for canHedge', async () => { 23 | await openPosition({ 24 | strikeId: strikeId, 25 | setCollateralTo: 0, 26 | optionType: OptionType.LONG_CALL, 27 | amount: toBN('100'), 28 | }) 29 | 30 | await hre.f.c.testCurve.setRate(hre.f.pc.sUSD.address, toBN('0.8')); 31 | await hre.f.c.testCurve.setRate(hre.f.c.snx.quoteAsset.address, toBN('1.5')); 32 | expect(await hre.f.pc.perpHedger.canHedge(100, false, 100)).to.be.eq(false); 33 | }) 34 | 35 | it.skip('when pending margin is 0, i.e trying to can hedge on no change, ') 36 | }) -------------------------------------------------------------------------------- /test/contracts/SynthetixAdapter/2_Getters.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from 'ethers'; 2 | import { toBN, toBytes32, ZERO_ADDRESS } from '../../../scripts/util/web3utils'; 3 | import { ExchangeParamsStructOutput } from '../../../typechain-types/SynthetixAdapter'; 4 | import { defaultBTCExchange, setETHExchangerInvalid, setETHFeeRate, setETHPrice } from '../../utils/contractHelpers'; 5 | import { seedFixture } from '../../utils/fixture'; 6 | import { expect, hre } from '../../utils/testSetup'; 7 | 8 | describe('Getters', async () => { 9 | beforeEach(seedFixture); 10 | 11 | // unit test: getSpotPrice, getSpotPriceForMarket 12 | describe('spot price', async () => { 13 | it('gets spot price from key', async () => { 14 | await setETHPrice(toBN('1000')); 15 | expect(await hre.f.c.synthetixAdapter.getSpotPrice(toBytes32('sETH'))).to.eq(toBN('1000')); 16 | expect(await hre.f.c.synthetixAdapter.getSpotPriceForMarket(hre.f.c.optionMarket.address, 2)).to.eq(toBN('1000')); 17 | 18 | await setETHPrice(toBN('2000')); 19 | expect(await hre.f.c.synthetixAdapter.getSpotPrice(toBytes32('sETH'))).to.eq(toBN('2000')); 20 | expect(await hre.f.c.synthetixAdapter.getSpotPriceForMarket(hre.f.c.optionMarket.address, 2)).to.eq(toBN('2000')); 21 | }); 22 | it('reverts if snx returns invalid flag or rate = 0', async () => { 23 | await setETHExchangerInvalid(); 24 | await expect(hre.f.c.synthetixAdapter.getSpotPrice(toBytes32('sETH'))).to.revertedWith('RateIsInvalid'); 25 | await setETHPrice(0); 26 | await expect(hre.f.c.synthetixAdapter.getSpotPrice(toBytes32('sETH'))).to.revertedWith('RateIsInvalid'); 27 | }); 28 | }); 29 | 30 | // unit test: getExchangeParams (pausing tested in Pause.ts) 31 | describe('exchange params', async () => { 32 | let params: ExchangeParamsStructOutput; 33 | 34 | it('gets exchange params', async () => { 35 | await setETHPrice(toBN('2000')); 36 | await setETHFeeRate('sUSD', 'sETH', toBN('0.01'), toBN('0.05')); 37 | 38 | params = await hre.f.c.synthetixAdapter.getExchangeParams(hre.f.c.optionMarket.address); 39 | await verifyExchangeParams(params, toBN('2000'), 'sUSD', 'sETH', toBN('0.01'), toBN('0.05')); 40 | }); 41 | it('gets correct values after globals changed', async () => { 42 | await defaultBTCExchange(); 43 | 44 | params = await hre.f.c.synthetixAdapter.getExchangeParams(ZERO_ADDRESS); 45 | await verifyExchangeParams(params, toBN('20000'), 'sUSD', 'sBTC', toBN('0.005'), toBN('0.001')); 46 | }); 47 | it('reverts if invalid base or quote key', async () => { 48 | await setETHExchangerInvalid(); 49 | await expect(hre.f.c.synthetixAdapter.getExchangeParams(ZERO_ADDRESS)).to.revertedWith('RateIsInvalid'); 50 | }); 51 | }); 52 | }); 53 | 54 | export async function verifyExchangeParams( 55 | exchangeParams: ExchangeParamsStructOutput, 56 | spot: BigNumber, 57 | quoteKey: string, 58 | baseKey: string, 59 | quoteFee: BigNumber, 60 | baseFee: BigNumber, 61 | ) { 62 | expect(exchangeParams.spotPrice).to.eq(spot); 63 | expect(exchangeParams.quoteKey).to.eq(toBytes32(quoteKey)); 64 | expect(exchangeParams.baseKey).to.eq(toBytes32(baseKey)); 65 | expect(exchangeParams.quoteBaseFeeRate).to.eq(quoteFee); 66 | expect(exchangeParams.baseQuoteFeeRate).to.eq(baseFee); 67 | } 68 | -------------------------------------------------------------------------------- /test/contracts/USDC_quoteAsset/LiquidityPool/13_LockCollateral.ts: -------------------------------------------------------------------------------- 1 | import { toBN } from '../../../../scripts/util/web3utils'; 2 | import { 3 | fillLiquidityWithLongCall, 4 | fillLiquidityWithLongPut, 5 | getLiquidity, 6 | openDefaultLongCall, 7 | openDefaultLongPut, 8 | } from '../../../utils/contractHelpers'; 9 | import { seedFixtureUSDC } from '../../../utils/fixture'; 10 | import { expect, hre } from '../../../utils/testSetup'; 11 | 12 | // OptionMarket tests check for correct transfer amounts, so can do unit tests here 13 | 14 | // unit tests 15 | describe('Lock Collateral', async () => { 16 | beforeEach(async () => { 17 | await seedFixtureUSDC({ useUSDC: true }); 18 | }); 19 | describe('lock quote', async () => { 20 | it('locked quote is updated correctly given amount < freeVolLiq', async () => { 21 | await openDefaultLongPut(); 22 | const lockedCollateral = await hre.f.c.liquidityPool.lockedCollateral(); 23 | const strike = await hre.f.c.optionMarket.getStrike(hre.f.strike.strikeId); 24 | expect(lockedCollateral.quote).to.eq(strike.strikePrice); 25 | expect(lockedCollateral.base).to.eq(toBN('0')); 26 | }); 27 | 28 | it('reverts if amount > freeVolLiq', async () => { 29 | await fillLiquidityWithLongCall(); 30 | expect((await getLiquidity()).freeLiquidity).to.eq(toBN('0')); 31 | await expect(openDefaultLongPut()).to.revertedWith('LockingMoreQuoteThanIsFree'); 32 | }); 33 | }); 34 | 35 | describe('lock base', async () => { 36 | it('Locks the correct amount of base', async () => { 37 | // await hre.f.c.snx.exchanger.setFeeRateForExchange( 38 | // toBytes32("sUSD"), toBytes32("sETH"), toBN("0")) 39 | await openDefaultLongCall(); 40 | const lockedCollateral = await hre.f.c.liquidityPool.lockedCollateral(); 41 | expect(lockedCollateral.quote).to.eq(toBN('0')); 42 | expect(lockedCollateral.base).to.eq(toBN('1')); 43 | }); 44 | 45 | it('Reverts if not enough freeLiq to exchange for base', async () => { 46 | await fillLiquidityWithLongPut(); 47 | expect((await getLiquidity()).freeLiquidity).to.eq(toBN('0')); 48 | await expect(openDefaultLongCall()).to.revertedWith('LockingMoreQuoteThanIsFree'); 49 | }); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /test/contracts/USDC_quoteAsset/OptionMarket/6_ForceClose.ts: -------------------------------------------------------------------------------- 1 | import { BigNumberish } from 'ethers'; 2 | import { HOUR_SEC, MONTH_SEC, OptionType, toBN } from '../../../../scripts/util/web3utils'; 3 | import { forceClosePositionWithOverrides, openPositionWithOverrides } from '../../../utils/contractHelpers'; 4 | import { fastForward } from '../../../utils/evm'; 5 | import { seedFixtureUSDCwBTC } from '../../../utils/fixture'; 6 | import { hre } from '../../../utils/testSetup'; 7 | 8 | describe('USDC_quote - ForceClose', () => { 9 | // exact limits and cutoffs tested in 6_Cutoffs 10 | 11 | let strikeId: BigNumberish; 12 | beforeEach(async () => { 13 | await seedFixtureUSDCwBTC(); 14 | strikeId = hre.f.market.liveBoards[0].strikes[1].strikeId; 15 | }); 16 | 17 | it('long call', async () => { 18 | const [, positionId] = await openPositionWithOverrides(hre.f.c, { 19 | amount: toBN('2'), 20 | optionType: OptionType.LONG_CALL, 21 | strikeId, 22 | }); 23 | await fastForward(MONTH_SEC - 3 * HOUR_SEC); 24 | await forceClosePositionWithOverrides(hre.f.c, { 25 | amount: toBN('2'), 26 | optionType: OptionType.LONG_CALL, 27 | strikeId, 28 | positionId, 29 | }); 30 | }); 31 | it('long put', async () => { 32 | const [, positionId] = await openPositionWithOverrides(hre.f.c, { 33 | amount: toBN('2'), 34 | optionType: OptionType.LONG_PUT, 35 | strikeId, 36 | }); 37 | await fastForward(MONTH_SEC - 3 * HOUR_SEC); 38 | await forceClosePositionWithOverrides(hre.f.c, { 39 | amount: toBN('2'), 40 | optionType: OptionType.LONG_PUT, 41 | strikeId, 42 | positionId, 43 | }); 44 | }); 45 | it('short call base', async () => { 46 | const [, positionId] = await openPositionWithOverrides(hre.f.c, { 47 | amount: toBN('2'), 48 | optionType: OptionType.SHORT_CALL_BASE, 49 | setCollateralTo: toBN('1'), 50 | strikeId, 51 | }); 52 | await fastForward(MONTH_SEC - 3 * HOUR_SEC); 53 | await forceClosePositionWithOverrides(hre.f.c, { 54 | amount: toBN('2'), 55 | optionType: OptionType.SHORT_CALL_BASE, 56 | strikeId, 57 | positionId, 58 | }); 59 | }); 60 | it('short call quote', async () => { 61 | const [, positionId] = await openPositionWithOverrides(hre.f.c, { 62 | amount: toBN('2'), 63 | optionType: OptionType.SHORT_CALL_QUOTE, 64 | setCollateralTo: toBN('3000'), 65 | strikeId, 66 | }); 67 | await fastForward(MONTH_SEC - 3 * HOUR_SEC); 68 | await forceClosePositionWithOverrides(hre.f.c, { 69 | amount: toBN('2'), 70 | optionType: OptionType.SHORT_CALL_QUOTE, 71 | strikeId, 72 | positionId, 73 | }); 74 | }); 75 | it('short put quote', async () => { 76 | const [, positionId] = await openPositionWithOverrides(hre.f.c, { 77 | amount: toBN('2'), 78 | optionType: OptionType.SHORT_PUT_QUOTE, 79 | setCollateralTo: toBN('3000'), 80 | strikeId, 81 | }); 82 | await fastForward(MONTH_SEC - 3 * HOUR_SEC); 83 | await forceClosePositionWithOverrides(hre.f.c, { 84 | amount: toBN('2'), 85 | optionType: OptionType.SHORT_PUT_QUOTE, 86 | strikeId, 87 | positionId, 88 | }); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /test/contracts/USDC_quoteAsset/OptionMarket/8_Claim.ts: -------------------------------------------------------------------------------- 1 | import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; 2 | import { toBN } from '../../../../scripts/util/web3utils'; 3 | import { openAllTrades } from '../../../utils/contractHelpers'; 4 | import { DEFAULT_OPTION_MARKET_PARAMS } from '../../../utils/defaultParams'; 5 | import { seedFixtureUSDCwBTC } from '../../../utils/fixture'; 6 | import { expect, hre } from '../../../utils/testSetup'; 7 | 8 | describe('USDC_quote - OptionMarket - SM Claim', () => { 9 | let sm: SignerWithAddress; 10 | beforeEach(async () => { 11 | await seedFixtureUSDCwBTC(); 12 | sm = hre.f.alice; 13 | await hre.f.c.optionMarket.setOptionMarketParams({ 14 | ...DEFAULT_OPTION_MARKET_PARAMS, 15 | securityModule: sm.address, 16 | }); 17 | }); 18 | 19 | it('reverts if called by non-SM', async () => { 20 | await expect(hre.f.c.optionMarket.connect(hre.f.deployer).smClaim()).to.revertedWith('OnlySecurityModule'); 21 | }); 22 | it('can harvest all quote', async () => { 23 | await hre.f.c.snx.quoteAsset.mint(hre.f.c.optionMarket.address, 1000e6); 24 | const oldSMBal = await hre.f.c.snx.quoteAsset.balanceOf(sm.address); 25 | expect(await hre.f.c.snx.quoteAsset.balanceOf(hre.f.c.optionMarket.address)).eq(1000e6); 26 | 27 | await hre.f.c.snx.quoteAsset.setForceFail(true); 28 | await expect(hre.f.c.optionMarket.connect(sm).smClaim()).revertedWith('QuoteTransferFailed'); 29 | 30 | await hre.f.c.snx.quoteAsset.setForceFail(false); 31 | await hre.f.c.optionMarket.connect(sm).smClaim(); 32 | 33 | expect(await hre.f.c.snx.quoteAsset.balanceOf(hre.f.c.optionMarket.address)).to.eq(0); 34 | expect(oldSMBal.add(1000e6)).to.eq(await hre.f.c.snx.quoteAsset.balanceOf(sm.address)); 35 | }); 36 | 37 | it('claim 0 amounts', async () => { 38 | await openAllTrades(); 39 | await hre.f.c.optionMarket.connect(sm).smClaim(); 40 | expect(await hre.f.c.snx.quoteAsset.balanceOf(hre.f.c.optionMarket.address)).to.eq(toBN('0')); 41 | 42 | const oldSMQuoteBal = await hre.f.c.snx.quoteAsset.balanceOf(sm.address); 43 | 44 | await hre.f.c.optionMarket.connect(sm).smClaim(); 45 | 46 | expect(oldSMQuoteBal).to.eq(await hre.f.c.snx.quoteAsset.balanceOf(sm.address)); 47 | }); 48 | 49 | it('only claims quote', async () => { 50 | await hre.f.c.snx.baseAsset.mint(hre.f.c.optionMarket.address, toBN('1', 8)); 51 | await hre.f.c.snx.quoteAsset.mint(hre.f.c.optionMarket.address, toBN('1000', 6)); 52 | const oldSMQuoteBal = await hre.f.c.snx.quoteAsset.balanceOf(sm.address); 53 | 54 | await hre.f.c.optionMarket.connect(sm).smClaim(); 55 | 56 | expect(oldSMQuoteBal.add(toBN('1000', 6))).to.eq(await hre.f.c.snx.quoteAsset.balanceOf(sm.address)); 57 | expect(toBN('1', 8)).to.eq(await hre.f.c.snx.baseAsset.balanceOf(hre.f.c.optionMarket.address)); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /test/contracts/libraries/MathTest.ts: -------------------------------------------------------------------------------- 1 | import { Signer } from 'ethers'; 2 | import { ethers } from 'hardhat'; 3 | import { fromBN, toBN } from '../../../scripts/util/web3utils'; 4 | import { MathTest } from '../../../typechain-types'; 5 | describe('Oracle - unit test', async () => { 6 | let deployer: Signer; 7 | let math: MathTest; 8 | 9 | before(async () => { 10 | [deployer] = await ethers.getSigners(); 11 | 12 | math = (await (await ethers.getContractFactory('MathTest')).connect(deployer).deploy()) as MathTest; 13 | }); 14 | 15 | it('compares accuracy and gas cost of ln', async () => { 16 | const lnTests = [0.0000001, 1, 5, 10, 1000, 7314, 1423512.512335216]; 17 | for (const i of lnTests) { 18 | const jsVersion = Math.log(i); 19 | const bnTest = toBN(i.toString()); 20 | 21 | const newVal = parseFloat(fromBN(await math.lnV1(bnTest))); 22 | const oldVal = parseFloat(fromBN(await math.lnV2(bnTest))); 23 | const newGas = (await math.estimateGas.lnV1(bnTest)).toString(); 24 | const oldGas = (await math.estimateGas.lnV2(bnTest)).toString(); 25 | 26 | console.log(`ln(${i}) = ${jsVersion}`); 27 | console.log(`new: ${newVal} diff: ${((newVal - jsVersion) / jsVersion) * 100}% gas: ${newGas}`); 28 | console.log(`old: ${oldVal} diff: ${((oldVal - jsVersion) / jsVersion) * 100}% gas: ${oldGas}`); 29 | console.log(); 30 | } 31 | }); 32 | 33 | it('compares accuracy and gas cost of exp', async () => { 34 | const expTests = [ 35 | -40.1345, -20.1234, -10.881325, -2.7521, -1, 0, 1, 2.7521, 10.881325, 20.1324, 40.1345, 60.16999, 99.333, 36 | ]; 37 | for (const i of expTests) { 38 | const jsVersion = Math.exp(i); 39 | const bnTest = toBN(i.toString()); 40 | const newVal = parseFloat(fromBN(await math.expV1(bnTest))); 41 | const oldVal = parseFloat(fromBN(await math.expV2(bnTest))); 42 | const newGas = (await math.estimateGas.expV1(bnTest)).toString(); 43 | const oldGas = (await math.estimateGas.expV2(bnTest)).toString(); 44 | 45 | console.log(`exp(${i}) = ${jsVersion}`); 46 | console.log(`new: ${newVal} diff: ${((newVal - jsVersion) / jsVersion) * 100}% gas: ${newGas}`); 47 | console.log(`old: ${oldVal} diff: ${((oldVal - jsVersion) / jsVersion) * 100}% gas: ${oldGas}`); 48 | console.log(); 49 | } 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /test/contracts/periphery/KeeperHelper/3_updateCache.ts: -------------------------------------------------------------------------------- 1 | import { toBN } from '../../../../scripts/util/web3utils'; 2 | import { setETHPrice } from '../../../utils/contractHelpers'; 3 | import { DEFAULT_BASE_PRICE } from '../../../utils/defaultParams'; 4 | import { seedFixture } from '../../../utils/fixture'; 5 | import { expect, hre } from '../../../utils/testSetup'; 6 | 7 | describe('KeeperHelper - updateCache', () => { 8 | beforeEach(async () => { 9 | await seedFixture(); 10 | }); 11 | 12 | it('updates all boards', async () => { 13 | let globalCache = await hre.f.c.optionGreekCache.getGlobalCache(); 14 | expect(globalCache.maxUpdatedAtPrice).eq(DEFAULT_BASE_PRICE); 15 | await setETHPrice(DEFAULT_BASE_PRICE.sub(toBN('1'))); 16 | await hre.f.c.keeperHelper.updateAllBoardCachedGreeks(); 17 | globalCache = await hre.f.c.optionGreekCache.getGlobalCache(); 18 | expect(globalCache.maxUpdatedAtPrice).eq(DEFAULT_BASE_PRICE.sub(toBN('1'))); 19 | }); 20 | 21 | it('updates all stale boards', async () => { 22 | let globalCache = await hre.f.c.optionGreekCache.getGlobalCache(); 23 | expect(globalCache.maxUpdatedAtPrice).eq(DEFAULT_BASE_PRICE); 24 | await setETHPrice(DEFAULT_BASE_PRICE.sub(toBN('1'))); 25 | await hre.f.c.keeperHelper.updateStaleBoardCachedGreeks(); 26 | // doesnt actually update because the board isn't stale 27 | globalCache = await hre.f.c.optionGreekCache.getGlobalCache(); 28 | expect(globalCache.maxUpdatedAtPrice).eq(DEFAULT_BASE_PRICE); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/contracts/periphery/MiscPeriphery.ts: -------------------------------------------------------------------------------- 1 | import { beforeEach } from 'mocha'; 2 | import { ZERO_ADDRESS } from '../../../scripts/util/web3utils'; 3 | import { seedFixture } from '../../utils/fixture'; 4 | import { expect, hre } from '../../utils/testSetup'; 5 | 6 | describe('MiscPeriphery', async () => { 7 | beforeEach(seedFixture); 8 | 9 | it('BasicLiquidityCounter edge cases', async () => { 10 | await expect(hre.f.c.basicLiquidityCounter.addTokens(ZERO_ADDRESS, 0)).revertedWith( 11 | 'can only be called by LiquidityToken', 12 | ); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/integration/util/cannonHelpers.ts: -------------------------------------------------------------------------------- 1 | import { DeploymentParams } from '../../../scripts/util'; 2 | import { BigNumber } from 'ethers'; 3 | import { 4 | callExternalFunction, 5 | deployMockExternalContract, 6 | executeExternalFunction, 7 | getExternalContract, 8 | } from '../../../scripts/util/transactions'; 9 | import { send } from '../../utils/evm'; 10 | import { MONTH_SEC } from '../../../scripts/util/web3utils'; 11 | import { MockPyth } from '../../../typechain-types'; 12 | 13 | export async function setSUSDBalance(deploymentParams: DeploymentParams, recipient: string, amount: BigNumber) { 14 | const sUSD_underlying = await getExternalContract(deploymentParams, 'SynthsUSD'); 15 | // add ETH 16 | await send( 17 | 'hardhat_setBalance', 18 | [sUSD_underlying.address, '0x1' + '0'.repeat(18)], // ~400 ETH 19 | ); 20 | 21 | await send('hardhat_impersonateAccount', [sUSD_underlying.address]); 22 | const caller: any = await deploymentParams.provider?.getSigner(sUSD_underlying.address); 23 | 24 | await executeExternalFunction(deploymentParams, 'TokenStatesUSD', 'setBalanceOf', [recipient, amount], caller); 25 | 26 | await send('hardhat_stopImpersonatingAccount', [sUSD_underlying.address]); 27 | } 28 | 29 | export async function setUSDCBalance(deploymentParams: DeploymentParams, recipient: string, amount: BigNumber) { 30 | // add ETH 31 | await send( 32 | 'hardhat_setBalance', 33 | ['0x4200000000000000000000000000000000000010', '0x1' + '0'.repeat(18)], // ~400 ETH 34 | ); 35 | 36 | await send('hardhat_impersonateAccount', ['0x4200000000000000000000000000000000000010']); 37 | const caller: any = await deploymentParams.provider?.getSigner('0x4200000000000000000000000000000000000010'); 38 | 39 | await executeExternalFunction(deploymentParams, 'USDC', 'mint', [recipient, amount], caller); 40 | 41 | await send('hardhat_stopImpersonatingAccount', ['0x4200000000000000000000000000000000000010']); 42 | } 43 | 44 | export async function deployMockPyth(deploymentParams: DeploymentParams): Promise { 45 | const mockPyth: MockPyth = (await deployMockExternalContract( 46 | deploymentParams, 47 | 'MockPyth', 48 | 'MockPyth', 49 | MONTH_SEC * 12, 50 | 1, 51 | )) as any; 52 | 53 | const owner = await callExternalFunction(deploymentParams, 'PerpsV2ExchangeRate', 'owner', []); 54 | await send('hardhat_impersonateAccount', [owner]); 55 | await send( 56 | 'hardhat_setBalance', 57 | [owner, '0x1' + '0'.repeat(18)], // ~400 ETH 58 | ); 59 | const caller: any = await deploymentParams.provider?.getSigner(owner); 60 | 61 | await executeExternalFunction( 62 | deploymentParams, 63 | 'PerpsV2ExchangeRate', 64 | 'setOffchainOracle', 65 | [mockPyth.address], 66 | caller, 67 | ); 68 | 69 | await send('hardhat_stopImpersonatingAccount', [owner]); 70 | 71 | return mockPyth; 72 | } 73 | -------------------------------------------------------------------------------- /test/integration/util/writeExternals.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | import * as externals from './externals.json'; 4 | 5 | export function writeExternalsSync() { 6 | // TODO: uniswap, curve 7 | fs.writeFileSync( 8 | path.join(__dirname, '../../..', '.cannon-deployment', 'UniSwapRouter.json'), 9 | JSON.stringify(externals.UniSwapRouter), 10 | ); 11 | fs.writeFileSync(path.join(__dirname, '../../..', '.cannon-deployment', 'USDC.json'), JSON.stringify(externals.USDC)); 12 | fs.writeFileSync(path.join(__dirname, '../../..', '.cannon-deployment', 'wETH.json'), JSON.stringify(externals.WETH)); 13 | fs.writeFileSync( 14 | path.join(__dirname, '../../..', '.cannon-deployment', 'CurveRegistry.json'), 15 | JSON.stringify(externals.CurveRegistry), 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /test/utils/arrayCombiner.ts: -------------------------------------------------------------------------------- 1 | export function combineArrays(array_of_arrays: any[][]) { 2 | // First, handle some degenerate cases... 3 | 4 | if (!array_of_arrays) { 5 | // Or maybe we should toss an exception...? 6 | return []; 7 | } 8 | 9 | if (!Array.isArray(array_of_arrays)) { 10 | // Or maybe we should toss an exception...? 11 | return []; 12 | } 13 | 14 | if (array_of_arrays.length == 0) { 15 | return []; 16 | } 17 | 18 | for (let i = 0; i < array_of_arrays.length; i++) { 19 | if (!Array.isArray(array_of_arrays[i]) || array_of_arrays[i].length == 0) { 20 | // If any of the arrays in array_of_arrays are not arrays or zero-length, return an empty array... 21 | return []; 22 | } 23 | } 24 | 25 | // Done with degenerate cases... 26 | 27 | // Start "odometer" with a 0 for each array in array_of_arrays. 28 | const odometer = new Array(array_of_arrays.length); 29 | odometer.fill(0); 30 | 31 | const output = []; 32 | 33 | let newCombination = formCombination(odometer, array_of_arrays); 34 | 35 | output.push(newCombination); 36 | 37 | while (odometer_increment(odometer, array_of_arrays)) { 38 | newCombination = formCombination(odometer, array_of_arrays); 39 | output.push(newCombination); 40 | } 41 | 42 | return output; 43 | } /* combineArrays() */ 44 | 45 | // Translate "odometer" to combinations from array_of_arrays 46 | function formCombination(odometer: any, array_of_arrays: any[][]) { 47 | // In Imperative Programmingese (i.e., English): 48 | // let s_output = ""; 49 | // for( let i=0; i < odometer.length; i++ ){ 50 | // s_output += "" + array_of_arrays[i][odometer[i]]; 51 | // } 52 | // return s_output; 53 | 54 | // In Functional Programmingese (Henny Youngman one-liner): 55 | return odometer.reduce(function (accumulator: any, odometer_value: any, odometer_index: any) { 56 | return [...accumulator, array_of_arrays[odometer_index][odometer_value]]; 57 | }, []); 58 | } /* formCombination() */ 59 | 60 | function odometer_increment(odometer: any, array_of_arrays: any[][]) { 61 | // Basically, work you way from the rightmost digit of the "odometer"... 62 | // if you're able to increment without cycling that digit back to zero, 63 | // you're all done, otherwise, cycle that digit to zero and go one digit to the 64 | // left, and begin again until you're able to increment a digit 65 | // without cycling it...simple, huh...? 66 | 67 | for (let i_odometer_digit = odometer.length - 1; i_odometer_digit >= 0; i_odometer_digit--) { 68 | const maxee = array_of_arrays[i_odometer_digit].length - 1; 69 | 70 | if (odometer[i_odometer_digit] + 1 <= maxee) { 71 | // increment, and you're done... 72 | odometer[i_odometer_digit]++; 73 | return true; 74 | } else { 75 | if (i_odometer_digit - 1 < 0) { 76 | // No more digits left to increment, end of the line... 77 | return false; 78 | } else { 79 | // Can't increment this digit, cycle it to zero and continue 80 | // the loop to go over to the next digit... 81 | odometer[i_odometer_digit] = 0; 82 | continue; 83 | } 84 | } 85 | } /* for( let odometer_digit = odometer.length-1; odometer_digit >=0; odometer_digit-- ) */ 86 | } /* odometer_increment() */ 87 | -------------------------------------------------------------------------------- /test/utils/assert.ts: -------------------------------------------------------------------------------- 1 | import chai, { expect } from 'chai'; 2 | import { solidity } from 'ethereum-waffle'; 3 | import { BigNumber } from 'ethers'; 4 | import { fromBN, toBN } from '../../scripts/util/web3utils'; 5 | chai.use(solidity); 6 | 7 | export function assertCloseTo(a: BigNumber, b: BigNumber, delta: BigNumber = toBN('0.5')) { 8 | expect(a.sub(b).abs().lte(delta), `${fromBN(a)} is not close to ${fromBN(b)} +/- ${fromBN(delta)}`).is.true; 9 | } 10 | 11 | export function assertCloseToPercentage(a: BigNumber, b: BigNumber, percentage: BigNumber = toBN('0.0005')) { 12 | if (b.eq(0)) { 13 | expect(a.eq(0), `${fromBN(a)} is not close to ${fromBN(b)} +/- ${fromBN(percentage.mul(100))}%`).is.true; 14 | return; 15 | } 16 | expect( 17 | b.sub(a).mul(toBN('1')).div(b).abs().lte(percentage), 18 | `${fromBN(a)} is not close to ${fromBN(b)} +/- ${fromBN(percentage.mul(100))}%`, 19 | ).is.true; 20 | } 21 | 22 | export function assertNotCloseToPercentage(a: BigNumber, b: BigNumber, percentage: BigNumber = toBN('0.0005')) { 23 | if (b.eq(0)) { 24 | expect(a.eq(0), `${fromBN(a)} is close to ${fromBN(b)} +/- ${fromBN(percentage.mul(100))}%`).is.false; 25 | return; 26 | } 27 | expect( 28 | b.sub(a).mul(toBN('1')).div(b).abs().lte(percentage), 29 | `${fromBN(a)} is close to ${fromBN(b)} +/- ${fromBN(percentage.mul(100))}%`, 30 | ).is.false; 31 | } 32 | 33 | export function getPercentageDiff(a: BigNumber, b: BigNumber): BigNumber { 34 | if (b.eq(0)) { 35 | if (a.eq(0)) { 36 | return BigNumber.from(0); 37 | } 38 | return toBN('1'); 39 | } 40 | return b.sub(a).mul(toBN('1')).div(b).abs(); 41 | } 42 | -------------------------------------------------------------------------------- /test/utils/contractHelpers/boards.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from 'ethers'; 2 | import { MONTH_SEC } from '../../../scripts/util/web3utils'; 3 | import { StrikeStruct } from '../../../typechain-types/OptionMarket'; 4 | import { fastForward } from '../evm'; 5 | import { createDefaultBoardWithOverrides } from '../seedTestSystem'; 6 | import { hre } from '../testSetup'; 7 | import { setETHPrice } from './synthetix'; 8 | 9 | export async function createBoard(overrides?: { 10 | expiresIn?: number; 11 | baseIV?: string; 12 | strikePrices?: string[]; 13 | skews?: string[]; 14 | }) { 15 | return await createDefaultBoardWithOverrides(hre.f.c, overrides); 16 | } 17 | 18 | export async function settleBoardAtPrice(price: BigNumber) { 19 | await fastForward(MONTH_SEC); 20 | await setETHPrice(price); 21 | await hre.f.c.optionMarket.settleExpiredBoard(hre.f.board.boardId); 22 | } 23 | 24 | export const emptyStrikeObject: StrikeStruct = { 25 | boardId: 0, 26 | id: 0, 27 | longCall: 0, 28 | longPut: 0, 29 | shortCallBase: 0, 30 | shortCallQuote: 0, 31 | shortPut: 0, 32 | skew: 0, 33 | strikePrice: 0, 34 | }; 35 | -------------------------------------------------------------------------------- /test/utils/contractHelpers/fees.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber, BigNumberish, ContractTransaction } from 'ethers'; 2 | import { CONVERTUSDC, getEventArgs, toBN, UNIT } from '../../../scripts/util/web3utils'; 3 | import { TradeEvent } from '../../../typechain-types/OptionMarket'; 4 | import { hre } from '../testSetup'; 5 | 6 | export function calculateReservedFee(event: any, feePortion: BigNumberish) { 7 | let totalFee: BigNumber = toBN('0'); 8 | 9 | for (const result of event.tradeResults) { 10 | totalFee = totalFee.add(result.totalFee); 11 | } 12 | 13 | return totalFee.mul(feePortion).div(UNIT); 14 | } 15 | 16 | export async function getRoutedFunds( 17 | tx: ContractTransaction, 18 | isOpen?: boolean, 19 | ): Promise<{ userDiff: BigNumber; optionMarketDiff: BigNumber; lpDiff: BigNumber }> { 20 | let args: any; 21 | if (isOpen || isOpen == undefined) { 22 | args = getEventArgs(await tx.wait(), 'Trade') as TradeEvent['args']; 23 | } else { 24 | args = getEventArgs(await tx.wait(), 'Trade') as TradeEvent['args']; 25 | } 26 | 27 | const reservedFee = calculateReservedFee( 28 | args, 29 | (await hre.f.c.optionMarket.getOptionMarketParams()).feePortionReserved, 30 | ); 31 | return { 32 | userDiff: args.trade.totalCost, 33 | optionMarketDiff: reservedFee, 34 | lpDiff: args.trade.totalCost.sub(reservedFee), 35 | }; 36 | } 37 | 38 | export async function getRoutedFunds6dp( 39 | tx: ContractTransaction, 40 | isOpen?: boolean, 41 | ): Promise<{ userDiff: BigNumber; optionMarketDiff: BigNumber; lpDiff: BigNumber }> { 42 | let args: any; 43 | if (isOpen || isOpen == undefined) { 44 | args = getEventArgs(await tx.wait(), 'Trade') as TradeEvent['args']; 45 | } else { 46 | args = getEventArgs(await tx.wait(), 'Trade') as TradeEvent['args']; 47 | } 48 | 49 | const reservedFee = calculateReservedFee( 50 | args, 51 | (await hre.f.c.optionMarket.getOptionMarketParams()).feePortionReserved, 52 | ); 53 | return { 54 | userDiff: args.trade.totalCost.div(CONVERTUSDC), 55 | optionMarketDiff: reservedFee.div(CONVERTUSDC), 56 | lpDiff: args.trade.totalCost.sub(reservedFee).div(CONVERTUSDC), 57 | }; 58 | } 59 | -------------------------------------------------------------------------------- /test/utils/contractHelpers/hedging.ts: -------------------------------------------------------------------------------- 1 | import { BigNumberish } from 'ethers'; 2 | import { openPosition } from '.'; 3 | import { OptionType, toBN } from '../../../scripts/util/web3utils'; 4 | import { hre } from '../testSetup'; 5 | 6 | export async function getRequiredHedge() { 7 | return await hre.f.c.optionGreekCache.getGlobalNetDelta(); 8 | // await hre.f.c.snx.baseAsset.balanceOf(hre.f.c.liquidityPool.address), 9 | // (await hre.f.c.liquidityPool.lockedCollateral()).base 10 | } 11 | 12 | // export async function forceCloseShortAccount() { 13 | // const shortAccountId = await hre.f.c.poolHedger.shortId(); 14 | // await hre.f.c.snx.collateralShort.testForceClose(shortAccountId); 15 | // } 16 | 17 | // export async function getShortAmount() { 18 | // const [shortAmount] = await hre.f.c.poolHedger.getShortPosition(); 19 | // return shortAmount; 20 | // } 21 | 22 | // export async function getShortCollateral() { 23 | // const [, collateral] = await hre.f.c.poolHedger.getShortPosition(); 24 | // return collateral; 25 | // } 26 | 27 | // todo: double check these, since AMM now does not hold base 28 | export async function setPositiveExpectedHedge(amtOverride?: BigNumberish, collatOverride?: BigNumberish) { 29 | const result = await openPosition({ 30 | strikeId: hre.f.strike.strikeId, 31 | optionType: OptionType.SHORT_PUT_QUOTE, 32 | amount: amtOverride || toBN('10'), 33 | setCollateralTo: collatOverride || toBN('20000'), 34 | iterations: 1, 35 | }); 36 | 37 | return result[1]; 38 | } 39 | 40 | export async function setNegativeExpectedHedge(amtOverride?: BigNumberish, collatOverride?: BigNumberish) { 41 | const result = await openPosition({ 42 | strikeId: hre.f.strike.strikeId, 43 | optionType: OptionType.SHORT_CALL_BASE, 44 | amount: amtOverride || toBN('10'), 45 | setCollateralTo: collatOverride || toBN('10'), 46 | iterations: 1, 47 | }); 48 | 49 | return result[1]; 50 | } 51 | -------------------------------------------------------------------------------- /test/utils/contractHelpers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './boards'; 2 | export * from './hedging'; 3 | export * from './liquidity'; 4 | export * from './openClose'; 5 | export * from './parameters'; 6 | export * from './synthetix'; 7 | -------------------------------------------------------------------------------- /test/utils/contractHelpers/keeperHelperPacking.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber, BigNumberish } from 'ethers'; 2 | 3 | const TWO = BigNumber.from(2); 4 | 5 | enum datatypes { 6 | uint32, 7 | uint64, 8 | } 9 | 10 | const packedParams: { [key: string]: { [key: string]: { offset: number; type: datatypes } } } = { 11 | pid8: { 12 | pid1: { offset: 0, type: datatypes.uint32 }, 13 | pid2: { offset: 32, type: datatypes.uint32 }, 14 | pid3: { offset: 64, type: datatypes.uint32 }, 15 | pid4: { offset: 96, type: datatypes.uint32 }, 16 | pid5: { offset: 128, type: datatypes.uint32 }, 17 | pid6: { offset: 160, type: datatypes.uint32 }, 18 | pid7: { offset: 192, type: datatypes.uint32 }, 19 | pid8: { offset: 224, type: datatypes.uint32 }, 20 | }, 21 | }; 22 | 23 | function paramAddValue(val: BigNumberish | boolean, paramMeta: { offset: number; type: datatypes }) { 24 | const bnVal = BigNumber.from(val); 25 | if (paramMeta.type == datatypes.uint32 && bnVal.gte(TWO.pow(32))) throw Error('value too large for datatype uint32'); 26 | if (paramMeta.type == datatypes.uint64 && bnVal.gte(TWO.pow(64))) throw Error('value too large for datatype uint64'); 27 | // console.log(`bnVal ${bnVal.mul(TWO.pow(paramMeta.offset))}`); 28 | return bnVal.mul(TWO.pow(paramMeta.offset)); 29 | } 30 | 31 | function getPackedParams(params: any, paramsForType: { [key: string]: { offset: number; type: datatypes } }) { 32 | let res = BigNumber.from(0); 33 | for (const key of Object.keys(params)) { 34 | if (!paramsForType[key]) throw Error(`key ${key} missing from paramsForType ${JSON.stringify(paramsForType)}`); 35 | if (params[key] == undefined) continue; 36 | // console.log(`paramAddValue ${(params)[key]}`) 37 | res = res.add(paramAddValue((params as any)[key], paramsForType[key])); 38 | // console.log(`res ${res}`) 39 | } 40 | return res; 41 | } 42 | 43 | function packBatch(params: { 44 | pid1: BigNumberish; 45 | pid2: BigNumberish; 46 | pid3: BigNumberish; 47 | pid4: BigNumberish; 48 | pid5: BigNumberish; 49 | pid6: BigNumberish; 50 | pid7: BigNumberish; 51 | pid8: BigNumberish; 52 | }): BigNumber { 53 | return getPackedParams(params, packedParams.pid8); 54 | } 55 | 56 | export function getBatches(positions: BigNumberish[]) { 57 | // Create packed batches for optimising calldata 58 | const numBatches: number = Math.ceil(positions.length / 8); 59 | const batches = []; 60 | 61 | for (let i = 0; i < numBatches; i++) { 62 | const pids: BigNumberish[] = []; 63 | for (let j = i * 8; j < (i + 1) * 8; j++) { 64 | if (positions[j] == undefined) continue; 65 | pids[j] = positions[j]; 66 | } 67 | 68 | batches[i] = packBatch({ 69 | pid1: pids[i * 8], 70 | pid2: pids[i * 8 + 1], 71 | pid3: pids[i * 8 + 2], 72 | pid4: pids[i * 8 + 3], 73 | pid5: pids[i * 8 + 4], 74 | pid6: pids[i * 8 + 5], 75 | pid7: pids[i * 8 + 6], 76 | pid8: pids[i * 8 + 7], 77 | }); 78 | } 79 | return batches; 80 | } 81 | -------------------------------------------------------------------------------- /test/utils/evm.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from 'hardhat'; 2 | import { currentTime } from '../../scripts/util/web3utils'; 3 | 4 | export function send(method: string, params?: Array) { 5 | return ethers.provider.send(method, params === undefined ? [] : params); 6 | } 7 | 8 | export function mineBlock() { 9 | return send('evm_mine', []); 10 | } 11 | 12 | /** 13 | * Increases the time in the EVM. 14 | * @param seconds Number of seconds to increase the time by 15 | */ 16 | export async function fastForward(seconds: number) { 17 | const method = 'evm_increaseTime'; 18 | const params = [seconds]; 19 | 20 | await send(method, params); 21 | 22 | await mineBlock(); 23 | } 24 | 25 | /** 26 | * Increases the time in the EVM to as close to a specific timestamp as possible 27 | */ 28 | export async function fastForwardTo(time: number, silentError: boolean = false) { 29 | const timestamp = await currentTime(); 30 | if (time < timestamp) { 31 | if (silentError) { 32 | console.log(`Time parameter (${time}) is less than now ${timestamp}. Continuing.`); 33 | return; 34 | } 35 | throw new Error( 36 | `Time parameter (${time}) is less than now ${timestamp}. You can only fast forward to times in the future.`, 37 | ); 38 | } 39 | 40 | const secondsBetween = Math.floor(time - timestamp); 41 | await fastForward(secondsBetween); 42 | } 43 | 44 | /** 45 | * Takes a snapshot and returns the ID of the snapshot for restoring later. 46 | */ 47 | export async function takeSnapshot(): Promise { 48 | const result = await send('evm_snapshot'); 49 | await mineBlock(); 50 | return result; 51 | } 52 | 53 | /** 54 | * Restores a snapshot that was previously taken with takeSnapshot 55 | * @param id The ID that was returned when takeSnapshot was called. 56 | */ 57 | export async function restoreSnapshot(id: number) { 58 | await send('evm_revert', [id]); 59 | await mineBlock(); 60 | } 61 | -------------------------------------------------------------------------------- /test/utils/gmxHelpers.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derivexyz/v1-core/ea9e36abae3a4ed789e84bf2d4e385bbb01e13a8/test/utils/gmxHelpers.ts -------------------------------------------------------------------------------- /test/utils/package/index-artifacts.ts: -------------------------------------------------------------------------------- 1 | import { getContractArtifact } from '../../../scripts/util/parseFiles'; 2 | 3 | export function getArtifacts() { 4 | return { 5 | BasicLiquidityCounter: getContractArtifact('local', 'BasicLiquidityCounter', '../../artifacts/contracts/'), 6 | BasicFeeCounter: getContractArtifact('local', 'BasicFeeCounter', '../../artifacts/contracts'), 7 | BlackScholes: getContractArtifact('local', 'BlackScholes', '../../artifacts/contracts'), 8 | GWAV: getContractArtifact('local', 'GWAV', '../../artifacts/contracts'), 9 | GWAVOracle: getContractArtifact('local', 'GWAVOracle', '../../artifacts/contracts'), 10 | LiquidityPool: getContractArtifact('local', 'LiquidityPool', '../../artifacts/contracts'), 11 | LiquidityToken: getContractArtifact('local', 'LiquidityToken', '../../artifacts/contracts'), 12 | LyraRegistry: getContractArtifact('local', 'LyraRegistry', '../../artifacts/contracts'), 13 | OptionGreekCache: getContractArtifact('local', 'OptionGreekCache', '../../artifacts/contracts'), 14 | OptionMarket: getContractArtifact('local', 'OptionMarket', '../../artifacts/contracts'), 15 | OptionMarketPricer: getContractArtifact('local', 'OptionMarketPricer', '../../artifacts/contracts'), 16 | OptionMarketViewer: getContractArtifact('local', 'OptionMarketViewer', '../../artifacts/contracts'), 17 | OptionMarketWrapper: getContractArtifact('local', 'OptionMarketWrapper', '../../artifacts/contracts'), 18 | OptionToken: getContractArtifact('local', 'OptionToken', '../../artifacts/contracts'), 19 | ShortPoolHedger: getContractArtifact('local', 'ShortPoolHedger', '../../artifacts/contracts'), 20 | ShortCollateral: getContractArtifact('local', 'ShortCollateral', '../../artifacts/contracts'), 21 | SynthetixAdapter: getContractArtifact('local', 'SynthetixAdapter', '../../artifacts/contracts'), 22 | TestAddressResolver: getContractArtifact('local', 'TestAddressResolver', '../../artifacts/contracts'), 23 | TestCollateralShort: getContractArtifact('local', 'TestCollateralShort', '../../artifacts/contracts'), 24 | TestCurve: getContractArtifact('local', 'TestCurve', '../../artifacts/contracts'), 25 | TestDelegateApprovals: getContractArtifact('local', 'TestDelegateApprovals', '../../artifacts/contracts'), 26 | TestERC20Fail: getContractArtifact('local', 'TestERC20Fail', '../../artifacts/contracts'), 27 | TestSynthetixReturnZero: getContractArtifact('local', 'TestSynthetixReturnZero', '../../artifacts/contracts'), 28 | TestFaucet: getContractArtifact('local', 'TestFaucet', '../../artifacts/contracts'), 29 | TestExchanger: getContractArtifact('local', 'TestExchanger', '../../artifacts/contracts'), 30 | TestExchangeRates: getContractArtifact('local', 'TestExchangeRates', '../../artifacts/contracts'), 31 | MockAggregator: getContractArtifact('local', 'MockAggregatorV2V3', '../../artifacts/contracts'), 32 | KeeperHelper: getContractArtifact('local', 'KeeperHelper', '../../artifacts/contracts'), 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /test/utils/package/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | currentTime, 3 | DAY_SEC, 4 | fromBN, 5 | getEvent, 6 | getEventArgs, 7 | HOUR_SEC, 8 | MAX_UINT, 9 | MONTH_SEC, 10 | OptionType, 11 | PositionState, 12 | toBN, 13 | toBytes32, 14 | TradeDirection, 15 | UNIT, 16 | WEEK_SEC, 17 | YEAR_SEC, 18 | ZERO_ADDRESS, 19 | } from '../../../scripts/util/web3utils'; 20 | import { 21 | DEFAULT_BASE_PRICE, 22 | DEFAULT_BOARD_PARAMS, 23 | DEFAULT_FEE_RATE_FOR_BASE, 24 | DEFAULT_FEE_RATE_FOR_QUOTE, 25 | DEFAULT_FORCE_CLOSE_PARAMS, 26 | DEFAULT_GREEK_CACHE_PARAMS, 27 | DEFAULT_LIQUIDITY_POOL_PARAMS, 28 | DEFAULT_MIN_COLLATERAL_PARAMS, 29 | DEFAULT_OPTION_MARKET_PARAMS, 30 | DEFAULT_PARTIAL_COLLAT_PARAMS, 31 | DEFAULT_POOL_HEDGER_PARAMS, 32 | DEFAULT_PRICING_PARAMS, 33 | DEFAULT_TRADE_LIMIT_PARAMS, 34 | } from '../defaultParams'; 35 | import { deployTestSystem } from '../deployTestSystem'; 36 | import { fastForward, mineBlock, restoreSnapshot, takeSnapshot } from '../evm'; 37 | import { 38 | createDefaultBoardWithOverrides, 39 | mockPrice, 40 | seedBalanceAndApprovalFor, 41 | seedTestSystem, 42 | } from '../seedTestSystem'; 43 | 44 | // all .sol paths relevant to deploys/testing 45 | // testSystem type used to define lyra mock markets when integration testing 46 | export { TestSystemContractsType } from '../deployTestSystem'; 47 | export { lyraContractPaths } from './index-paths'; 48 | // getting deploy addresses/abis for all lyra contracts 49 | export { getGlobalDeploys, getMarketDeploys } from './parseFiles'; 50 | 51 | // helper for integration testing on hardhat/local 52 | export const TestSystem = { 53 | deploy: deployTestSystem, 54 | seed: seedTestSystem, 55 | seedBalanceAndApprovalFor: seedBalanceAndApprovalFor, 56 | marketActions: { 57 | createBoard: createDefaultBoardWithOverrides, 58 | mockPrice: mockPrice, 59 | }, 60 | OptionType, 61 | PositionState, 62 | TradeDirection, 63 | }; 64 | 65 | // default market params for manual control when integration testing 66 | export const lyraDefaultParams = { 67 | OPTION_MARKET: DEFAULT_OPTION_MARKET_PARAMS, 68 | LIQUIDITY_POOL_PARAMS: DEFAULT_LIQUIDITY_POOL_PARAMS, 69 | POOL_HEDGER_PARAMS: DEFAULT_POOL_HEDGER_PARAMS, 70 | GREEK_CACHE_PARAMS: DEFAULT_GREEK_CACHE_PARAMS, 71 | MIN_COLLATERAL_PARAMS: DEFAULT_MIN_COLLATERAL_PARAMS, 72 | FORCE_CLOSE_PARAMS: DEFAULT_FORCE_CLOSE_PARAMS, 73 | PRICING_PARAMS: DEFAULT_PRICING_PARAMS, 74 | TRADE_LIMIT_PARAMS: DEFAULT_TRADE_LIMIT_PARAMS, 75 | PARTIAL_COLLAT_PARAMS: DEFAULT_PARTIAL_COLLAT_PARAMS, 76 | FEE_RATE_FOR_BASE: DEFAULT_FEE_RATE_FOR_BASE, 77 | FEE_RATE_FOR_QUOTE: DEFAULT_FEE_RATE_FOR_QUOTE, 78 | BASE_PRICE: DEFAULT_BASE_PRICE, 79 | BOARD_PARAMS: DEFAULT_BOARD_PARAMS, 80 | }; 81 | 82 | // helpful constants 83 | export const lyraConstants = { 84 | ZERO_ADDRESS, 85 | HOUR_SEC, 86 | DAY_SEC, 87 | WEEK_SEC, 88 | MONTH_SEC, 89 | YEAR_SEC, 90 | MAX_UINT, 91 | UNIT, 92 | }; 93 | 94 | // helpers 95 | export const lyraUtils = { 96 | toBN, 97 | fromBN, 98 | toBytes32, 99 | currentTime, 100 | getEvent, 101 | getEventArgs, 102 | }; 103 | 104 | export const lyraEvm = { 105 | fastForward, 106 | takeSnapshot, 107 | restoreSnapshot, 108 | mineBlock, 109 | }; 110 | -------------------------------------------------------------------------------- /test/utils/package/merge.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple object check. 3 | * @param item 4 | * @returns {boolean} 5 | */ 6 | export function isObject(item: any) { 7 | return item && typeof item === 'object' && !Array.isArray(item); 8 | } 9 | 10 | // /** 11 | // * Deep merge two objects. 12 | // * @param target 13 | // * @param ...sources 14 | // */ 15 | // export function mergeDeep(target: any, ...sources: any[]): any { 16 | // if (!sources.length) return target; 17 | // const source = sources.shift(); 18 | 19 | // if (isObject(target) && isObject(source)) { 20 | // for (const key in source) { 21 | // if (isObject(source[key])) { 22 | // if (!target[key]) Object.assign(target, { [key]: {} }); 23 | // mergeDeep(target[key], source[key]); 24 | // } else { 25 | // Object.assign(target, { [key]: source[key] }); 26 | // } 27 | // } 28 | // } 29 | 30 | // return mergeDeep(target, ...sources); 31 | // } 32 | 33 | export function mergeDeep(target: any, source: any) { 34 | const output = Object.assign({}, target); 35 | if (isObject(target) && isObject(source)) { 36 | Object.keys(source).forEach(key => { 37 | if (isObject(source[key])) { 38 | if (!(key in target)) Object.assign(output, { [key]: source[key] }); 39 | else output[key] = mergeDeep(target[key], source[key]); 40 | } else { 41 | Object.assign(output, { [key]: source[key] }); 42 | } 43 | }); 44 | } 45 | return output; 46 | } 47 | -------------------------------------------------------------------------------- /test/utils/package/prepare-artifacts.ts: -------------------------------------------------------------------------------- 1 | import fse from 'fs-extra'; 2 | import { deleteRecursive } from './parseFiles'; 3 | 4 | // Moving artifacts from root to dist/ for transpiled deploy scripts in @lyrafinance/protocol 5 | 6 | async function main() { 7 | if (fse.existsSync('dist/artifacts')) { 8 | deleteRecursive('dist/artifacts'); 9 | } 10 | fse.mkdirSync('dist/artifacts'); 11 | 12 | try { 13 | fse.copySync('artifacts', 'dist/artifacts', { overwrite: true }); 14 | console.log('Success - artifacts copied!'); 15 | } catch (err) { 16 | console.error(err); 17 | } 18 | } 19 | 20 | main() 21 | .then(() => process.exit(0)) 22 | .catch(error => { 23 | console.error(error); 24 | process.exit(1); 25 | }); 26 | -------------------------------------------------------------------------------- /test/utils/testSetup.ts: -------------------------------------------------------------------------------- 1 | import chai, { expect } from 'chai'; 2 | import { solidity } from 'ethereum-waffle'; 3 | import * as hardhatRuntimeEnvironment from 'hardhat'; 4 | import { HardhatRuntimeEnvironmentWithFixture } from './fixture'; 5 | 6 | chai.use(solidity); 7 | 8 | const hre: HardhatRuntimeEnvironmentWithFixture = 9 | hardhatRuntimeEnvironment as any as HardhatRuntimeEnvironmentWithFixture; 10 | 11 | export { expect as expect, hre }; 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["es2019", "es2020.promise", "es2020.bigint", "es2020.string"], 4 | "module": "commonjs", 5 | "target": "es2019", 6 | "strict": true, 7 | "esModuleInterop": true, 8 | "outDir": "dist", 9 | "resolveJsonModule": true, 10 | "declaration": true 11 | }, 12 | "include": [ 13 | "./scripts", 14 | "./test", 15 | "./types", 16 | "./simulation", 17 | "test-old/SynthetixAdapter.ts", 18 | "./deployments/kovan-ovm/lyra.realPricing.json", 19 | "./deployments/kovan-ovm/synthetix.json", 20 | "./deployments/kovan-ovm/synthetix.mocked.json", 21 | "./deployments/mainnet-ovm/lyra.json", 22 | "./deployments/mainnet-ovm/synthetix.json", 23 | "./examples" 24 | ], 25 | "exclude": [ 26 | "node_modules", 27 | "artifacts", 28 | "cache", 29 | ".git", 30 | "typechain-types", 31 | ".lyra" 32 | ], 33 | "files": ["./hardhat.config.ts", "typechain-types/hardhat.d.ts", "types.d.ts"] 34 | } 35 | -------------------------------------------------------------------------------- /types.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'stochastic'; 2 | declare module 'synthetix/test/integration/utils/deploy'; 3 | declare module 'gmx/scripts/core/deployGMXComplete'; 4 | --------------------------------------------------------------------------------