├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── .prettierignore ├── .prettierrc ├── README.md ├── package.json ├── scripts ├── .env ├── admin.ts ├── balances.ts ├── board.ts ├── events.ts ├── index.ts ├── initiate-deposit.ts ├── initiate-withdraw.ts ├── leaderboard.ts ├── liquidity-deposit.ts ├── liquidity-history.ts ├── liquidity-withdrawal.ts ├── listen.ts ├── market-pool-stats.ts ├── market.ts ├── markets.ts ├── mint.ts ├── my-positions.ts ├── net-delta-history.ts ├── option-price.ts ├── option-trading-volume.ts ├── position-trade.ts ├── position.ts ├── quote.ts ├── reward-epoch.ts ├── simple-trade.ts ├── trade.ts ├── trading-volume.ts ├── utils │ ├── approve.ts │ ├── coerce.ts │ ├── getLyra.ts │ └── getSigner.ts └── vault-apy.ts ├── src ├── account │ ├── fetchAccountBalancesAndAllowances.ts │ └── index.ts ├── account_reward_epoch │ ├── getDistributedTradingRewards.ts │ ├── getDistributedVaultRewards.ts │ ├── getTotalClaimableTradingRewards.ts │ ├── getTotalClaimableVaultRewards.ts │ └── index.ts ├── admin │ └── index.ts ├── board │ └── index.ts ├── collateral_update_event │ └── index.ts ├── constants │ ├── bn.ts │ ├── chain.ts │ ├── contracts.ts │ ├── events.ts │ ├── links.ts │ ├── mappings.ts │ ├── network.ts │ ├── queries.ts │ ├── rewards.ts │ ├── snapshots.ts │ ├── time.ts │ └── views.ts ├── contracts │ ├── avalon │ │ ├── abis │ │ │ ├── AvalonLiquidityPool.json │ │ │ ├── AvalonLiquidityToken.json │ │ │ ├── AvalonLyraRegistry.json │ │ │ ├── AvalonOptionGreekCache.json │ │ │ ├── AvalonOptionMarket.json │ │ │ ├── AvalonOptionMarketPricer.json │ │ │ ├── AvalonOptionMarketViewer.json │ │ │ ├── AvalonOptionToken.json │ │ │ ├── AvalonShortCollateral.json │ │ │ ├── AvalonShortPoolHedger.json │ │ │ ├── AvalonSynthetixAdapter.json │ │ │ └── AvalonTestFaucet.json │ │ ├── addresses │ │ │ ├── mainnet.addresses.json │ │ │ └── testnet.addresses.json │ │ └── typechain │ │ │ ├── AvalonLiquidityPool.ts │ │ │ ├── AvalonLiquidityToken.ts │ │ │ ├── AvalonLyraRegistry.ts │ │ │ ├── AvalonOptionGreekCache.ts │ │ │ ├── AvalonOptionMarket.ts │ │ │ ├── AvalonOptionMarketPricer.ts │ │ │ ├── AvalonOptionMarketViewer.ts │ │ │ ├── AvalonOptionToken.ts │ │ │ ├── AvalonShortCollateral.ts │ │ │ ├── AvalonShortPoolHedger.ts │ │ │ ├── AvalonSynthetixAdapter.ts │ │ │ ├── AvalonTestFaucet.ts │ │ │ ├── common.ts │ │ │ ├── factories │ │ │ ├── AvalonLiquidityPool__factory.ts │ │ │ ├── AvalonLiquidityToken__factory.ts │ │ │ ├── AvalonLyraRegistry__factory.ts │ │ │ ├── AvalonOptionGreekCache__factory.ts │ │ │ ├── AvalonOptionMarketPricer__factory.ts │ │ │ ├── AvalonOptionMarketViewer__factory.ts │ │ │ ├── AvalonOptionMarket__factory.ts │ │ │ ├── AvalonOptionToken__factory.ts │ │ │ ├── AvalonShortCollateral__factory.ts │ │ │ ├── AvalonShortPoolHedger__factory.ts │ │ │ ├── AvalonSynthetixAdapter__factory.ts │ │ │ ├── AvalonTestFaucet__factory.ts │ │ │ └── index.ts │ │ │ └── index.ts │ ├── common │ │ ├── abis │ │ │ ├── ERC20.json │ │ │ ├── MultiDistributor.json │ │ │ └── Multicall3.json │ │ ├── addresses │ │ │ ├── arbitrum-goerli.addresses.json │ │ │ ├── arbitrum.addresses.json │ │ │ ├── optimism-goerli.addresses.json │ │ │ └── optimism.addresses.json │ │ └── typechain │ │ │ ├── ERC20.ts │ │ │ ├── MultiDistributor.ts │ │ │ ├── Multicall3.ts │ │ │ ├── common.ts │ │ │ ├── factories │ │ │ ├── ERC20__factory.ts │ │ │ ├── MultiDistributor__factory.ts │ │ │ ├── Multicall3__factory.ts │ │ │ └── index.ts │ │ │ └── index.ts │ ├── newport │ │ ├── abis │ │ │ ├── NewportExchangeAdapter.json │ │ │ ├── NewportGMXAdapter.json │ │ │ ├── NewportGMXFuturesPoolHedger.json │ │ │ ├── NewportLiquidityPool.json │ │ │ ├── NewportLiquidityToken.json │ │ │ ├── NewportLyraRegistry.json │ │ │ ├── NewportOptionGreekCache.json │ │ │ ├── NewportOptionMarketPricer.json │ │ │ ├── NewportOptionMarketViewer.json │ │ │ ├── NewportOptionToken.json │ │ │ ├── NewportPoolHedger.json │ │ │ ├── NewportSNXPerpV2Adapter.json │ │ │ ├── NewportSNXPerpsV2PoolHedger.json │ │ │ ├── NewportShortCollateral.json │ │ │ └── NewportTestFaucet.json │ │ ├── addresses │ │ │ ├── arbitrum-goerli.addresses.json │ │ │ ├── arbitrum.addresses.json │ │ │ ├── optimism-goerli.addresses.json │ │ │ └── optimism.addresses.json │ │ ├── arbitrum │ │ │ ├── abis │ │ │ │ └── NewportOptionMarket.json │ │ │ └── typechain │ │ │ │ ├── NewportOptionMarket.ts │ │ │ │ ├── common.ts │ │ │ │ ├── factories │ │ │ │ ├── NewportOptionMarket__factory.ts │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ ├── optimism │ │ │ ├── abis │ │ │ │ └── NewportOptionMarket.json │ │ │ └── typechain │ │ │ │ ├── NewportOptionMarket.ts │ │ │ │ ├── common.ts │ │ │ │ ├── factories │ │ │ │ ├── NewportOptionMarket__factory.ts │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ └── typechain │ │ │ ├── NewportExchangeAdapter.ts │ │ │ ├── NewportGMXAdapter.ts │ │ │ ├── NewportGMXFuturesPoolHedger.ts │ │ │ ├── NewportLiquidityPool.ts │ │ │ ├── NewportLiquidityToken.ts │ │ │ ├── NewportLyraRegistry.ts │ │ │ ├── NewportOptionGreekCache.ts │ │ │ ├── NewportOptionMarketPricer.ts │ │ │ ├── NewportOptionMarketViewer.ts │ │ │ ├── NewportOptionToken.ts │ │ │ ├── NewportPoolHedger.ts │ │ │ ├── NewportSNXPerpV2Adapter.ts │ │ │ ├── NewportSNXPerpsV2PoolHedger.ts │ │ │ ├── NewportShortCollateral.ts │ │ │ ├── NewportTestFaucet.ts │ │ │ ├── common.ts │ │ │ ├── factories │ │ │ ├── NewportExchangeAdapter__factory.ts │ │ │ ├── NewportGMXAdapter__factory.ts │ │ │ ├── NewportGMXFuturesPoolHedger__factory.ts │ │ │ ├── NewportLiquidityPool__factory.ts │ │ │ ├── NewportLiquidityToken__factory.ts │ │ │ ├── NewportLyraRegistry__factory.ts │ │ │ ├── NewportOptionGreekCache__factory.ts │ │ │ ├── NewportOptionMarketPricer__factory.ts │ │ │ ├── NewportOptionMarketViewer__factory.ts │ │ │ ├── NewportOptionToken__factory.ts │ │ │ ├── NewportPoolHedger__factory.ts │ │ │ ├── NewportSNXPerpV2Adapter__factory.ts │ │ │ ├── NewportSNXPerpsV2PoolHedger__factory.ts │ │ │ ├── NewportShortCollateral__factory.ts │ │ │ ├── NewportTestFaucet__factory.ts │ │ │ └── index.ts │ │ │ └── index.ts │ └── typegen.ts ├── global_reward_epoch │ └── index.ts ├── index.ts ├── liquidity_deposit │ └── index.ts ├── liquidity_withdrawal │ └── index.ts ├── lyra.ts ├── market │ └── index.ts ├── option │ └── index.ts ├── position │ ├── getMaxLoss.ts │ ├── getMaxProfit.ts │ ├── getPositionCollateral.ts │ ├── getPositionPnl.ts │ └── index.ts ├── quote │ ├── getForceClosePrice.ts │ ├── getIVImpactForTrade.ts │ ├── getOptionPriceFee.ts │ ├── getPrice.ts │ ├── getQuoteDisabledReason.ts │ ├── getQuoteIteration.ts │ ├── getSpotPriceFee.ts │ ├── getTimeWeightedFee.ts │ ├── getVarianceFee.ts │ ├── getVegaUtilFee.ts │ └── index.ts ├── settle_event │ └── index.ts ├── strike │ └── index.ts ├── trade │ ├── getMaxLoss.ts │ ├── getMaxProfit.ts │ ├── getTradeCollateral.ts │ ├── getTradeDisabledReason.ts │ └── index.ts ├── trade_event │ ├── getTradeEventNewSize.ts │ ├── getTradeEventPreviousSize.ts │ └── index.ts ├── transfer_event │ └── index.ts └── utils │ ├── blackScholes.ts │ ├── buildTx.ts │ ├── canHedge.ts │ ├── canHedgeArbitrum.ts │ ├── canHedgeOptimism.ts │ ├── convertBNDecimals.ts │ ├── fetchAccountRewardEpochData.ts │ ├── fetchAllPositionDataByOwner.ts │ ├── fetchAvalonMarketView.ts │ ├── fetchClaimAddedEvents.ts │ ├── fetchClaimEvents.ts │ ├── fetchGlobalOwner.ts │ ├── fetchGlobalRewardEpochData.ts │ ├── fetchLatestLiquidity.ts │ ├── fetchLatestNetGreeks.ts │ ├── fetchLiquidityDepositEventDataByOwner.ts │ ├── fetchLiquidityHistory.ts │ ├── fetchLiquidityWithdrawalEventDataByOwner.ts │ ├── fetchLyraPrice.ts │ ├── fetchMarketAddresses.ts │ ├── fetchMarketOwner.ts │ ├── fetchNetGreeksHistory.ts │ ├── fetchNewportMarketViews.ts │ ├── fetchNewportOptimismMarketViews.ts │ ├── fetchOpenPositionDataByOwner.ts │ ├── fetchOptionPriceHistory.ts │ ├── fetchOptionVolumeHistory.ts │ ├── fetchPositionDataByID.ts │ ├── fetchPositionDataByOwner.ts │ ├── fetchPositionEventDataByHash.ts │ ├── fetchPositionEventDataByIDs.ts │ ├── fetchPositionPriceHistoryByIDs.ts │ ├── fetchSnapshots.ts │ ├── fetchSpotPriceHistory.ts │ ├── fetchStrikeIVHistory.ts │ ├── fetchTradeListener.ts │ ├── fetchTradingVolumeHistory.ts │ ├── fetchWithCache.ts │ ├── filterNulls.ts │ ├── findMarket.ts │ ├── findMarketX.ts │ ├── fromBigNumber.ts │ ├── getAverageCollateralSpotPrice.ts │ ├── getAverageCostPerOption.ts │ ├── getBoardView.ts │ ├── getBoardViewForStrikeId.ts │ ├── getBreakEvenPrice.ts │ ├── getCappedExpectedHedge.ts │ ├── getCollateralUpdateDataFromRecentEvent.ts │ ├── getCollateralUpdateDataFromSubgraph.ts │ ├── getCollateralUpdatePnl.ts │ ├── getDefaultPeriod.ts │ ├── getDefaultVersionForChain.ts │ ├── getERC20Contract.ts │ ├── getEffectiveLiquidityTokens.ts │ ├── getGlobalContract.ts │ ├── getIsBaseCollateral.ts │ ├── getIsBuy.ts │ ├── getIsCall.ts │ ├── getIsLong.ts │ ├── getLiquidationPrice.ts │ ├── getLyraChainForChainId.ts │ ├── getLyraChainIdForChain.ts │ ├── getLyraContract.ts │ ├── getLyraDeploymentChainId.ts │ ├── getLyraDeploymentForChain.ts │ ├── getLyraDeploymentProvider.ts │ ├── getLyraDeploymentRPCURL.ts │ ├── getLyraDeploymentSubgraphURI.ts │ ├── getLyraGovernanceSubgraphURI.ts │ ├── getLyraMarketContract.ts │ ├── getLyraMarketContractForAddress.ts │ ├── getLyraNetworkForChain.ts │ ├── getLyraNetworkForChainId.ts │ ├── getMarketName.ts │ ├── getMaxCollateral.ts │ ├── getMinCollateralForSpotPrice.ts │ ├── getOpenPositionDataFromStruct.ts │ ├── getOptionType.ts │ ├── getPositionDataFromSubgraph.ts │ ├── getPositionOwner.ts │ ├── getPositionPreviousTrades.ts │ ├── getPriceType.ts │ ├── getPriceVariance.ts │ ├── getProjectedSettlePnl.ts │ ├── getQuoteSpotPrice.ts │ ├── getSettleDataFromSubgraph.ts │ ├── getSnapshotPeriod.ts │ ├── getTimeToExpiryAnnualized.ts │ ├── getTradeDataFromRecentEvent.ts │ ├── getTradeDataFromSubgraph.ts │ ├── getTradePnl.ts │ ├── getTransferDataFromSubgraph.ts │ ├── getUniqueBy.ts │ ├── groupTimeSnapshots.ts │ ├── isMarketEqual.ts │ ├── isNDecimalPlaces.ts │ ├── isTestnet.ts │ ├── isTokenEqual.ts │ ├── multicall.ts │ ├── packTrades.ts │ ├── parseBaseKeyBytes32.ts │ ├── parseBaseSymbol.ts │ ├── parseMarketName.ts │ ├── parsePartialPositionUpdatedEventsFromLogs.ts │ ├── parsePartialTradeEventsFromLogs.ts │ ├── printObject.ts │ ├── roundToDp.ts │ ├── subgraphRequest.ts │ ├── subgraphRequestWithLoop.ts │ └── toBigNumber.ts ├── tsconfig.cjs.json ├── tsconfig.esm.json ├── tsconfig.node.json └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | dist -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], 3 | env: { 4 | node: true, 5 | browser: true, 6 | commonjs: true, 7 | es6: true, 8 | }, 9 | parser: '@typescript-eslint/parser', 10 | plugins: ['@typescript-eslint', 'simple-import-sort', 'unused-imports'], 11 | rules: { 12 | '@typescript-eslint/no-inferrable-types': 0, 13 | 'simple-import-sort/imports': 'error', 14 | 'unused-imports/no-unused-imports': 'error', 15 | 'unused-imports/no-unused-vars': [ 16 | 'warn', 17 | { 18 | vars: 'all', 19 | varsIgnorePattern: '^_', 20 | args: 'after-used', 21 | argsIgnorePattern: '^_', 22 | }, 23 | ], 24 | }, 25 | settings: {}, 26 | } 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules 3 | .yarn/* 4 | !.yarn/patches 5 | !.yarn/releases 6 | !.yarn/plugins 7 | !.yarn/sdks 8 | !.yarn/versions 9 | .pnp.* 10 | 11 | # misc 12 | .DS_Store 13 | .idea 14 | .vscode 15 | 16 | # debug 17 | npm-debug.log* 18 | yarn-debug.log* 19 | yarn-error.log* 20 | **/tsconfig.tsbuildinfo 21 | 22 | # local env files 23 | .env.local 24 | .env.**.local 25 | 26 | # builds 27 | /dist 28 | /build -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules 3 | .yarn/* 4 | !.yarn/patches 5 | !.yarn/releases 6 | !.yarn/plugins 7 | !.yarn/sdks 8 | !.yarn/versions 9 | .pnp.* 10 | 11 | # misc 12 | .DS_Store 13 | .idea 14 | .vscode 15 | 16 | # debug 17 | npm-debug.log* 18 | yarn-debug.log* 19 | yarn-error.log* 20 | **/tsconfig.tsbuildinfo 21 | 22 | # local env files 23 | .env.local 24 | .env.**.local -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules 3 | /.pnp 4 | .pnp.js 5 | 6 | # testing 7 | coverage 8 | 9 | # next.js 10 | .next/ 11 | out/ 12 | 13 | # production 14 | /build 15 | 16 | # misc 17 | .DS_Store 18 | .idea 19 | 20 | # debug 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | # local env files 26 | .env.local 27 | .env.**.local 28 | 29 | # Files generated by next-on-netlify command 30 | out_publish/ 31 | out_functions/ 32 | 33 | # local dev files 34 | **/constants/addresses/local-* 35 | generated 36 | typechain 37 | 38 | robots.txt 39 | sitemap.xml -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "proseWrap": "preserve", 4 | "semi": false, 5 | "singleQuote": true, 6 | "useTabs": false, 7 | "tabWidth": 2, 8 | "arrowParens": "avoid", 9 | "trailingComma": "es5" 10 | } 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@lyrafinance/lyra-js", 3 | "version": "0.0.29", 4 | "description": "A JavaScript SDK for the Lyra Protocol.", 5 | "main": "./dist/esm/index.js", 6 | "types": "./dist/types/index.d.ts", 7 | "exports": { 8 | "import": "./dist/esm/index.js", 9 | "require": "./dist/cjs/index.js", 10 | "default": "./dist/esm/index.js" 11 | }, 12 | "homepage": "www.lyra.finance", 13 | "author": "Lyra Finance", 14 | "license": "ISC", 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/lyra-finance/lyra-js" 18 | }, 19 | "scripts": { 20 | "build": "tsc --project tsconfig.esm.json && tsc --project tsconfig.cjs.json", 21 | "tsc": "yarn build", 22 | "watch": "concurrently \"tsc --project tsconfig.esm.json --watch\" \"tsc --project tsconfig.cjs.json --watch\"", 23 | "script": "env-cmd -f scripts/.env.local ts-node --project tsconfig.node.json scripts/index.ts", 24 | "typegen": "ts-node --project tsconfig.node.json src/contracts/typegen.ts", 25 | "sync": "ts-node --project tsconfig.node.json src/contracts/sync.ts", 26 | "format": "prettier --write \"src/**/*.ts\"", 27 | "lint": "eslint '**/*.ts'", 28 | "clean": "rimraf node_modules dist", 29 | "ts-prune": "ts-prune -p tsconfig.esm.json -i 'src/contracts/.*/typechain/|src/index.ts'", 30 | "prepare": "yarn build", 31 | "preversion": "yarn lint", 32 | "version": "yarn format && git add -A src", 33 | "postversion": "git push && git push --tags" 34 | }, 35 | "dependencies": { 36 | "@apollo/client": "^3.7.3", 37 | "cross-fetch": "^3.1.5", 38 | "ethers": "^5.6.1", 39 | "graphql": "^16.6.0" 40 | }, 41 | "devDependencies": { 42 | "@typechain/ethers-v5": "^10.1.1", 43 | "@types/fs-extra": "^9.0.13", 44 | "@types/mkdirp": "^1.0.2", 45 | "@types/node": "^17.0.21", 46 | "@types/underscore": "^1.11.4", 47 | "@types/yargs": "^17.0.8", 48 | "@typescript-eslint/eslint-plugin": "^5.32.0", 49 | "@typescript-eslint/parser": "^5.32.0", 50 | "concurrently": "^7.1.0", 51 | "env-cmd": "^10.1.0", 52 | "eslint": "^8.21.0", 53 | "eslint-plugin-simple-import-sort": "^7.0.0", 54 | "eslint-plugin-unused-imports": "^2.0.0", 55 | "fs-extra": "^10.0.1", 56 | "prettier": "^2.7.0", 57 | "rimraf": "^3.0.2", 58 | "ts-node": "^10.5.0", 59 | "ts-prune": "^0.10.3", 60 | "tslib": "^2.4.0", 61 | "typechain": "^8.1.1", 62 | "typescript": "^4.7.3", 63 | "underscore": "^1.13.6", 64 | "yargs": "^17.3.1" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /scripts/.env: -------------------------------------------------------------------------------- 1 | # Copy to .env.local 2 | 3 | PRIVATE_KEY= 4 | RPC_URL= 5 | CHAIN= 6 | -------------------------------------------------------------------------------- /scripts/admin.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | import yargs from 'yargs' 3 | 4 | import fromBigNumber from '../src/utils/fromBigNumber' 5 | import toBigNumber from '../src/utils/toBigNumber' 6 | import getLyra from './utils/getLyra' 7 | 8 | export default async function admin(argv: string[]) { 9 | const lyra = getLyra() 10 | const args = await yargs(argv).options({ 11 | market: { type: 'string', alias: 'm', require: true }, 12 | }).argv 13 | const market = await lyra.market(args.market) 14 | const admin = lyra.admin() 15 | const boards = market.liveBoards() 16 | const owner = await market.owner() 17 | const globalOwner = await admin.owner() 18 | 19 | console.log({ owner, globalOwner }) 20 | const expiry = BigNumber.from('1653091199') 21 | const baseIV = toBigNumber(1) 22 | const strikePrices = [market.spotPrice] 23 | const skews = [toBigNumber(1)] 24 | 25 | const { board, tx } = await admin.addBoard(market.address, expiry, baseIV, strikePrices, skews) 26 | console.log({ board, tx }) 27 | const { strike: strikeParams, tx: strikeTx } = await lyra 28 | .admin() 29 | .addStrikeToBoard(market.address, BigNumber.from(boards[0].id), strikePrices[0], skews[0]) 30 | console.log({ strikeParams, strikeTx }) 31 | 32 | const { params: newParams, tx: _greekCacheTx } = await admin.setGreekCacheParams(lyra.version, market.address, { 33 | maxStrikesPerBoard: toBigNumber(20), 34 | }) 35 | console.log({ 36 | greekCacheParams: { 37 | maxStrikePerBoard: fromBigNumber(newParams.maxStrikesPerBoard), 38 | gwavSkewCap: fromBigNumber(newParams.gwavSkewCap), 39 | gwavSkewFloor: fromBigNumber(newParams.gwavSkewFloor), 40 | }, 41 | }) 42 | 43 | // console.log({ 44 | // id: board.id, 45 | // expiryTimestamp: board.expiryTimestamp, 46 | // isExpired: board.isExpired, 47 | // priceAtExpiry: board.priceAtExpiry ? fromBigNumber(board.priceAtExpiry) : null, 48 | // strikes: board.strikes().map(s => ({ 49 | // id: s.id, 50 | // strike: fromBigNumber(s.strikePrice), 51 | // isDeltaInRange: s.isDeltaInRange, 52 | // })), 53 | // }) 54 | } 55 | -------------------------------------------------------------------------------- /scripts/balances.ts: -------------------------------------------------------------------------------- 1 | import yargs from 'yargs' 2 | 3 | import printObject from '../src/utils/printObject' 4 | import getLyra from './utils/getLyra' 5 | 6 | export default async function balances(argv: string[]) { 7 | const lyra = getLyra() 8 | const args = await yargs(argv).options({ 9 | account: { type: 'string', alias: 'a', require: true }, 10 | }).argv 11 | const account = lyra.account(args.account) 12 | const balances = await account.marketBalances('eth') 13 | printObject(balances) 14 | } 15 | -------------------------------------------------------------------------------- /scripts/board.ts: -------------------------------------------------------------------------------- 1 | import yargs from 'yargs' 2 | 3 | import fromBigNumber from '../src/utils/fromBigNumber' 4 | import getLyra from './utils/getLyra' 5 | 6 | export default async function board(argv: string[]) { 7 | const lyra = getLyra() 8 | const args = await yargs(argv).options({ 9 | market: { type: 'string', alias: 'm', require: true }, 10 | board: { type: 'number', alias: 'b', require: true }, 11 | }).argv 12 | const board = await lyra.board(args.market, args.board) 13 | console.log({ 14 | id: board.id, 15 | expiryTimestamp: board.expiryTimestamp, 16 | isExpired: board.isExpired, 17 | spotPriceAtExpiry: board.spotPriceAtExpiry ? fromBigNumber(board.spotPriceAtExpiry) : null, 18 | strikes: board.strikes().map(s => ({ 19 | id: s.id, 20 | strike: fromBigNumber(s.strikePrice), 21 | isDeltaInRange: s.isDeltaInRange, 22 | })), 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /scripts/events.ts: -------------------------------------------------------------------------------- 1 | import yargs from 'yargs' 2 | 3 | import printObject from '../src/utils/printObject' 4 | import getLyra from './utils/getLyra' 5 | 6 | export default async function events(argv: string[]): Promise { 7 | const lyra = getLyra() 8 | const args = await yargs(argv).options({ 9 | hash: { type: 'string', alias: 'h', require: true }, 10 | }).argv 11 | // Add test code here 12 | const { trades, collateralUpdates, settles, transfers } = await lyra.events(args.hash) 13 | printObject({ 14 | trades: trades.map(trade => ({ 15 | marketName: trade.marketName, 16 | positionId: trade.positionId, 17 | trader: trade.trader, 18 | size: trade.size, 19 | collateral: trade.collateralValue, 20 | isBuy: trade.isBuy, 21 | isLong: trade.isLong, 22 | isLiquidation: trade.isLiquidation, 23 | pricePerOption: trade.pricePerOption, 24 | premium: trade.premium, 25 | })), 26 | collateralUpdates: collateralUpdates.map(collateralUpdate => ({ 27 | marketName: collateralUpdate.marketName, 28 | positionId: collateralUpdate.positionId, 29 | amount: collateralUpdate.amount, 30 | value: collateralUpdate.value, 31 | })), 32 | settles: settles.map(settle => ({ 33 | marketName: settle.marketName, 34 | positionId: settle.positionId, 35 | settlement: settle.settlement, 36 | })), 37 | transfers, 38 | }) 39 | } 40 | -------------------------------------------------------------------------------- /scripts/index.ts: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | const script = process.argv[2] 3 | import(path.join(__dirname, script + '.ts')).then(main => { 4 | console.time(script) 5 | main.default(process.argv.slice(3)).then(() => { 6 | console.timeEnd(script) 7 | process.exit() 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /scripts/initiate-deposit.ts: -------------------------------------------------------------------------------- 1 | import yargs from 'yargs' 2 | 3 | import { MAX_BN } from '../src/constants/bn' 4 | import toBigNumber from '../src/utils/toBigNumber' 5 | import getLyra from './utils/getLyra' 6 | import getSigner from './utils/getSigner' 7 | 8 | export default async function initiateDeposit(argv: string[]) { 9 | const lyra = getLyra() 10 | const signer = getSigner(lyra) 11 | const args = await yargs(argv).options({ 12 | beneficiary: { type: 'string', alias: 'b', require: true }, 13 | marketAddressOrName: { type: 'string', alias: 'm', require: true }, 14 | amountQuote: { type: 'number', alias: 'a', require: true }, 15 | }).argv 16 | 17 | const beneficiary = args.beneficiary 18 | const marketAddressOrName = args.marketAddressOrName 19 | const amountQuote = toBigNumber(args.amountQuote) 20 | const account = lyra.account(args.beneficiary ?? signer.address) 21 | const approveTx = await account.approveDeposit(marketAddressOrName, MAX_BN) 22 | const approveResponse = await signer.sendTransaction(approveTx) 23 | await approveResponse.wait() 24 | console.log('Approved sUSD') 25 | const deposit = await lyra.deposit(beneficiary, marketAddressOrName, amountQuote) 26 | if (deposit) { 27 | const response = await signer.sendTransaction(deposit) 28 | const receipt = await response.wait() 29 | console.log('tx', receipt.transactionHash) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /scripts/initiate-withdraw.ts: -------------------------------------------------------------------------------- 1 | import yargs from 'yargs' 2 | 3 | import toBigNumber from '../src/utils/toBigNumber' 4 | import getLyra from './utils/getLyra' 5 | import getSigner from './utils/getSigner' 6 | 7 | export default async function initiateWithdraw(argv: string[]) { 8 | const lyra = getLyra() 9 | const signer = getSigner(lyra) 10 | const args = await yargs(argv).options({ 11 | beneficiary: { type: 'string', alias: 'b', require: true }, 12 | marketAddressOrName: { type: 'string', alias: 'm', require: true }, 13 | tokenAddressOrName: { type: 'string', alias: 't', require: true }, 14 | amountLiquidityTokens: { type: 'number', alias: 'a', require: true }, 15 | }).argv 16 | 17 | const beneficiary = args.beneficiary 18 | const marketAddressOrName = args.marketAddressOrName 19 | const amountLiquidityTokens = toBigNumber(args.amountLiquidityTokens) 20 | // no approval is needed 21 | const market = await lyra.market(marketAddressOrName) 22 | const withdraw = await market.withdraw(beneficiary, amountLiquidityTokens) 23 | if (withdraw) { 24 | const response = await signer.sendTransaction(withdraw) 25 | const receipt = await response.wait() 26 | console.log('tx', receipt.transactionHash) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /scripts/leaderboard.ts: -------------------------------------------------------------------------------- 1 | import getLyra from './utils/getLyra' 2 | 3 | export default async function leaderboard() { 4 | const lyra = getLyra() 5 | const leaderboard = await lyra.leaderboard({ 6 | minPositionIds: { 7 | eth: 4490, 8 | btc: 178, 9 | }, 10 | }) 11 | console.log('pid', leaderboard.length) 12 | } 13 | -------------------------------------------------------------------------------- /scripts/liquidity-deposit.ts: -------------------------------------------------------------------------------- 1 | import yargs from 'yargs' 2 | 3 | import getLyra from './utils/getLyra' 4 | 5 | export default async function liquidityDeposits(argv: string[]) { 6 | const lyra = getLyra() 7 | const args = await yargs(argv).options({ 8 | market: { type: 'string', alias: 'm', require: true }, 9 | owner: { type: 'string', alias: 'o', require: true }, 10 | }).argv 11 | const liquidityDeposits = await lyra.liquidityDeposits(args.market, args.owner) 12 | console.log(liquidityDeposits) 13 | } 14 | -------------------------------------------------------------------------------- /scripts/liquidity-history.ts: -------------------------------------------------------------------------------- 1 | import yargs from 'yargs' 2 | 3 | import getLyra from './utils/getLyra' 4 | 5 | export default async function liquidityHistory(argv: string[]) { 6 | const lyra = getLyra() 7 | const args = await yargs(argv).options({ 8 | market: { type: 'string', alias: 'm', require: true }, 9 | }).argv 10 | const market = await lyra.market(args.market) 11 | const liquidityHistory = await market.liquidityHistory() 12 | console.log(liquidityHistory) 13 | } 14 | -------------------------------------------------------------------------------- /scripts/liquidity-withdrawal.ts: -------------------------------------------------------------------------------- 1 | import yargs from 'yargs' 2 | 3 | import getLyra from './utils/getLyra' 4 | 5 | export default async function liquidityWithdrawals(argv: string[]) { 6 | const lyra = getLyra() 7 | const args = await yargs(argv).options({ 8 | market: { type: 'string', alias: 'm', require: true }, 9 | owner: { type: 'string', alias: 'o', require: true }, 10 | }).argv 11 | const liquidityWithdrawals = await lyra.liquidityWithdrawals(args.market, args.owner) 12 | console.log(liquidityWithdrawals) 13 | } 14 | -------------------------------------------------------------------------------- /scripts/listen.ts: -------------------------------------------------------------------------------- 1 | import yargs from 'yargs' 2 | 3 | import getLyra from './utils/getLyra' 4 | 5 | function sleep(ms: number) { 6 | return new Promise(resolve => { 7 | setTimeout(resolve, ms) 8 | }) 9 | } 10 | 11 | // Listen to trades 12 | export default async function listen(argv: string[]) { 13 | const lyra = getLyra() 14 | const args = await yargs(argv).options({ 15 | block: { type: 'number', alias: 'b', require: false }, 16 | poll: { type: 'number', alias: 'p', require: false }, 17 | }).argv 18 | lyra.onTrade( 19 | trade => { 20 | console.log({ 21 | trader: trade.trader, 22 | positionId: trade.positionId, 23 | market: trade.marketName, 24 | size: trade.size, 25 | isBuy: trade.isBuy, 26 | isLong: trade.isLong, 27 | premium: trade.premium, 28 | collateral: trade.collateralValue, 29 | isLiquidation: trade.isLiquidation, 30 | }) 31 | }, 32 | { 33 | startBlockNumber: args.block, 34 | pollInterval: args.poll, 35 | } 36 | ) 37 | 38 | // Keep process alive 39 | await sleep(1 << 30) 40 | } 41 | -------------------------------------------------------------------------------- /scripts/market-pool-stats.ts: -------------------------------------------------------------------------------- 1 | import yargs from 'yargs' 2 | 3 | import { ZERO_BN } from '../src/constants/bn' 4 | import getLyra from './utils/getLyra' 5 | 6 | export default async function marketPoolStats(argv: string[]) { 7 | const lyra = getLyra() 8 | const args = await yargs(argv).options({ 9 | market: { type: 'string', alias: 'm', require: true }, 10 | }).argv 11 | const market = await lyra.market(args.market) 12 | const tradingVolumeHistory = await market.tradingVolumeHistory() 13 | const tradingVolume = tradingVolumeHistory.reduce( 14 | (sum, tradingVolume) => sum.add(tradingVolume.notionalVolume), 15 | ZERO_BN 16 | ) 17 | const openInterest = market.openInterest 18 | console.log({ 19 | tradingVolume, 20 | openInterest, 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /scripts/market.ts: -------------------------------------------------------------------------------- 1 | import yargs from 'yargs' 2 | 3 | import fromBigNumber from '../src/utils/fromBigNumber' 4 | import getLyra from './utils/getLyra' 5 | 6 | export default async function market(argv: string[]) { 7 | const lyra = getLyra() 8 | const args = await yargs(argv).options({ 9 | market: { type: 'string', alias: 'm', require: true }, 10 | }).argv 11 | const market = await lyra.market(args.market) 12 | console.log({ 13 | address: market.address, 14 | name: market.name, 15 | strikes: market 16 | .liveBoards() 17 | .map(board => 18 | board.strikes().map(strike => ({ 19 | boardId: board.id, 20 | strikeId: strike.id, 21 | expiryTimestamp: board.expiryTimestamp, 22 | strikePrice: fromBigNumber(strike.strikePrice), 23 | isDeltaInRange: strike.isDeltaInRange, 24 | })) 25 | ) 26 | .flat(), 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /scripts/markets.ts: -------------------------------------------------------------------------------- 1 | import getLyra from './utils/getLyra' 2 | 3 | export default async function markets(argv: string[]) { 4 | const lyra = getLyra() 5 | 6 | // Fetch all markets 7 | const markets = await lyra.markets() 8 | 9 | console.log( 10 | markets.map(market => ({ 11 | address: market.address, 12 | name: market.name, 13 | // List all live boards (expiries) 14 | expiries: market.liveBoards().map(board => ({ 15 | id: board.id, 16 | expiryTimestamp: board.expiryTimestamp, 17 | // List all strikes 18 | strikes: board.strikes().map(strike => ({ 19 | id: strike.id, 20 | strikePrice: strike.strikePrice, 21 | })), 22 | })), 23 | })) 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /scripts/mint.ts: -------------------------------------------------------------------------------- 1 | import printObject from '../src/utils/printObject' 2 | import getLyra from './utils/getLyra' 3 | import getSigner from './utils/getSigner' 4 | 5 | export default async function mint(argv: string[]): Promise { 6 | const lyra = getLyra() 7 | const signer = getSigner(lyra) 8 | console.log('minting...', signer.address) 9 | const tx = await lyra.drip(signer.address) 10 | const res = await signer.sendTransaction(tx) 11 | console.log('tx', res.hash) 12 | await res.wait() 13 | console.log('minted', signer.address) 14 | const balances = await lyra.account(signer.address).balances() 15 | printObject('balances', balances) 16 | } 17 | -------------------------------------------------------------------------------- /scripts/my-positions.ts: -------------------------------------------------------------------------------- 1 | import yargs from 'yargs' 2 | 3 | import fromBigNumber from '../src/utils/fromBigNumber' 4 | import printObject from '../src/utils/printObject' 5 | import getLyra from './utils/getLyra' 6 | 7 | export default async function myPositions(argv: string[]) { 8 | const lyra = getLyra() 9 | const args = await yargs(argv).options({ 10 | account: { type: 'string', alias: 'a', require: true }, 11 | open: { type: 'boolean', alias: 'o', require: false }, 12 | }).argv 13 | const isOpen = args.open 14 | const account = args.account 15 | const positions = isOpen ? await lyra.openPositions(account) : await lyra.positions(account) 16 | printObject( 17 | positions.map(pos => ({ 18 | __source: pos.__source, 19 | id: pos.id, 20 | size: pos.size, 21 | isOpen: pos.isOpen, 22 | isCall: pos.isCall, 23 | isLong: pos.isLong, 24 | isSettled: pos.isSettled, 25 | isBaseCollateral: pos.collateral?.isBase, 26 | numTrades: pos.trades().length, 27 | avgCostPerOption: pos.averageCostPerOption(), 28 | pricePerOption: pos.pricePerOption, 29 | collateral: pos.collateral, 30 | pnl: pos.pnl(), 31 | strikeId: pos.strikeId, 32 | strike: fromBigNumber(pos.strikePrice), 33 | })) 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /scripts/net-delta-history.ts: -------------------------------------------------------------------------------- 1 | import yargs from 'yargs' 2 | 3 | import getLyra from './utils/getLyra' 4 | 5 | export default async function netDeltaHistory(argv: string[]) { 6 | const lyra = getLyra() 7 | const args = await yargs(argv).options({ 8 | market: { type: 'string', alias: 'm', require: true }, 9 | }).argv 10 | const market = await lyra.market(args.market) 11 | const netDeltaHistory = await market.netGreeksHistory() 12 | console.log(netDeltaHistory) 13 | } 14 | -------------------------------------------------------------------------------- /scripts/option-price.ts: -------------------------------------------------------------------------------- 1 | import yargs from 'yargs' 2 | 3 | import getLyra from './utils/getLyra' 4 | 5 | export default async function optionPrice(argv: string[]) { 6 | const lyra = getLyra() 7 | /* 8 | example args: { 9 | market: 0x1d42a98848e022908069c2c545ae44cc78509bc8 10 | strikeId: 100 11 | isCall: true 12 | } 13 | */ 14 | const args = await yargs(argv).options({ 15 | market: { type: 'string', alias: 'm', require: true }, 16 | strikeId: { type: 'number', alias: 's', require: true }, 17 | isCall: { type: 'boolean', alias: 'i', require: true }, 18 | timestamp: { type: 'number', alias: 't', require: false }, 19 | }).argv 20 | const option = await lyra.option(args.market, args.strikeId, args.isCall) 21 | const prices = await option.priceHistory({ 22 | startTimestamp: args.timestamp, 23 | }) 24 | console.log(prices.length) 25 | } 26 | -------------------------------------------------------------------------------- /scripts/option-trading-volume.ts: -------------------------------------------------------------------------------- 1 | import yargs from 'yargs' 2 | 3 | import getLyra from './utils/getLyra' 4 | 5 | export default async function optionTradingVolume(argv: string[]) { 6 | const lyra = getLyra() 7 | /* 8 | example args: { 9 | market: 0x1d42a98848e022908069c2c545ae44cc78509bc8 10 | strikeId: 100 11 | isCall: true 12 | } 13 | */ 14 | const args = await yargs(argv).options({ 15 | market: { type: 'string', alias: 'm', require: true }, 16 | strikeId: { type: 'number', alias: 's', require: true }, 17 | isCall: { type: 'boolean', alias: 'i', require: true }, 18 | timestamp: { type: 'number', alias: 't', require: false }, 19 | }).argv 20 | const option = await lyra.option(args.market, args.strikeId, args.isCall) 21 | const optionVolumes = await option.tradingVolumeHistory({ 22 | startTimestamp: args.timestamp, 23 | }) 24 | console.log(optionVolumes.length) 25 | } 26 | -------------------------------------------------------------------------------- /scripts/position.ts: -------------------------------------------------------------------------------- 1 | import yargs from 'yargs' 2 | 3 | import printObject from '../src/utils/printObject' 4 | import getLyra from './utils/getLyra' 5 | 6 | export default async function position(argv: string[]) { 7 | const lyra = getLyra() 8 | const args = await yargs(argv).options({ 9 | market: { type: 'string', alias: 'm', require: true }, 10 | id: { type: 'string', alias: 'i', require: true }, 11 | }).argv 12 | const position = await lyra.position(args.market, parseInt(args.id)) 13 | printObject('Position', { 14 | __source: position.__source, 15 | marketAddress: position.marketAddress, 16 | owner: position.owner, 17 | isOpen: position.isOpen, 18 | isCall: position.isCall, 19 | isLong: position.isLong, 20 | isSettled: position.isSettled, 21 | isLiquidated: position.isLiquidated, 22 | size: position.sizeBeforeClose(), 23 | strikePrice: position.strikePrice, 24 | breakEven: position.breakEven(), 25 | avgCost: position.averageCostPerOption(), 26 | pricePerOption: position.pricePerOption, 27 | isInTheMoney: position.isInTheMoney, 28 | trades: position.trades().map(trade => ({ 29 | hash: trade.transactionHash, 30 | size: trade.size, 31 | cost: trade.premium, 32 | isBuy: trade.isBuy, 33 | isOpen: trade.isOpen, 34 | collateral: trade.collateralValue, 35 | })), 36 | collatUpdates: position.collateralUpdates().map(collatUpdate => ({ 37 | hash: collatUpdate.transactionHash, 38 | amount: collatUpdate.amount, 39 | value: collatUpdate.value, 40 | })), 41 | collateral: position.collateral, 42 | settle: { 43 | hash: position.settle()?.transactionHash, 44 | settlement: position.settle()?.settlement, 45 | returnedCollateralValue: position.settle()?.returnedCollateralValue, 46 | }, 47 | pnl: position.pnl(), 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /scripts/quote.ts: -------------------------------------------------------------------------------- 1 | import { ONE_BN } from '../src/constants/bn' 2 | import fromBigNumber from '../src/utils/fromBigNumber' 3 | import getLyra from './utils/getLyra' 4 | 5 | export default async function quote(argv: string[]) { 6 | const lyra = getLyra() 7 | const data = await lyra.markets() 8 | const market = data[0] 9 | const strike = market.liveBoards()[0].strikes()[1] 10 | const quote = await strike.quote(true, false, ONE_BN.div(100)) 11 | console.log({ 12 | size: fromBigNumber(quote.size), 13 | pricePerOption: fromBigNumber(quote.pricePerOption), 14 | premium: fromBigNumber(quote.premium), 15 | fee: quote.fee, 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /scripts/reward-epoch.ts: -------------------------------------------------------------------------------- 1 | import getLyra from './utils/getLyra' 2 | 3 | export default async function rewardEpoch() { 4 | const lyra = getLyra() 5 | const epoch = await lyra.latestGlobalRewardEpoch() 6 | const minVaultApy = epoch?.minVaultApy('eth') 7 | const maxVaultApy = epoch?.maxVaultApy('eth') 8 | console.log('Global', { minVaultApy, maxVaultApy }) 9 | } 10 | -------------------------------------------------------------------------------- /scripts/simple-trade.ts: -------------------------------------------------------------------------------- 1 | import { MAX_BN, ONE_BN } from '../src/constants/bn' 2 | import { Trade } from '../src/trade' 3 | import { TradeEvent } from '../src/trade_event' 4 | import printObject from '../src/utils/printObject' 5 | import getLyra from './utils/getLyra' 6 | import getSigner from './utils/getSigner' 7 | 8 | export default async function simpleTrade() { 9 | const lyra = getLyra() 10 | const signer = getSigner(lyra) 11 | 12 | const account = lyra.account(signer.address) 13 | 14 | const market = await lyra.market('eth') 15 | 16 | // Select most recent expiry 17 | const board = market.liveBoards()[0] 18 | 19 | // Select first strike in delta range 20 | const strike = board.strikes().find(strike => strike.isDeltaInRange) 21 | if (!strike) { 22 | throw new Error('No strike in delta range') 23 | } 24 | 25 | // Prepare trade (Open 1.0 Long ETH Call) 26 | const trade = await Trade.get(lyra, account.address, 'eth', strike.id, true, true, ONE_BN.div(100), { 27 | slippage: 0.1 / 100, // 0.1% 28 | }) 29 | 30 | // Approve 31 | const approveTx = await trade.approveQuote(account.address, MAX_BN) 32 | const approveResponse = await signer.sendTransaction(approveTx) 33 | await approveResponse.wait() 34 | console.log('Approved sUSD') 35 | 36 | // Check if trade is disabled 37 | if (trade.disabledReason) { 38 | throw new Error(`Trade is disabled: ${trade.disabledReason}`) 39 | } 40 | 41 | // Execute trade 42 | const tradeResponse = await signer.sendTransaction(trade.tx) 43 | console.log('Executed trade:', tradeResponse.hash) 44 | const tradeReceipt = await tradeResponse.wait() 45 | 46 | // Get trade result 47 | const tradeEvent = (await TradeEvent.getByHash(lyra, tradeReceipt.transactionHash))[0] 48 | 49 | printObject('Trade Result', { 50 | blockNumber: tradeEvent.blockNumber, 51 | positionId: tradeEvent.positionId, 52 | premium: tradeEvent.premium, 53 | fee: tradeEvent.fee, 54 | }) 55 | } 56 | -------------------------------------------------------------------------------- /scripts/trading-volume.ts: -------------------------------------------------------------------------------- 1 | import yargs from 'yargs' 2 | 3 | import { SECONDS_IN_DAY } from '../src/constants/time' 4 | import getLyra from './utils/getLyra' 5 | 6 | export default async function tradingVolume(argv: string[]) { 7 | const lyra = getLyra() 8 | const args = await yargs(argv).options({ 9 | market: { type: 'string', alias: 'm', require: true }, 10 | }).argv 11 | const market = await lyra.market(args.market) 12 | const tradingVolumeHistory = await market.tradingVolumeHistory({ 13 | startTimestamp: market.block.timestamp - SECONDS_IN_DAY * 7, 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /scripts/utils/approve.ts: -------------------------------------------------------------------------------- 1 | import { Trade, TradeDisabledReason } from '../..' 2 | import { MAX_BN } from '../../src/constants/bn' 3 | import getLyra from './getLyra' 4 | import getSigner from './getSigner' 5 | 6 | export default async function approve(trade: Trade): Promise { 7 | const lyra = getLyra() 8 | const signer = getSigner(lyra) 9 | 10 | if (trade.disabledReason === TradeDisabledReason.InsufficientQuoteAllowance) { 11 | console.log('approving quote...') 12 | const tx = await trade.approveQuote(MAX_BN) 13 | const response = await signer.sendTransaction(tx) 14 | await response.wait() 15 | console.log('approved quote') 16 | } 17 | 18 | if (trade.disabledReason === TradeDisabledReason.InsufficientBaseAllowance) { 19 | console.log('approving base...') 20 | const tx = await trade.approveBase(MAX_BN) 21 | const response = await signer.sendTransaction(tx) 22 | await response.wait() 23 | console.log('approved base') 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /scripts/utils/coerce.ts: -------------------------------------------------------------------------------- 1 | type GenericEnum = { [key: string]: string | number } 2 | 3 | export default function coerce( 4 | obj: T, 5 | value: any, 6 | defaultValue?: DefaultValue 7 | ): T[keyof T] | DefaultValue { 8 | const coercedValue = Object.values(obj).find( 9 | val => val.toString().toLowerCase() === value?.toString().toLowerCase() 10 | ) as any 11 | return coercedValue != null ? coercedValue : defaultValue 12 | } 13 | -------------------------------------------------------------------------------- /scripts/utils/getLyra.ts: -------------------------------------------------------------------------------- 1 | import { StaticJsonRpcProvider } from '@ethersproject/providers' 2 | 3 | import { Chain } from '../../src/constants/chain' 4 | import Lyra from '../../src/lyra' 5 | import getLyraDeploymentChainId from '../../src/utils/getLyraDeploymentChainId' 6 | import getLyraDeploymentRPCURL from '../../src/utils/getLyraDeploymentRPCURL' 7 | import coerce from './coerce' 8 | 9 | export default function getLyra(): Lyra { 10 | const chain = coerce(Chain, process.env.CHAIN ?? '', Chain.Optimism) 11 | const chainId = getLyraDeploymentChainId(chain) 12 | const rpcUrl = process.env.RPC_URL ?? getLyraDeploymentRPCURL(chain) 13 | const lyra = new Lyra({ 14 | provider: new StaticJsonRpcProvider(rpcUrl, chainId), 15 | }) 16 | return lyra 17 | } 18 | -------------------------------------------------------------------------------- /scripts/utils/getSigner.ts: -------------------------------------------------------------------------------- 1 | import { Wallet } from '@ethersproject/wallet' 2 | 3 | import Lyra from '../../src/lyra' 4 | 5 | export type ScriptLyra = { 6 | lyra: Lyra 7 | signer: Wallet 8 | } 9 | 10 | export default function getSigner(lyra: Lyra): Wallet { 11 | const privateKey = process.env.PRIVATE_KEY 12 | if (!privateKey) { 13 | throw new Error('PRIVATE_KEY is missing in environment') 14 | } 15 | return new Wallet(privateKey, lyra.provider) 16 | } 17 | -------------------------------------------------------------------------------- /scripts/vault-apy.ts: -------------------------------------------------------------------------------- 1 | import { AccountRewardEpoch } from '..' 2 | import getLyra from './utils/getLyra' 3 | 4 | export default async function vaultApy() { 5 | const lyra = getLyra() 6 | const epoch = await AccountRewardEpoch.getByOwner(lyra, '') 7 | 8 | // const apy = epoch[0].vaultApy('ETH').total 9 | // const apyMultiplier = epoch[0].vaultApyMultiplier('ETH') 10 | // const minApy = epoch[0].globalEpoch.minVaultApy('ETH').total 11 | // const maxApy = epoch[0].globalEpoch.maxVaultApy('ETH').total 12 | 13 | // console.log('result', { apy, minApy, maxApy, apyMultiplier }) 14 | } 15 | -------------------------------------------------------------------------------- /src/account/index.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | import { PopulatedTransaction } from '@ethersproject/contracts' 3 | 4 | import { Deployment, LyraContractId } from '../constants/contracts' 5 | import Lyra from '../lyra' 6 | import { Market } from '../market' 7 | import buildTx from '../utils/buildTx' 8 | import getLyraContract from '../utils/getLyraContract' 9 | import fetchAccountBalancesAndAllowances from './fetchAccountBalancesAndAllowances' 10 | 11 | export type AccountTokenBalance = { 12 | address: string 13 | symbol: string 14 | decimals: number 15 | balance: BigNumber 16 | } 17 | 18 | export type AccountQuoteBalance = AccountTokenBalance & { 19 | tradeAllowance: BigNumber 20 | depositAllowance: BigNumber 21 | } 22 | 23 | export type AccountBaseBalance = AccountTokenBalance & { 24 | tradeAllowance: BigNumber 25 | } 26 | 27 | export type AccountLiquidityTokenBalance = AccountTokenBalance 28 | 29 | export type AccountBalances = { 30 | owner: string 31 | market: Market 32 | marketAddress: string 33 | marketName: string 34 | quoteAsset: AccountQuoteBalance 35 | baseAsset: AccountBaseBalance 36 | liquidityToken: AccountLiquidityTokenBalance 37 | } 38 | 39 | export type AccountPnlSnapshot = { 40 | timestamp: number 41 | livePnl: number 42 | } 43 | 44 | export class Account { 45 | private lyra: Lyra 46 | address: string 47 | 48 | constructor(lyra: Lyra, address: string) { 49 | this.lyra = lyra 50 | this.address = address 51 | } 52 | 53 | // Getters 54 | 55 | static get(lyra: Lyra, account: string): Account { 56 | return new Account(lyra, account) 57 | } 58 | 59 | // Dynamic Fields 60 | 61 | async balances(): Promise { 62 | return await fetchAccountBalancesAndAllowances(this.lyra, this.address) 63 | } 64 | 65 | async marketBalances(marketAddressOrName: string): Promise { 66 | const [market, balances] = await Promise.all([this.lyra.market(marketAddressOrName), this.balances()]) 67 | const balance = balances.find(balance => balance.marketAddress.toLowerCase() === market.address.toLowerCase()) 68 | if (!balance) { 69 | throw new Error(`No balances exist for market`) 70 | } 71 | return balance 72 | } 73 | 74 | drip(): PopulatedTransaction { 75 | if (this.lyra.deployment !== Deployment.Testnet) { 76 | throw new Error('Faucet is only supported on testnet contracts') 77 | } 78 | const faucet = getLyraContract(this.lyra, this.lyra.version, LyraContractId.TestFaucet) 79 | const data = faucet.interface.encodeFunctionData('drip') 80 | return buildTx(this.lyra.provider, this.lyra.provider.network.chainId, faucet.address, this.address, data) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/account_reward_epoch/getDistributedTradingRewards.ts: -------------------------------------------------------------------------------- 1 | import { ClaimAddedEvent, GlobalRewardEpoch, RewardEpochTokenAmount } from '..' 2 | import fromBigNumber from '../utils/fromBigNumber' 3 | 4 | const CLAIM_ADDED_TRADING_TAG = 'TRADING' 5 | 6 | export default function getDistributedTradingRewards( 7 | globalRewardEpoch: GlobalRewardEpoch, 8 | claimAddedEvents: ClaimAddedEvent[] 9 | ): RewardEpochTokenAmount[] { 10 | const tradingRewardsMap: Record = claimAddedEvents 11 | .filter(event => { 12 | const tags = event.tag.split('-') 13 | return tags.includes(CLAIM_ADDED_TRADING_TAG) && event.epochTimestamp === globalRewardEpoch.startTimestamp 14 | }) 15 | .reduce((tradingRewardsMap, event) => { 16 | const rewardToken = globalRewardEpoch.tradingRewardTokens.find( 17 | t => t.address.toLowerCase() === event.rewardToken.toLowerCase() 18 | ) 19 | if (!rewardToken) { 20 | // console.warn('Missing token info in global epoch config', event.rewardToken) 21 | return tradingRewardsMap 22 | } 23 | 24 | if (tradingRewardsMap[rewardToken.address]) { 25 | tradingRewardsMap[rewardToken.address].amount += fromBigNumber(event.amount, rewardToken.decimals) 26 | } 27 | 28 | return { 29 | ...tradingRewardsMap, 30 | [rewardToken.address]: { ...rewardToken, amount: fromBigNumber(event.amount, rewardToken.decimals) }, 31 | } 32 | }, {} as Record) 33 | 34 | return Object.values(tradingRewardsMap) 35 | } 36 | -------------------------------------------------------------------------------- /src/account_reward_epoch/getDistributedVaultRewards.ts: -------------------------------------------------------------------------------- 1 | import { ClaimAddedEvent, GlobalRewardEpoch, Market, RewardEpochTokenAmount } from '..' 2 | import fromBigNumber from '../utils/fromBigNumber' 3 | 4 | const CLAIM_ADDED_VAULTS_TAG = 'MMV' 5 | 6 | export default function getDistributedVaultRewards( 7 | market: Market, 8 | globalRewardEpoch: GlobalRewardEpoch, 9 | claimAddedEvents: ClaimAddedEvent[] 10 | ): RewardEpochTokenAmount[] { 11 | const vaultRewardsMap: Record = claimAddedEvents 12 | .filter(event => { 13 | const tags = event.tag.split('-') 14 | const allUpperCaseTags = tags.map(tag => tag.toUpperCase()) 15 | return ( 16 | allUpperCaseTags.includes(CLAIM_ADDED_VAULTS_TAG) && 17 | allUpperCaseTags.includes(market.baseToken.symbol.toUpperCase()) && 18 | market.baseToken.symbol.toUpperCase() !== 'OP' && 19 | event.epochTimestamp === globalRewardEpoch.startTimestamp 20 | ) 21 | }) 22 | .reduce((vaultRewardsMap, event) => { 23 | const rewardToken = globalRewardEpoch.vaultRewardTokens.find( 24 | t => t.address.toLowerCase() === event.rewardToken.toLowerCase() 25 | ) 26 | if (!rewardToken) { 27 | // console.warn('Missing token info in global epoch config', event.rewardToken) 28 | return vaultRewardsMap 29 | } 30 | 31 | if (vaultRewardsMap[rewardToken.address]) { 32 | vaultRewardsMap[rewardToken.address].amount += fromBigNumber(event.amount, rewardToken.decimals) 33 | } 34 | 35 | return { 36 | ...vaultRewardsMap, 37 | [rewardToken.address]: { ...rewardToken, amount: fromBigNumber(event.amount, rewardToken.decimals) }, 38 | } 39 | }, {} as Record) 40 | 41 | return Object.values(vaultRewardsMap) 42 | } 43 | -------------------------------------------------------------------------------- /src/account_reward_epoch/getTotalClaimableTradingRewards.ts: -------------------------------------------------------------------------------- 1 | import { ClaimAddedEvent, ClaimEvent, RewardEpochToken, RewardEpochTokenAmount } from '..' 2 | import fromBigNumber from '../utils/fromBigNumber' 3 | 4 | const CLAIM_ADDED_TRADING_TAG = 'TRADING' 5 | 6 | export default function getTotalClaimableTradingRewards( 7 | rewardTokens: RewardEpochToken[], 8 | claimAddedEvents: ClaimAddedEvent[], 9 | claimEvents: ClaimEvent[] 10 | ): RewardEpochTokenAmount[] { 11 | const tradingRewardsMap: Record = claimAddedEvents 12 | .filter(event => { 13 | const tags = event.tag.split('-') 14 | return tags.includes(CLAIM_ADDED_TRADING_TAG) 15 | }) 16 | .reduce((tradingRewardsMap, event) => { 17 | const isClaimed = claimEvents.some( 18 | claimEvent => claimEvent.timestamp > event.timestamp && claimEvent.rewardToken === event.rewardToken 19 | ) 20 | 21 | if (isClaimed) { 22 | return tradingRewardsMap 23 | } 24 | 25 | const rewardToken = rewardTokens.find(t => t.address.toLowerCase() === event.rewardToken.toLowerCase()) 26 | if (!rewardToken) { 27 | console.warn('Missing token info in global epoch config', event.rewardToken) 28 | return tradingRewardsMap 29 | } 30 | 31 | if (tradingRewardsMap[rewardToken.address]) { 32 | tradingRewardsMap[rewardToken.address].amount += fromBigNumber(event.amount, rewardToken.decimals) 33 | } 34 | 35 | return { 36 | ...tradingRewardsMap, 37 | [rewardToken.address]: { ...rewardToken, amount: fromBigNumber(event.amount, rewardToken.decimals) }, 38 | } 39 | }, {} as Record) 40 | 41 | return Object.values(tradingRewardsMap).filter(r => r.amount > 0) 42 | } 43 | -------------------------------------------------------------------------------- /src/account_reward_epoch/getTotalClaimableVaultRewards.ts: -------------------------------------------------------------------------------- 1 | import { ClaimAddedEvent, ClaimEvent, Market, RewardEpochToken, RewardEpochTokenAmount } from '..' 2 | import fromBigNumber from '../utils/fromBigNumber' 3 | 4 | const CLAIM_ADDED_VAULTS_TAG = 'MMV' 5 | 6 | export default function getTotalClaimableVaultRewards( 7 | market: Market, 8 | rewardTokens: RewardEpochToken[], 9 | claimAddedEvents: ClaimAddedEvent[], 10 | claimEvents: ClaimEvent[] 11 | ): RewardEpochTokenAmount[] { 12 | const vaultRewardsMap: Record = claimAddedEvents 13 | .filter(event => { 14 | const tags = event.tag.split('-') 15 | const allUpperCaseTags = tags.map(tag => tag.toUpperCase()) 16 | return ( 17 | allUpperCaseTags.includes(CLAIM_ADDED_VAULTS_TAG) && 18 | market.baseToken.symbol.toUpperCase() !== 'OP' && 19 | allUpperCaseTags.includes(market.baseToken.symbol.toUpperCase()) 20 | ) 21 | }) 22 | .reduce((vaultRewardsMap, event) => { 23 | const isClaimed = claimEvents.some( 24 | claimEvent => claimEvent.timestamp > event.timestamp && claimEvent.rewardToken === event.rewardToken 25 | ) 26 | 27 | if (isClaimed) { 28 | return vaultRewardsMap 29 | } 30 | 31 | const rewardToken = rewardTokens.find(t => t.address.toLowerCase() === event.rewardToken.toLowerCase()) 32 | if (!rewardToken) { 33 | console.warn('Missing token info in global epoch config', event.rewardToken) 34 | return vaultRewardsMap 35 | } 36 | 37 | if (vaultRewardsMap[rewardToken.address]) { 38 | vaultRewardsMap[rewardToken.address].amount += fromBigNumber(event.amount, rewardToken.decimals) 39 | } 40 | 41 | return { 42 | ...vaultRewardsMap, 43 | [rewardToken.address]: { ...rewardToken, amount: fromBigNumber(event.amount, rewardToken.decimals) }, 44 | } 45 | }, {} as Record) 46 | 47 | return Object.values(vaultRewardsMap).filter(r => r.amount > 0) 48 | } 49 | -------------------------------------------------------------------------------- /src/constants/bn.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | export const ZERO_BN = BigNumber.from(0) 4 | export const UNIT = BigNumber.from(10).pow(18) 5 | export const ONE_BN = BigNumber.from(1).mul(UNIT) 6 | export const MAX_BN = BigNumber.from(2).pow(256).sub(1) 7 | export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' 8 | -------------------------------------------------------------------------------- /src/constants/chain.ts: -------------------------------------------------------------------------------- 1 | export enum Chain { 2 | Optimism = 'optimism', 3 | OptimismGoerli = 'optimism-goerli', 4 | Arbitrum = 'arbitrum', 5 | ArbitrumGoerli = 'arbitrum-goerli', 6 | } 7 | -------------------------------------------------------------------------------- /src/constants/contracts.ts: -------------------------------------------------------------------------------- 1 | // NOTE: Do not import typechain types in this file 2 | // Contract name mappings 3 | 4 | export enum LyraContractId { 5 | OptionMarketViewer = 'OptionMarketViewer', 6 | LyraRegistry = 'LyraRegistry', 7 | ExchangeAdapter = 'ExchangeAdapter', 8 | TestFaucet = 'TestFaucet', 9 | } 10 | 11 | export enum LyraGlobalContractId { 12 | MultiDistributor = 'MultiDistributor', 13 | Multicall3 = 'Multicall3', 14 | } 15 | 16 | // Per-market contract name mappings 17 | export enum LyraMarketContractId { 18 | OptionMarket = 'OptionMarket', 19 | OptionMarketPricer = 'OptionMarketPricer', 20 | OptionToken = 'OptionToken', 21 | ShortCollateral = 'ShortCollateral', 22 | OptionGreekCache = 'OptionGreekCache', 23 | LiquidityToken = 'LiquidityToken', 24 | LiquidityPool = 'LiquidityPool', 25 | PoolHedger = 'PoolHedger', 26 | } 27 | 28 | // Ordered enum from contracts 29 | export enum OptionType { 30 | LongCall, 31 | LongPut, 32 | ShortCoveredCall, 33 | ShortCall, 34 | ShortPut, 35 | } 36 | 37 | export enum Deployment { 38 | Testnet = 'testnet', 39 | Mainnet = 'mainnet', 40 | } 41 | export const DEFAULT_ITERATIONS = 1 42 | export const DEFAULT_PREMIUM_SLIPPAGE = 0.1 / 100 // 0.1% 43 | export const DEFAULT_SWAP_SLIPPAGE = 0.1 / 100 // 0.1% 44 | export const CURVE_POOL_FEE_RATE = 0.4 / 100 // 0.4% 45 | export enum EventName { 46 | Trade = 'Trade', 47 | PositionUpdated = 'PositionUpdated', 48 | Transfer = 'Transfer', 49 | } 50 | export enum PositionState { 51 | Empty, 52 | Active, 53 | Closed, 54 | Liquidated, 55 | Settled, 56 | Merged, 57 | } 58 | export enum PositionUpdatedType { 59 | Opened, 60 | Adjusted, 61 | Closed, 62 | SplitFrom, 63 | SplitInto, 64 | Merged, 65 | MergedInto, 66 | Settled, 67 | Liquidated, 68 | Transfer, 69 | } 70 | export enum TradeDirection { 71 | Open, 72 | Close, 73 | Liquidate, 74 | } 75 | export enum DataSource { 76 | ContractCall = 'ContractCall', 77 | Log = 'Log', 78 | Subgraph = 'Subgraph', 79 | } 80 | 81 | export const POSITION_UPDATED_TYPES = [ 82 | PositionUpdatedType.Adjusted, 83 | PositionUpdatedType.Closed, 84 | PositionUpdatedType.Liquidated, 85 | PositionUpdatedType.Opened, 86 | PositionUpdatedType.Settled, 87 | PositionUpdatedType.Merged, 88 | PositionUpdatedType.MergedInto, 89 | PositionUpdatedType.SplitFrom, 90 | PositionUpdatedType.SplitInto, 91 | ] 92 | 93 | export const LYRA_ETHEREUM_MAINNET_ADDRESS = '0x01BA67AAC7f75f647D94220Cc98FB30FCc5105Bf' 94 | export const OP_OPTIMISM_MAINNET_ADDRESS = '0x4200000000000000000000000000000000000042' 95 | export const LYRA_ETHEREUM_KOVAN_ADDRESS = '0xC9801013F0c45F836Ad07Dded1df9C475d2844FC' 96 | 97 | export const VAULTS_UTILIZATION_THRESHOLD = 0.99 98 | -------------------------------------------------------------------------------- /src/constants/links.ts: -------------------------------------------------------------------------------- 1 | export const LYRA_API_URL = 'https://api.lyra.finance' 2 | -------------------------------------------------------------------------------- /src/constants/network.ts: -------------------------------------------------------------------------------- 1 | export enum Network { 2 | Arbitrum = 'arbitrum', 3 | Optimism = 'optimism', 4 | } 5 | -------------------------------------------------------------------------------- /src/constants/rewards.ts: -------------------------------------------------------------------------------- 1 | export const MIN_REWARD_AMOUNT = 0.005 2 | -------------------------------------------------------------------------------- /src/constants/snapshots.ts: -------------------------------------------------------------------------------- 1 | import { SnapshotPeriod } from './queries' 2 | 3 | export type SnapshotOptions = { 4 | startTimestamp?: number 5 | endTimestamp?: number 6 | period?: SnapshotPeriod 7 | } 8 | -------------------------------------------------------------------------------- /src/constants/time.ts: -------------------------------------------------------------------------------- 1 | export const SECONDS_IN_HOUR = 60 * 60 2 | export const SECONDS_IN_DAY = 24 * SECONDS_IN_HOUR 3 | export const SECONDS_IN_WEEK = SECONDS_IN_DAY * 7 4 | export const SECONDS_IN_TWO_WEEKS = SECONDS_IN_WEEK * 2 5 | export const SECONDS_IN_MONTH = SECONDS_IN_WEEK * 4 6 | export const SECONDS_IN_SIX_MONTHS = SECONDS_IN_MONTH * 6 7 | export const SECONDS_IN_YEAR = 365.25 * SECONDS_IN_DAY 8 | -------------------------------------------------------------------------------- /src/constants/views.ts: -------------------------------------------------------------------------------- 1 | import { AvalonOptionMarketViewer as TAvalonOptionMarketViewer } from '../contracts/avalon/typechain' 2 | import { OptionMarketViewer as NAvalonOptionMarketViewer } from '../contracts/avalon/typechain/AvalonOptionMarketViewer' 3 | import { NewportOptionMarketViewer as TNewportOptionMarketViewer } from '../contracts/newport/typechain' 4 | import { OptionMarketViewer as NNewportOptionMarketViewer } from '../contracts/newport/typechain/NewportOptionMarketViewer' 5 | 6 | export type OptionMarketViewer = TAvalonOptionMarketViewer | TNewportOptionMarketViewer 7 | 8 | export type MarketParametersStructOutput = 9 | | NAvalonOptionMarketViewer.MarketParametersStructOutput 10 | | NNewportOptionMarketViewer.MarketParametersStructOutput 11 | 12 | export type MarketViewWithBoardsStructOutput = 13 | | NAvalonOptionMarketViewer.MarketViewWithBoardsStructOutput 14 | | NNewportOptionMarketViewer.MarketViewStructOutput 15 | 16 | export type BoardViewStructOutput = 17 | | NAvalonOptionMarketViewer.BoardViewStructOutput 18 | | NNewportOptionMarketViewer.BoardViewStructOutput 19 | 20 | export type StrikeViewStructOutput = 21 | | NAvalonOptionMarketViewer.StrikeViewStructOutput 22 | | NNewportOptionMarketViewer.StrikeViewStructOutput 23 | 24 | export type MarketStaticLiquidity = MarketViewWithBoardsStructOutput['liquidity'] 25 | 26 | export type MarketStaticNetGreeks = MarketViewWithBoardsStructOutput['globalNetGreeks'] 27 | -------------------------------------------------------------------------------- /src/contracts/avalon/abis/AvalonTestFaucet.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [], 4 | "name": "drip", 5 | "outputs": [], 6 | "stateMutability": "nonpayable", 7 | "type": "function" 8 | } 9 | ] 10 | -------------------------------------------------------------------------------- /src/contracts/avalon/addresses/mainnet.addresses.json: -------------------------------------------------------------------------------- 1 | { 2 | "OptionMarketViewer": "0xEAf788AD8abd9C98bA05F6802a62B8DbC673D76B", 3 | "ExchangeAdapter": "0xbfa31380ED380cEb325153eA08f296A45A489108", 4 | "LyraRegistry": "0xF5A0442D4753cA1Ea36427ec071aa5E786dA5916", 5 | "TestFaucet": "" 6 | } 7 | -------------------------------------------------------------------------------- /src/contracts/avalon/addresses/testnet.addresses.json: -------------------------------------------------------------------------------- 1 | { 2 | "OptionMarketViewer": "0x9114B0a3D10D5f267b1FA21A0333e094d900f2f5", 3 | "LyraRegistry": "0xE3E2061d66ea546dD9CAD1fA171d0173A3Ab4b1E", 4 | "ExchangeAdapter": "0x1C2Ca80c108ff73E281B20556afEB52ABa7a56B1", 5 | "TestFaucet": "0xfDd5592e9d657950ACBcCA69A51ecb3e3Bb2F1e7" 6 | } 7 | -------------------------------------------------------------------------------- /src/contracts/avalon/typechain/AvalonTestFaucet.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | import type { 5 | BaseContract, 6 | BigNumber, 7 | BytesLike, 8 | CallOverrides, 9 | ContractTransaction, 10 | Overrides, 11 | PopulatedTransaction, 12 | Signer, 13 | utils, 14 | } from "ethers"; 15 | import type { FunctionFragment, Result } from "@ethersproject/abi"; 16 | import type { Listener, Provider } from "@ethersproject/providers"; 17 | import type { 18 | TypedEventFilter, 19 | TypedEvent, 20 | TypedListener, 21 | OnEvent, 22 | PromiseOrValue, 23 | } from "./common"; 24 | 25 | export interface AvalonTestFaucetInterface extends utils.Interface { 26 | functions: { 27 | "drip()": FunctionFragment; 28 | }; 29 | 30 | getFunction(nameOrSignatureOrTopic: "drip"): FunctionFragment; 31 | 32 | encodeFunctionData(functionFragment: "drip", values?: undefined): string; 33 | 34 | decodeFunctionResult(functionFragment: "drip", data: BytesLike): Result; 35 | 36 | events: {}; 37 | } 38 | 39 | export interface AvalonTestFaucet extends BaseContract { 40 | connect(signerOrProvider: Signer | Provider | string): this; 41 | attach(addressOrName: string): this; 42 | deployed(): Promise; 43 | 44 | interface: AvalonTestFaucetInterface; 45 | 46 | queryFilter( 47 | event: TypedEventFilter, 48 | fromBlockOrBlockhash?: string | number | undefined, 49 | toBlock?: string | number | undefined 50 | ): Promise>; 51 | 52 | listeners( 53 | eventFilter?: TypedEventFilter 54 | ): Array>; 55 | listeners(eventName?: string): Array; 56 | removeAllListeners( 57 | eventFilter: TypedEventFilter 58 | ): this; 59 | removeAllListeners(eventName?: string): this; 60 | off: OnEvent; 61 | on: OnEvent; 62 | once: OnEvent; 63 | removeListener: OnEvent; 64 | 65 | functions: { 66 | drip( 67 | overrides?: Overrides & { from?: PromiseOrValue } 68 | ): Promise; 69 | }; 70 | 71 | drip( 72 | overrides?: Overrides & { from?: PromiseOrValue } 73 | ): Promise; 74 | 75 | callStatic: { 76 | drip(overrides?: CallOverrides): Promise; 77 | }; 78 | 79 | filters: {}; 80 | 81 | estimateGas: { 82 | drip( 83 | overrides?: Overrides & { from?: PromiseOrValue } 84 | ): Promise; 85 | }; 86 | 87 | populateTransaction: { 88 | drip( 89 | overrides?: Overrides & { from?: PromiseOrValue } 90 | ): Promise; 91 | }; 92 | } 93 | -------------------------------------------------------------------------------- /src/contracts/avalon/typechain/common.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | import type { Listener } from "@ethersproject/providers"; 5 | import type { Event, EventFilter } from "ethers"; 6 | 7 | export interface TypedEvent< 8 | TArgsArray extends Array = any, 9 | TArgsObject = any 10 | > extends Event { 11 | args: TArgsArray & TArgsObject; 12 | } 13 | 14 | export interface TypedEventFilter<_TEvent extends TypedEvent> 15 | extends EventFilter {} 16 | 17 | export interface TypedListener { 18 | (...listenerArg: [...__TypechainArgsArray, TEvent]): void; 19 | } 20 | 21 | type __TypechainArgsArray = T extends TypedEvent ? U : never; 22 | 23 | export interface OnEvent { 24 | ( 25 | eventFilter: TypedEventFilter, 26 | listener: TypedListener 27 | ): TRes; 28 | (eventName: string, listener: Listener): TRes; 29 | } 30 | 31 | export type MinEthersFactory = { 32 | deploy(...a: ARGS[]): Promise; 33 | }; 34 | 35 | export type GetContractTypeFromFactory = F extends MinEthersFactory< 36 | infer C, 37 | any 38 | > 39 | ? C 40 | : never; 41 | 42 | export type GetARGsTypeFromFactory = F extends MinEthersFactory 43 | ? Parameters 44 | : never; 45 | 46 | export type PromiseOrValue = T | Promise; 47 | -------------------------------------------------------------------------------- /src/contracts/avalon/typechain/factories/AvalonTestFaucet__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Signer, utils } from "ethers"; 6 | import type { Provider } from "@ethersproject/providers"; 7 | import type { 8 | AvalonTestFaucet, 9 | AvalonTestFaucetInterface, 10 | } from "../AvalonTestFaucet"; 11 | 12 | const _abi = [ 13 | { 14 | inputs: [], 15 | name: "drip", 16 | outputs: [], 17 | stateMutability: "nonpayable", 18 | type: "function", 19 | }, 20 | ]; 21 | 22 | export class AvalonTestFaucet__factory { 23 | static readonly abi = _abi; 24 | static createInterface(): AvalonTestFaucetInterface { 25 | return new utils.Interface(_abi) as AvalonTestFaucetInterface; 26 | } 27 | static connect( 28 | address: string, 29 | signerOrProvider: Signer | Provider 30 | ): AvalonTestFaucet { 31 | return new Contract(address, _abi, signerOrProvider) as AvalonTestFaucet; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/contracts/avalon/typechain/factories/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export { AvalonLiquidityPool__factory } from "./AvalonLiquidityPool__factory"; 5 | export { AvalonLiquidityToken__factory } from "./AvalonLiquidityToken__factory"; 6 | export { AvalonLyraRegistry__factory } from "./AvalonLyraRegistry__factory"; 7 | export { AvalonOptionGreekCache__factory } from "./AvalonOptionGreekCache__factory"; 8 | export { AvalonOptionMarket__factory } from "./AvalonOptionMarket__factory"; 9 | export { AvalonOptionMarketPricer__factory } from "./AvalonOptionMarketPricer__factory"; 10 | export { AvalonOptionMarketViewer__factory } from "./AvalonOptionMarketViewer__factory"; 11 | export { AvalonOptionToken__factory } from "./AvalonOptionToken__factory"; 12 | export { AvalonShortCollateral__factory } from "./AvalonShortCollateral__factory"; 13 | export { AvalonShortPoolHedger__factory } from "./AvalonShortPoolHedger__factory"; 14 | export { AvalonSynthetixAdapter__factory } from "./AvalonSynthetixAdapter__factory"; 15 | export { AvalonTestFaucet__factory } from "./AvalonTestFaucet__factory"; 16 | -------------------------------------------------------------------------------- /src/contracts/avalon/typechain/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export type { AvalonLiquidityPool } from "./AvalonLiquidityPool"; 5 | export type { AvalonLiquidityToken } from "./AvalonLiquidityToken"; 6 | export type { AvalonLyraRegistry } from "./AvalonLyraRegistry"; 7 | export type { AvalonOptionGreekCache } from "./AvalonOptionGreekCache"; 8 | export type { AvalonOptionMarket } from "./AvalonOptionMarket"; 9 | export type { AvalonOptionMarketPricer } from "./AvalonOptionMarketPricer"; 10 | export type { AvalonOptionMarketViewer } from "./AvalonOptionMarketViewer"; 11 | export type { AvalonOptionToken } from "./AvalonOptionToken"; 12 | export type { AvalonShortCollateral } from "./AvalonShortCollateral"; 13 | export type { AvalonShortPoolHedger } from "./AvalonShortPoolHedger"; 14 | export type { AvalonSynthetixAdapter } from "./AvalonSynthetixAdapter"; 15 | export type { AvalonTestFaucet } from "./AvalonTestFaucet"; 16 | export * as factories from "./factories"; 17 | export { AvalonLiquidityPool__factory } from "./factories/AvalonLiquidityPool__factory"; 18 | export { AvalonLiquidityToken__factory } from "./factories/AvalonLiquidityToken__factory"; 19 | export { AvalonLyraRegistry__factory } from "./factories/AvalonLyraRegistry__factory"; 20 | export { AvalonOptionGreekCache__factory } from "./factories/AvalonOptionGreekCache__factory"; 21 | export { AvalonOptionMarket__factory } from "./factories/AvalonOptionMarket__factory"; 22 | export { AvalonOptionMarketPricer__factory } from "./factories/AvalonOptionMarketPricer__factory"; 23 | export { AvalonOptionMarketViewer__factory } from "./factories/AvalonOptionMarketViewer__factory"; 24 | export { AvalonOptionToken__factory } from "./factories/AvalonOptionToken__factory"; 25 | export { AvalonShortCollateral__factory } from "./factories/AvalonShortCollateral__factory"; 26 | export { AvalonShortPoolHedger__factory } from "./factories/AvalonShortPoolHedger__factory"; 27 | export { AvalonSynthetixAdapter__factory } from "./factories/AvalonSynthetixAdapter__factory"; 28 | export { AvalonTestFaucet__factory } from "./factories/AvalonTestFaucet__factory"; 29 | -------------------------------------------------------------------------------- /src/contracts/common/abis/MultiDistributor.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": true, 7 | "internalType": "contract IERC20", 8 | "name": "rewardToken", 9 | "type": "address" 10 | }, 11 | { 12 | "indexed": true, 13 | "internalType": "address", 14 | "name": "claimer", 15 | "type": "address" 16 | }, 17 | { 18 | "indexed": false, 19 | "internalType": "uint256", 20 | "name": "amount", 21 | "type": "uint256" 22 | }, 23 | { 24 | "indexed": true, 25 | "internalType": "uint256", 26 | "name": "epochTimestamp", 27 | "type": "uint256" 28 | }, 29 | { 30 | "indexed": false, 31 | "internalType": "string", 32 | "name": "tag", 33 | "type": "string" 34 | } 35 | ], 36 | "name": "ClaimAdded", 37 | "type": "event" 38 | }, 39 | { 40 | "anonymous": false, 41 | "inputs": [ 42 | { 43 | "indexed": true, 44 | "internalType": "contract IERC20", 45 | "name": "rewardToken", 46 | "type": "address" 47 | }, 48 | { 49 | "indexed": true, 50 | "internalType": "address", 51 | "name": "claimer", 52 | "type": "address" 53 | }, 54 | { 55 | "indexed": false, 56 | "internalType": "uint256", 57 | "name": "amount", 58 | "type": "uint256" 59 | } 60 | ], 61 | "name": "Claimed", 62 | "type": "event" 63 | }, 64 | { 65 | "inputs": [ 66 | { 67 | "internalType": "contract IERC20[]", 68 | "name": "tokens", 69 | "type": "address[]" 70 | } 71 | ], 72 | "name": "claim", 73 | "outputs": [], 74 | "stateMutability": "nonpayable", 75 | "type": "function" 76 | }, 77 | { 78 | "inputs": [ 79 | { 80 | "internalType": "address", 81 | "name": "", 82 | "type": "address" 83 | }, 84 | { 85 | "internalType": "contract IERC20", 86 | "name": "", 87 | "type": "address" 88 | } 89 | ], 90 | "name": "claimableBalances", 91 | "outputs": [ 92 | { 93 | "internalType": "uint256", 94 | "name": "", 95 | "type": "uint256" 96 | } 97 | ], 98 | "stateMutability": "view", 99 | "type": "function" 100 | } 101 | ] 102 | -------------------------------------------------------------------------------- /src/contracts/common/addresses/arbitrum-goerli.addresses.json: -------------------------------------------------------------------------------- 1 | { 2 | "Multicall3": "0xcA11bde05977b3631167028862bE2a173976CA11" 3 | } 4 | -------------------------------------------------------------------------------- /src/contracts/common/addresses/arbitrum.addresses.json: -------------------------------------------------------------------------------- 1 | { 2 | "Multicall3": "0xcA11bde05977b3631167028862bE2a173976CA11", 3 | "MultiDistributor": "0xecB73D4621Cabbf199e778CAEBc74bE27f2EcEe1" 4 | } 5 | -------------------------------------------------------------------------------- /src/contracts/common/addresses/optimism-goerli.addresses.json: -------------------------------------------------------------------------------- 1 | { 2 | "Multicall3": "0xcA11bde05977b3631167028862bE2a173976CA11" 3 | } 4 | -------------------------------------------------------------------------------- /src/contracts/common/addresses/optimism.addresses.json: -------------------------------------------------------------------------------- 1 | { 2 | "Multicall3": "0xcA11bde05977b3631167028862bE2a173976CA11", 3 | "MultiDistributor": "0x019F0233C0277B9422FCDb1213B09C86f5f27D87" 4 | } 5 | -------------------------------------------------------------------------------- /src/contracts/common/typechain/common.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | import type { Listener } from "@ethersproject/providers"; 5 | import type { Event, EventFilter } from "ethers"; 6 | 7 | export interface TypedEvent< 8 | TArgsArray extends Array = any, 9 | TArgsObject = any 10 | > extends Event { 11 | args: TArgsArray & TArgsObject; 12 | } 13 | 14 | export interface TypedEventFilter<_TEvent extends TypedEvent> 15 | extends EventFilter {} 16 | 17 | export interface TypedListener { 18 | (...listenerArg: [...__TypechainArgsArray, TEvent]): void; 19 | } 20 | 21 | type __TypechainArgsArray = T extends TypedEvent ? U : never; 22 | 23 | export interface OnEvent { 24 | ( 25 | eventFilter: TypedEventFilter, 26 | listener: TypedListener 27 | ): TRes; 28 | (eventName: string, listener: Listener): TRes; 29 | } 30 | 31 | export type MinEthersFactory = { 32 | deploy(...a: ARGS[]): Promise; 33 | }; 34 | 35 | export type GetContractTypeFromFactory = F extends MinEthersFactory< 36 | infer C, 37 | any 38 | > 39 | ? C 40 | : never; 41 | 42 | export type GetARGsTypeFromFactory = F extends MinEthersFactory 43 | ? Parameters 44 | : never; 45 | 46 | export type PromiseOrValue = T | Promise; 47 | -------------------------------------------------------------------------------- /src/contracts/common/typechain/factories/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export { ERC20__factory } from "./ERC20__factory"; 5 | export { MultiDistributor__factory } from "./MultiDistributor__factory"; 6 | export { Multicall3__factory } from "./Multicall3__factory"; 7 | -------------------------------------------------------------------------------- /src/contracts/common/typechain/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export type { ERC20 } from "./ERC20"; 5 | export type { MultiDistributor } from "./MultiDistributor"; 6 | export type { Multicall3 } from "./Multicall3"; 7 | export * as factories from "./factories"; 8 | export { ERC20__factory } from "./factories/ERC20__factory"; 9 | export { Multicall3__factory } from "./factories/Multicall3__factory"; 10 | export { MultiDistributor__factory } from "./factories/MultiDistributor__factory"; 11 | -------------------------------------------------------------------------------- /src/contracts/newport/abis/NewportTestFaucet.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [], 4 | "name": "drip", 5 | "outputs": [], 6 | "stateMutability": "nonpayable", 7 | "type": "function" 8 | } 9 | ] 10 | -------------------------------------------------------------------------------- /src/contracts/newport/addresses/arbitrum-goerli.addresses.json: -------------------------------------------------------------------------------- 1 | { 2 | "OptionMarketViewer": "0xB582CC9483ee1F862364C92C22CFD57f0fb607bc", 3 | "ExchangeAdapter": "0xD7A69233b82F53b416c839ff1fB6C51bA59180d2", 4 | "LyraRegistry": "0x1034C04bC515F18e5D1dFe5a94de6e65e7277fF7", 5 | "TestFaucet": "0xF2eDd52E32f8fE2eC76055C1A61958f1EFecb592" 6 | } 7 | -------------------------------------------------------------------------------- /src/contracts/newport/addresses/arbitrum.addresses.json: -------------------------------------------------------------------------------- 1 | { 2 | "OptionMarketViewer": "0x0527A05c3CEBc9Ef8171FeD29DE5900A7ea093a4", 3 | "ExchangeAdapter": "0x7D135662818d3540bd6f23294bFDB6946c52C9AB", 4 | "LyraRegistry": "0x6c87e4364Fd44B0D425ADfD0328e56b89b201329", 5 | "TestFaucet": "" 6 | } 7 | -------------------------------------------------------------------------------- /src/contracts/newport/addresses/optimism-goerli.addresses.json: -------------------------------------------------------------------------------- 1 | { 2 | "OptionMarketViewer": "0x4D0EB92F4CFcB6b4Cc9A88baF1f6Ad9E9C92380A", 3 | "ExchangeAdapter": "0x46CeCCaf391aFF578963042790C2ee1F88661DDf", 4 | "LyraRegistry": "0xB13E26A28979b32F157F030dC795DDAbF59fEac4", 5 | "TestFaucet": "" 6 | } 7 | -------------------------------------------------------------------------------- /src/contracts/newport/addresses/optimism.addresses.json: -------------------------------------------------------------------------------- 1 | { 2 | "OptionMarketViewer": "0x136d92f1d103BA5267c85555b28787AE53Ee3CEF", 3 | "ExchangeAdapter": "0x2b1dF9A55Ceb1bba7D830C1a6731ff37383c4A53", 4 | "LyraRegistry": "0x0FEd189bCD4A680e05B153dC7c3dC87004e162fb", 5 | "TestFaucet": "" 6 | } -------------------------------------------------------------------------------- /src/contracts/newport/arbitrum/typechain/common.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | import type { Listener } from "@ethersproject/providers"; 5 | import type { Event, EventFilter } from "ethers"; 6 | 7 | export interface TypedEvent< 8 | TArgsArray extends Array = any, 9 | TArgsObject = any 10 | > extends Event { 11 | args: TArgsArray & TArgsObject; 12 | } 13 | 14 | export interface TypedEventFilter<_TEvent extends TypedEvent> 15 | extends EventFilter {} 16 | 17 | export interface TypedListener { 18 | (...listenerArg: [...__TypechainArgsArray, TEvent]): void; 19 | } 20 | 21 | type __TypechainArgsArray = T extends TypedEvent ? U : never; 22 | 23 | export interface OnEvent { 24 | ( 25 | eventFilter: TypedEventFilter, 26 | listener: TypedListener 27 | ): TRes; 28 | (eventName: string, listener: Listener): TRes; 29 | } 30 | 31 | export type MinEthersFactory = { 32 | deploy(...a: ARGS[]): Promise; 33 | }; 34 | 35 | export type GetContractTypeFromFactory = F extends MinEthersFactory< 36 | infer C, 37 | any 38 | > 39 | ? C 40 | : never; 41 | 42 | export type GetARGsTypeFromFactory = F extends MinEthersFactory 43 | ? Parameters 44 | : never; 45 | 46 | export type PromiseOrValue = T | Promise; 47 | -------------------------------------------------------------------------------- /src/contracts/newport/arbitrum/typechain/factories/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export { NewportOptionMarket__factory } from "./NewportOptionMarket__factory"; 5 | -------------------------------------------------------------------------------- /src/contracts/newport/arbitrum/typechain/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export type { NewportOptionMarket } from "./NewportOptionMarket"; 5 | export * as factories from "./factories"; 6 | export { NewportOptionMarket__factory } from "./factories/NewportOptionMarket__factory"; 7 | -------------------------------------------------------------------------------- /src/contracts/newport/optimism/typechain/common.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | import type { Listener } from "@ethersproject/providers"; 5 | import type { Event, EventFilter } from "ethers"; 6 | 7 | export interface TypedEvent< 8 | TArgsArray extends Array = any, 9 | TArgsObject = any 10 | > extends Event { 11 | args: TArgsArray & TArgsObject; 12 | } 13 | 14 | export interface TypedEventFilter<_TEvent extends TypedEvent> 15 | extends EventFilter {} 16 | 17 | export interface TypedListener { 18 | (...listenerArg: [...__TypechainArgsArray, TEvent]): void; 19 | } 20 | 21 | type __TypechainArgsArray = T extends TypedEvent ? U : never; 22 | 23 | export interface OnEvent { 24 | ( 25 | eventFilter: TypedEventFilter, 26 | listener: TypedListener 27 | ): TRes; 28 | (eventName: string, listener: Listener): TRes; 29 | } 30 | 31 | export type MinEthersFactory = { 32 | deploy(...a: ARGS[]): Promise; 33 | }; 34 | 35 | export type GetContractTypeFromFactory = F extends MinEthersFactory< 36 | infer C, 37 | any 38 | > 39 | ? C 40 | : never; 41 | 42 | export type GetARGsTypeFromFactory = F extends MinEthersFactory 43 | ? Parameters 44 | : never; 45 | 46 | export type PromiseOrValue = T | Promise; 47 | -------------------------------------------------------------------------------- /src/contracts/newport/optimism/typechain/factories/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export { NewportOptionMarket__factory } from "./NewportOptionMarket__factory"; 5 | -------------------------------------------------------------------------------- /src/contracts/newport/optimism/typechain/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export type { NewportOptionMarket } from "./NewportOptionMarket"; 5 | export * as factories from "./factories"; 6 | export { NewportOptionMarket__factory } from "./factories/NewportOptionMarket__factory"; 7 | -------------------------------------------------------------------------------- /src/contracts/newport/typechain/NewportTestFaucet.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | import type { 5 | BaseContract, 6 | BigNumber, 7 | BytesLike, 8 | CallOverrides, 9 | ContractTransaction, 10 | Overrides, 11 | PopulatedTransaction, 12 | Signer, 13 | utils, 14 | } from "ethers"; 15 | import type { FunctionFragment, Result } from "@ethersproject/abi"; 16 | import type { Listener, Provider } from "@ethersproject/providers"; 17 | import type { 18 | TypedEventFilter, 19 | TypedEvent, 20 | TypedListener, 21 | OnEvent, 22 | PromiseOrValue, 23 | } from "./common"; 24 | 25 | export interface NewportTestFaucetInterface extends utils.Interface { 26 | functions: { 27 | "drip()": FunctionFragment; 28 | }; 29 | 30 | getFunction(nameOrSignatureOrTopic: "drip"): FunctionFragment; 31 | 32 | encodeFunctionData(functionFragment: "drip", values?: undefined): string; 33 | 34 | decodeFunctionResult(functionFragment: "drip", data: BytesLike): Result; 35 | 36 | events: {}; 37 | } 38 | 39 | export interface NewportTestFaucet extends BaseContract { 40 | connect(signerOrProvider: Signer | Provider | string): this; 41 | attach(addressOrName: string): this; 42 | deployed(): Promise; 43 | 44 | interface: NewportTestFaucetInterface; 45 | 46 | queryFilter( 47 | event: TypedEventFilter, 48 | fromBlockOrBlockhash?: string | number | undefined, 49 | toBlock?: string | number | undefined 50 | ): Promise>; 51 | 52 | listeners( 53 | eventFilter?: TypedEventFilter 54 | ): Array>; 55 | listeners(eventName?: string): Array; 56 | removeAllListeners( 57 | eventFilter: TypedEventFilter 58 | ): this; 59 | removeAllListeners(eventName?: string): this; 60 | off: OnEvent; 61 | on: OnEvent; 62 | once: OnEvent; 63 | removeListener: OnEvent; 64 | 65 | functions: { 66 | drip( 67 | overrides?: Overrides & { from?: PromiseOrValue } 68 | ): Promise; 69 | }; 70 | 71 | drip( 72 | overrides?: Overrides & { from?: PromiseOrValue } 73 | ): Promise; 74 | 75 | callStatic: { 76 | drip(overrides?: CallOverrides): Promise; 77 | }; 78 | 79 | filters: {}; 80 | 81 | estimateGas: { 82 | drip( 83 | overrides?: Overrides & { from?: PromiseOrValue } 84 | ): Promise; 85 | }; 86 | 87 | populateTransaction: { 88 | drip( 89 | overrides?: Overrides & { from?: PromiseOrValue } 90 | ): Promise; 91 | }; 92 | } 93 | -------------------------------------------------------------------------------- /src/contracts/newport/typechain/common.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | import type { Listener } from "@ethersproject/providers"; 5 | import type { Event, EventFilter } from "ethers"; 6 | 7 | export interface TypedEvent< 8 | TArgsArray extends Array = any, 9 | TArgsObject = any 10 | > extends Event { 11 | args: TArgsArray & TArgsObject; 12 | } 13 | 14 | export interface TypedEventFilter<_TEvent extends TypedEvent> 15 | extends EventFilter {} 16 | 17 | export interface TypedListener { 18 | (...listenerArg: [...__TypechainArgsArray, TEvent]): void; 19 | } 20 | 21 | type __TypechainArgsArray = T extends TypedEvent ? U : never; 22 | 23 | export interface OnEvent { 24 | ( 25 | eventFilter: TypedEventFilter, 26 | listener: TypedListener 27 | ): TRes; 28 | (eventName: string, listener: Listener): TRes; 29 | } 30 | 31 | export type MinEthersFactory = { 32 | deploy(...a: ARGS[]): Promise; 33 | }; 34 | 35 | export type GetContractTypeFromFactory = F extends MinEthersFactory< 36 | infer C, 37 | any 38 | > 39 | ? C 40 | : never; 41 | 42 | export type GetARGsTypeFromFactory = F extends MinEthersFactory 43 | ? Parameters 44 | : never; 45 | 46 | export type PromiseOrValue = T | Promise; 47 | -------------------------------------------------------------------------------- /src/contracts/newport/typechain/factories/NewportTestFaucet__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Signer, utils } from "ethers"; 6 | import type { Provider } from "@ethersproject/providers"; 7 | import type { 8 | NewportTestFaucet, 9 | NewportTestFaucetInterface, 10 | } from "../NewportTestFaucet"; 11 | 12 | const _abi = [ 13 | { 14 | inputs: [], 15 | name: "drip", 16 | outputs: [], 17 | stateMutability: "nonpayable", 18 | type: "function", 19 | }, 20 | ]; 21 | 22 | export class NewportTestFaucet__factory { 23 | static readonly abi = _abi; 24 | static createInterface(): NewportTestFaucetInterface { 25 | return new utils.Interface(_abi) as NewportTestFaucetInterface; 26 | } 27 | static connect( 28 | address: string, 29 | signerOrProvider: Signer | Provider 30 | ): NewportTestFaucet { 31 | return new Contract(address, _abi, signerOrProvider) as NewportTestFaucet; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/contracts/newport/typechain/factories/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export { NewportExchangeAdapter__factory } from "./NewportExchangeAdapter__factory"; 5 | export { NewportGMXAdapter__factory } from "./NewportGMXAdapter__factory"; 6 | export { NewportGMXFuturesPoolHedger__factory } from "./NewportGMXFuturesPoolHedger__factory"; 7 | export { NewportLiquidityPool__factory } from "./NewportLiquidityPool__factory"; 8 | export { NewportLiquidityToken__factory } from "./NewportLiquidityToken__factory"; 9 | export { NewportLyraRegistry__factory } from "./NewportLyraRegistry__factory"; 10 | export { NewportOptionGreekCache__factory } from "./NewportOptionGreekCache__factory"; 11 | export { NewportOptionMarketPricer__factory } from "./NewportOptionMarketPricer__factory"; 12 | export { NewportOptionMarketViewer__factory } from "./NewportOptionMarketViewer__factory"; 13 | export { NewportOptionToken__factory } from "./NewportOptionToken__factory"; 14 | export { NewportPoolHedger__factory } from "./NewportPoolHedger__factory"; 15 | export { NewportSNXPerpV2Adapter__factory } from "./NewportSNXPerpV2Adapter__factory"; 16 | export { NewportSNXPerpsV2PoolHedger__factory } from "./NewportSNXPerpsV2PoolHedger__factory"; 17 | export { NewportShortCollateral__factory } from "./NewportShortCollateral__factory"; 18 | export { NewportTestFaucet__factory } from "./NewportTestFaucet__factory"; 19 | -------------------------------------------------------------------------------- /src/contracts/newport/typechain/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export type { NewportExchangeAdapter } from "./NewportExchangeAdapter"; 5 | export type { NewportGMXAdapter } from "./NewportGMXAdapter"; 6 | export type { NewportGMXFuturesPoolHedger } from "./NewportGMXFuturesPoolHedger"; 7 | export type { NewportLiquidityPool } from "./NewportLiquidityPool"; 8 | export type { NewportLiquidityToken } from "./NewportLiquidityToken"; 9 | export type { NewportLyraRegistry } from "./NewportLyraRegistry"; 10 | export type { NewportOptionGreekCache } from "./NewportOptionGreekCache"; 11 | export type { NewportOptionMarketPricer } from "./NewportOptionMarketPricer"; 12 | export type { NewportOptionMarketViewer } from "./NewportOptionMarketViewer"; 13 | export type { NewportOptionToken } from "./NewportOptionToken"; 14 | export type { NewportPoolHedger } from "./NewportPoolHedger"; 15 | export type { NewportSNXPerpV2Adapter } from "./NewportSNXPerpV2Adapter"; 16 | export type { NewportSNXPerpsV2PoolHedger } from "./NewportSNXPerpsV2PoolHedger"; 17 | export type { NewportShortCollateral } from "./NewportShortCollateral"; 18 | export type { NewportTestFaucet } from "./NewportTestFaucet"; 19 | export * as factories from "./factories"; 20 | export { NewportExchangeAdapter__factory } from "./factories/NewportExchangeAdapter__factory"; 21 | export { NewportGMXAdapter__factory } from "./factories/NewportGMXAdapter__factory"; 22 | export { NewportGMXFuturesPoolHedger__factory } from "./factories/NewportGMXFuturesPoolHedger__factory"; 23 | export { NewportLiquidityPool__factory } from "./factories/NewportLiquidityPool__factory"; 24 | export { NewportLiquidityToken__factory } from "./factories/NewportLiquidityToken__factory"; 25 | export { NewportLyraRegistry__factory } from "./factories/NewportLyraRegistry__factory"; 26 | export { NewportOptionGreekCache__factory } from "./factories/NewportOptionGreekCache__factory"; 27 | export { NewportOptionMarketPricer__factory } from "./factories/NewportOptionMarketPricer__factory"; 28 | export { NewportOptionMarketViewer__factory } from "./factories/NewportOptionMarketViewer__factory"; 29 | export { NewportOptionToken__factory } from "./factories/NewportOptionToken__factory"; 30 | export { NewportPoolHedger__factory } from "./factories/NewportPoolHedger__factory"; 31 | export { NewportShortCollateral__factory } from "./factories/NewportShortCollateral__factory"; 32 | export { NewportSNXPerpsV2PoolHedger__factory } from "./factories/NewportSNXPerpsV2PoolHedger__factory"; 33 | export { NewportSNXPerpV2Adapter__factory } from "./factories/NewportSNXPerpV2Adapter__factory"; 34 | export { NewportTestFaucet__factory } from "./factories/NewportTestFaucet__factory"; 35 | -------------------------------------------------------------------------------- /src/contracts/typegen.ts: -------------------------------------------------------------------------------- 1 | import { glob, runTypeChain } from 'typechain' 2 | 3 | export default async function main() { 4 | const cwd = process.cwd() 5 | // find all files matching the glob 6 | const allFiles = glob(cwd, [`src/contracts/**/abis/+([a-zA-Z0-9_]).json`]) 7 | const deploymentFiles = allFiles.reduce((deployments, file) => { 8 | const deployment = file.split('/abis')[0] 9 | const deploymentFiles = deployments[deployment] ?? [] 10 | return { 11 | ...deployments, 12 | [deployment]: deploymentFiles.concat(file), 13 | } 14 | }, {} as Record>) 15 | await Promise.all( 16 | Object.entries(deploymentFiles).map(([deployment, files]) => { 17 | console.log({ files, deployment }) 18 | runTypeChain({ 19 | cwd, 20 | filesToProcess: files, 21 | allFiles: files, 22 | outDir: `${deployment}/typechain`, 23 | target: 'ethers-v5', 24 | }) 25 | }) 26 | ) 27 | } 28 | 29 | main().catch(console.error) 30 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lyra' 2 | export * from './account' 3 | export * from './board' 4 | export * from './collateral_update_event' 5 | export * from './market' 6 | export * from './option' 7 | export * from './position' 8 | export * from './quote' 9 | export * from './strike' 10 | export * from './settle_event' 11 | export * from './trade' 12 | export * from './trade_event' 13 | export * from './liquidity_deposit' 14 | export * from './liquidity_withdrawal' 15 | export * from './admin' 16 | export * from './global_reward_epoch' 17 | export * from './account_reward_epoch' 18 | export * from './constants/contracts' 19 | export * from './constants/queries' 20 | export * from './constants/network' 21 | export * from './constants/chain' 22 | export * from './utils/getGlobalContract' 23 | export * from './utils/getLyraContract' 24 | export * from './utils/getLyraMarketContract' 25 | import Lyra from './lyra' 26 | export default Lyra 27 | -------------------------------------------------------------------------------- /src/position/getMaxLoss.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derivexyz/lyra-js/b5b4cc22e15a4fea377def78fcec2eb61f0c53d4/src/position/getMaxLoss.ts -------------------------------------------------------------------------------- /src/position/getMaxProfit.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derivexyz/lyra-js/b5b4cc22e15a4fea377def78fcec2eb61f0c53d4/src/position/getMaxProfit.ts -------------------------------------------------------------------------------- /src/position/getPositionCollateral.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | import { UNIT, ZERO_BN } from '../constants/bn' 4 | import { Option } from '../option' 5 | import getLiquidationPrice from '../utils/getLiquidationPrice' 6 | import getMaxCollateral from '../utils/getMaxCollateral' 7 | import getMinCollateralForSpotPrice from '../utils/getMinCollateralForSpotPrice' 8 | 9 | export type PositionCollateral = { 10 | amount: BigNumber 11 | value: BigNumber 12 | min: BigNumber 13 | max: BigNumber | null 14 | isBase: boolean 15 | liquidationPrice: BigNumber | null 16 | } 17 | 18 | export default function getPositionCollateral( 19 | option: Option, 20 | size: BigNumber, 21 | collateral: BigNumber, 22 | isBaseCollateral?: boolean 23 | ): PositionCollateral { 24 | const strike = option.strike() 25 | const board = option.board() 26 | const market = option.market() 27 | const spotPrice = board.isExpired ? board.spotPriceAtExpiry ?? ZERO_BN : market.spotPrice 28 | return { 29 | amount: collateral, 30 | value: isBaseCollateral ? collateral.mul(spotPrice).div(UNIT) : collateral, 31 | min: getMinCollateralForSpotPrice(option, size, spotPrice, isBaseCollateral), 32 | max: getMaxCollateral(option.isCall, strike.strikePrice, size, isBaseCollateral), 33 | isBase: option.isCall ? !!isBaseCollateral : false, 34 | liquidationPrice: getLiquidationPrice(option, size, collateral, isBaseCollateral), 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/quote/getIVImpactForTrade.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | import { UNIT } from '../constants/bn' 4 | import { Option } from '../option' 5 | 6 | export default function getIVImpactForTrade( 7 | option: Option, 8 | baseIv: BigNumber, 9 | skew: BigNumber, 10 | size: BigNumber, 11 | isBuy: boolean 12 | ): { 13 | newSkew: BigNumber 14 | newBaseIv: BigNumber 15 | } { 16 | const market = option.market() 17 | const orderSize = size.mul(UNIT).div(market.params.standardSize) // 10^18 18 | const orderMoveBaseIv = orderSize.div(100) 19 | const orderMoveSkew = orderMoveBaseIv.mul(market.params.skewAdjustmentFactor).div(UNIT) 20 | const newBaseIv = isBuy ? baseIv.add(orderMoveBaseIv) : baseIv.sub(orderMoveBaseIv) 21 | const newSkew = isBuy ? skew.add(orderMoveSkew) : skew.sub(orderMoveSkew) 22 | return { 23 | newBaseIv, 24 | newSkew, 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/quote/getOptionPriceFee.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | import { Board } from '../board' 4 | import { UNIT } from '../constants/bn' 5 | import getTimeWeightedFee from './getTimeWeightedFee' 6 | 7 | export default function getOptionPriceFee(board: Board, pricePerOption: BigNumber, size: BigNumber) { 8 | const market = board.market() 9 | const timeWeightedOptionPriceFee = getTimeWeightedFee( 10 | board.timeToExpiry, 11 | market.params.optionPriceFee1xPoint, 12 | market.params.optionPriceFee2xPoint, 13 | market.params.optionPriceFeeCoefficient 14 | ) 15 | return timeWeightedOptionPriceFee.mul(size).div(UNIT).mul(pricePerOption).div(UNIT) 16 | } 17 | -------------------------------------------------------------------------------- /src/quote/getPrice.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | import { UNIT } from '../constants/bn' 4 | import { Option } from '../option' 5 | import { getBlackScholesPrice } from '../utils/blackScholes' 6 | import fromBigNumber from '../utils/fromBigNumber' 7 | import getTimeToExpiryAnnualized from '../utils/getTimeToExpiryAnnualized' 8 | import toBigNumber from '../utils/toBigNumber' 9 | 10 | export default function getPrice( 11 | option: Option, 12 | spotPrice: BigNumber, 13 | newBaseIv: BigNumber, 14 | newSkew: BigNumber 15 | ): { 16 | price: BigNumber 17 | volTraded: BigNumber 18 | } { 19 | const timeToExpiryAnnualized = getTimeToExpiryAnnualized(option.board()) 20 | const rate = option.market().params.rateAndCarry 21 | 22 | const newVol = newBaseIv.mul(newSkew).div(UNIT) 23 | 24 | const strikePrice = option.strike().strikePrice 25 | 26 | const price = toBigNumber( 27 | getBlackScholesPrice( 28 | timeToExpiryAnnualized, 29 | fromBigNumber(newVol), 30 | fromBigNumber(spotPrice), 31 | fromBigNumber(strikePrice), 32 | fromBigNumber(rate), 33 | option.isCall 34 | ) 35 | ) 36 | return { 37 | price, 38 | volTraded: newVol, 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/quote/getQuoteIteration.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | import { UNIT, ZERO_BN } from '../constants/bn' 4 | import { Option } from '../option' 5 | import { QuoteIteration } from '.' 6 | import getForceClosePrice from './getForceClosePrice' 7 | import getIVImpactForTrade from './getIVImpactForTrade' 8 | import getOptionPriceFee from './getOptionPriceFee' 9 | import getPrice from './getPrice' 10 | import getSpotPriceFee from './getSpotPriceFee' 11 | import getVarianceFee from './getVarianceFee' 12 | import getVegaUtilFee from './getVegaUtilFee' 13 | 14 | export default function getQuoteIteration({ 15 | option, 16 | isBuy, 17 | size, 18 | spotPrice, 19 | baseIv, 20 | skew, 21 | netStdVega, 22 | preTradeAmmNetStdVega, 23 | isForceClose, 24 | }: { 25 | option: Option 26 | isBuy: boolean 27 | size: BigNumber 28 | spotPrice: BigNumber 29 | baseIv: BigNumber 30 | skew: BigNumber 31 | netStdVega: BigNumber 32 | preTradeAmmNetStdVega: BigNumber 33 | isForceClose: boolean 34 | }): QuoteIteration { 35 | // Post-impact iv and skew 36 | const { newBaseIv: proposedNewBaseIv, newSkew } = getIVImpactForTrade(option, baseIv, skew, size, isBuy) 37 | 38 | // Calculate (force close) base bsc price per option 39 | const basePriceData = getPrice(option, spotPrice, proposedNewBaseIv, newSkew) 40 | const { price, volTraded } = isForceClose 41 | ? getForceClosePrice(option, isBuy, spotPrice, proposedNewBaseIv, newSkew) 42 | : basePriceData 43 | const basePrice = basePriceData.price 44 | 45 | // Penalty 46 | const forceClosePenalty = price 47 | .sub(basePrice) 48 | .mul(size) 49 | .div(UNIT) 50 | .mul(isBuy ? 1 : -1) 51 | 52 | // Option fee 53 | const optionPriceFee = getOptionPriceFee(option.board(), price, size) 54 | 55 | // Spot fee 56 | const spotPriceFee = getSpotPriceFee(option.board(), size, spotPrice) 57 | 58 | // Update AMM net standard vega 59 | const netStdVegaDiff = netStdVega 60 | .mul(size) 61 | .mul(isBuy ? 1 : -1) 62 | .div(UNIT) 63 | const postTradeAmmNetStdVega = preTradeAmmNetStdVega.add(netStdVegaDiff) 64 | 65 | // Vega util fee 66 | const vegaUtilFee = getVegaUtilFee(option.market(), preTradeAmmNetStdVega, postTradeAmmNetStdVega, volTraded, size) 67 | 68 | // Skip baseIv update on force close 69 | const newBaseIv = isForceClose ? baseIv : proposedNewBaseIv 70 | 71 | // Variance fee 72 | const varianceFee = getVarianceFee(option, spotPrice, volTraded, newSkew, newBaseIv, size, isForceClose) 73 | 74 | // Total fees 75 | const fees = optionPriceFee.add(spotPriceFee).add(vegaUtilFee.vegaUtilFee).add(varianceFee.varianceFee) 76 | 77 | // Add fees for buys, subtract fees for sells 78 | const base = price.mul(size).div(UNIT) 79 | 80 | const premium = isBuy ? base.add(fees) : fees.lt(base) ? base.sub(fees) : ZERO_BN 81 | 82 | return { 83 | premium, 84 | optionPriceFee, 85 | spotPriceFee, 86 | vegaUtilFee, 87 | varianceFee, 88 | forceClosePenalty, 89 | postTradeAmmNetStdVega, 90 | volTraded, 91 | newSkew, 92 | newBaseIv, 93 | spotPrice, 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/quote/getSpotPriceFee.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | import { Board } from '../board' 4 | import { UNIT } from '../constants/bn' 5 | import getTimeWeightedFee from './getTimeWeightedFee' 6 | 7 | export default function getSpotPriceFee(board: Board, size: BigNumber, spotPrice: BigNumber) { 8 | const market = board.market() 9 | const timeWeightedSpotPriceFee = getTimeWeightedFee( 10 | board.timeToExpiry, 11 | market.params.spotPriceFee1xPoint, 12 | market.params.spotPriceFee2xPoint, 13 | market.params.spotPriceFeeCoefficient 14 | ) 15 | const spotPriceFee = timeWeightedSpotPriceFee.mul(size).div(UNIT).mul(spotPrice).div(UNIT) 16 | return spotPriceFee 17 | } 18 | -------------------------------------------------------------------------------- /src/quote/getTimeWeightedFee.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | import { UNIT } from '../constants/bn' 4 | import toBigNumber from '../utils/toBigNumber' 5 | 6 | export default function getTimeWeightedFee( 7 | timeToExpiry: number, 8 | pointA: number, 9 | pointB: number, 10 | coefficient: BigNumber 11 | ) { 12 | if (timeToExpiry <= pointA) { 13 | return coefficient 14 | } else { 15 | const factor = toBigNumber(timeToExpiry - pointA) 16 | .mul(UNIT) 17 | .div(pointB - pointA) 18 | .div(UNIT) 19 | return coefficient.mul(UNIT.add(factor)).div(UNIT) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/quote/getVarianceFee.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | import { UNIT, ZERO_BN } from '../constants/bn' 4 | import { Option } from '../option' 5 | import { getVega } from '../utils/blackScholes' 6 | import fromBigNumber from '../utils/fromBigNumber' 7 | import getTimeToExpiryAnnualized from '../utils/getTimeToExpiryAnnualized' 8 | import toBigNumber from '../utils/toBigNumber' 9 | import { QuoteVarianceFeeComponents } from '.' 10 | 11 | export default function getVarianceFee( 12 | option: Option, 13 | spotPrice: BigNumber, 14 | volTraded: BigNumber, 15 | newSkew: BigNumber, 16 | newBaseIv: BigNumber, 17 | size: BigNumber, 18 | isForceClose: boolean 19 | ): QuoteVarianceFeeComponents { 20 | const market = option.market() 21 | const coefficient = isForceClose 22 | ? market.params.forceCloseVarianceFeeCoefficient 23 | : market.params.defaultVarianceFeeCoefficient 24 | const varianceGwavIv = option.board().params.varianceGwavIv 25 | const ivVariance = varianceGwavIv.sub(newBaseIv).abs() 26 | const rate = option.market().params.rateAndCarry 27 | const timeToExpiryAnnualized = getTimeToExpiryAnnualized(option.board()) 28 | const vega = toBigNumber( 29 | getVega( 30 | timeToExpiryAnnualized, 31 | fromBigNumber(volTraded), 32 | fromBigNumber(spotPrice), 33 | fromBigNumber(option.strike().strikePrice), 34 | fromBigNumber(rate) 35 | ) * 100 36 | ) 37 | if (coefficient.isZero()) { 38 | return { 39 | varianceFeeCoefficient: ZERO_BN, 40 | vega, 41 | vegaCoefficient: ZERO_BN, 42 | skew: newSkew, 43 | skewCoefficient: ZERO_BN, 44 | ivVariance, 45 | ivVarianceCoefficient: ZERO_BN, 46 | varianceFee: ZERO_BN, 47 | } 48 | } 49 | const vegaCoefficient = market.params.minimumStaticVega.add(vega.mul(market.params.vegaCoefficient).div(UNIT)) 50 | const skewDiff = newSkew.sub(market.params.referenceSkew).abs() 51 | const skewCoefficient = market.params.minimumStaticSkewAdjustment.add( 52 | skewDiff.mul(market.params.skewAdjustmentCoefficient).div(UNIT) 53 | ) 54 | const ivVarianceCoefficient = market.params.minimumStaticIvVariance.add( 55 | ivVariance.mul(market.params.ivVarianceCoefficient).div(UNIT) 56 | ) 57 | const varianceFee = coefficient 58 | .mul(vegaCoefficient) 59 | .div(UNIT) 60 | .mul(skewCoefficient) 61 | .div(UNIT) 62 | .mul(ivVarianceCoefficient) 63 | .div(UNIT) 64 | .mul(size) 65 | .div(UNIT) 66 | return { 67 | varianceFeeCoefficient: coefficient, 68 | vega, 69 | vegaCoefficient, 70 | skew: newSkew, 71 | skewCoefficient, 72 | ivVariance, 73 | ivVarianceCoefficient, 74 | varianceFee, 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/quote/getVegaUtilFee.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | import { UNIT, ZERO_BN } from '../constants/bn' 4 | import { Market } from '../market' 5 | import { QuoteVegaUtilFeeComponents } from '.' 6 | 7 | export default function getVegaUtilFee( 8 | market: Market, 9 | preTradeAmmNetStdVega: BigNumber, 10 | postTradeAmmNetStdVega: BigNumber, 11 | volTraded: BigNumber, 12 | size: BigNumber 13 | ): QuoteVegaUtilFeeComponents { 14 | const NAV = market.params.NAV 15 | if (preTradeAmmNetStdVega.abs().gte(postTradeAmmNetStdVega.abs())) { 16 | return { 17 | preTradeAmmNetStdVega, 18 | postTradeAmmNetStdVega, 19 | vegaUtil: ZERO_BN, 20 | volTraded, 21 | NAV, 22 | vegaUtilFee: ZERO_BN, 23 | } 24 | } 25 | const vegaUtil = NAV.gt(0) ? volTraded.mul(postTradeAmmNetStdVega.abs()).div(NAV) : ZERO_BN 26 | const _vegaUtilFee = market.params.vegaFeeCoefficient.mul(vegaUtil).div(UNIT).mul(size).div(UNIT) 27 | const vegaUtilFee = _vegaUtilFee.lt(0) ? ZERO_BN : _vegaUtilFee 28 | return { 29 | preTradeAmmNetStdVega, 30 | postTradeAmmNetStdVega, 31 | vegaUtil, 32 | volTraded, 33 | NAV, 34 | vegaUtilFee, 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/settle_event/index.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | import { TransactionReceipt } from '@ethersproject/providers' 3 | 4 | import Lyra, { Position } from '..' 5 | import { DataSource } from '../constants/contracts' 6 | import fetchPositionEventDataByHash from '../utils/fetchPositionEventDataByHash' 7 | 8 | export type SettleEventData = { 9 | source: DataSource 10 | blockNumber: number 11 | timestamp: number 12 | transactionHash: string 13 | positionId: number 14 | spotPriceAtExpiry: BigNumber 15 | owner: string 16 | size: BigNumber 17 | marketName: string 18 | marketAddress: string 19 | expiryTimestamp: number 20 | strikePrice: BigNumber 21 | isCall: boolean 22 | isLong: boolean 23 | isBaseCollateral: boolean 24 | settlement: BigNumber 25 | returnedCollateralAmount: BigNumber 26 | returnedCollateralValue: BigNumber 27 | isInTheMoney: boolean 28 | } 29 | 30 | export class SettleEvent { 31 | private __settleData: SettleEventData 32 | __source: DataSource 33 | lyra: Lyra 34 | blockNumber: number 35 | positionId: number 36 | spotPriceAtExpiry: BigNumber 37 | timestamp: number 38 | transactionHash: string 39 | owner: string 40 | size: BigNumber 41 | marketName: string 42 | marketAddress: string 43 | expiryTimestamp: number 44 | strikePrice: BigNumber 45 | isCall: boolean 46 | isLong: boolean 47 | isBaseCollateral: boolean 48 | settlement: BigNumber 49 | returnedCollateralAmount: BigNumber 50 | returnedCollateralValue: BigNumber 51 | isInTheMoney: boolean 52 | 53 | constructor(lyra: Lyra, settle: SettleEventData) { 54 | this.lyra = lyra 55 | this.__settleData = settle 56 | this.__source = settle.source 57 | this.blockNumber = settle.blockNumber 58 | this.positionId = settle.positionId 59 | this.spotPriceAtExpiry = settle.spotPriceAtExpiry 60 | this.timestamp = settle.timestamp 61 | this.transactionHash = settle.transactionHash 62 | this.owner = settle.owner 63 | this.size = settle.size 64 | this.marketName = settle.marketName 65 | this.blockNumber = settle.blockNumber 66 | this.marketAddress = settle.marketAddress 67 | this.expiryTimestamp = settle.expiryTimestamp 68 | this.isCall = settle.isCall 69 | this.isLong = settle.isLong 70 | this.isBaseCollateral = settle.isBaseCollateral 71 | this.strikePrice = settle.strikePrice 72 | this.settlement = settle.settlement 73 | this.returnedCollateralAmount = settle.returnedCollateralAmount 74 | this.returnedCollateralValue = settle.returnedCollateralValue 75 | this.isInTheMoney = settle.isInTheMoney 76 | } 77 | 78 | // Getters 79 | 80 | static async getByHash(lyra: Lyra, transactionHashOrReceipt: string | TransactionReceipt): Promise { 81 | const { settles } = await fetchPositionEventDataByHash(lyra, transactionHashOrReceipt) 82 | return settles 83 | } 84 | 85 | // Dynamic Fields 86 | 87 | pnl(position: Position): BigNumber { 88 | return position.pnl().settlementPnl 89 | } 90 | 91 | // Edges 92 | 93 | async position(): Promise { 94 | return await this.lyra.position(this.marketAddress, this.positionId) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/trade/getMaxLoss.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | import { Trade } from '..' 4 | import { MAX_BN, UNIT } from '../constants/bn' 5 | 6 | export default function getMaxLoss(trade: Trade): BigNumber { 7 | if (trade.isCall && trade.isBuy) { 8 | return trade.premium 9 | } else if (trade.isCall && !trade.isBuy) { 10 | return MAX_BN 11 | } else if (!trade.isCall && trade.isBuy) { 12 | return trade.premium 13 | } else { 14 | return trade.strikePrice.sub(trade.pricePerOption).mul(trade.size).div(UNIT) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/trade/getMaxProfit.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | import { Trade } from '..' 4 | import { MAX_BN, UNIT } from '../constants/bn' 5 | 6 | export default function getMaxProfit(trade: Trade): BigNumber { 7 | if (trade.isCall && trade.isBuy) { 8 | return MAX_BN 9 | } else if (trade.isCall && !trade.isBuy) { 10 | return trade.premium 11 | } else if (!trade.isCall && trade.isBuy) { 12 | return trade.strikePrice.sub(trade.pricePerOption).mul(trade.size).div(UNIT) 13 | } else { 14 | return trade.premium 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/trade/getTradeCollateral.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | import { TradeCollateral } from '..' 4 | import { ZERO_BN } from '../constants/bn' 5 | import { Option } from '../option' 6 | import getLiquidationPrice from '../utils/getLiquidationPrice' 7 | import getMaxCollateral from '../utils/getMaxCollateral' 8 | import getMinCollateralForSpotPrice from '../utils/getMinCollateralForSpotPrice' 9 | 10 | export default function getTradeCollateral({ 11 | option, 12 | postTradeSize, 13 | setToCollateral: _setCollateralTo, 14 | setToFullCollateral, 15 | isBaseCollateral, 16 | }: { 17 | option: Option 18 | postTradeSize: BigNumber 19 | setToCollateral?: BigNumber 20 | setToFullCollateral?: boolean 21 | isBaseCollateral?: boolean 22 | }): TradeCollateral { 23 | const isBase = option.isCall ? !!isBaseCollateral : false 24 | 25 | if (postTradeSize.isZero()) { 26 | // Position is being closed 27 | return { 28 | amount: ZERO_BN, 29 | min: ZERO_BN, 30 | max: ZERO_BN, 31 | isMin: false, 32 | isMax: false, 33 | liquidationPrice: null, 34 | isBase, 35 | } 36 | } 37 | 38 | const market = option.market() 39 | 40 | const spotPrice = market.spotPrice 41 | const minCollateral = getMinCollateralForSpotPrice(option, postTradeSize, spotPrice, isBaseCollateral) 42 | let maxCollateral = getMaxCollateral(option.isCall, option.strike().strikePrice, postTradeSize, isBaseCollateral) 43 | if (maxCollateral && minCollateral.gt(maxCollateral)) { 44 | // Account for case where min collateral is greater than max 45 | maxCollateral = minCollateral 46 | } 47 | 48 | let setToCollateral: BigNumber 49 | // TODO: Maintain current position collateral 50 | if (setToFullCollateral) { 51 | if (!maxCollateral) { 52 | // No max collateral for cash-secured short calls 53 | throw new Error('Cannot fully collateralize a cash-secured short call') 54 | } 55 | setToCollateral = maxCollateral 56 | } else { 57 | setToCollateral = _setCollateralTo ?? ZERO_BN 58 | } 59 | 60 | const isMin = setToCollateral.lte(minCollateral) 61 | const isMax = !!(maxCollateral && setToCollateral.gte(maxCollateral)) 62 | 63 | const liquidationPrice = getLiquidationPrice(option, postTradeSize, setToCollateral, isBaseCollateral) 64 | 65 | return { 66 | amount: setToCollateral, 67 | isBase, 68 | max: maxCollateral, 69 | min: minCollateral, 70 | isMin, 71 | isMax, 72 | liquidationPrice, 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/trade_event/getTradeEventNewSize.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | import { Position } from '../position' 4 | import { TradeEvent } from '.' 5 | import getTradeEventPreviousSize from './getTradeEventPreviousSize' 6 | 7 | export default function getTradeEventNewSize(position: Position, trade: TradeEvent): BigNumber { 8 | const prevSize = getTradeEventPreviousSize(position, trade) 9 | const newSize = trade.isOpen ? prevSize.add(trade.size) : prevSize.sub(trade.size) 10 | return newSize 11 | } 12 | -------------------------------------------------------------------------------- /src/trade_event/getTradeEventPreviousSize.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | import { ZERO_BN } from '../constants/bn' 4 | import { Position } from '../position' 5 | import getPositionPreviousTrades from '../utils/getPositionPreviousTrades' 6 | import { TradeEvent } from '.' 7 | 8 | export default function getTradeEventPreviousSize(position: Position, trade: TradeEvent): BigNumber { 9 | const trades = getPositionPreviousTrades(position, trade) 10 | const prevSize = trades.reduce((size, trade) => (trade.isOpen ? size.add(trade.size) : size.sub(trade.size)), ZERO_BN) 11 | return prevSize 12 | } 13 | -------------------------------------------------------------------------------- /src/transfer_event/index.ts: -------------------------------------------------------------------------------- 1 | import { TransactionReceipt } from '@ethersproject/providers' 2 | 3 | import { DataSource } from '../constants/contracts' 4 | import Lyra from '../lyra' 5 | import fetchPositionEventDataByHash from '../utils/fetchPositionEventDataByHash' 6 | 7 | export type TransferEventData = { 8 | transactionHash: string 9 | source: DataSource 10 | blockNumber: number 11 | from: string 12 | to: string 13 | marketAddress: string 14 | positionId: number 15 | } 16 | 17 | export class TransferEvent { 18 | private lyra: Lyra 19 | private __transferData: TransferEventData 20 | __source: DataSource 21 | transactionHash: string 22 | blockNumber: number 23 | from: string 24 | to: string 25 | marketAddress: string 26 | positionId: number 27 | 28 | constructor(lyra: Lyra, transfer: TransferEventData) { 29 | this.lyra = lyra 30 | this.__transferData = transfer 31 | this.__source = transfer.source 32 | this.transactionHash = transfer.transactionHash 33 | this.blockNumber = transfer.blockNumber 34 | this.from = transfer.from 35 | this.to = transfer.to 36 | this.marketAddress = transfer.marketAddress 37 | this.positionId = transfer.positionId 38 | } 39 | 40 | // Getters 41 | 42 | static async getByHash(lyra: Lyra, transactionHashOrReceipt: string | TransactionReceipt): Promise { 43 | const { transfers } = await fetchPositionEventDataByHash(lyra, transactionHashOrReceipt) 44 | return transfers 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/utils/buildTx.ts: -------------------------------------------------------------------------------- 1 | import { PopulatedTransaction } from '@ethersproject/contracts' 2 | import { JsonRpcProvider } from '@ethersproject/providers' 3 | 4 | export default function buildTx( 5 | provider: JsonRpcProvider, 6 | chainId: number, 7 | to: string, 8 | from: string, 9 | data: string 10 | ): PopulatedTransaction { 11 | return { 12 | to, 13 | data, 14 | from, 15 | chainId: chainId ?? provider.network.chainId, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/utils/canHedge.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from 'ethers' 2 | 3 | import { Network, PoolHedgerParams } from '..' 4 | import { GMXFuturesPoolHedger } from '../contracts/newport/typechain/NewportGMXFuturesPoolHedger' 5 | import { SNXPerpsV2PoolHedger } from '../contracts/newport/typechain/NewportSNXPerpsV2PoolHedger' 6 | import { PoolHedgerView } from '../market' 7 | import { Option } from '../option' 8 | import canHedgeOnArbitrum from './canHedgeArbitrum' 9 | import canHedgeOnOptimism from './canHedgeOptimism' 10 | 11 | export default function canHedge( 12 | quoteSpotPrice: BigNumber, 13 | netDelta: BigNumber, 14 | option: Option, 15 | size: BigNumber, 16 | increasesPoolDelta: boolean, 17 | hedgerView: PoolHedgerView, 18 | poolHedgerParams: PoolHedgerParams, 19 | network: Network 20 | ): boolean { 21 | switch (network) { 22 | case Network.Arbitrum: 23 | return canHedgeOnArbitrum( 24 | quoteSpotPrice, 25 | netDelta, 26 | option, 27 | size, 28 | increasesPoolDelta, 29 | hedgerView as GMXFuturesPoolHedger.GMXFuturesPoolHedgerViewStructOutput, 30 | poolHedgerParams 31 | ) 32 | case Network.Optimism: 33 | return canHedgeOnOptimism( 34 | quoteSpotPrice, 35 | netDelta, 36 | option, 37 | size, 38 | increasesPoolDelta, 39 | hedgerView as SNXPerpsV2PoolHedger.HedgerStateStructOutput, 40 | poolHedgerParams 41 | ) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/utils/canHedgeArbitrum.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from 'ethers' 2 | 3 | import { PoolHedgerParams } from '../admin' 4 | import { UNIT } from '../constants/bn' 5 | import { GMXFuturesPoolHedger } from '../contracts/newport/typechain/NewportGMXFuturesPoolHedger' 6 | import { Option } from '../option' 7 | import getCappedExpectedHedge from './getCappedExpectedHedge' 8 | 9 | export default function canHedgeOnArbitrum( 10 | spotPrice: BigNumber, 11 | netDelta: BigNumber, 12 | option: Option, 13 | size: BigNumber, 14 | increasesPoolDelta: boolean, 15 | hedgerView: GMXFuturesPoolHedger.GMXFuturesPoolHedgerViewStructOutput, 16 | poolHedgerParams: PoolHedgerParams 17 | ) { 18 | const { currentHedge, gmxView, futuresPoolHedgerParams } = hedgerView 19 | if (!futuresPoolHedgerParams.vaultLiquidityCheckEnabled) { 20 | return true 21 | } 22 | 23 | const cappedExpectedHedge = getCappedExpectedHedge(option, size, netDelta, poolHedgerParams, increasesPoolDelta) 24 | const cappedExpectedHedgeAbs = cappedExpectedHedge.abs() 25 | const currentHedgeAbs = currentHedge.abs() 26 | 27 | if (!futuresPoolHedgerParams) { 28 | return true 29 | } 30 | if (cappedExpectedHedgeAbs.lte(currentHedgeAbs)) { 31 | // Delta is shrinking (potentially flipping, but still smaller than current hedge), so we skip the check 32 | return true 33 | } 34 | 35 | // expected hedge is positive, and trade increases delta of the pool - risk is reduced, so accept trade 36 | if (increasesPoolDelta && cappedExpectedHedge.gte(0)) { 37 | return true 38 | } 39 | 40 | // expected hedge is negative, and trade decreases delta of the pool - risk is reduced, so accept trade 41 | if (!increasesPoolDelta && cappedExpectedHedge.lte(0)) { 42 | return true 43 | } 44 | 45 | // Figure out the amount of remaining dollars for the specific direction the pool needs to hedge 46 | let remainingDollars: BigNumber 47 | if (cappedExpectedHedge.gt(0)) { 48 | const { remainingLongDollars } = gmxView 49 | remainingDollars = remainingLongDollars 50 | } else { 51 | const { remainingShortDollars } = gmxView 52 | remainingDollars = remainingShortDollars 53 | } 54 | // Convert the dollar amount to deltas by dividing by spot. 55 | const remainingDeltas = remainingDollars.div(spotPrice).mul(UNIT) 56 | 57 | const absHedgeDiff = cappedExpectedHedgeAbs.sub(currentHedgeAbs) 58 | if (remainingDeltas.lt(absHedgeDiff.mul(futuresPoolHedgerParams.marketDepthBuffer).div(UNIT))) { 59 | return false 60 | } 61 | 62 | return true 63 | } 64 | -------------------------------------------------------------------------------- /src/utils/convertBNDecimals.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | export function from18DecimalBN(val: BigNumber, decimals: number): BigNumber { 4 | return val.div(BigNumber.from(10).pow(18 - decimals)) 5 | } 6 | export function to18DecimalBN(val: BigNumber, decimals: number): BigNumber { 7 | return val.mul(BigNumber.from(10).pow(18 - decimals)) 8 | } 9 | -------------------------------------------------------------------------------- /src/utils/fetchAccountRewardEpochData.ts: -------------------------------------------------------------------------------- 1 | import { RewardEpochTokenAmount } from '../global_reward_epoch' 2 | import Lyra, { Deployment } from '../lyra' 3 | import fetchWithCache from './fetchWithCache' 4 | 5 | export type AccountRewardEpochData = { 6 | account: string // indexed, 7 | deployment: Deployment // indexed 8 | startTimestamp: number // indexed 9 | endTimestamp: number 10 | lastUpdated: number 11 | stakingRewards: AccountStakingRewards 12 | mmvRewards: AccountMMVRewards 13 | tradingRewards: AccountTradingRewards 14 | integratorTradingRewards?: AccountTradingRewards 15 | } 16 | 17 | export type AccountStakingRewards = { 18 | isIgnored: boolean 19 | rewards: RewardEpochTokenAmount[] 20 | stkLyraDays: number 21 | } 22 | 23 | export type AccountMMVRewards = { 24 | [market: string]: { 25 | lpDays: number 26 | boostedLpDays: number 27 | rewards: RewardEpochTokenAmount[] 28 | isIgnored: boolean 29 | } 30 | } 31 | 32 | export type DailyPoint = { 33 | points: number 34 | day: number 35 | fees: number 36 | premium: number 37 | size: number 38 | volume: number 39 | durationInSeconds: number 40 | startOfDayTimestamp: number 41 | endOfDayTimestamp: number 42 | stakingBoost: number 43 | tradingBoost: number 44 | referralBoost: number 45 | referrer: string | null 46 | referrerFees: number 47 | referrerBoost: number 48 | } 49 | 50 | export type DailyPoints = { 51 | [startTimestamp: number]: DailyPoint 52 | } 53 | 54 | export type NewTradingRewardsReferredTraders = { 55 | [trader: string]: { 56 | trader: string 57 | trades: number 58 | fees: number 59 | premium: number 60 | volume: number 61 | tokens: RewardEpochTokenAmount[] 62 | } 63 | } 64 | 65 | export type NewTradingRewards = { 66 | points: { 67 | daily: DailyPoints 68 | trades: number 69 | fees: number 70 | premium: number 71 | size: number 72 | durationInSeconds: number 73 | averageBoost: number 74 | total: number 75 | volume: number 76 | totalPercent: number 77 | } 78 | tokens: RewardEpochTokenAmount[] 79 | referredTraders: NewTradingRewardsReferredTraders 80 | } 81 | 82 | export type AccountTradingRewards = { 83 | fees: number // USD 84 | effectiveRebateRate: number // post xLyra percentage 85 | tradingRebateRewardDollars: number 86 | shortCollateralRewardDollars: number 87 | totalTradingRewardDollars: number 88 | shortCallSeconds: number 89 | shortPutSeconds: number 90 | rewards: { 91 | trading: RewardEpochTokenAmount[] 92 | shortCollateral: RewardEpochTokenAmount[] 93 | } 94 | newRewards: NewTradingRewards 95 | } 96 | 97 | export default async function fetchAccountRewardEpochData( 98 | lyra: Lyra, 99 | account: string 100 | ): Promise { 101 | if (lyra.deployment !== Deployment.Mainnet) { 102 | return [] 103 | } 104 | return fetchWithCache( 105 | `${lyra.apiUri}/rewards/account?network=${lyra.network}&account=${account}&version=${lyra.version}` 106 | ) 107 | } 108 | -------------------------------------------------------------------------------- /src/utils/fetchAllPositionDataByOwner.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/core' 2 | import { getAddress } from '@ethersproject/address' 3 | 4 | import { POSITION_QUERY_FRAGMENT, PositionQueryResult } from '../constants/queries' 5 | import Lyra from '../lyra' 6 | import { Market } from '../market' 7 | import { PositionData } from '../position' 8 | import filterNulls from './filterNulls' 9 | import getCollateralUpdateDataFromSubgraph from './getCollateralUpdateDataFromSubgraph' 10 | import getPositionDataFromSubgraph from './getPositionDataFromSubgraph' 11 | import getSettleDataFromSubgraph from './getSettleDataFromSubgraph' 12 | import getTradeDataFromSubgraph from './getTradeDataFromSubgraph' 13 | import getTransferDataFromSubgraph from './getTransferDataFromSubgraph' 14 | import getUniqueBy from './getUniqueBy' 15 | import subgraphRequest from './subgraphRequest' 16 | 17 | // TODO: @dappbeast Handle more than 1k position queries 18 | const positionsQuery = gql` 19 | query positions($owner: String) { 20 | # Get all positions that have been transferred to $owner 21 | optionTransfers(first: 1000, where:{newOwner: $owner}) { 22 | position { 23 | ${POSITION_QUERY_FRAGMENT} 24 | } 25 | } 26 | # Get all positions that have been traded by $owner 27 | # This covers any positions a trader opened as well as collateral updates 28 | trades(first: 1000, where:{trader:$owner}) { 29 | position { 30 | ${POSITION_QUERY_FRAGMENT} 31 | } 32 | } 33 | } 34 | ` 35 | 36 | type PositionVariables = { 37 | owner: string 38 | } 39 | 40 | export default async function fetchAllPositionDataByOwner(lyra: Lyra, owner: string): Promise { 41 | const [{ data }, markets] = await Promise.all([ 42 | subgraphRequest< 43 | { 44 | optionTransfers: { position: PositionQueryResult }[] 45 | trades: { position: PositionQueryResult }[] 46 | }, 47 | PositionVariables 48 | >(lyra.subgraphClient, { 49 | query: positionsQuery, 50 | variables: { 51 | owner: owner.toLowerCase(), 52 | }, 53 | }), 54 | lyra.markets(), 55 | ]) 56 | 57 | const transferPositions = data?.optionTransfers.map(t => t.position) ?? [] 58 | const tradedPositions = data?.trades.map(t => t.position) ?? [] 59 | const positions = getUniqueBy(tradedPositions.concat(transferPositions), p => p.id) 60 | 61 | const marketsByAddress: Record = markets.reduce( 62 | (dict, market) => ({ ...dict, [market.address]: market }), 63 | {} as Record 64 | ) 65 | 66 | return filterNulls( 67 | positions.map(pos => { 68 | const market = marketsByAddress[getAddress(pos.market.id)] 69 | if (!market) { 70 | // Handle positions from previous versions 71 | return null 72 | } 73 | const trades = pos.trades.map(getTradeDataFromSubgraph) 74 | const collateralUpdates = pos.collateralUpdates.map(getCollateralUpdateDataFromSubgraph) 75 | const transfers = pos.transfers.map(getTransferDataFromSubgraph) 76 | const settle = pos.settle ? getSettleDataFromSubgraph(pos.settle) : null 77 | return getPositionDataFromSubgraph(pos, market, trades, collateralUpdates, transfers, settle) 78 | }) 79 | ) 80 | } 81 | -------------------------------------------------------------------------------- /src/utils/fetchAvalonMarketView.ts: -------------------------------------------------------------------------------- 1 | import { isAddress } from '@ethersproject/address' 2 | 3 | import { LyraContractId } from '../constants/contracts' 4 | import { LyraContractMap } from '../constants/mappings' 5 | import { OptionMarketViewer } from '../contracts/avalon/typechain/AvalonOptionMarketViewer' 6 | import Lyra, { Version } from '../lyra' 7 | import getLyraContract from './getLyraContract' 8 | import multicall, { MulticallRequest } from './multicall' 9 | import parseBaseKeyBytes32 from './parseBaseKeyBytes32' 10 | import parseBaseSymbol from './parseBaseSymbol' 11 | 12 | type RequestIsGlobalPaused = MulticallRequest, 'isGlobalPaused'> 13 | type RequestGlobalOwner = MulticallRequest, 'owner'> 14 | 15 | export default async function fetchAvalonMarketView( 16 | lyra: Lyra, 17 | marketAddressOrName: string 18 | ): Promise<{ 19 | marketView: OptionMarketViewer.MarketViewWithBoardsStructOutput 20 | isGlobalPaused: boolean 21 | owner: string 22 | blockNumber: number 23 | }> { 24 | const viewerContract = getLyraContract(lyra, Version.Avalon, LyraContractId.OptionMarketViewer) 25 | const exchangeContract = getLyraContract(lyra, Version.Avalon, LyraContractId.ExchangeAdapter) 26 | const isGlobalPausedReq: RequestIsGlobalPaused = { 27 | contract: exchangeContract, 28 | function: 'isGlobalPaused', 29 | args: [], 30 | } 31 | const globalOwner: RequestGlobalOwner = { 32 | contract: exchangeContract, 33 | function: 'owner', 34 | args: [], 35 | } 36 | if (isAddress(marketAddressOrName)) { 37 | const { 38 | returnData: [marketView, isGlobalPaused, owner], 39 | blockNumber, 40 | } = await multicall< 41 | [ 42 | MulticallRequest, 'getMarket'>, 43 | RequestIsGlobalPaused, 44 | RequestGlobalOwner 45 | ] 46 | >(lyra, [ 47 | { 48 | contract: viewerContract, 49 | function: 'getMarket', 50 | args: [marketAddressOrName], 51 | }, 52 | isGlobalPausedReq, 53 | globalOwner, 54 | ]) 55 | return { marketView, isGlobalPaused, owner, blockNumber } 56 | } else { 57 | const baseSymbol = parseBaseSymbol(lyra, marketAddressOrName) 58 | const { 59 | returnData: [marketView, isGlobalPaused, owner], 60 | blockNumber, 61 | } = await multicall< 62 | [ 63 | MulticallRequest, 'getMarketForBaseKey'>, 64 | RequestIsGlobalPaused, 65 | RequestGlobalOwner 66 | ] 67 | >(lyra, [ 68 | { 69 | contract: viewerContract, 70 | function: 'getMarketForBaseKey', 71 | args: [parseBaseKeyBytes32(baseSymbol)], 72 | }, 73 | isGlobalPausedReq, 74 | globalOwner, 75 | ]) 76 | return { marketView, isGlobalPaused, owner, blockNumber } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/utils/fetchClaimAddedEvents.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/core' 2 | import { BigNumber } from 'ethers' 3 | 4 | import { ClaimAddedEvent } from '../account_reward_epoch' 5 | import { CLAIM_ADDED_FRAGMENT, ClaimAddedQueryResult } from '../constants/queries' 6 | import Lyra from '../lyra' 7 | import subgraphRequest from './subgraphRequest' 8 | 9 | const claimAddedQuery = gql` 10 | query claimAddeds($user: String!) { 11 | claimAddeds(where: { 12 | claimer: $user 13 | }) { 14 | ${CLAIM_ADDED_FRAGMENT} 15 | } 16 | } 17 | ` 18 | 19 | type ClaimAddedVariables = { 20 | user: string 21 | } 22 | 23 | export default async function fetchClaimAddedEvents(lyra: Lyra, address: string): Promise { 24 | const { data } = await subgraphRequest<{ claimAddeds: ClaimAddedQueryResult[] }, ClaimAddedVariables>( 25 | lyra.govSubgraphClient, 26 | { 27 | query: claimAddedQuery, 28 | variables: { 29 | user: address.toLowerCase(), 30 | }, 31 | } 32 | ) 33 | return ( 34 | data?.claimAddeds 35 | .map(ev => ({ 36 | amount: BigNumber.from(ev.amount), 37 | blockNumber: ev.blockNumber, 38 | claimer: ev.claimer, 39 | epochTimestamp: parseInt(ev.epochTimestamp), 40 | rewardToken: ev.rewardToken, 41 | timestamp: ev.timestamp, 42 | tag: ev.tag, 43 | })) 44 | // HACK @michaelxuwu - Filter claimAdded mistake 45 | .filter( 46 | event => event.rewardToken.toLowerCase() !== '0xCb9f85730f57732fc899fb158164b9Ed60c77D49'.toLowerCase() 47 | ) ?? [] 48 | ) 49 | } 50 | -------------------------------------------------------------------------------- /src/utils/fetchClaimEvents.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/core' 2 | import { BigNumber } from 'ethers' 3 | 4 | import { ClaimEvent } from '../account_reward_epoch' 5 | import { CLAIM_FRAGMENT, ClaimAddedQueryResult } from '../constants/queries' 6 | import Lyra from '../lyra' 7 | import subgraphRequest from './subgraphRequest' 8 | 9 | const claimQuery = gql` 10 | query claims($user: String!) { 11 | claims(where: { 12 | claimer: $user 13 | }) { 14 | ${CLAIM_FRAGMENT} 15 | } 16 | } 17 | ` 18 | 19 | type ClaimAddedVariables = { 20 | user: string 21 | } 22 | 23 | export default async function fetchClaimEvents(lyra: Lyra, address: string): Promise { 24 | const { data } = await subgraphRequest<{ claims: ClaimAddedQueryResult[] }, ClaimAddedVariables>( 25 | lyra.govSubgraphClient, 26 | { 27 | query: claimQuery, 28 | variables: { 29 | user: address.toLowerCase(), 30 | }, 31 | } 32 | ) 33 | return ( 34 | data?.claims.map(ev => ({ 35 | amount: BigNumber.from(ev.amount), 36 | blockNumber: ev.blockNumber, 37 | claimer: ev.claimer, 38 | rewardToken: ev.rewardToken, 39 | timestamp: ev.timestamp, 40 | })) ?? [] 41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /src/utils/fetchGlobalOwner.ts: -------------------------------------------------------------------------------- 1 | import { LyraContractId } from '../constants/contracts' 2 | import Lyra from '../lyra' 3 | import getLyraContract from './getLyraContract' 4 | 5 | export default async function fetchGlobalOwner(lyra: Lyra): Promise { 6 | const exchangeAdapter = getLyraContract(lyra, lyra.version, LyraContractId.ExchangeAdapter) 7 | return await exchangeAdapter.owner() 8 | } 9 | -------------------------------------------------------------------------------- /src/utils/fetchGlobalRewardEpochData.ts: -------------------------------------------------------------------------------- 1 | import { 2 | RewardEpochToken, 3 | RewardEpochTokenAmount, 4 | RewardEpochTokenConfig, 5 | RewardEpochTokenPrice, 6 | } from '../global_reward_epoch' 7 | import Lyra, { Deployment } from '../lyra' 8 | import fetchWithCache from './fetchWithCache' 9 | 10 | export type GlobalRewardEpochData = { 11 | deployment: Deployment // indexed 12 | startTimestamp: number // indexed 13 | startEarningTimestamp?: number 14 | endTimestamp: number 15 | distributionTimestamp: number 16 | isDepositPeriod?: boolean 17 | lastUpdated: number 18 | totalStkLyraDays: number 19 | scaledStkLyraDays: { 20 | [market: string]: number 21 | } 22 | totalLpTokenDays: { 23 | [market: string]: number 24 | } 25 | totalBoostedLpTokenDays: { 26 | [market: string]: number 27 | } 28 | globalStakingRewards: RewardEpochTokenAmount[] 29 | globalMMVRewards: { 30 | [market: string]: RewardEpochTokenAmount[] 31 | } 32 | globalTradingRewards: GlobalTradingRewards 33 | tradingRewardConfig: GlobalTradingRewardsConfig 34 | MMVConfig: GlobalMMVConfig 35 | tokenPrices?: RewardEpochTokenPrice[] 36 | } 37 | 38 | export type GlobalTradingRewards = { 39 | totalRewards?: RewardEpochTokenAmount[] 40 | totalFees: number 41 | totalTradingRebateRewards: RewardEpochTokenAmount[] 42 | totalShortCollateralRewards: RewardEpochTokenAmount[] 43 | totalShortCallSeconds: number 44 | totalShortPutSeconds: number 45 | scaleFactors: RewardEpochTokenAmount[] 46 | } 47 | 48 | export type GlobalTradingRewardsConfig = { 49 | useRebateTable: boolean 50 | rebateRateTable: { cutoff: number; returnRate: number }[] 51 | boostRateTable: { 52 | stakingCutoff: number 53 | tradingCutoff: number 54 | isReferred: boolean 55 | label: string 56 | boostRate: number 57 | }[] 58 | maxRebatePercentage: number 59 | netVerticalStretch: number // param a // netVerticalStretch 60 | verticalShift: number // param b // verticalShift 61 | vertIntercept: number // param c // minReward // vertIntercept 62 | stretchiness: number // param d // stretchiness 63 | tokens: GlobalTradingRewardsRewardEpochTokenConfig[] 64 | referredTradersTokens?: RewardEpochToken[] 65 | } 66 | 67 | export type GlobalMMVConfig = { 68 | [market: string]: { 69 | tokens: RewardEpochTokenConfig[] 70 | x: number 71 | totalStkScaleFactor: number 72 | ignoreList: string[] 73 | } 74 | } 75 | 76 | type GlobalTradingRewardsRewardEpochTokenConfig = RewardEpochToken & { 77 | cap: number 78 | floorTokenPrice: number 79 | fixedPrice: number 80 | portion: number 81 | } 82 | 83 | const EMPTY: GlobalRewardEpochData[] = [] 84 | 85 | export default async function fetchGlobalRewardEpochData(lyra: Lyra): Promise { 86 | if (lyra.deployment !== Deployment.Mainnet) { 87 | return EMPTY 88 | } 89 | return fetchWithCache(`${lyra.apiUri}/rewards/global?network=${lyra.network}&version=${lyra.version}`) 90 | } 91 | -------------------------------------------------------------------------------- /src/utils/fetchLatestNetGreeks.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/core' 2 | import { BigNumber } from '@ethersproject/bignumber' 3 | 4 | import Lyra from '..' 5 | import { ZERO_BN } from '../constants/bn' 6 | import { MARKET_GREEKS_SNAPSHOT_FRAGMENT, MarketGreeksSnapshotQueryResult, SnapshotPeriod } from '../constants/queries' 7 | import { Market, MarketNetGreeksSnapshot } from '../market' 8 | import subgraphRequest from './subgraphRequest' 9 | 10 | const marketGreeksSnapshotsQuery = gql` 11 | query marketGreeksSnapshots($market: String!) { 12 | marketGreeksSnapshots(first: 1, orderBy: timestamp, orderDirection: desc, where: 13 | { 14 | market: $market, 15 | period: ${SnapshotPeriod.OneHour} 16 | }) { 17 | ${MARKET_GREEKS_SNAPSHOT_FRAGMENT} 18 | } 19 | } 20 | ` 21 | 22 | const EMPTY: Omit = { 23 | poolNetDelta: ZERO_BN, 24 | hedgerNetDelta: ZERO_BN, 25 | netDelta: ZERO_BN, 26 | netStdVega: ZERO_BN, 27 | } 28 | 29 | export default async function fetchLatestNetGreeks(lyra: Lyra, market: Market): Promise { 30 | const { data } = await subgraphRequest< 31 | { marketGreeksSnapshots: MarketGreeksSnapshotQueryResult[] }, 32 | { market: string } 33 | >(lyra.subgraphClient, { 34 | query: marketGreeksSnapshotsQuery, 35 | variables: { 36 | market: market.address.toLowerCase(), 37 | }, 38 | }) 39 | 40 | if (!data || data.marketGreeksSnapshots.length === 0) { 41 | return { ...EMPTY, timestamp: market.block.timestamp } 42 | } 43 | 44 | const marketGreeksSnapshot = data.marketGreeksSnapshots[0] 45 | const poolNetDelta = BigNumber.from(marketGreeksSnapshot.poolNetDelta) 46 | const hedgerNetDelta = BigNumber.from(marketGreeksSnapshot.hedgerNetDelta) 47 | const netDelta = BigNumber.from(marketGreeksSnapshot.netDelta) 48 | const netStdVega = BigNumber.from(marketGreeksSnapshot.netStdVega) 49 | 50 | return { 51 | poolNetDelta, 52 | hedgerNetDelta, 53 | netDelta, 54 | netStdVega, 55 | timestamp: marketGreeksSnapshot.timestamp, 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/utils/fetchLyraPrice.ts: -------------------------------------------------------------------------------- 1 | import Lyra from '../lyra' 2 | import fetchWithCache from './fetchWithCache' 3 | 4 | export default async function fetchLyraPrice(lyra: Lyra): Promise { 5 | const res = await fetchWithCache<{ spotPrice: number }>(`${lyra.apiUri}/lyra-price`) 6 | return res.spotPrice 7 | } 8 | -------------------------------------------------------------------------------- /src/utils/fetchMarketAddresses.ts: -------------------------------------------------------------------------------- 1 | import { LyraContractId } from '../constants/contracts' 2 | import Lyra from '../lyra' 3 | import { MarketContractAddresses } from '../market' 4 | import getLyraContract from './getLyraContract' 5 | 6 | export default async function fetchMarketAddresses(lyra: Lyra): Promise { 7 | const viewer = await getLyraContract(lyra, lyra.version, LyraContractId.OptionMarketViewer) 8 | return await viewer.getMarketAddresses() 9 | } 10 | -------------------------------------------------------------------------------- /src/utils/fetchMarketOwner.ts: -------------------------------------------------------------------------------- 1 | import { LyraMarketContractId } from '../constants/contracts' 2 | import Lyra from '../lyra' 3 | import { MarketContractAddresses } from '../market' 4 | import getLyraMarketContract from './getLyraMarketContract' 5 | 6 | export default async function fetchMarketOwner( 7 | lyra: Lyra, 8 | marketContractAddresses: MarketContractAddresses 9 | ): Promise { 10 | const optionMarket = getLyraMarketContract( 11 | lyra, 12 | marketContractAddresses, 13 | lyra.version, 14 | LyraMarketContractId.OptionMarket 15 | ) 16 | return await optionMarket.owner() 17 | } 18 | -------------------------------------------------------------------------------- /src/utils/fetchNetGreeksHistory.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/core' 2 | import { BigNumber } from '@ethersproject/bignumber' 3 | 4 | import Lyra from '..' 5 | import { ZERO_BN } from '../constants/bn' 6 | import { 7 | MARKET_GREEKS_SNAPSHOT_FRAGMENT, 8 | MarketGreeksSnapshotQueryResult, 9 | SNAPSHOT_RESULT_LIMIT, 10 | } from '../constants/queries' 11 | import { SnapshotOptions } from '../constants/snapshots' 12 | import { Market, MarketNetGreeksSnapshot } from '../market' 13 | import fetchSnapshots from './fetchSnapshots' 14 | 15 | const marketGreeksSnapshotsQuery = gql` 16 | query marketGreeksSnapshots( 17 | $market: String!, $min: Int!, $max: Int! $period: Int!, 18 | ) { 19 | marketGreeksSnapshots(first: ${SNAPSHOT_RESULT_LIMIT}, orderBy: timestamp, orderDirection: asc, where: { market: $market, 20 | timestamp_gte: $min, 21 | timestamp_lte: $max, 22 | period_gte: $period 23 | }) { 24 | ${MARKET_GREEKS_SNAPSHOT_FRAGMENT} 25 | } 26 | } 27 | ` 28 | 29 | const EMPTY: Omit = { 30 | poolNetDelta: ZERO_BN, 31 | hedgerNetDelta: ZERO_BN, 32 | netDelta: ZERO_BN, 33 | netStdVega: ZERO_BN, 34 | } 35 | 36 | export default async function fetchNetGreeksHistory( 37 | lyra: Lyra, 38 | market: Market, 39 | options?: SnapshotOptions 40 | ): Promise { 41 | const data = await fetchSnapshots( 42 | lyra, 43 | marketGreeksSnapshotsQuery, 44 | { 45 | market: market.address.toLowerCase(), 46 | }, 47 | { 48 | ...options, 49 | endTimestamp: options?.endTimestamp ?? market.block.timestamp, 50 | } 51 | ) 52 | 53 | if (data.length === 0) { 54 | // Always return at least 1 snapshot 55 | return [{ ...EMPTY, timestamp: market.block.timestamp }] 56 | } 57 | 58 | return data.map(marketGreeksSnapshot => { 59 | const poolNetDelta = BigNumber.from(marketGreeksSnapshot.poolNetDelta) 60 | const hedgerNetDelta = BigNumber.from(marketGreeksSnapshot.hedgerNetDelta) 61 | const netDelta = BigNumber.from(marketGreeksSnapshot.netDelta) 62 | const netStdVega = BigNumber.from(marketGreeksSnapshot.netStdVega) 63 | return { 64 | poolNetDelta, 65 | hedgerNetDelta, 66 | netDelta, 67 | netStdVega, 68 | timestamp: marketGreeksSnapshot.timestamp, 69 | } 70 | }) 71 | } 72 | -------------------------------------------------------------------------------- /src/utils/fetchOpenPositionDataByOwner.ts: -------------------------------------------------------------------------------- 1 | import { LyraContractId } from '../constants/contracts' 2 | import Lyra from '../lyra' 3 | import { Market } from '../market' 4 | import { PositionData } from '../position' 5 | import fetchPositionEventDataByIDs from './fetchPositionEventDataByIDs' 6 | import getIsCall from './getIsCall' 7 | import getLyraContract from './getLyraContract' 8 | import getOpenPositionDataFromStruct from './getOpenPositionDataFromStruct' 9 | 10 | export default async function fetchOpenPositionDataByOwner(lyra: Lyra, owner: string): Promise { 11 | // Fetch all owner positions across all markets 12 | const positionsByMarketAddress = await getLyraContract( 13 | lyra, 14 | lyra.version, 15 | LyraContractId.OptionMarketViewer 16 | ).getOwnerPositions(owner) 17 | 18 | const positionIds = positionsByMarketAddress.flatMap(({ market, positions }) => 19 | positions.map(position => ({ positionId: position.positionId.toNumber(), marketAddress: market })) 20 | ) 21 | 22 | const [positionEventsDict, markets] = await Promise.all([ 23 | fetchPositionEventDataByIDs(lyra, positionIds), 24 | lyra.markets(), 25 | ]) 26 | 27 | const marketsByAddress: Record = markets.reduce( 28 | (dict, market) => ({ ...dict, [market.address]: market }), 29 | {} as Record 30 | ) 31 | 32 | return positionsByMarketAddress.flatMap(({ positions: openPositionStructs, market: marketAddress }) => { 33 | return openPositionStructs.map(openPositionStruct => { 34 | const positionId = openPositionStruct.positionId.toNumber() 35 | 36 | const strikeId = openPositionStruct.strikeId.toNumber() 37 | const isCall = getIsCall(openPositionStruct.optionType) 38 | const { trades, collateralUpdates, transfers, settle } = positionEventsDict[marketAddress][positionId] 39 | const option = marketsByAddress[marketAddress].liveOption(strikeId, isCall) 40 | return getOpenPositionDataFromStruct( 41 | owner, 42 | openPositionStruct, 43 | option, 44 | trades, 45 | collateralUpdates, 46 | transfers, 47 | settle 48 | ) 49 | }) 50 | }) 51 | } 52 | -------------------------------------------------------------------------------- /src/utils/fetchOptionPriceHistory.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/core' 2 | import { BigNumber } from '@ethersproject/bignumber' 3 | 4 | import Lyra from '..' 5 | import { 6 | OPTION_PRICE_AND_GREEKS_SNAPSHOT_FRAGMENT, 7 | OptionPriceAndGreeksSnapshotQueryResult, 8 | } from '../constants/queries' 9 | import { SnapshotOptions } from '../constants/snapshots' 10 | import { Option, OptionPriceSnapshot } from '../option' 11 | import fetchSnapshots from './fetchSnapshots' 12 | 13 | const optionPriceAndGreeksSnapshotsQuery = gql` 14 | query optionPriceAndGreeksSnapshots($optionId: String!, $min: Int!, $max: Int!, $period: Int!) { 15 | optionPriceAndGreeksSnapshots( 16 | first: 1000 17 | orderBy: timestamp 18 | orderDirection: asc 19 | where: { option: $optionId, timestamp_gte: $min, timestamp_lte: $max, period_gte: $period } 20 | ) { 21 | ${OPTION_PRICE_AND_GREEKS_SNAPSHOT_FRAGMENT} 22 | } 23 | } 24 | ` 25 | 26 | type OptionPriceAndGreeksSnapshotVariables = { 27 | optionId: string 28 | } 29 | 30 | export default async function fetchOptionPriceHistory( 31 | lyra: Lyra, 32 | option: Option, 33 | options?: SnapshotOptions 34 | ): Promise { 35 | const board = option.board() 36 | const blockTimestamp = option.block.timestamp 37 | const endTimestamp = Math.min(board.expiryTimestamp, options?.endTimestamp ?? blockTimestamp) 38 | 39 | const data = await fetchSnapshots( 40 | lyra, 41 | optionPriceAndGreeksSnapshotsQuery, 42 | { 43 | optionId: `${option.market().address.toLowerCase()}-${option.strike().id}-${option.isCall ? 'call' : 'put'}`, 44 | }, 45 | { 46 | ...options, 47 | endTimestamp, 48 | } 49 | ) 50 | const subgraphSnapshots: OptionPriceSnapshot[] = data.map((snapshot: OptionPriceAndGreeksSnapshotQueryResult) => ({ 51 | optionPrice: BigNumber.from(snapshot.optionPrice), 52 | blockNumber: snapshot.blockNumber, 53 | timestamp: snapshot.timestamp, 54 | })) 55 | 56 | const currSnapshot: OptionPriceSnapshot = { 57 | optionPrice: option.price, 58 | blockNumber: option.block.number, 59 | timestamp: endTimestamp, 60 | } 61 | 62 | return [...subgraphSnapshots, currSnapshot].filter(s => s.optionPrice.gt(0)) 63 | } 64 | -------------------------------------------------------------------------------- /src/utils/fetchOptionVolumeHistory.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/core' 2 | import { BigNumber } from '@ethersproject/bignumber' 3 | 4 | import Lyra from '..' 5 | import { 6 | MAX_END_TIMESTAMP, 7 | OPTION_VOLUME_FRAGMENT, 8 | OptionVolumeQueryResult, 9 | SnapshotPeriod, 10 | } from '../constants/queries' 11 | import { SnapshotOptions } from '../constants/snapshots' 12 | import { Option, OptionTradingVolumeSnapshot } from '../option' 13 | import fetchSnapshots from './fetchSnapshots' 14 | 15 | const optionVolumeQuery = gql` 16 | query optionVolumeQuery($optionId: String!, $min: Int!, $max: Int!, $period: Int!) { 17 | optionVolumeSnapshots( 18 | first: 1000 19 | orderBy: timestamp 20 | orderDirection: asc 21 | where: { option: $optionId, timestamp_gte: $min, timestamp_lte: $max, period: $period } 22 | ) { 23 | ${OPTION_VOLUME_FRAGMENT} 24 | } 25 | } 26 | ` 27 | 28 | export default async function fetchOptionVolumeHistory( 29 | lyra: Lyra, 30 | option: Option, 31 | options?: SnapshotOptions 32 | ): Promise { 33 | const board = option.board() 34 | const endTimestamp = Math.min(board.expiryTimestamp, options?.endTimestamp ?? MAX_END_TIMESTAMP) 35 | const optionId = `${option.market().address.toLowerCase()}-${option.strike().id}-${option.isCall ? 'call' : 'put'}` 36 | const data = await fetchSnapshots< 37 | OptionVolumeQueryResult, 38 | { 39 | optionId: string 40 | } 41 | >( 42 | lyra, 43 | optionVolumeQuery, 44 | { 45 | optionId, 46 | }, 47 | { 48 | ...options, 49 | period: SnapshotPeriod.OneHour, 50 | endTimestamp, 51 | } 52 | ) 53 | return data.map(snapshot => ({ 54 | notionalVolume: BigNumber.from(snapshot.notionalVolume), 55 | premiumVolume: BigNumber.from(snapshot.premiumVolume), 56 | timestamp: snapshot.timestamp, 57 | })) 58 | } 59 | -------------------------------------------------------------------------------- /src/utils/fetchPositionDataByOwner.ts: -------------------------------------------------------------------------------- 1 | import Lyra from '../lyra' 2 | import { PositionData } from '../position' 3 | import fetchAllPositionDataByOwner from './fetchAllPositionDataByOwner' 4 | import fetchOpenPositionDataByOwner from './fetchOpenPositionDataByOwner' 5 | import getUniqueBy from './getUniqueBy' 6 | 7 | export default async function fetchPositionDataByOwner(lyra: Lyra, owner: string): Promise { 8 | const [openPositions, allPositions] = await Promise.all([ 9 | // Contract (realtime) data 10 | fetchOpenPositionDataByOwner(lyra, owner), 11 | // Subgraph data 12 | fetchAllPositionDataByOwner(lyra, owner), 13 | ]) 14 | 15 | const positions = openPositions.concat(allPositions) 16 | // Prefer position struct data over position subgrpah data 17 | return getUniqueBy(positions, p => p.id) 18 | } 19 | -------------------------------------------------------------------------------- /src/utils/fetchPositionPriceHistoryByIDs.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/core' 2 | import { BigNumber } from '@ethersproject/bignumber' 3 | 4 | import Lyra from '..' 5 | import { 6 | OPTION_PRICE_AND_GREEKS_SNAPSHOT_FRAGMENT, 7 | OptionPriceAndGreeksSnapshotQueryResult, 8 | } from '../constants/queries' 9 | import { SnapshotOptions } from '../constants/snapshots' 10 | import { OptionPriceSnapshot } from '../option' 11 | import { Position } from '../position' 12 | import fetchSnapshots from './fetchSnapshots' 13 | 14 | const optionPriceAndGreeksSnapshotsQuery = gql` 15 | query optionPriceAndGreeksSnapshots($optionIds: [String!]!, $min: Int!, $max: Int!, $period: Int!) { 16 | optionPriceAndGreeksSnapshots( 17 | first: 1000 18 | orderBy: timestamp 19 | orderDirection: asc 20 | where: { option_in: $optionIds, timestamp_gte: $min, timestamp_lte: $max, period_gte: $period } 21 | ) { 22 | ${OPTION_PRICE_AND_GREEKS_SNAPSHOT_FRAGMENT} 23 | } 24 | } 25 | ` 26 | 27 | export default async function fetchPositionPriceHistoryByIDs( 28 | lyra: Lyra, 29 | positions: Position[], 30 | snapshotOptions?: SnapshotOptions 31 | ): Promise> { 32 | const optionIdByPositionId: Record = positions.reduce( 33 | (dict, { id, marketAddress, strikeId, isCall }) => ({ 34 | ...dict, 35 | [id]: `${marketAddress.toLowerCase()}-${strikeId}-${isCall ? 'call' : 'put'}`, 36 | }), 37 | {} 38 | ) 39 | const optionIds = Array.from(new Set(Object.values(optionIdByPositionId))) 40 | const data = await fetchSnapshots( 41 | lyra, 42 | optionPriceAndGreeksSnapshotsQuery, 43 | { 44 | optionIds, 45 | }, 46 | snapshotOptions 47 | ) 48 | const pricesByOptionId: Record = data.reduce((dict, snapshot) => { 49 | const prices = dict[snapshot.option.id] ?? [] 50 | prices.push({ 51 | optionPrice: BigNumber.from(snapshot.optionPrice), 52 | timestamp: snapshot.blockTimestamp, // Use last updated timestamp 53 | blockNumber: snapshot.blockNumber, 54 | }) 55 | return { 56 | ...dict, 57 | [snapshot.option.id]: prices, 58 | } 59 | }, {} as Record) 60 | const pricesByPositionId: Record = Object.entries(optionIdByPositionId).reduce( 61 | (dict, [positionId, optionId]) => ({ 62 | ...dict, 63 | [positionId]: pricesByOptionId[optionId], 64 | }), 65 | {} 66 | ) 67 | return pricesByPositionId 68 | } 69 | -------------------------------------------------------------------------------- /src/utils/fetchSnapshots.ts: -------------------------------------------------------------------------------- 1 | import { DocumentNode } from '@apollo/client/core' 2 | 3 | import Lyra from '..' 4 | import { MAX_END_TIMESTAMP, MIN_START_TIMESTAMP, SnapshotPeriod } from '../constants/queries' 5 | import { SnapshotOptions } from '../constants/snapshots' 6 | import getSnapshotPeriod from './getSnapshotPeriod' 7 | import subgraphRequestWithLoop from './subgraphRequestWithLoop' 8 | 9 | export default async function fetchSnapshots< 10 | Snapshot extends Record, 11 | Variables extends Record 12 | >(lyra: Lyra, query: DocumentNode, variables: Variables, options?: SnapshotOptions): Promise { 13 | const min = options?.startTimestamp ?? MIN_START_TIMESTAMP 14 | const max = options?.endTimestamp ?? MAX_END_TIMESTAMP 15 | // Use 1h, 1d periods common to all snapshots 16 | const period = options?.period ?? getSnapshotPeriod(min, max, [SnapshotPeriod.OneHour, SnapshotPeriod.OneDay]) 17 | return subgraphRequestWithLoop(lyra, query, { ...variables, min, max, period }, 'timestamp') 18 | } 19 | -------------------------------------------------------------------------------- /src/utils/fetchStrikeIVHistory.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/core' 2 | import { BigNumber } from '@ethersproject/bignumber' 3 | 4 | import Lyra, { Strike } from '..' 5 | import { STRIKE_IV_AND_GREEKS_SNAPSHOT_FRAGMENT, StrikeIVAndGreeksSnapshotQueryResult } from '../constants/queries' 6 | import { SnapshotOptions } from '../constants/snapshots' 7 | import { StrikeIVHistory } from '../strike' 8 | import fetchSnapshots from './fetchSnapshots' 9 | import fromBigNumber from './fromBigNumber' 10 | import groupTimeSnapshots from './groupTimeSnapshots' 11 | 12 | const strikeIVAndGreeksSnapshotsQuery = gql` 13 | query strikeIVAndGreeksSnapshots($strikeId: String!, $min: Int!, $max: Int!, $period: Int!) { 14 | strikeIVAndGreeksSnapshots( 15 | first: 1000 16 | orderBy: timestamp 17 | orderDirection: asc 18 | where: { strike: $strikeId, timestamp_gte: $min, timestamp_lte: $max, period_gte: $period } 19 | ) { 20 | ${STRIKE_IV_AND_GREEKS_SNAPSHOT_FRAGMENT} 21 | } 22 | } 23 | ` 24 | 25 | export default async function fetchStrikeIVHistory( 26 | lyra: Lyra, 27 | strike: Strike, 28 | options?: SnapshotOptions 29 | ): Promise { 30 | const board = strike.board() 31 | const blockTimestamp = strike.block.timestamp 32 | const endTimestamp = Math.min(board.expiryTimestamp, options?.endTimestamp ?? blockTimestamp) 33 | const strikeId = `${strike.market().address.toLowerCase()}-${strike.id}` 34 | const data = await fetchSnapshots< 35 | StrikeIVAndGreeksSnapshotQueryResult, 36 | { 37 | strikeId: string 38 | } 39 | >( 40 | lyra, 41 | strikeIVAndGreeksSnapshotsQuery, 42 | { 43 | strikeId: strikeId, 44 | }, 45 | { 46 | ...options, 47 | endTimestamp, 48 | } 49 | ) 50 | 51 | const snapshots: StrikeIVHistory[] = groupTimeSnapshots( 52 | data.map(snapshot => ({ 53 | iv: fromBigNumber(BigNumber.from(snapshot.iv)), 54 | timestamp: snapshot.timestamp, 55 | })), 56 | data[0].timestamp, 57 | endTimestamp 58 | ) 59 | 60 | const currSnapshot: StrikeIVHistory = { iv: fromBigNumber(strike.iv), timestamp: strike.block.timestamp } 61 | 62 | return [...snapshots, currSnapshot].filter(s => s.iv > 0) 63 | } 64 | -------------------------------------------------------------------------------- /src/utils/fetchTradeListener.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | import { Contract } from '@ethersproject/contracts' 3 | 4 | import Lyra, { 5 | LyraMarketContractId, 6 | TradeEvent, 7 | TradeEventListener, 8 | TradeEventListenerCallback, 9 | TradeEventListenerOptions, 10 | } from '..' 11 | import { ZERO_ADDRESS } from '../constants/bn' 12 | import { PartialTradeEvent } from '../constants/events' 13 | import fetchMarketAddresses from './fetchMarketAddresses' 14 | import { getMarketContractABI } from './getLyraMarketContract' 15 | 16 | const DEFAULT_POLL_INTERVAL = 10 * 1000 17 | 18 | export default function fetchTradeListener( 19 | lyra: Lyra, 20 | callback: TradeEventListenerCallback, 21 | options?: TradeEventListenerOptions 22 | ): TradeEventListener { 23 | const ms = options?.pollInterval ?? DEFAULT_POLL_INTERVAL 24 | const startBlockTag = options?.startBlockNumber ?? 'latest' 25 | 26 | let timeout: NodeJS.Timeout | null 27 | 28 | const optionMarket = new Contract( 29 | ZERO_ADDRESS, 30 | getMarketContractABI(lyra.version, LyraMarketContractId.OptionMarket, lyra.network) 31 | ) 32 | 33 | Promise.all([fetchMarketAddresses(lyra), lyra.provider.getBlock(startBlockTag)]).then(async ([addresses, block]) => { 34 | console.debug(`Polling from block ${block.number} every ${ms}ms`) 35 | let prevBlock = block 36 | 37 | const poll = async () => { 38 | const latestBlock = await lyra.provider.getBlock('latest') 39 | const fromBlockNumber = prevBlock.number + 1 40 | const toBlockNumber = latestBlock.number 41 | if (fromBlockNumber >= toBlockNumber) { 42 | // Skip if no new blocks 43 | setTimeout(poll, ms) 44 | return 45 | } 46 | console.debug( 47 | `Querying block range: ${fromBlockNumber} to ${toBlockNumber} (${toBlockNumber - fromBlockNumber} blocks)` 48 | ) 49 | // Fetch new trades 50 | const trades: PartialTradeEvent[] = await lyra.provider.send('eth_getLogs', [ 51 | { 52 | address: addresses.map(a => a.optionMarket), 53 | fromBlock: BigNumber.from(fromBlockNumber).toHexString(), 54 | toBlock: BigNumber.from(toBlockNumber).toHexString(), 55 | topics: [[(optionMarket.filters.Trade().topics ?? [])[0]]], 56 | }, 57 | ]) 58 | if (trades.length > 0) { 59 | console.debug(`Found ${trades.length} new trades`) 60 | } 61 | // Parse trade events 62 | await Promise.all( 63 | trades.map(async trade => { 64 | const tradeEvents = await TradeEvent.getByHash(lyra, trade.transactionHash) 65 | tradeEvents.map(tradeEvent => callback(tradeEvent)) 66 | }) 67 | ) 68 | 69 | // Poll 70 | prevBlock = latestBlock 71 | setTimeout(poll, ms) 72 | } 73 | 74 | timeout = setTimeout(poll, ms) 75 | }) 76 | 77 | return { 78 | off: () => { 79 | if (timeout) { 80 | clearTimeout(timeout) 81 | } 82 | }, 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/utils/fetchWithCache.ts: -------------------------------------------------------------------------------- 1 | import fetch from 'cross-fetch' 2 | 3 | const CACHE: Record }> = {} 4 | const CACHE_TIMEOUT = 10 * 1000 5 | 6 | async function fetcher(url: string): Promise { 7 | const data = await fetch(url) 8 | return await data.json() 9 | } 10 | 11 | export default async function fetchWithCache(url: string): Promise { 12 | const now = Date.now() 13 | if (!CACHE[url] || now > CACHE[url].lastUpdated + CACHE_TIMEOUT) { 14 | CACHE[url] = { 15 | fetch: fetcher(url), 16 | lastUpdated: now, 17 | } 18 | } 19 | return CACHE[url].fetch 20 | } 21 | -------------------------------------------------------------------------------- /src/utils/filterNulls.ts: -------------------------------------------------------------------------------- 1 | export default function filterNulls(array: (TValue | null | undefined)[]): TValue[] { 2 | return array.filter((val: TValue | null | undefined) => val !== null && val !== undefined) as TValue[] 3 | } 4 | -------------------------------------------------------------------------------- /src/utils/findMarket.ts: -------------------------------------------------------------------------------- 1 | import { Market } from '../market' 2 | import isMarketEqual from './isMarketEqual' 3 | 4 | export default function findMarket(markets: Market[], marketAddressOrName: string): Market | null { 5 | const market = Object.values(markets).find(market => isMarketEqual(market, marketAddressOrName)) 6 | return market ?? null 7 | } 8 | -------------------------------------------------------------------------------- /src/utils/findMarketX.ts: -------------------------------------------------------------------------------- 1 | import { Market } from '../market' 2 | import findMarket from './findMarket' 3 | 4 | export default function findMarketX(markets: Market[], marketAddressOrName: string): Market { 5 | const market = findMarket(markets, marketAddressOrName) 6 | if (!market) { 7 | throw new Error('Failed to find market') 8 | } 9 | return market 10 | } 11 | -------------------------------------------------------------------------------- /src/utils/fromBigNumber.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | import { formatUnits } from '@ethersproject/units' 3 | 4 | export default function fromBigNumber(number: BigNumber, decimals: number = 18): number { 5 | return parseFloat(formatUnits(number.toString(), decimals)) 6 | } 7 | -------------------------------------------------------------------------------- /src/utils/getAverageCollateralSpotPrice.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | import { CollateralUpdateEvent, Position } from '..' 4 | import { ONE_BN, UNIT, ZERO_BN } from '../constants/bn' 5 | import { Trade } from '../trade' 6 | 7 | export default function getAverageCollateralSpotPrice( 8 | position: Position, 9 | collateralUpdates: (CollateralUpdateEvent | Trade)[] 10 | ): BigNumber { 11 | // Skip longs 12 | if (position.isLong || !position.collateral || !collateralUpdates.length) { 13 | return ZERO_BN 14 | } 15 | // Dollar collateral always $1 16 | if (!position.collateral.isBase) { 17 | return ONE_BN 18 | } 19 | 20 | const firstCollateralUpdate = collateralUpdates[0] 21 | 22 | const firstCollateralAmount = 23 | firstCollateralUpdate instanceof CollateralUpdateEvent 24 | ? firstCollateralUpdate.amount 25 | : firstCollateralUpdate.collateral?.amount ?? ZERO_BN 26 | 27 | const firstSpotPrice = 28 | firstCollateralUpdate instanceof CollateralUpdateEvent 29 | ? firstCollateralUpdate.spotPrice 30 | : firstCollateralUpdate.market().spotPrice 31 | 32 | let currCollateralAmount = firstCollateralAmount 33 | let averageSpotPrice = firstSpotPrice 34 | 35 | for (const collateralUpdate of collateralUpdates.slice(1)) { 36 | const prevCollateralAmount = currCollateralAmount 37 | 38 | currCollateralAmount = 39 | collateralUpdate instanceof CollateralUpdateEvent 40 | ? collateralUpdate.amount 41 | : collateralUpdate.collateral?.amount ?? ZERO_BN 42 | 43 | const collateralChange = currCollateralAmount.sub(prevCollateralAmount) 44 | 45 | // Update rolling average if adding collateral 46 | if (collateralChange.gt(0)) { 47 | const prevTotalValue = averageSpotPrice.mul(prevCollateralAmount).div(UNIT) 48 | 49 | const spotPrice = 50 | collateralUpdate instanceof CollateralUpdateEvent 51 | ? collateralUpdate.spotPrice 52 | : collateralUpdate.market().spotPrice 53 | const addedCollateralValue = collateralChange.mul(spotPrice).div(UNIT) 54 | 55 | const newTotalValue = prevTotalValue.add(addedCollateralValue) 56 | 57 | averageSpotPrice = newTotalValue.mul(UNIT).div(currCollateralAmount) 58 | } 59 | } 60 | 61 | return averageSpotPrice 62 | } 63 | -------------------------------------------------------------------------------- /src/utils/getAverageCostPerOption.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | import { UNIT, ZERO_BN } from '../constants/bn' 4 | import { Trade } from '../trade' 5 | import { TradeEvent } from '../trade_event' 6 | 7 | export default function getAverageCostPerOption(trades: (Trade | TradeEvent)[]): BigNumber { 8 | if (trades.length === 0) { 9 | return ZERO_BN 10 | } 11 | 12 | let currOpenSize = trades[0].size 13 | let averageCostPerOption = trades[0].pricePerOption 14 | 15 | for (const trade of trades.slice(1)) { 16 | const prevOpenSize = currOpenSize 17 | const { size, premium, isOpen } = trade 18 | // Add or remove size from position 19 | currOpenSize = isOpen ? currOpenSize.add(size) : currOpenSize.sub(size) 20 | if (isOpen && currOpenSize.gt(0)) { 21 | const prevTotalCost = averageCostPerOption.mul(prevOpenSize).div(UNIT) 22 | const newTotalCost = prevTotalCost.add(premium) 23 | averageCostPerOption = newTotalCost.mul(UNIT).div(currOpenSize) 24 | } 25 | } 26 | 27 | return averageCostPerOption 28 | } 29 | -------------------------------------------------------------------------------- /src/utils/getBoardView.ts: -------------------------------------------------------------------------------- 1 | import { isAddress } from '@ethersproject/address' 2 | 3 | import { LyraContractId } from '../constants/contracts' 4 | import { BoardViewStructOutput } from '../constants/views' 5 | import Lyra, { Version } from '../lyra' 6 | import getLyraContract from './getLyraContract' 7 | import parseBaseKeyBytes32 from './parseBaseKeyBytes32' 8 | import parseBaseSymbol from './parseBaseSymbol' 9 | 10 | export default async function getBoardView( 11 | lyra: Lyra, 12 | marketAddressOrName: string, 13 | boardId: number 14 | ): Promise { 15 | if (isAddress(marketAddressOrName)) { 16 | const viewer = getLyraContract(lyra, lyra.version, LyraContractId.OptionMarketViewer) 17 | return await viewer.getBoard(marketAddressOrName, boardId) 18 | } else { 19 | const baseSymbol = parseBaseSymbol(lyra, marketAddressOrName) 20 | switch (lyra.version) { 21 | case Version.Avalon: 22 | return getLyraContract(lyra, lyra.version, LyraContractId.OptionMarketViewer).getBoardForBaseKey( 23 | parseBaseKeyBytes32(baseSymbol), 24 | boardId 25 | ) 26 | case Version.Newport: 27 | return getLyraContract(lyra, lyra.version, LyraContractId.OptionMarketViewer).getBoardForBase( 28 | baseSymbol, 29 | boardId 30 | ) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/utils/getBoardViewForStrikeId.ts: -------------------------------------------------------------------------------- 1 | import { LyraContractId } from '../constants/contracts' 2 | import { BoardViewStructOutput } from '../constants/views' 3 | import Lyra from '../lyra' 4 | import getLyraContract from './getLyraContract' 5 | 6 | export default async function getBoardViewForStrikeId( 7 | lyra: Lyra, 8 | marketAddressOrName: string, 9 | strikeId: number 10 | ): Promise { 11 | const viewer = getLyraContract(lyra, lyra.version, LyraContractId.OptionMarketViewer) 12 | return await viewer.getBoardForStrikeId(marketAddressOrName, strikeId) 13 | } 14 | -------------------------------------------------------------------------------- /src/utils/getBreakEvenPrice.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | export default function getBreakEvenPrice( 4 | isCall: boolean, 5 | strikePrice: BigNumber, 6 | optionPrice: BigNumber, 7 | isBaseCollateral?: boolean 8 | ): BigNumber { 9 | return isCall && !isBaseCollateral ? strikePrice.add(optionPrice) : strikePrice.sub(optionPrice) 10 | } 11 | -------------------------------------------------------------------------------- /src/utils/getCappedExpectedHedge.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from 'ethers' 2 | 3 | import { PoolHedgerParams } from '..' 4 | import { UNIT } from '../constants/bn' 5 | import { Option } from '../option' 6 | 7 | export default function getCappedExpectedHedge( 8 | option: Option, 9 | size: BigNumber, 10 | netDelta: BigNumber, 11 | poolHedgerParams: PoolHedgerParams, 12 | increasesPoolDelta: boolean 13 | ) { 14 | const hedgeCap = poolHedgerParams.hedgeCap 15 | // netDelta += amount * cached delta * direction 16 | const deltaImpact = size 17 | .mul(option.delta) 18 | .div(UNIT) 19 | .mul(increasesPoolDelta ? 1 : -1) 20 | const expectedHedge = netDelta.add(deltaImpact) 21 | const exceedsCap = expectedHedge.abs().gt(hedgeCap) 22 | const cappedExpectedHedge = exceedsCap ? (expectedHedge.lt(0) ? hedgeCap.mul(-1) : hedgeCap) : expectedHedge 23 | return cappedExpectedHedge 24 | } 25 | -------------------------------------------------------------------------------- /src/utils/getCollateralUpdateDataFromRecentEvent.ts: -------------------------------------------------------------------------------- 1 | import { CollateralUpdateData } from '../collateral_update_event' 2 | import { UNIT } from '../constants/bn' 3 | import { DataSource } from '../constants/contracts' 4 | import { PartialPositionUpdatedEvent, PartialTransferEvent } from '../constants/events' 5 | import { Market } from '../market' 6 | import getIsBaseCollateral from './getIsBaseCollateral' 7 | import getIsCall from './getIsCall' 8 | import getIsLong from './getIsLong' 9 | import getPositionOwner from './getPositionOwner' 10 | 11 | export default function getCollateralUpdateDataFromRecentEvent( 12 | update: PartialPositionUpdatedEvent, 13 | market: Market, 14 | transfers: PartialTransferEvent[] 15 | ): CollateralUpdateData { 16 | const positionId = update.args.positionId.toNumber() 17 | const blockNumber = update.blockNumber 18 | const amount = update.args.position.collateral 19 | const transactionHash = update.transactionHash 20 | const strikeId = update.args.position.strikeId.toNumber() 21 | const isCall = getIsCall(update.args.position.optionType) 22 | const isLong = getIsLong(update.args.position.optionType) 23 | 24 | if (isLong) { 25 | throw new Error('Attempted to create CollateralUpdate for long position') 26 | } 27 | 28 | // Warning: Can throw if option isn't live 29 | const option = market.liveOption(strikeId, isCall) 30 | 31 | const marketName = option.market().name 32 | const strikePrice = option.strike().strikePrice 33 | const marketAddress = option.market().address 34 | const expiryTimestamp = option.board().expiryTimestamp 35 | const isBaseCollateral = getIsBaseCollateral(update.args.position.optionType) 36 | 37 | // Use current spot price as estimate for recent collateral update 38 | const spotPrice = option.market().spotPrice 39 | const value = isBaseCollateral ? amount.mul(spotPrice).div(UNIT) : amount 40 | 41 | const timestamp = update.args.timestamp.toNumber() 42 | const owner = getPositionOwner(transfers, blockNumber) 43 | 44 | return { 45 | owner, 46 | source: DataSource.Log, 47 | timestamp, 48 | positionId, 49 | strikeId, 50 | transactionHash, 51 | marketAddress, 52 | expiryTimestamp, 53 | blockNumber, 54 | amount, 55 | value, 56 | marketName, 57 | strikePrice, 58 | isCall, 59 | isBaseCollateral, 60 | spotPrice, 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/utils/getCollateralUpdateDataFromSubgraph.ts: -------------------------------------------------------------------------------- 1 | import { getAddress } from '@ethersproject/address' 2 | import { BigNumber } from '@ethersproject/bignumber' 3 | 4 | import { CollateralUpdateData } from '../collateral_update_event' 5 | import { UNIT } from '../constants/bn' 6 | import { DataSource } from '../constants/contracts' 7 | import { CollateralUpdateQueryResult } from '../constants/queries' 8 | 9 | export default function getCollateralUpdateDataFromSubgraph(update: CollateralUpdateQueryResult): CollateralUpdateData { 10 | const amount = BigNumber.from(update.amount) 11 | const spotPrice = BigNumber.from(update.spotPrice) 12 | const isBaseCollateral = update.isBaseCollateral 13 | const value = isBaseCollateral ? amount.mul(spotPrice).div(UNIT) : amount 14 | const strikePrice = BigNumber.from(update.strike.strikePrice) 15 | // TODO: @dappbeast Fix strikeId type in subgraph 16 | const strikeId = parseInt(update.strike.strikeId) 17 | // Remove "s" prefix from name 18 | const marketName = update.market.name.substring(1) 19 | return { 20 | owner: getAddress(update.trader), 21 | source: DataSource.Subgraph, 22 | timestamp: update.timestamp, 23 | amount, 24 | value, 25 | positionId: update.position.positionId, 26 | blockNumber: update.blockNumber, 27 | isBaseCollateral, 28 | marketName, 29 | marketAddress: getAddress(update.market.id), 30 | isCall: update.option.isCall, 31 | strikeId, 32 | strikePrice, 33 | spotPrice, 34 | expiryTimestamp: update.board.expiryTimestamp, 35 | transactionHash: update.transactionHash, 36 | swap: update.externalSwapFees 37 | ? { 38 | address: update.externalSwapAddress, 39 | } 40 | : undefined, 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/utils/getCollateralUpdatePnl.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | import { CollateralUpdateEvent } from '../collateral_update_event' 4 | import { UNIT, ZERO_BN } from '../constants/bn' 5 | import { Position } from '../position' 6 | import { Trade } from '../trade' 7 | 8 | export default function getCollateralUpdatePnl( 9 | position: Position, 10 | collateralUpdate: CollateralUpdateEvent | Trade 11 | ): BigNumber { 12 | const changeAmount = 13 | collateralUpdate instanceof CollateralUpdateEvent 14 | ? collateralUpdate.changeAmount(position) 15 | : collateralUpdate.collateralChangeAmount() 16 | 17 | const isBaseCollateral = 18 | collateralUpdate instanceof CollateralUpdateEvent 19 | ? collateralUpdate.isBaseCollateral 20 | : !!collateralUpdate.collateral?.isBase 21 | 22 | if (!isBaseCollateral || changeAmount.gt(0)) { 23 | // No profitability for stable collateral or adding base collateral 24 | return ZERO_BN 25 | } 26 | 27 | // average spot until collateral update 28 | const averageSpotPrice = collateralUpdate.prevAverageCollateralSpotPrice(position) 29 | const spotPrice = position.market().spotPrice 30 | 31 | // Profit is fair value minus average locked spot price 32 | return spotPrice.sub(averageSpotPrice).mul(changeAmount).div(UNIT) 33 | } 34 | -------------------------------------------------------------------------------- /src/utils/getDefaultPeriod.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SECONDS_IN_DAY, 3 | SECONDS_IN_HOUR, 4 | SECONDS_IN_MONTH, 5 | SECONDS_IN_SIX_MONTHS, 6 | SECONDS_IN_WEEK, 7 | } from '../constants/time' 8 | 9 | // generate default period for historical data 10 | export default function getDefaultPeriod(startTimestamp: number, endTimestamp: number): number { 11 | const duration = Math.max(0, endTimestamp - startTimestamp) 12 | if (duration > SECONDS_IN_SIX_MONTHS) { 13 | // 6m+, 1w period, 25+ data points 14 | return SECONDS_IN_WEEK 15 | } else if (duration > SECONDS_IN_MONTH * 3) { 16 | // 3m-6m, 2d period, 30-90 data points 17 | return SECONDS_IN_DAY * 2 18 | } else if (duration > SECONDS_IN_MONTH) { 19 | // 1m-6m, 1d period, 30-90 data points 20 | return SECONDS_IN_DAY 21 | } else if (duration > SECONDS_IN_WEEK * 2) { 22 | // 2w-1m, 12 hr period, 28-56 data points 23 | return SECONDS_IN_HOUR * 12 24 | } else if (duration > SECONDS_IN_WEEK) { 25 | // 1w-2w, 6 hr period, 28-56 data points 26 | return SECONDS_IN_HOUR * 6 27 | } else if (duration > SECONDS_IN_DAY * 4) { 28 | // 4d-1w, 3 hr period, 32-56 data points 29 | return SECONDS_IN_HOUR * 3 30 | } else { 31 | // 0d-3d, 1 hr period, <75 data points 32 | return SECONDS_IN_HOUR 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/utils/getDefaultVersionForChain.ts: -------------------------------------------------------------------------------- 1 | import { Chain, Version } from '..' 2 | 3 | export default function getDefaultVersionForChain(chain: Chain): Version { 4 | switch (chain) { 5 | case Chain.Arbitrum: 6 | case Chain.ArbitrumGoerli: 7 | case Chain.Optimism: 8 | case Chain.OptimismGoerli: 9 | return Version.Newport 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/utils/getERC20Contract.ts: -------------------------------------------------------------------------------- 1 | import { Contract } from '@ethersproject/contracts' 2 | import { JsonRpcProvider } from '@ethersproject/providers' 3 | 4 | import ERC20_ABI from '../contracts/common/abis/ERC20.json' 5 | import { ERC20 } from '../contracts/common/typechain/ERC20' 6 | 7 | export default function getERC20Contract(provider: JsonRpcProvider, address: string): ERC20 { 8 | return new Contract(address, ERC20_ABI, provider) as ERC20 9 | } 10 | -------------------------------------------------------------------------------- /src/utils/getEffectiveLiquidityTokens.ts: -------------------------------------------------------------------------------- 1 | export default function getEffectiveLiquidityTokens( 2 | lpTokens: number, 3 | totalLpTokens: number, 4 | stkLyra: number, 5 | totalStkLyra: number, 6 | x: number 7 | ): number { 8 | return totalStkLyra > 0 9 | ? Math.min(x * lpTokens + (((1 - x) * stkLyra) / totalStkLyra) * totalLpTokens, lpTokens) 10 | : lpTokens 11 | } 12 | 13 | export function getMinimumStakedLyra(totalStkLyra: number, lpTokens: number, totalLpTokens: number): number { 14 | if (totalLpTokens === 0 || lpTokens === 0) { 15 | return 0 16 | } 17 | return totalStkLyra * (lpTokens / totalLpTokens) 18 | } 19 | -------------------------------------------------------------------------------- /src/utils/getGlobalContract.ts: -------------------------------------------------------------------------------- 1 | import { Contract, ContractInterface } from '@ethersproject/contracts' 2 | 3 | import Lyra, { Chain } from '..' 4 | import { LyraGlobalContractId } from '../constants/contracts' 5 | import { LyraGlobalContractMap } from '../constants/mappings' 6 | import MULTICALL_3_ABI from '../contracts/common/abis/Multicall3.json' 7 | import MULTIDISTRIBUTOR_ABI from '../contracts/common/abis/MultiDistributor.json' 8 | import COMMON_ARBITRUM_MAINNET_ADDRESS_MAP from '../contracts/common/addresses/arbitrum.addresses.json' 9 | import COMMON_ARBITRUM_TESTNET_ADDRESS_MAP from '../contracts/common/addresses/arbitrum-goerli.addresses.json' 10 | import COMMON_OPTIMISM_MAINNET_ADDRESS_MAP from '../contracts/common/addresses/optimism.addresses.json' 11 | import COMMON_OPTIMISM_TESTNET_ADDRESS_MAP from '../contracts/common/addresses/optimism-goerli.addresses.json' 12 | 13 | const getGlobalContractAddress = (lyra: Lyra, contractId: LyraGlobalContractId): string | undefined => { 14 | switch (lyra.chain) { 15 | case Chain.Arbitrum: 16 | return (COMMON_ARBITRUM_MAINNET_ADDRESS_MAP as Record)[contractId] 17 | case Chain.ArbitrumGoerli: 18 | return (COMMON_ARBITRUM_TESTNET_ADDRESS_MAP as Record)[contractId] 19 | case Chain.Optimism: 20 | return (COMMON_OPTIMISM_MAINNET_ADDRESS_MAP as Record)[contractId] 21 | case Chain.OptimismGoerli: 22 | return (COMMON_OPTIMISM_TESTNET_ADDRESS_MAP as Record)[contractId] 23 | } 24 | } 25 | 26 | const getGlobalContractABI = (contractId: LyraGlobalContractId): ContractInterface => { 27 | switch (contractId) { 28 | case LyraGlobalContractId.MultiDistributor: 29 | return MULTIDISTRIBUTOR_ABI 30 | case LyraGlobalContractId.Multicall3: 31 | return MULTICALL_3_ABI 32 | } 33 | } 34 | 35 | export default function getGlobalContract( 36 | lyra: Lyra, 37 | contractId: C 38 | ): LyraGlobalContractMap[C] { 39 | const { provider } = lyra 40 | const address = getGlobalContractAddress(lyra, contractId) 41 | if (!address) { 42 | throw new Error('Contract does not exist for specified chain') 43 | } 44 | const abi = getGlobalContractABI(contractId) 45 | return new Contract(address, abi, provider) as LyraGlobalContractMap[C] 46 | } 47 | -------------------------------------------------------------------------------- /src/utils/getIsBaseCollateral.ts: -------------------------------------------------------------------------------- 1 | import { OptionType } from '../constants/contracts' 2 | 3 | export default function getIsBaseCollateral(optionType: OptionType) { 4 | return optionType === OptionType.ShortCoveredCall 5 | } 6 | -------------------------------------------------------------------------------- /src/utils/getIsBuy.ts: -------------------------------------------------------------------------------- 1 | import { OptionType } from '../constants/contracts' 2 | import getIsLong from './getIsLong' 3 | 4 | export default function getIsBuy(optionType: OptionType, isOpen: boolean) { 5 | const isLong = getIsLong(optionType) 6 | return (isLong && isOpen) || (!isLong && !isOpen) 7 | } 8 | -------------------------------------------------------------------------------- /src/utils/getIsCall.ts: -------------------------------------------------------------------------------- 1 | import { OptionType } from '../constants/contracts' 2 | 3 | export default function getIsCall(optionType: OptionType) { 4 | return [OptionType.LongCall, OptionType.ShortCoveredCall, OptionType.ShortCall].includes(optionType) 5 | } 6 | -------------------------------------------------------------------------------- /src/utils/getIsLong.ts: -------------------------------------------------------------------------------- 1 | import { OptionType } from '../constants/contracts' 2 | 3 | export default function getIsLong(optionType: OptionType) { 4 | return [OptionType.LongCall, OptionType.LongPut].includes(optionType) 5 | } 6 | -------------------------------------------------------------------------------- /src/utils/getLiquidationPrice.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | import { UNIT } from '../constants/bn' 4 | import { Option } from '../option' 5 | import fromBigNumber from './fromBigNumber' 6 | import getMaxCollateral from './getMaxCollateral' 7 | import getMinCollateralForSpotPrice from './getMinCollateralForSpotPrice' 8 | 9 | const MAX_ITERATIONS = 20 10 | const ACCURACY = 0.001 // 0.1% 11 | 12 | const closeToPercentage = (a: BigNumber, b: BigNumber, percentage: number) => 13 | b.gt(0) ? fromBigNumber(b.sub(a).mul(UNIT).div(b).abs()) <= percentage : a.eq(b) // zero comparison 14 | 15 | export default function getLiquidationPrice( 16 | option: Option, 17 | size: BigNumber, 18 | collateral: BigNumber, 19 | isBaseCollateral?: boolean 20 | ): BigNumber | null { 21 | const board = option.board() 22 | const timeToExpiry = board.timeToExpiry 23 | 24 | const minCollateral = getMinCollateralForSpotPrice(option, size, option.market().spotPrice, isBaseCollateral, true) 25 | const maxCollateral = getMaxCollateral(option.isCall, option.strike().strikePrice, size, isBaseCollateral) 26 | 27 | const isCashSecuredCall = option.isCall && !isBaseCollateral 28 | const spotPrice = option.market().spotPrice 29 | 30 | if (timeToExpiry <= 0 || size.eq(0) || collateral.eq(0)) { 31 | // Closed position or empty input 32 | return null 33 | } else if (maxCollateral && collateral.gte(maxCollateral) && !isCashSecuredCall) { 34 | // Fully collateralized cash secured puts and covered calls are not liquidatable 35 | return null 36 | } else if (collateral.lt(minCollateral)) { 37 | // Position is immediately liquidatable 38 | return spotPrice 39 | } 40 | 41 | // Acceptable spot price range: 0.2x to 5x spot 42 | let low: BigNumber = spotPrice.div(5) 43 | let high: BigNumber = spotPrice.mul(5) 44 | let n = 0 45 | while (low.lt(high) && n < MAX_ITERATIONS) { 46 | // Search for price liquidation match 47 | const mid = low.add(high).div(2) 48 | // Get the largest min collateral value for a given spot price 49 | const currMinCollateral = getMinCollateralForSpotPrice(option, size, mid, isBaseCollateral, true) 50 | if (option.isCall) { 51 | if (collateral.lt(currMinCollateral)) { 52 | high = mid 53 | } else { 54 | low = mid 55 | } 56 | } else { 57 | // Search opposite direction for short puts 58 | if (collateral.lt(currMinCollateral)) { 59 | low = mid 60 | } else { 61 | high = mid 62 | } 63 | } 64 | n++ 65 | if (closeToPercentage(currMinCollateral, collateral, ACCURACY)) { 66 | return mid 67 | } 68 | } 69 | console.warn('Failed to find liquidation price') 70 | return low.add(high).div(2) 71 | } 72 | -------------------------------------------------------------------------------- /src/utils/getLyraChainForChainId.ts: -------------------------------------------------------------------------------- 1 | import { Chain } from '../constants/chain' 2 | 3 | const getLyraChainForChainId = (chainId: number): Chain => { 4 | switch (chainId) { 5 | case 420: 6 | return Chain.OptimismGoerli 7 | case 421613: 8 | return Chain.ArbitrumGoerli 9 | case 10: 10 | return Chain.Optimism 11 | case 42161: 12 | return Chain.Arbitrum 13 | default: 14 | throw new Error('Chain ID is not supported by Lyra') 15 | } 16 | } 17 | 18 | export default getLyraChainForChainId 19 | -------------------------------------------------------------------------------- /src/utils/getLyraChainIdForChain.ts: -------------------------------------------------------------------------------- 1 | import { Chain } from '../constants/chain' 2 | 3 | const getLyraChainIdForChain = (chain: Chain): number => { 4 | switch (chain) { 5 | case Chain.OptimismGoerli: 6 | return 420 7 | case Chain.ArbitrumGoerli: 8 | return 421613 9 | case Chain.Optimism: 10 | return 10 11 | case Chain.Arbitrum: 12 | return 42161 13 | default: 14 | throw new Error('Chain is not supported by Lyra') 15 | } 16 | } 17 | 18 | export default getLyraChainIdForChain 19 | -------------------------------------------------------------------------------- /src/utils/getLyraDeploymentChainId.ts: -------------------------------------------------------------------------------- 1 | import { Chain } from '../constants/chain' 2 | 3 | const getLyraDeploymentChainId = (chain: Chain): number => { 4 | switch (chain) { 5 | case Chain.Optimism: 6 | return 10 7 | case Chain.OptimismGoerli: 8 | return 420 9 | case Chain.Arbitrum: 10 | return 42161 11 | case Chain.ArbitrumGoerli: 12 | return 421613 13 | } 14 | } 15 | 16 | export default getLyraDeploymentChainId 17 | -------------------------------------------------------------------------------- /src/utils/getLyraDeploymentForChain.ts: -------------------------------------------------------------------------------- 1 | import { Chain } from '../constants/chain' 2 | import { Deployment } from '../constants/contracts' 3 | 4 | const getLyraDeploymentForChain = (chain: Chain): Deployment => { 5 | switch (chain) { 6 | case Chain.Arbitrum: 7 | case Chain.Optimism: 8 | return Deployment.Mainnet 9 | case Chain.OptimismGoerli: 10 | case Chain.ArbitrumGoerli: 11 | return Deployment.Testnet 12 | } 13 | } 14 | 15 | export default getLyraDeploymentForChain 16 | -------------------------------------------------------------------------------- /src/utils/getLyraDeploymentProvider.ts: -------------------------------------------------------------------------------- 1 | import { JsonRpcProvider, StaticJsonRpcProvider } from '@ethersproject/providers' 2 | 3 | import { Chain } from '../constants/chain' 4 | import getLyraDeploymentChainId from './getLyraDeploymentChainId' 5 | import getLyraDeploymentRPCURL from './getLyraDeploymentRPCURL' 6 | 7 | const getLyraDeploymentProvider = (chain: Chain): JsonRpcProvider => { 8 | const rpcUrl = getLyraDeploymentRPCURL(chain) 9 | const chainId = getLyraDeploymentChainId(chain) 10 | return new StaticJsonRpcProvider(rpcUrl, chainId) 11 | } 12 | 13 | export default getLyraDeploymentProvider 14 | -------------------------------------------------------------------------------- /src/utils/getLyraDeploymentRPCURL.ts: -------------------------------------------------------------------------------- 1 | import { Chain } from '../constants/chain' 2 | 3 | const getLyraDeploymentRPCURL = (chain: Chain): string => { 4 | switch (chain) { 5 | case Chain.Optimism: 6 | return 'https://mainnet.optimism.io' 7 | case Chain.OptimismGoerli: 8 | return 'https://goerli.optimism.io' 9 | case Chain.Arbitrum: 10 | return 'https://arb1.arbitrum.io/rpc' 11 | case Chain.ArbitrumGoerli: 12 | return 'https://goerli-rollup.arbitrum.io/rpc' 13 | } 14 | } 15 | 16 | export default getLyraDeploymentRPCURL 17 | -------------------------------------------------------------------------------- /src/utils/getLyraDeploymentSubgraphURI.ts: -------------------------------------------------------------------------------- 1 | import { Chain } from '../constants/chain' 2 | import { Version } from '../lyra' 3 | 4 | const getLyraDeploymentSubgraphURI = (chain: Chain, version: Version): string => { 5 | switch (chain) { 6 | case Chain.Optimism: 7 | switch (version) { 8 | case Version.Avalon: 9 | return 'https://subgraph.satsuma-prod.com/d14de8f7fd46/lyra/optimism-mainnet/api' 10 | case Version.Newport: 11 | default: 12 | return 'https://subgraph.satsuma-prod.com/d14de8f7fd46/lyra/optimism-mainnet-newport/api' 13 | } 14 | case Chain.OptimismGoerli: 15 | return 'https://subgraph.satsuma-prod.com/d14de8f7fd46/lyra/optimism-goerli/api' 16 | case Chain.Arbitrum: 17 | return 'https://subgraph.satsuma-prod.com/d14de8f7fd46/lyra/arbitrum-mainnet/api' 18 | case Chain.ArbitrumGoerli: 19 | return 'https://subgraph.satsuma-prod.com/d14de8f7fd46/lyra/arbitrum-goerli/api' 20 | } 21 | } 22 | 23 | export default getLyraDeploymentSubgraphURI 24 | -------------------------------------------------------------------------------- /src/utils/getLyraGovernanceSubgraphURI.ts: -------------------------------------------------------------------------------- 1 | import { Chain } from '../constants/chain' 2 | 3 | const getLyraGovernanceSubgraphURI = (chain: Chain): string => { 4 | switch (chain) { 5 | case Chain.Optimism: 6 | case Chain.OptimismGoerli: 7 | return 'https://subgraph.satsuma-prod.com/d14de8f7fd46/lyra/optimism-governance/api' 8 | case Chain.Arbitrum: 9 | case Chain.ArbitrumGoerli: 10 | return 'https://subgraph.satsuma-prod.com/d14de8f7fd46/lyra/arbitrum-governance/api' 11 | } 12 | } 13 | 14 | export default getLyraGovernanceSubgraphURI 15 | -------------------------------------------------------------------------------- /src/utils/getLyraMarketContractForAddress.ts: -------------------------------------------------------------------------------- 1 | import { LyraMarketContractId } from '../constants/contracts' 2 | import { LyraMarketContractMap } from '../constants/mappings' 3 | import Lyra, { Version } from '../lyra' 4 | import { MarketContractAddresses } from '../market' 5 | import getLyraMarketContract from './getLyraMarketContract' 6 | 7 | export default function getLyraMarketContractForAddress( 8 | lyra: Lyra, 9 | version: V, 10 | marketContractAddresses: MarketContractAddresses, 11 | address: string 12 | ): { contractId: string; contract: LyraMarketContractMap } | null { 13 | const keyValPair = Object.entries(marketContractAddresses).find( 14 | ([key, val]) => isNaN(parseInt(key)) && val === address 15 | ) 16 | if (!keyValPair) { 17 | return null 18 | } 19 | const [key] = keyValPair 20 | let contractId 21 | switch (key) { 22 | case 'optionMarketPricer': 23 | contractId = LyraMarketContractId.OptionMarketPricer 24 | break 25 | case 'liquidityPool': 26 | contractId = LyraMarketContractId.LiquidityPool 27 | break 28 | case 'liquidityToken': 29 | contractId = LyraMarketContractId.LiquidityToken 30 | break 31 | case 'greekCache': 32 | contractId = LyraMarketContractId.OptionGreekCache 33 | break 34 | case 'optionMarket': 35 | contractId = LyraMarketContractId.OptionMarket 36 | break 37 | case 'optionToken': 38 | contractId = LyraMarketContractId.OptionToken 39 | break 40 | case 'shortCollateral': 41 | contractId = LyraMarketContractId.ShortCollateral 42 | break 43 | case 'poolHedger': 44 | contractId = LyraMarketContractId.PoolHedger 45 | break 46 | } 47 | if (!contractId) { 48 | return null 49 | } 50 | return { 51 | contractId, 52 | contract: getLyraMarketContract(lyra, marketContractAddresses, version, contractId) as LyraMarketContractMap, 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/utils/getLyraNetworkForChain.ts: -------------------------------------------------------------------------------- 1 | import { Chain } from '../constants/chain' 2 | import { Network } from '../constants/network' 3 | 4 | export default function getLyraNetworkForChain(chain: Chain): Network { 5 | switch (chain) { 6 | case Chain.Arbitrum: 7 | case Chain.ArbitrumGoerli: 8 | return Network.Arbitrum 9 | case Chain.Optimism: 10 | case Chain.OptimismGoerli: 11 | return Network.Optimism 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/utils/getLyraNetworkForChainId.ts: -------------------------------------------------------------------------------- 1 | import { Network } from '../constants/network' 2 | 3 | export default function getLyraNetworkForChainId(chainId: number): Network { 4 | switch (chainId) { 5 | case 42161: 6 | case 421613: 7 | return Network.Arbitrum 8 | case 10: 9 | case 420: 10 | return Network.Optimism 11 | default: 12 | throw new Error('Chain ID is not supported by Lyra') 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/utils/getMarketName.ts: -------------------------------------------------------------------------------- 1 | export default function getMarketName(_baseSymbol: string, quoteSymbol: string) { 2 | let baseSymbol = _baseSymbol 3 | switch (baseSymbol.toLowerCase()) { 4 | case 'weth': 5 | baseSymbol = 'ETH' 6 | break 7 | case 'lyarb': 8 | baseSymbol = 'ARB' 9 | break 10 | } 11 | return `${baseSymbol}-${quoteSymbol}` 12 | } 13 | -------------------------------------------------------------------------------- /src/utils/getMaxCollateral.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | import { UNIT } from '../constants/bn' 4 | 5 | export default function getMaxCollateral( 6 | isCall: boolean, 7 | strikePrice: BigNumber, 8 | postTradeSize: BigNumber, 9 | isBaseCollateral?: boolean 10 | ): BigNumber | null { 11 | if (isCall) { 12 | if (isBaseCollateral) { 13 | // size 14 | return postTradeSize 15 | } else { 16 | // no max collateral for cash-secured calls 17 | return null 18 | } 19 | } else { 20 | // size * strike 21 | return postTradeSize.mul(strikePrice).div(UNIT) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/utils/getOptionType.ts: -------------------------------------------------------------------------------- 1 | import { OptionType } from '../constants/contracts' 2 | 3 | export default function getOptionType(isCall: boolean, isLong: boolean, isBaseCollateral: boolean) { 4 | if (isCall) { 5 | return isLong ? OptionType.LongCall : isBaseCollateral ? OptionType.ShortCoveredCall : OptionType.ShortCall 6 | } else { 7 | return isLong ? OptionType.LongPut : OptionType.ShortPut 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/utils/getPositionOwner.ts: -------------------------------------------------------------------------------- 1 | import { ZERO_ADDRESS } from '../constants/bn' 2 | import { PartialTransferEvent } from '../constants/events' 3 | 4 | export default function getPositionOwner(transfers: PartialTransferEvent[], toBlockNumber: number): string { 5 | const events = transfers 6 | // Filter out future blocks 7 | .filter(t => t.blockNumber <= toBlockNumber) 8 | 9 | if (events.length === 0) { 10 | throw new Error('Missing transfer events') 11 | } 12 | 13 | const lastTransfer = events[events.length - 1] 14 | if (lastTransfer.args.to === ZERO_ADDRESS) { 15 | // Burn event, use first transfer "from" address with same transaction hash 16 | const firstLastTransfer = events.find(t => t.transactionHash === lastTransfer.transactionHash) ?? lastTransfer 17 | return firstLastTransfer.args.from 18 | } else { 19 | // Mint or transfer event, use last transfer "to" address 20 | return lastTransfer.args.to 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/utils/getPositionPreviousTrades.ts: -------------------------------------------------------------------------------- 1 | import { Position } from '../position' 2 | import { TradeEvent } from '../trade_event' 3 | 4 | export default function getPositionPreviousTrades(position: Position, trade: TradeEvent): TradeEvent[] { 5 | const trades = position.trades() 6 | if (!trades.length) { 7 | return [] 8 | } 9 | const closeTradeIndex = trades.findIndex(t => t.transactionHash === trade.transactionHash) 10 | if (closeTradeIndex === -1) { 11 | throw new Error('TradeEvent does not exist for position') 12 | } 13 | return trades.slice(0, closeTradeIndex) 14 | } 15 | -------------------------------------------------------------------------------- /src/utils/getPriceType.ts: -------------------------------------------------------------------------------- 1 | import { PriceType } from './getQuoteSpotPrice' 2 | 3 | export default function getPriceType(isCall: boolean, isForceClose: boolean, isLong: boolean, isOpen: boolean) { 4 | // LONG_CALL or SHORT_PUT 5 | if ((isLong && isCall) || (!isLong && !isCall)) { 6 | return isOpen ? PriceType.MAX_PRICE : isForceClose ? PriceType.FORCE_MIN : PriceType.MIN_PRICE 7 | } else { 8 | return isOpen ? PriceType.MIN_PRICE : isForceClose ? PriceType.FORCE_MAX : PriceType.MAX_PRICE 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/utils/getPriceVariance.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | import { UNIT } from '../constants/bn' 4 | 5 | export default function getPriceVariance(price: BigNumber, refPrice: BigNumber) { 6 | return price.mul(UNIT).div(refPrice).sub(UNIT).abs() 7 | } 8 | -------------------------------------------------------------------------------- /src/utils/getProjectedSettlePnl.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | import { UNIT } from '../constants/bn' 4 | 5 | // Base calculation for an option payoff 6 | // Set baseCollateralOptions to account for partially collateralized covered calls 7 | export default function getProjectedSettlePnl( 8 | isLong: boolean, 9 | isCall: boolean, 10 | strikePrice: BigNumber, 11 | spotPriceAtExpiry: BigNumber, 12 | pricePerOption: BigNumber, 13 | size: BigNumber, 14 | liquidationPrice?: BigNumber | null 15 | ): BigNumber { 16 | if (isLong) { 17 | if (isCall) { 18 | // Long call 19 | return ( 20 | spotPriceAtExpiry.gte(strikePrice) 21 | ? // ITM 22 | spotPriceAtExpiry.sub(strikePrice).sub(pricePerOption) 23 | : // OTM 24 | pricePerOption.mul(-1) 25 | ) 26 | .mul(size) 27 | .div(UNIT) 28 | } else { 29 | // Long put 30 | return ( 31 | spotPriceAtExpiry.lte(strikePrice) 32 | ? // ITM 33 | strikePrice.sub(spotPriceAtExpiry).sub(pricePerOption) 34 | : // OTM 35 | pricePerOption.mul(-1) 36 | ) 37 | .mul(size) 38 | .div(UNIT) 39 | } 40 | } else { 41 | if (isCall) { 42 | return ( 43 | liquidationPrice && spotPriceAtExpiry.gte(liquidationPrice) 44 | ? pricePerOption.sub(spotPriceAtExpiry) // Liquidation (max loss) 45 | : spotPriceAtExpiry.lte(strikePrice) 46 | ? // OTM 47 | pricePerOption 48 | : // ITM 49 | pricePerOption.sub(spotPriceAtExpiry).add(strikePrice) 50 | ) 51 | .mul(size) 52 | .div(UNIT) 53 | } else { 54 | // Cash secured put 55 | return ( 56 | liquidationPrice && spotPriceAtExpiry.lte(liquidationPrice) 57 | ? pricePerOption.sub(strikePrice) // Liquidation (max loss) 58 | : spotPriceAtExpiry.lte(strikePrice) 59 | ? // ITM 60 | spotPriceAtExpiry.sub(strikePrice).add(pricePerOption) 61 | : // OTM 62 | pricePerOption 63 | ) 64 | .mul(size) 65 | .div(UNIT) 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/utils/getQuoteSpotPrice.ts: -------------------------------------------------------------------------------- 1 | import { Network } from '../constants/network' 2 | import { GMXAdapter } from '../contracts/newport/typechain/NewportGMXAdapter' 3 | import { Version } from '../lyra' 4 | import { Market } from '../market' 5 | import getPriceVariance from './getPriceVariance' 6 | 7 | export enum PriceType { 8 | MIN_PRICE, // minimise the spot based on logic in adapter - can revert 9 | MAX_PRICE, // maximise the spot based on logic in adapter 10 | REFERENCE, 11 | FORCE_MIN, // minimise the spot based on logic in adapter - shouldn't revert unless feeds are compromised 12 | FORCE_MAX, 13 | } 14 | 15 | export default function getQuoteSpotPrice(market: Market, priceType: PriceType) { 16 | // Reference spot price 17 | const spotPrice = market.params.referenceSpotPrice 18 | if ( 19 | market.lyra.version === Version.Avalon || 20 | !market.params.adapterView || 21 | priceType === PriceType.REFERENCE || 22 | market.lyra.network === Network.Optimism 23 | ) { 24 | return spotPrice 25 | } 26 | const gmxAdapterView = market.params.adapterView as GMXAdapter.GMXAdapterStateStructOutput 27 | if (!gmxAdapterView) { 28 | throw new Error('Mismatching adapter view and getQuoteSpotPrice') 29 | } 30 | const { gmxMaxPrice: forceMaxSpotPrice, gmxMinPrice: forceMinSpotPrice, marketPricingParams } = gmxAdapterView 31 | 32 | const { gmxUsageThreshold } = marketPricingParams 33 | const minVariance = getPriceVariance(forceMinSpotPrice, spotPrice) 34 | const maxVariance = getPriceVariance(forceMaxSpotPrice, spotPrice) 35 | 36 | // In the case where the gmxUsageThreshold is crossed, we want to use the worst case price between cl and gmx 37 | let useWorstCase = false 38 | if (minVariance.gt(gmxUsageThreshold) || maxVariance.gt(gmxUsageThreshold)) { 39 | useWorstCase = true 40 | } 41 | 42 | if (priceType == PriceType.FORCE_MIN || priceType == PriceType.MIN_PRICE) { 43 | return useWorstCase && forceMinSpotPrice.gt(spotPrice) ? spotPrice : forceMinSpotPrice 44 | } else { 45 | return useWorstCase && forceMaxSpotPrice.lt(spotPrice) ? spotPrice : forceMaxSpotPrice 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/utils/getSettleDataFromSubgraph.ts: -------------------------------------------------------------------------------- 1 | import { getAddress } from '@ethersproject/address' 2 | import { BigNumber } from '@ethersproject/bignumber' 3 | 4 | import { UNIT, ZERO_BN } from '../constants/bn' 5 | import { DataSource } from '../constants/contracts' 6 | import { SettleQueryResult } from '../constants/queries' 7 | import { SettleEventData } from '../settle_event' 8 | 9 | export default function getSettleDataFromSubgraph(settle: SettleQueryResult): SettleEventData { 10 | const spotPriceAtExpiry = BigNumber.from(settle.spotPriceAtExpiry) 11 | const strikePrice = BigNumber.from(settle.position.strike.strikePrice) 12 | const expiryTimestamp = settle.position.board.expiryTimestamp 13 | const size = BigNumber.from(settle.size) 14 | 15 | const isLong = settle.position.isLong 16 | const isBaseCollateral = settle.position.isBaseCollateral 17 | const settleAmount = BigNumber.from(settle.settleAmount) 18 | const settlement = isLong ? settleAmount : ZERO_BN 19 | const returnedCollateralAmount = !isLong ? settleAmount : ZERO_BN 20 | const returnedCollateralValue = isBaseCollateral 21 | ? returnedCollateralAmount.mul(spotPriceAtExpiry).div(UNIT) 22 | : returnedCollateralAmount 23 | 24 | const isCall = settle.position.option.isCall 25 | 26 | const isInTheMoney = isCall ? spotPriceAtExpiry.gt(strikePrice) : spotPriceAtExpiry.lt(strikePrice) 27 | 28 | return { 29 | source: DataSource.Subgraph, 30 | blockNumber: settle.blockNumber, 31 | positionId: settle.position.positionId, 32 | timestamp: settle.timestamp, 33 | size, 34 | spotPriceAtExpiry, 35 | transactionHash: settle.transactionHash, 36 | owner: getAddress(settle.owner), 37 | marketName: settle.position.market.name.substring(1), 38 | marketAddress: getAddress(settle.position.market.id), 39 | expiryTimestamp, 40 | isCall: settle.position.option.isCall, 41 | strikePrice, 42 | isBaseCollateral: settle.position.isBaseCollateral, 43 | isLong: settle.position.isLong, 44 | settlement, 45 | isInTheMoney, 46 | returnedCollateralAmount, 47 | returnedCollateralValue, 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/utils/getSnapshotPeriod.ts: -------------------------------------------------------------------------------- 1 | import { SNAPSHOT_RESULT_LIMIT, SnapshotPeriod } from '../constants/queries' 2 | 3 | export default function getSnapshotPeriod( 4 | startTimestamp: number, 5 | endTimestamp: number, 6 | periods: SnapshotPeriod[] 7 | ): SnapshotPeriod { 8 | const durationSeconds = Math.max(endTimestamp - startTimestamp, 0) 9 | while (periods.length > 1) { 10 | const period = periods.shift() as SnapshotPeriod 11 | const numItems = Math.ceil(durationSeconds / period) 12 | if (numItems > SNAPSHOT_RESULT_LIMIT) { 13 | continue 14 | } else { 15 | return period 16 | } 17 | } 18 | return periods[0] 19 | } 20 | -------------------------------------------------------------------------------- /src/utils/getTimeToExpiryAnnualized.ts: -------------------------------------------------------------------------------- 1 | import { Board } from '../board' 2 | 3 | export default function getTimeToExpiryAnnualized(board: Board) { 4 | const timeToExpiry = board.timeToExpiry 5 | const timeToExpiryAnnualized = timeToExpiry / (60 * 60 * 24 * 365) 6 | return timeToExpiryAnnualized 7 | } 8 | -------------------------------------------------------------------------------- /src/utils/getTradeDataFromSubgraph.ts: -------------------------------------------------------------------------------- 1 | import { getAddress } from '@ethersproject/address' 2 | import { BigNumber } from '@ethersproject/bignumber' 3 | 4 | import { UNIT, ZERO_BN } from '../constants/bn' 5 | import { DataSource } from '../constants/contracts' 6 | import { TradeQueryResult } from '../constants/queries' 7 | import { TradeEventData } from '../trade_event' 8 | 9 | export default function getTradeDataFromSubgraph(trade: TradeQueryResult): TradeEventData { 10 | const size = BigNumber.from(trade.size) 11 | const spotPrice = BigNumber.from(trade.spotPrice) 12 | const premium = BigNumber.from(trade.premium) 13 | const spotPriceFee = BigNumber.from(trade.spotPriceFee) 14 | const optionPriceFee = BigNumber.from(trade.optionPriceFee) 15 | const vegaUtilFee = BigNumber.from(trade.vegaUtilFee) 16 | const varianceFee = BigNumber.from(trade.varianceFee) 17 | const swapFee = BigNumber.from(trade.externalSwapFees ?? 0) 18 | const strikePrice = BigNumber.from(trade.strike.strikePrice) 19 | const collateralAmount = !trade.position.isLong 20 | ? trade.setCollateralTo 21 | ? BigNumber.from(trade.setCollateralTo) 22 | : ZERO_BN 23 | : undefined 24 | const isBaseCollateral = !trade.position.isLong ? trade.position.isBaseCollateral : undefined 25 | const collateralValue = collateralAmount 26 | ? isBaseCollateral 27 | ? collateralAmount.mul(spotPrice).div(UNIT) 28 | : collateralAmount 29 | : undefined 30 | return { 31 | timestamp: trade.timestamp, 32 | source: DataSource.Subgraph, 33 | positionId: trade.position.positionId, 34 | blockNumber: trade.blockNumber, 35 | marketName: trade.market.name.substring(1), 36 | marketAddress: getAddress(trade.market.id), 37 | isCall: trade.option.isCall, 38 | strikeId: parseInt(trade.strike.strikeId), 39 | strikePrice, 40 | expiryTimestamp: trade.board.expiryTimestamp, 41 | transactionHash: trade.transactionHash, 42 | trader: getAddress(trade.trader), 43 | size, 44 | isOpen: trade.isOpen, 45 | isBuy: trade.isBuy, 46 | isLong: trade.position.isLong, 47 | spotPrice, 48 | pricePerOption: premium.mul(UNIT).div(size), 49 | premium, 50 | fee: spotPriceFee.add(optionPriceFee).add(vegaUtilFee).add(varianceFee).add(swapFee), 51 | feeComponents: { 52 | spotPriceFee, 53 | optionPriceFee, 54 | vegaUtilFee, 55 | varianceFee, 56 | }, 57 | iv: BigNumber.from(trade.newIv), 58 | baseIv: BigNumber.from(trade.newBaseIv), 59 | skew: BigNumber.from(trade.newSkew), 60 | volTraded: BigNumber.from(trade.volTraded), 61 | collateralAmount, 62 | collateralValue, 63 | isBaseCollateral, 64 | isForceClose: trade.isForceClose, 65 | isLiquidation: trade.isLiquidation, 66 | swap: trade.externalSwapFees 67 | ? { 68 | fee: BigNumber.from(trade.externalSwapFees), 69 | address: trade.externalSwapAddress, 70 | } 71 | : undefined, 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/utils/getTradePnl.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | import { UNIT, ZERO_BN } from '../constants/bn' 4 | import { Position } from '../position' 5 | import { Trade } from '../trade' 6 | import { TradeEvent } from '../trade_event' 7 | 8 | export default function getTradePnl(position: Position, trade: TradeEvent | Trade): BigNumber { 9 | if (trade.isOpen) { 10 | return ZERO_BN 11 | } 12 | 13 | const size = trade.size 14 | const isLong = trade.isLong 15 | const pricePerOption = trade.pricePerOption 16 | 17 | const averageCostPerOption = 18 | trade instanceof TradeEvent ? trade.prevAverageCostPerOption(position) : trade.prevAverageCostPerOption() 19 | 20 | // For longs, profit is fair value minus average premiums paid 21 | // For shorts, profit is average premiums received minus fair value 22 | return (isLong ? pricePerOption.sub(averageCostPerOption) : averageCostPerOption.sub(pricePerOption)) 23 | .mul(size) 24 | .div(UNIT) 25 | } 26 | -------------------------------------------------------------------------------- /src/utils/getTransferDataFromSubgraph.ts: -------------------------------------------------------------------------------- 1 | import { getAddress } from '@ethersproject/address' 2 | 3 | import { DataSource } from '../constants/contracts' 4 | import { TransferQueryResult } from '../constants/queries' 5 | import { TransferEventData } from '../transfer_event' 6 | 7 | export default function getTransferDataFromSubgraph(transfer: TransferQueryResult): TransferEventData { 8 | return { 9 | source: DataSource.Subgraph, 10 | from: getAddress(transfer.oldOwner), 11 | to: getAddress(transfer.newOwner), 12 | transactionHash: transfer.transactionHash, 13 | blockNumber: transfer.blockNumber, 14 | positionId: transfer.position.positionId, 15 | marketAddress: getAddress(transfer.position.id.split('-')[0]), 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/utils/getUniqueBy.ts: -------------------------------------------------------------------------------- 1 | export default function getUniqueBy(array: T[], by: (item: T) => any): T[] { 2 | return array.filter((value: T, index: number, self: T[]) => { 3 | return self.findIndex(item => by(item) === by(value)) === index 4 | }) as T[] 5 | } 6 | -------------------------------------------------------------------------------- /src/utils/groupTimeSnapshots.ts: -------------------------------------------------------------------------------- 1 | import getDefaultPeriod from './getDefaultPeriod' 2 | 3 | type Snapshot = { 4 | timestamp: number 5 | } 6 | 7 | export default function groupTimeSnapshots( 8 | snapshots: T[], 9 | startTimestamp: number, 10 | endTimestamp: number, 11 | period?: number 12 | ): T[] { 13 | if (!snapshots.length) { 14 | return [] 15 | } 16 | 17 | const truePeriod = period ?? getDefaultPeriod(startTimestamp, endTimestamp) 18 | let snapshotIdx = 0 19 | const smoothSnapshots = [] 20 | let timestamp = startTimestamp 21 | // allow last snapshot to be captured with +truePeriod 22 | for (; timestamp < endTimestamp + truePeriod; timestamp += truePeriod) { 23 | // always ensure there is a next snapshot available 24 | while (snapshotIdx < snapshots.length - 1 && snapshots[snapshotIdx].timestamp <= timestamp) { 25 | snapshotIdx++ 26 | } 27 | // ensure timestamp is not greater than endTimestamp 28 | smoothSnapshots.push({ ...snapshots[snapshotIdx], timestamp: Math.min(timestamp, endTimestamp) }) 29 | } 30 | return smoothSnapshots 31 | } 32 | -------------------------------------------------------------------------------- /src/utils/isMarketEqual.ts: -------------------------------------------------------------------------------- 1 | import { getAddress, isAddress } from '@ethersproject/address' 2 | 3 | import { Market } from '../market' 4 | import parseBaseSymbol from './parseBaseSymbol' 5 | 6 | export default function isMarketEqual(market: Market, marketAddressOrName: string): boolean { 7 | if (isAddress(marketAddressOrName)) { 8 | return market.address === getAddress(marketAddressOrName) 9 | } else { 10 | return market.baseToken.symbol.toLowerCase() === parseBaseSymbol(market.lyra, marketAddressOrName).toLowerCase() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/utils/isNDecimalPlaces.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | import fromBigNumber from './fromBigNumber' 4 | 5 | // Checks if a BigNumber has greater than (or equal to) n decimal places 6 | export default function isNDecimalPlaces(val: BigNumber, n: number): boolean { 7 | const exp = 10 ** (n - 1) 8 | const valNum = fromBigNumber(val) 9 | const floor = Math.floor(valNum * exp) 10 | const ceil = Math.ceil(valNum * exp) 11 | return floor < valNum * exp && valNum * exp < ceil 12 | } 13 | -------------------------------------------------------------------------------- /src/utils/isTestnet.ts: -------------------------------------------------------------------------------- 1 | import Lyra, { Chain } from '../' 2 | 3 | export default function isTestnet(lyra: Lyra) { 4 | switch (lyra.chain) { 5 | case Chain.Arbitrum: 6 | case Chain.Optimism: 7 | return false 8 | case Chain.ArbitrumGoerli: 9 | case Chain.OptimismGoerli: 10 | return true 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/utils/isTokenEqual.ts: -------------------------------------------------------------------------------- 1 | import { AccountTokenBalance } from '../account' 2 | 3 | export default function isTokenEqual(token: T, tokenAddressOrName: string): boolean { 4 | return ( 5 | token.address.toLowerCase() === tokenAddressOrName.toLowerCase() || 6 | token.symbol.toLowerCase() === tokenAddressOrName.toLowerCase() 7 | ) 8 | } 9 | -------------------------------------------------------------------------------- /src/utils/multicall.ts: -------------------------------------------------------------------------------- 1 | import { Contract } from 'ethers' 2 | 3 | import Lyra from '..' 4 | import { LyraGlobalContractId } from '../constants/contracts' 5 | import getGlobalContract from './getGlobalContract' 6 | 7 | export type MulticallRequest = { 8 | contract: C 9 | function: F 10 | args: Parameters 11 | } 12 | 13 | type MulticallResponse = Awaited>[0] 14 | 15 | type MulticallResponses = { [K in keyof Reqs]: MulticallResponse } 16 | 17 | export default async function multicall( 18 | lyra: Lyra, 19 | requests: Reqs 20 | ): Promise<{ 21 | returnData: MulticallResponses 22 | blockNumber: number 23 | }> { 24 | const multicall3Contract = getGlobalContract(lyra, LyraGlobalContractId.Multicall3) 25 | const calls = requests.map(req => ({ 26 | target: req.contract.address, 27 | callData: req.contract.interface.encodeFunctionData(req.function, req.args), 28 | })) 29 | const { returnData, blockNumber } = await multicall3Contract.callStatic.aggregate(calls) 30 | const result = requests.map((req, idx) => { 31 | const contract = req.contract 32 | const result = contract.interface.decodeFunctionResult(req.function, returnData[idx]) 33 | return result[0] 34 | }) 35 | return { 36 | returnData: result as MulticallResponses, 37 | blockNumber: blockNumber.toNumber(), 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/utils/parseBaseKeyBytes32.ts: -------------------------------------------------------------------------------- 1 | import { formatBytes32String } from '@ethersproject/strings' 2 | 3 | export default function parseBaseKeyBytes32(baseKey: string) { 4 | if (baseKey.startsWith('0x')) { 5 | // Assume variable is base key in bytes32 6 | return baseKey 7 | } else { 8 | // Account for "sETH", "ETH" or "eth" formats 9 | // Check that key starts with "s" and rest of string is uppercase 10 | const parsedBasekey = 11 | baseKey.startsWith('s') && baseKey.substring(1).toUpperCase() === baseKey.substring(1) 12 | ? baseKey 13 | : baseKey.startsWith('s') 14 | ? 's' + baseKey.substring(1).toUpperCase() 15 | : 's' + baseKey.toUpperCase() 16 | return formatBytes32String(parsedBasekey) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/utils/parseBaseSymbol.ts: -------------------------------------------------------------------------------- 1 | import Lyra, { Version } from '../lyra' 2 | 3 | export default function parseBaseSymbol(lyra: Lyra, marketAddressOrName: string): string { 4 | const [rawBaseKey] = marketAddressOrName.split('-') 5 | if (lyra.version === Version.Avalon) { 6 | // Hardcode sETH, sBTC, sSOL 7 | switch (rawBaseKey.toLowerCase()) { 8 | case 'eth': 9 | case 'seth': 10 | return 'sETH' 11 | case 'btc': 12 | case 'sbtc': 13 | return 'sBTC' 14 | case 'sol': 15 | case 'ssol': 16 | return 'sSOL' 17 | default: 18 | // Not reachable 19 | return rawBaseKey 20 | } 21 | } else { 22 | switch (rawBaseKey.toLowerCase()) { 23 | case 'eth': 24 | case 'weth': 25 | return 'WETH' 26 | case 'btc': 27 | case 'wbtc': 28 | return 'WBTC' 29 | case 'lyarb': 30 | case 'arb': 31 | return 'LyARB' 32 | default: 33 | // Add overrides for markets as individual cases 34 | return rawBaseKey.toUpperCase() 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/utils/parseMarketName.ts: -------------------------------------------------------------------------------- 1 | export default function parseMarketName(marketName: string) { 2 | const [baseKey, quoteKey] = marketName.split('-') 3 | if (!baseKey) { 4 | throw new Error(`Invalid market name arg: ${marketName}`) 5 | } 6 | return { baseKey, quoteKey } 7 | } 8 | -------------------------------------------------------------------------------- /src/utils/parsePartialPositionUpdatedEventsFromLogs.ts: -------------------------------------------------------------------------------- 1 | import { Contract } from '@ethersproject/contracts' 2 | import { Log } from '@ethersproject/providers' 3 | 4 | import { ZERO_ADDRESS } from '../constants/bn' 5 | import { EventName, LyraMarketContractId } from '../constants/contracts' 6 | import { PartialPositionUpdatedEvent } from '../constants/events' 7 | import { Network } from '../constants/network' 8 | import { Version } from '../lyra' 9 | import filterNulls from './filterNulls' 10 | import { getMarketContractABI } from './getLyraMarketContract' 11 | 12 | export default function parsePartialPositionUpdatedEventsFromLogs( 13 | logs: Log[], 14 | network: Network 15 | ): PartialPositionUpdatedEvent[] { 16 | const optionToken = new Contract( 17 | ZERO_ADDRESS, 18 | // Hard-coded version as these ABI events are functionally the same 19 | getMarketContractABI(Version.Newport, LyraMarketContractId.OptionToken, network) 20 | ) 21 | const events = filterNulls( 22 | logs.map(log => { 23 | try { 24 | const event = optionToken.interface.parseLog(log) 25 | if (event.name === EventName.PositionUpdated) { 26 | return { 27 | address: log.address, 28 | blockNumber: log.blockNumber, 29 | transactionHash: log.transactionHash, 30 | logIndex: log.logIndex, 31 | args: event.args as PartialPositionUpdatedEvent['args'], 32 | } 33 | } 34 | return null 35 | } catch (e) { 36 | return null 37 | } 38 | }) 39 | ) 40 | return events 41 | } 42 | -------------------------------------------------------------------------------- /src/utils/parsePartialTradeEventsFromLogs.ts: -------------------------------------------------------------------------------- 1 | import { Contract } from '@ethersproject/contracts' 2 | import { Log } from '@ethersproject/providers' 3 | 4 | import { Version } from '..' 5 | import { ZERO_ADDRESS } from '../constants/bn' 6 | import { EventName, LyraMarketContractId } from '../constants/contracts' 7 | import { PartialTradeEvent } from '../constants/events' 8 | import { Network } from '../constants/network' 9 | import filterNulls from './filterNulls' 10 | import { getMarketContractABI } from './getLyraMarketContract' 11 | 12 | // Some transactions, e.g. liquidations, can have multiple Trade events 13 | export default function parsePartialTradeEventsFromLogs(logs: Log[], network: Network): PartialTradeEvent[] { 14 | const optionMarket = new Contract( 15 | ZERO_ADDRESS, 16 | // Hard-coded Version as these ABI events are functionally the same 17 | getMarketContractABI(Version.Newport, LyraMarketContractId.OptionMarket, network) 18 | ) 19 | const events = filterNulls( 20 | logs.map(log => { 21 | try { 22 | const event = optionMarket.interface.parseLog(log) 23 | // Skip any Trade events with empty tradeResults (collateral adjustments) 24 | if (event.name === EventName.Trade && (event.args as PartialTradeEvent['args']).tradeResults.length > 0) { 25 | return { 26 | address: log.address, 27 | blockNumber: log.blockNumber, 28 | transactionHash: log.transactionHash, 29 | logIndex: log.logIndex, 30 | args: event.args as PartialTradeEvent['args'], 31 | } 32 | } 33 | return null 34 | } catch (e) { 35 | return null 36 | } 37 | }) 38 | ) 39 | return events 40 | } 41 | -------------------------------------------------------------------------------- /src/utils/printObject.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | import fromBigNumber from './fromBigNumber' 4 | 5 | function replacer(_name: string, val: any) { 6 | if (typeof val === 'object' && val != null) { 7 | if (!Array.isArray(val)) { 8 | // Handle objects 9 | const newVal = Object.assign({}, val) 10 | Object.entries(val).forEach(([key, val]) => { 11 | if (BigNumber.isBigNumber(val)) { 12 | newVal[key] = fromBigNumber(val) 13 | } else { 14 | newVal[key] = val 15 | } 16 | }) 17 | return newVal 18 | } else { 19 | // Handle arrays 20 | return val.map(val => { 21 | if (BigNumber.isBigNumber(val)) { 22 | return fromBigNumber(val) 23 | } else { 24 | return val 25 | } 26 | }) 27 | } 28 | } else { 29 | return val 30 | } 31 | } 32 | 33 | // Handles BigNumber printing (assumes 18dp) 34 | export default function printObject(...args: any[]): void { 35 | const parsedArgs = args.map(arg => { 36 | if (typeof arg === 'object' && arg != null) { 37 | return JSON.stringify(arg, replacer, 2).replace(/"/g, '') 38 | } else { 39 | return arg 40 | } 41 | }) 42 | console.log(...parsedArgs) 43 | } 44 | -------------------------------------------------------------------------------- /src/utils/roundToDp.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | 3 | import { ZERO_BN } from '../constants/bn' 4 | 5 | type Options = { 6 | ceil?: boolean 7 | bnDecimals?: number 8 | } 9 | 10 | // Round a BN to n decimal places, aassumes BN is 10^18 11 | export default function roundToDp(val: BigNumber, n: number, options?: Options): BigNumber { 12 | if (val.isZero()) { 13 | return ZERO_BN 14 | } 15 | const bnDecimals = options?.bnDecimals ?? 18 16 | let valBN = val.div(BigNumber.from(10).pow(bnDecimals - n)) 17 | const ceil = options?.ceil ?? true 18 | if (ceil) { 19 | valBN = valBN.add(1) 20 | } 21 | return valBN.mul(BigNumber.from(10).pow(bnDecimals - n)) 22 | } 23 | -------------------------------------------------------------------------------- /src/utils/subgraphRequest.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ApolloClient, 3 | ApolloQueryResult, 4 | NormalizedCacheObject, 5 | OperationVariables, 6 | QueryOptions, 7 | } from '@apollo/client/core' 8 | 9 | export default async function subgraphRequest( 10 | client: ApolloClient, 11 | options: QueryOptions 12 | ): Promise> { 13 | return client.query({ ...options, errorPolicy: 'all' }) 14 | } 15 | -------------------------------------------------------------------------------- /src/utils/subgraphRequestWithLoop.ts: -------------------------------------------------------------------------------- 1 | import { DocumentNode } from '@apollo/client/core' 2 | 3 | import Lyra from '..' 4 | import { SNAPSHOT_RESULT_LIMIT } from '../constants/queries' 5 | import subgraphRequest from './subgraphRequest' 6 | 7 | // GraphQL supports 32 bit signed int, i.e. max 2^31 - 1 8 | const MAX_SAFE_32_BIT_INT = 2147483647 9 | 10 | type IteratorVariables = { min: number; max: number; limit?: number } 11 | 12 | export default async function subgraphRequestWithLoop< 13 | Snapshot extends Record, 14 | Variables extends Record = Record 15 | >( 16 | lyra: Lyra, 17 | query: DocumentNode, 18 | variables: Variables & IteratorVariables, 19 | iteratorKey: keyof Snapshot, 20 | batchOptions?: { 21 | increment: number 22 | batch: number 23 | } 24 | ): Promise { 25 | let allFound = false 26 | let data: Snapshot[] = [] 27 | let min = variables.min 28 | const limit = variables.limit ?? SNAPSHOT_RESULT_LIMIT 29 | 30 | while (!allFound) { 31 | const varArr: (Variables & IteratorVariables)[] = [] 32 | if (batchOptions) { 33 | // when you have absolute min and next min for a query, optimize with batching 34 | // e.g. querying positions by position ID, absolute min = 0 and next min = 1000 35 | const { batch, increment } = batchOptions 36 | for (let b = 0; b < batch; b++) { 37 | varArr.push({ 38 | ...variables, 39 | min, 40 | max: Math.min(min + increment - 1, MAX_SAFE_32_BIT_INT), 41 | }) 42 | min += increment 43 | } 44 | } else { 45 | varArr.push({ 46 | ...variables, 47 | limit, 48 | min, 49 | }) 50 | } 51 | const batches = ( 52 | await Promise.all( 53 | varArr.map(async variables => { 54 | const { data } = await subgraphRequest<{ [key: string]: Snapshot[] }, Variables & IteratorVariables>( 55 | lyra.subgraphClient, 56 | { 57 | query, 58 | variables, 59 | } 60 | ) 61 | return data 62 | }) 63 | ) 64 | ) 65 | .filter(res => res != null) 66 | .map(res => Object.values(res as { [key: string]: Snapshot[] })[0]) 67 | const lastBatch = batches[batches.length - 1] 68 | data = [...data, ...batches.flat()] 69 | if (!lastBatch || !lastBatch.length || lastBatch.length < limit) { 70 | allFound = true 71 | } else { 72 | // Set skip to last iterator val 73 | min = lastBatch[lastBatch.length - 1][iteratorKey] + 1 74 | } 75 | } 76 | return data 77 | } 78 | -------------------------------------------------------------------------------- /src/utils/toBigNumber.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from '@ethersproject/bignumber' 2 | import { parseUnits } from '@ethersproject/units' 3 | 4 | export default function toBigNumber(number: number, decimals: number = 18): BigNumber { 5 | if (isNaN(number)) { 6 | throw new Error('Passed NaN to BigNumber converter') 7 | } 8 | return parseUnits(number.toFixed(18), decimals) 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "sourceMap": true, 6 | "outDir": "dist/cjs", 7 | "moduleResolution": "node", 8 | "resolveJsonModule": true, 9 | "esModuleInterop": true, 10 | "declaration": true, 11 | "declarationDir": "dist/types", 12 | "skipLibCheck": true, 13 | "strict": true 14 | }, 15 | "include": ["src"] 16 | } 17 | -------------------------------------------------------------------------------- /tsconfig.esm.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "module": "esnext", 5 | "sourceMap": true, 6 | "outDir": "dist/esm", 7 | "moduleResolution": "node", 8 | "resolveJsonModule": true, 9 | "esModuleInterop": true, 10 | "declaration": true, 11 | "declarationDir": "dist/types", 12 | "skipLibCheck": true, 13 | "strict": true 14 | }, 15 | "include": ["src"] 16 | } 17 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "sourceMap": true, 6 | "moduleResolution": "node", 7 | "resolveJsonModule": true, 8 | "esModuleInterop": true, 9 | "declaration": true, 10 | "skipLibCheck": true, 11 | "noEmit": true, 12 | "strict": true 13 | }, 14 | "include": ["src"] 15 | } 16 | --------------------------------------------------------------------------------