From ec02199477b3c09e744820bfca366ff788d3b0d4 Mon Sep 17 00:00:00 2001 From: Arjun Rao <2940142+arjun-io@users.noreply.github.com> Date: Wed, 30 Oct 2024 12:35:21 -0400 Subject: [PATCH 1/5] Add MetadataLens, fix max settlement fee --- .gitignore | 1 + contracts/Lens.sol | 56 +++++++++++++++++++++++ package.json | 2 +- pnpm-lock.yaml | 10 ++--- scripts/lensAbiCopy.js | 2 +- src/index.ts | 1 + src/lib/markets/chain.ts | 95 ++++++++++++++++++---------------------- src/lib/markets/index.ts | 2 +- 8 files changed, 109 insertions(+), 60 deletions(-) diff --git a/.gitignore b/.gitignore index 03641f1..735d226 100644 --- a/.gitignore +++ b/.gitignore @@ -132,6 +132,7 @@ dist src/abi/Lens.abi.ts src/abi/VaultLens.abi.ts +src/abi/MarketMetadataLens.abi.ts src/types/generated src/types/gql diff --git a/contracts/Lens.sol b/contracts/Lens.sol index 0e839a9..01c82e6 100644 --- a/contracts/Lens.sol +++ b/contracts/Lens.sol @@ -5,6 +5,8 @@ import '@perennial/core/contracts/interfaces/IMarket.sol'; import '@perennial/core/contracts/types/OracleReceipt.sol'; import '@perennial/oracle/contracts/interfaces/IOracle.sol'; import '@perennial/oracle/contracts/interfaces/IKeeperFactory.sol'; +import '@perennial/oracle/contracts/interfaces/IOracleFactory.sol'; +import '@perennial/oracle/contracts/interfaces/IKeeperOracle.sol'; import * as Vault from '@perennial/vault/contracts/interfaces/IVault.sol'; import '@perennial/vault/contracts/interfaces/IVaultFactory.sol'; import '@equilibria/root/number/types/UFixed6.sol'; @@ -317,3 +319,57 @@ contract VaultLens { } } } + +interface ILensOracle is IOracle { + function oracles(uint256 id) external view returns (Epoch memory); +} + +contract MarketMetadataLens { + struct MarketMetadata { + string name; + IMarket marketAddress; + RiskParameter riskParameter; + MarketParameter marketParameter; + ILensOracle oracle; + IOracleFactory oracleFactory; + IKeeperOracle subOracle; + IKeeperFactory subOracleFactory; + IKeeperFactory.PayoffDefinition oraclePayoffDefinition; + string subOracleFactoryType; + bytes32 oracleId; + bytes32 oracleUnderlyingId; + OracleParameter oracleFactoryParameter; + KeeperOracleParameter subOracleFactoryParameter; + IGasOracle commitmentGasOracle; + IGasOracle settlementGasOracle; + } + + function metadata(IMarket[] memory markets) public view returns (MarketMetadata[] memory marketMetadata) { + marketMetadata = new MarketMetadata[](markets.length); + for (uint i = 0; i < markets.length; i++) { + marketMetadata[i] = metadata(markets[i]); + } + } + + function metadata(IMarket market) public view returns (MarketMetadata memory marketMetadata) { + marketMetadata.marketAddress = market; + marketMetadata.riskParameter = market.riskParameter(); + marketMetadata.marketParameter = market.parameter(); + marketMetadata.oracle = ILensOracle(address(market.oracle())); + marketMetadata.oracleFactory = IOracleFactory(address(marketMetadata.oracle.factory())); + marketMetadata.oracleFactoryParameter = marketMetadata.oracleFactory.parameter(); + marketMetadata.name = marketMetadata.oracle.name(); + marketMetadata.oracleId = marketMetadata.oracleFactory.ids(marketMetadata.oracle); + + IOracle.OracleGlobal memory global = marketMetadata.oracle.global(); + marketMetadata.subOracle = IKeeperOracle(address(marketMetadata.oracle.oracles(global.current).provider)); + marketMetadata.subOracleFactory = IKeeperFactory(address(marketMetadata.subOracle.factory())); + marketMetadata.oraclePayoffDefinition = marketMetadata.subOracleFactory.toUnderlyingPayoff(marketMetadata.oracleId); + marketMetadata.subOracleFactoryType = marketMetadata.subOracleFactory.factoryType(); + marketMetadata.oracleUnderlyingId = marketMetadata.subOracleFactory.toUnderlyingId(marketMetadata.oracleId); + + marketMetadata.subOracleFactoryParameter = marketMetadata.subOracleFactory.parameter(); + marketMetadata.commitmentGasOracle = marketMetadata.subOracleFactory.commitmentGasOracle(); + marketMetadata.settlementGasOracle = marketMetadata.subOracleFactory.settlementGasOracle(); + } +} diff --git a/package.json b/package.json index 9ccbf66..f0c9c84 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "@graphql-typed-document-node/core": "^3.2.0", "@openzeppelin/contracts": "4.8.0", "@perennial/core": "1.3.0-rc1", - "@perennial/oracle": "1.3.0-rc2", + "@perennial/oracle": "1.3.0-rc3", "@perennial/vault": "1.3.0-rc1", "@perennial/verifier": "1.3.0-rc1", "@trivago/prettier-plugin-sort-imports": "^4.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9a5d55a..63a1c36 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -52,8 +52,8 @@ importers: specifier: 1.3.0-rc1 version: 1.3.0-rc1 '@perennial/oracle': - specifier: 1.3.0-rc2 - version: 1.3.0-rc2 + specifier: 1.3.0-rc3 + version: 1.3.0-rc3 '@perennial/vault': specifier: 1.3.0-rc1 version: 1.3.0-rc1 @@ -1055,8 +1055,8 @@ packages: '@perennial/core@1.3.0-rc1': resolution: {integrity: sha512-V8T75n53tRrH/nqWqk87/XTSvVkllCXgWY14g/5vNcNqpJ5nVI1vcM9gXQGajativHd6oJCH3cGMd/nJiga63w==} - '@perennial/oracle@1.3.0-rc2': - resolution: {integrity: sha512-jr0WlkcI3Mcg0xRam2ozIsRmw34gQex9upr35bPm0qBLuTldGUnHwl3X5SK7HbepXx1pkHrRBu/6czfqqf4kYQ==} + '@perennial/oracle@1.3.0-rc3': + resolution: {integrity: sha512-zadEHH6yqiWfXHngMsiUwYBNwJSzAaBAPPBMELCgjm5i+Zo0J/X04u2DsVJdcUvTbNTmB0bqgvSM6sK44VLD9A==} '@perennial/vault@1.3.0-rc1': resolution: {integrity: sha512-2u/k5AVc94m62d/TkyejyPz8FCMAoqtE4FMIrQtVYJgtgE9UuJFJbu293ps6OIRq0muXC/HluVEOmlZtNeaDng==} @@ -4842,7 +4842,7 @@ snapshots: '@chainlink/contracts': 0.3.1 '@perennial/verifier': 1.3.0-rc1 - '@perennial/oracle@1.3.0-rc2': + '@perennial/oracle@1.3.0-rc3': dependencies: '@perennial/core': 1.3.0-rc1 diff --git a/scripts/lensAbiCopy.js b/scripts/lensAbiCopy.js index d455895..0a6b2e6 100644 --- a/scripts/lensAbiCopy.js +++ b/scripts/lensAbiCopy.js @@ -2,7 +2,7 @@ const fs = require('fs') const path = require('path') -const lenses = ['Lens', 'VaultLens'] +const lenses = ['Lens', 'VaultLens', 'MarketMetadataLens'] lenses.forEach((lens) => { const lensJsonPath = path.join(__dirname, `../artifacts/contracts/Lens.sol/${lens}.json`) diff --git a/src/index.ts b/src/index.ts index 5d40b95..c0525fc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,6 +12,7 @@ export { type MarketSnapshots, fetchMarketOracles, fetchMarketSnapshots, + fetchMarketSettlementFees, } from './lib/markets/chain' // Market - Graph diff --git a/src/lib/markets/chain.ts b/src/lib/markets/chain.ts index 1c75d3f..8c52a51 100644 --- a/src/lib/markets/chain.ts +++ b/src/lib/markets/chain.ts @@ -12,18 +12,13 @@ import { addressToMarket, calculateFundingAndInterestForSides, chainMarketsWithAddress, + notEmpty, } from '../..' import { GasOracleAbi } from '../../abi/GasOracle.abi' import { LensAbi, LensDeployedBytecode } from '../../abi/Lens.abi' +import { MarketMetadataLensAbi, MarketMetadataLensDeployedBytecode } from '../../abi/MarketMetadataLens.abi' import { SupportedMarketMapping } from '../../constants' import { calcLeverage, calcNotional, getStatusForSnapshot, sideFromPosition } from '../../utils/positionUtils' -import { - getKeeperFactoryContract, - getKeeperOracleContract, - getMarketContract, - getOracleContract, - getOracleFactoryContract, -} from '../contracts' import { OracleClients, marketOraclesToUpdateDataRequest, oracleCommitmentsLatest } from '../oracle' export type MarketOracles = NonNullable>> @@ -39,63 +34,57 @@ export async function fetchMarketOracles( publicClient: PublicClient, markets?: SupportedMarket[], ) { - // TODO: Convert this to a Lens call? const marketsWithAddress = chainMarketsWithAddress(chainId, markets) - const fetchMarketOracles = async (market: SupportedMarket, marketAddress: Address) => { - const marketContract = getMarketContract(marketAddress, publicClient) - const [riskParameter, oracleAddress] = await Promise.all([ - marketContract.read.riskParameter(), - marketContract.read.oracle(), - ]) - // Fetch oracle data - const oracle = getOracleContract(oracleAddress, publicClient) - const [global, oracleName] = await Promise.all([oracle.read.global(), oracle.read.name()]) - const [keeperOracle] = await oracle.read.oracles([global[0]]) - const keeperOracleContract = getKeeperOracleContract(keeperOracle, publicClient) - const subOracleFactory = getKeeperFactoryContract(await keeperOracleContract.read.factory(), publicClient) + const marketMetadataLens = getContractAddress({ from: zeroAddress, nonce: MaxUint256 }) + const lensResult = await publicClient.readContract({ + address: marketMetadataLens, + abi: MarketMetadataLensAbi, + functionName: 'metadata', + args: [marketsWithAddress.map(({ marketAddress }) => marketAddress)], + stateOverride: [ + { + address: marketMetadataLens, + code: MarketMetadataLensDeployedBytecode, + balance: maxUint256, + }, + ], + }) - const oracleFactory = getOracleFactoryContract(chainId, publicClient) - const id = await oracleFactory.read.ids([oracleAddress]) - const [parameter, underlyingId, subOracleFactoryType, commitmentGasOracle, settlementGasOracle] = await Promise.all( - [ - subOracleFactory.read.parameter(), - subOracleFactory.read.toUnderlyingId([id]), - subOracleFactory.read.factoryType(), - subOracleFactory.read.commitmentGasOracle(), - subOracleFactory.read.settlementGasOracle(), - ], - ) + const parseLensResult = (market: SupportedMarket, marketAddress: Address) => { + const marketLensResult = lensResult.find((result) => result.marketAddress === marketAddress) + if (!marketLensResult) return return { market, marketAddress, - oracleName, - oracleFactoryAddress: oracleFactory.address, - oracleAddress, - subOracleFactoryAddress: subOracleFactory.address, - subOracleAddress: keeperOracle, - subOracleFactoryType, - id, - underlyingId, - minValidTime: parameter.validFrom, - staleAfter: riskParameter.staleAfter, - commitmentGasOracle, - settlementGasOracle, + oracleName: marketLensResult.name, + oracleFactoryAddress: marketLensResult.oracleFactory, + oracleAddress: marketLensResult.oracle, + subOracleFactoryAddress: marketLensResult.subOracleFactory, + subOracleAddress: marketLensResult.subOracle, + subOracleFactoryType: marketLensResult.subOracleFactoryType, + id: marketLensResult.oracleId, + underlyingId: marketLensResult.oracleUnderlyingId, + minValidTime: marketLensResult.subOracleFactoryParameter.validFrom, + staleAfter: marketLensResult.riskParameter.staleAfter, + maxSettlementFee: marketLensResult.oracleFactoryParameter.maxSettlementFee, + commitmentGasOracle: marketLensResult.commitmentGasOracle, + settlementGasOracle: marketLensResult.settlementGasOracle, } } - const marketData = await Promise.all( - marketsWithAddress.map(({ market, marketAddress }) => { - return fetchMarketOracles(market, marketAddress) - }), - ) + const marketData = marketsWithAddress + .map(({ market, marketAddress }) => { + return parseLensResult(market, marketAddress) + }) + .filter(notEmpty) return marketData.reduce( (acc, market) => { acc[market.market] = market return acc }, - {} as SupportedMarketMapping>>, + {} as SupportedMarketMapping>>>, ) } @@ -438,10 +427,12 @@ export async function fetchMarketSettlementFees({ (acc, market) => { const commitmentCost = commitmentCostCache.get(marketOracles[market].commitmentGasOracle) ?? 0n const settlementCost = settlementCostCache.get(marketOracles[market].settlementGasOracle) ?? 0n + const maxCost = marketOracles[market].maxSettlementFee + acc[market] = { - commitmentCost, - settlementCost, - totalCost: commitmentCost + settlementCost, + commitmentCost: Big6Math.min(commitmentCost, maxCost), + settlementCost: Big6Math.min(settlementCost, maxCost), + totalCost: Big6Math.min(commitmentCost + settlementCost, maxCost), } return acc }, diff --git a/src/lib/markets/index.ts b/src/lib/markets/index.ts index aefb7c6..6258376 100644 --- a/src/lib/markets/index.ts +++ b/src/lib/markets/index.ts @@ -299,7 +299,7 @@ export class MarketsModule { * @param isMaker boolean - Filter for maker orders * @returns User's open orders. */ - openOrders: (args: OmitBound[0]> & OptionalAddress & OptionalMarkets) => { + openOrders: (args: OmitBound[0]> & OptionalAddress & OptionalMarkets = {}) => { if (!this.config.graphClient) { throw new Error('Graph client required to fetch open orders.') } From 47ef99aa1ac9036c84814d9e3d1ffad5edcea3fa Mon Sep 17 00:00:00 2001 From: Arjun Rao <2940142+arjun-io@users.noreply.github.com> Date: Wed, 30 Oct 2024 14:19:04 -0400 Subject: [PATCH 2/5] 0.0.3-beta.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f0c9c84..46757e8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@perennial/sdk", - "version": "0.0.3-beta.1", + "version": "0.0.3-beta.2", "description": "", "main": "dist/index.js", "types": "dist/index.d.ts", From 3f70b6e534408124ecbed866325851187906b335 Mon Sep 17 00:00:00 2001 From: Arjun Rao <2940142+arjun-io@users.noreply.github.com> Date: Wed, 30 Oct 2024 14:24:54 -0400 Subject: [PATCH 3/5] fix format --- src/utils/big6Utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/big6Utils.ts b/src/utils/big6Utils.ts index b401a49..ccf963f 100644 --- a/src/utils/big6Utils.ts +++ b/src/utils/big6Utils.ts @@ -148,8 +148,8 @@ export class Big6Math { public static fromDecimals(amount: bigint, decimals: number): bigint { const exponent = BigInt(Big6Math.FIXED_DECIMALS - decimals) - if (exponent >= 0n) return amount * (10n ** exponent) - return amount / (10n ** (exponent * -1n)) + if (exponent >= 0n) return amount * 10n ** exponent + return amount / 10n ** (exponent * -1n) } public static to18Decimals(amount: bigint): bigint { From 4729b4868acf7f906dc47494c5829fd1c445805a Mon Sep 17 00:00:00 2001 From: Arjun Rao <2940142+arjun-io@users.noreply.github.com> Date: Wed, 30 Oct 2024 14:27:05 -0400 Subject: [PATCH 4/5] fix pre-commit hook --- .husky/{precommit => pre-commit} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .husky/{precommit => pre-commit} (100%) diff --git a/.husky/precommit b/.husky/pre-commit similarity index 100% rename from .husky/precommit rename to .husky/pre-commit From 047158ee606fbc94ec19ce76e1f79e8bbf850e07 Mon Sep 17 00:00:00 2001 From: Arjun Rao <2940142+arjun-io@users.noreply.github.com> Date: Wed, 30 Oct 2024 14:28:33 -0400 Subject: [PATCH 5/5] update codegen --- graph-codegen.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graph-codegen.ts b/graph-codegen.ts index ddd1352..0d00ae5 100644 --- a/graph-codegen.ts +++ b/graph-codegen.ts @@ -5,7 +5,7 @@ dotenvConfig({ path: './.env.local' }) // point to root of project for now const config: CodegenConfig = { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - schema: [process.env.GRAPH_URL_ARBITRUM!, process.env.GRAPH_URL_ARBITRUM_SEPOLIA!], + schema: [process.env.GRAPH_URL_ARBITRUM!], documents: ['src/graphQueries/**/*.{ts,tsx}'], generates: { './src/types/gql/': {