From 1379810cc37bf3ab08c9f1feb926751076d5a1eb Mon Sep 17 00:00:00 2001 From: karen-stepanyan <91897037+karen-stepanyan@users.noreply.github.com> Date: Tue, 2 Jan 2024 19:17:48 +0400 Subject: [PATCH] Added gm-token EA (#3123) * add gm-token EA * add gm-token ea to soakTestBlacklist * change response to include sources for prices * regenerate readme * change error message * remove unused property from PriceData type --- .changeset/neat-dryers-repair.md | 5 + .pnp.cjs | 22 + packages/composites/gm-token/CHANGELOG.md | 0 packages/composites/gm-token/README.md | 67 + packages/composites/gm-token/package.json | 42 + .../composites/gm-token/src/config/index.ts | 70 + .../gm-token/src/config/readerAbi.json | 3748 +++++++++++++++++ .../composites/gm-token/src/endpoint/index.ts | 1 + .../composites/gm-token/src/endpoint/price.ts | 59 + packages/composites/gm-token/src/index.ts | 13 + .../gm-token/src/transport/price.ts | 289 ++ .../gm-token/src/transport/utils.ts | 67 + .../composites/gm-token/test-payload.json | 8 + .../__snapshots__/adapter.test.ts.snap | 38 + .../gm-token/test/integration/adapter.test.ts | 81 + .../gm-token/test/integration/fixtures.ts | 389 ++ .../gm-token/test/unit/utils.test.ts | 19 + packages/composites/gm-token/tsconfig.json | 9 + .../composites/gm-token/tsconfig.test.json | 7 + .../get-changed-adapters/soakTestBlacklist.ts | 1 + packages/tsconfig.json | 3 + packages/tsconfig.test.json | 3 + yarn.lock | 15 + 23 files changed, 4956 insertions(+) create mode 100644 .changeset/neat-dryers-repair.md create mode 100644 packages/composites/gm-token/CHANGELOG.md create mode 100644 packages/composites/gm-token/README.md create mode 100644 packages/composites/gm-token/package.json create mode 100644 packages/composites/gm-token/src/config/index.ts create mode 100644 packages/composites/gm-token/src/config/readerAbi.json create mode 100644 packages/composites/gm-token/src/endpoint/index.ts create mode 100644 packages/composites/gm-token/src/endpoint/price.ts create mode 100644 packages/composites/gm-token/src/index.ts create mode 100644 packages/composites/gm-token/src/transport/price.ts create mode 100644 packages/composites/gm-token/src/transport/utils.ts create mode 100644 packages/composites/gm-token/test-payload.json create mode 100644 packages/composites/gm-token/test/integration/__snapshots__/adapter.test.ts.snap create mode 100644 packages/composites/gm-token/test/integration/adapter.test.ts create mode 100644 packages/composites/gm-token/test/integration/fixtures.ts create mode 100644 packages/composites/gm-token/test/unit/utils.test.ts create mode 100644 packages/composites/gm-token/tsconfig.json create mode 100644 packages/composites/gm-token/tsconfig.test.json diff --git a/.changeset/neat-dryers-repair.md b/.changeset/neat-dryers-repair.md new file mode 100644 index 0000000000..89a9f8eb49 --- /dev/null +++ b/.changeset/neat-dryers-repair.md @@ -0,0 +1,5 @@ +--- +'@chainlink/gm-token-adapter': major +--- + +Initial version of the EA diff --git a/.pnp.cjs b/.pnp.cjs index 362e42555d..bca0e53bd2 100644 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -71,6 +71,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "name": "@chainlink/dydx-rewards-adapter",\ "reference": "workspace:packages/composites/dydx-rewards"\ },\ + {\ + "name": "@chainlink/gm-token-adapter",\ + "reference": "workspace:packages/composites/gm-token"\ + },\ {\ "name": "@chainlink/google-weather-adapter",\ "reference": "workspace:packages/composites/google-weather"\ @@ -933,6 +937,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@chainlink/gemini-adapter", ["workspace:packages/sources/gemini"]],\ ["@chainlink/genesis-volatility-adapter", ["workspace:packages/sources/genesis-volatility"]],\ ["@chainlink/geodb-adapter", ["workspace:packages/sources/geodb"]],\ + ["@chainlink/gm-token-adapter", ["workspace:packages/composites/gm-token"]],\ ["@chainlink/google-bigquery-adapter", ["workspace:packages/sources/google-bigquery"]],\ ["@chainlink/google-weather-adapter", ["workspace:packages/composites/google-weather"]],\ ["@chainlink/gramchain-adapter", ["workspace:packages/sources/gramchain"]],\ @@ -6549,6 +6554,23 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "SOFT"\ }]\ ]],\ + ["@chainlink/gm-token-adapter", [\ + ["workspace:packages/composites/gm-token", {\ + "packageLocation": "./packages/composites/gm-token/",\ + "packageDependencies": [\ + ["@chainlink/gm-token-adapter", "workspace:packages/composites/gm-token"],\ + ["@chainlink/external-adapter-framework", "npm:0.33.1"],\ + ["@types/jest", "npm:27.5.2"],\ + ["@types/node", "npm:16.11.51"],\ + ["decimal.js", "npm:10.4.3"],\ + ["ethers", "npm:5.7.2"],\ + ["nock", "npm:13.2.9"],\ + ["tslib", "npm:2.4.1"],\ + ["typescript", "patch:typescript@npm%3A5.0.4#~builtin::version=5.0.4&hash=b5f058"]\ + ],\ + "linkType": "SOFT"\ + }]\ + ]],\ ["@chainlink/google-bigquery-adapter", [\ ["workspace:packages/sources/google-bigquery", {\ "packageLocation": "./packages/sources/google-bigquery/",\ diff --git a/packages/composites/gm-token/CHANGELOG.md b/packages/composites/gm-token/CHANGELOG.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/composites/gm-token/README.md b/packages/composites/gm-token/README.md new file mode 100644 index 0000000000..0efd0b9c47 --- /dev/null +++ b/packages/composites/gm-token/README.md @@ -0,0 +1,67 @@ +# GM_TOKEN + +![0.0.0](https://img.shields.io/github/package-json/v/smartcontractkit/external-adapters-js?filename=packages/composites/gm-token/package.json) ![v3](https://img.shields.io/badge/framework%20version-v3-blueviolet) + +This document was generated automatically. Please see [README Generator](../../scripts#readme-generator) for more info. + +## Environment Variables + +| Required? | Name | Description | Type | Options | Default | +| :-------: | :-------------------------: | :---------------------------------------------------------------------------------------: | :----: | :-----: | :------------------------------------------: | +| ✅ | ARBITRUM_RPC_URL | RPC url of Arbitrum node | string | | | +| ✅ | ARBITRUM_CHAIN_ID | The chain id to connect to | number | | `42161` | +| ✅ | DATASTORE_CONTRACT_ADDRESS | Address of Data Store contract | string | | `0xFD70de6b91282D8017aA4E741e9Ae325CAb992d8` | +| ✅ | READER_CONTRACT_ADDRESS | Address of Reader contract | string | | `0xf60becbba223EEA9495Da3f606753867eC10d139` | +| ✅ | PNL_FACTOR_TYPE | PnL factor type. See https://github.com/gmx-io/gmx-synthetics#market-token-price | string | | `MAX_PNL_FACTOR_FOR_TRADERS` | +| ✅ | TIINGO_ADAPTER_URL | URL of Tiingo EA | string | | | +| ✅ | NCFX_ADAPTER_URL | URL of NCFX EA | string | | | +| ✅ | COINMETRICS_ADAPTER_URL | URL of Coinmetrics EA | string | | | +| ✅ | MIN_REQUIRED_SOURCE_SUCCESS | Minimum number of source EAs that need to successfully return a value. | number | | `2` | +| | BACKGROUND_EXECUTE_MS | The amount of time the background execute should sleep before performing the next request | number | | `10000` | + +--- + +## Data Provider Rate Limits + +There are no rate limits for this adapter. + +--- + +## Input Parameters + +| Required? | Name | Description | Type | Options | Default | +| :-------: | :------: | :-----------------: | :----: | :----------------------: | :-----: | +| | endpoint | The endpoint to use | string | [price](#price-endpoint) | `price` | + +## Price Endpoint + +`price` is the only supported name for this endpoint. + +### Input Params + +| Required? | Name | Aliases | Description | Type | Options | Default | Depends On | Not Valid With | +| :-------: | :----: | :-----: | :----------------------------------------------------------------------------------: | :----: | :---------------------------------------------------------------------: | :-----: | :--------: | :------------: | +| ✅ | index | | Index token. Long and short tokens will be opened / closed based on this price feed. | string | `ARB`, `BTC`, `DOGE`, `ETH`, `LINK`, `LTC`, `SOL`, `UNI`, `WETH`, `XRP` | | | | +| ✅ | long | | Long token. This is the token that will back long positions. | string | `ARB`, `ETH`, `LINK`, `SOL`, `UNI`, `WBTC.b`, `WETH` | | | | +| ✅ | short | | Short token. This is the token that will back short positions. | string | `USDC` | | | | +| ✅ | market | | Market address of the market pool. | string | | | | | + +### Example + +Request: + +```json +{ + "data": { + "endpoint": "price", + "index": "LINK", + "long": "LINK", + "short": "USDC", + "market": "0x7f1fa204bb700853D36994DA19F830b6Ad18455C" + } +} +``` + +--- + +MIT License diff --git a/packages/composites/gm-token/package.json b/packages/composites/gm-token/package.json new file mode 100644 index 0000000000..6fc0f36772 --- /dev/null +++ b/packages/composites/gm-token/package.json @@ -0,0 +1,42 @@ +{ + "name": "@chainlink/gm-token-adapter", + "version": "0.0.0", + "description": "Chainlink gm-token adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle", + "gm-token" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, + "license": "MIT", + "scripts": { + "clean": "rm -rf dist && rm -f tsconfig.tsbuildinfo", + "prepack": "yarn build", + "build": "tsc -b", + "server": "node -e 'require(\"./index.js\").server()'", + "server:dist": "node -e 'require(\"./dist/index.js\").server()'", + "start": "yarn server:dist" + }, + "devDependencies": { + "@types/jest": "27.5.2", + "@types/node": "16.11.51", + "nock": "13.2.9", + "typescript": "5.0.4" + }, + "dependencies": { + "@chainlink/external-adapter-framework": "0.33.1", + "decimal.js": "^10.3.1", + "ethers": "^5.4.6", + "tslib": "2.4.1" + } +} diff --git a/packages/composites/gm-token/src/config/index.ts b/packages/composites/gm-token/src/config/index.ts new file mode 100644 index 0000000000..82704c148f --- /dev/null +++ b/packages/composites/gm-token/src/config/index.ts @@ -0,0 +1,70 @@ +import { AdapterConfig } from '@chainlink/external-adapter-framework/config' +import { validator } from '@chainlink/external-adapter-framework/validation/utils' + +export const config = new AdapterConfig( + { + ARBITRUM_RPC_URL: { + description: 'RPC url of Arbitrum node', + type: 'string', + required: true, + }, + ARBITRUM_CHAIN_ID: { + description: 'The chain id to connect to', + type: 'number', + required: true, + default: 42161, + }, + DATASTORE_CONTRACT_ADDRESS: { + description: 'Address of Data Store contract', + type: 'string', + required: true, + default: '0xFD70de6b91282D8017aA4E741e9Ae325CAb992d8', + }, + READER_CONTRACT_ADDRESS: { + description: 'Address of Reader contract', + type: 'string', + required: true, + default: '0xf60becbba223EEA9495Da3f606753867eC10d139', + }, + PNL_FACTOR_TYPE: { + description: + 'PnL factor type. See https://github.com/gmx-io/gmx-synthetics#market-token-price', + type: 'string', + required: true, + default: 'MAX_PNL_FACTOR_FOR_TRADERS', + }, + TIINGO_ADAPTER_URL: { + description: 'URL of Tiingo EA', + type: 'string', + required: true, + }, + NCFX_ADAPTER_URL: { + description: 'URL of NCFX EA', + type: 'string', + required: true, + }, + COINMETRICS_ADAPTER_URL: { + description: 'URL of Coinmetrics EA', + type: 'string', + required: true, + }, + MIN_REQUIRED_SOURCE_SUCCESS: { + description: 'Minimum number of source EAs that need to successfully return a value.', + type: 'number', + required: true, + default: 2, + validate: validator.integer({ min: 1, max: 3 }), + }, + BACKGROUND_EXECUTE_MS: { + description: + 'The amount of time the background execute should sleep before performing the next request', + type: 'number', + default: 10_000, + }, + }, + { + envDefaultOverrides: { + RETRY: 3, + }, + }, +) diff --git a/packages/composites/gm-token/src/config/readerAbi.json b/packages/composites/gm-token/src/config/readerAbi.json new file mode 100644 index 0000000000..328edddf7e --- /dev/null +++ b/packages/composites/gm-token/src/config/readerAbi.json @@ -0,0 +1,3748 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "market", + "type": "address" + } + ], + "name": "DisabledMarket", + "type": "error" + }, + { + "inputs": [], + "name": "EmptyMarket", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "contract DataStore", + "name": "dataStore", + "type": "address" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "start", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "end", + "type": "uint256" + } + ], + "name": "getAccountOrders", + "outputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "address", + "name": "callbackContract", + "type": "address" + }, + { + "internalType": "address", + "name": "uiFeeReceiver", + "type": "address" + }, + { + "internalType": "address", + "name": "market", + "type": "address" + }, + { + "internalType": "address", + "name": "initialCollateralToken", + "type": "address" + }, + { + "internalType": "address[]", + "name": "swapPath", + "type": "address[]" + } + ], + "internalType": "struct Order.Addresses", + "name": "addresses", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "enum Order.OrderType", + "name": "orderType", + "type": "uint8" + }, + { + "internalType": "enum Order.DecreasePositionSwapType", + "name": "decreasePositionSwapType", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "sizeDeltaUsd", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialCollateralDeltaAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "triggerPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "acceptablePrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "executionFee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "callbackGasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minOutputAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAtBlock", + "type": "uint256" + } + ], + "internalType": "struct Order.Numbers", + "name": "numbers", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bool", + "name": "isLong", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldUnwrapNativeToken", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isFrozen", + "type": "bool" + } + ], + "internalType": "struct Order.Flags", + "name": "flags", + "type": "tuple" + } + ], + "internalType": "struct Order.Props[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract DataStore", + "name": "dataStore", + "type": "address" + }, + { + "internalType": "contract IReferralStorage", + "name": "referralStorage", + "type": "address" + }, + { + "internalType": "bytes32[]", + "name": "positionKeys", + "type": "bytes32[]" + }, + { + "components": [ + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "indexTokenPrice", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "longTokenPrice", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "shortTokenPrice", + "type": "tuple" + } + ], + "internalType": "struct MarketUtils.MarketPrices[]", + "name": "prices", + "type": "tuple[]" + }, + { + "internalType": "address", + "name": "uiFeeReceiver", + "type": "address" + } + ], + "name": "getAccountPositionInfoList", + "outputs": [ + { + "components": [ + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "market", + "type": "address" + }, + { + "internalType": "address", + "name": "collateralToken", + "type": "address" + } + ], + "internalType": "struct Position.Addresses", + "name": "addresses", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "sizeInUsd", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "sizeInTokens", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "collateralAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowingFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fundingFeeAmountPerSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "longTokenClaimableFundingAmountPerSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortTokenClaimableFundingAmountPerSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "increasedAtBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "decreasedAtBlock", + "type": "uint256" + } + ], + "internalType": "struct Position.Numbers", + "name": "numbers", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bool", + "name": "isLong", + "type": "bool" + } + ], + "internalType": "struct Position.Flags", + "name": "flags", + "type": "tuple" + } + ], + "internalType": "struct Position.Props", + "name": "position", + "type": "tuple" + }, + { + "components": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "referralCode", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "affiliate", + "type": "address" + }, + { + "internalType": "address", + "name": "trader", + "type": "address" + }, + { + "internalType": "uint256", + "name": "totalRebateFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "traderDiscountFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalRebateAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "traderDiscountAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "affiliateRewardAmount", + "type": "uint256" + } + ], + "internalType": "struct PositionPricingUtils.PositionReferralFees", + "name": "referral", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "fundingFeeAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "claimableLongTokenAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "claimableShortTokenAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "latestFundingFeeAmountPerSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "latestLongTokenClaimableFundingAmountPerSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "latestShortTokenClaimableFundingAmountPerSize", + "type": "uint256" + } + ], + "internalType": "struct PositionPricingUtils.PositionFundingFees", + "name": "funding", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "borrowingFeeUsd", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowingFeeAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowingFeeReceiverFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowingFeeAmountForFeeReceiver", + "type": "uint256" + } + ], + "internalType": "struct PositionPricingUtils.PositionBorrowingFees", + "name": "borrowing", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "uiFeeReceiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "uiFeeReceiverFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "uiFeeAmount", + "type": "uint256" + } + ], + "internalType": "struct PositionPricingUtils.PositionUiFees", + "name": "ui", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "collateralTokenPrice", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "positionFeeFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "protocolFeeAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "positionFeeReceiverFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeReceiverAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeAmountForPool", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "positionFeeAmountForPool", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "positionFeeAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalCostAmountExcludingFunding", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalCostAmount", + "type": "uint256" + } + ], + "internalType": "struct PositionPricingUtils.PositionFees", + "name": "fees", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "int256", + "name": "priceImpactUsd", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "priceImpactDiffUsd", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "executionPrice", + "type": "uint256" + } + ], + "internalType": "struct ReaderPricingUtils.ExecutionPriceResult", + "name": "executionPriceResult", + "type": "tuple" + }, + { + "internalType": "int256", + "name": "basePnlUsd", + "type": "int256" + }, + { + "internalType": "int256", + "name": "uncappedBasePnlUsd", + "type": "int256" + }, + { + "internalType": "int256", + "name": "pnlAfterPriceImpactUsd", + "type": "int256" + } + ], + "internalType": "struct ReaderUtils.PositionInfo[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract DataStore", + "name": "dataStore", + "type": "address" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "start", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "end", + "type": "uint256" + } + ], + "name": "getAccountPositions", + "outputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "market", + "type": "address" + }, + { + "internalType": "address", + "name": "collateralToken", + "type": "address" + } + ], + "internalType": "struct Position.Addresses", + "name": "addresses", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "sizeInUsd", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "sizeInTokens", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "collateralAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowingFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fundingFeeAmountPerSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "longTokenClaimableFundingAmountPerSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortTokenClaimableFundingAmountPerSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "increasedAtBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "decreasedAtBlock", + "type": "uint256" + } + ], + "internalType": "struct Position.Numbers", + "name": "numbers", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bool", + "name": "isLong", + "type": "bool" + } + ], + "internalType": "struct Position.Flags", + "name": "flags", + "type": "tuple" + } + ], + "internalType": "struct Position.Props[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract DataStore", + "name": "dataStore", + "type": "address" + }, + { + "internalType": "address", + "name": "market", + "type": "address" + }, + { + "internalType": "bool", + "name": "isLong", + "type": "bool" + }, + { + "components": [ + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "indexTokenPrice", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "longTokenPrice", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "shortTokenPrice", + "type": "tuple" + } + ], + "internalType": "struct MarketUtils.MarketPrices", + "name": "prices", + "type": "tuple" + } + ], + "name": "getAdlState", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "int256", + "name": "", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract DataStore", + "name": "dataStore", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "key", + "type": "bytes32" + } + ], + "name": "getDeposit", + "outputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "address", + "name": "callbackContract", + "type": "address" + }, + { + "internalType": "address", + "name": "uiFeeReceiver", + "type": "address" + }, + { + "internalType": "address", + "name": "market", + "type": "address" + }, + { + "internalType": "address", + "name": "initialLongToken", + "type": "address" + }, + { + "internalType": "address", + "name": "initialShortToken", + "type": "address" + }, + { + "internalType": "address[]", + "name": "longTokenSwapPath", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "shortTokenSwapPath", + "type": "address[]" + } + ], + "internalType": "struct Deposit.Addresses", + "name": "addresses", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "initialLongTokenAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialShortTokenAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minMarketTokens", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAtBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "executionFee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "callbackGasLimit", + "type": "uint256" + } + ], + "internalType": "struct Deposit.Numbers", + "name": "numbers", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bool", + "name": "shouldUnwrapNativeToken", + "type": "bool" + } + ], + "internalType": "struct Deposit.Flags", + "name": "flags", + "type": "tuple" + } + ], + "internalType": "struct Deposit.Props", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract DataStore", + "name": "dataStore", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "marketToken", + "type": "address" + }, + { + "internalType": "address", + "name": "indexToken", + "type": "address" + }, + { + "internalType": "address", + "name": "longToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortToken", + "type": "address" + } + ], + "internalType": "struct Market.Props", + "name": "market", + "type": "tuple" + }, + { + "components": [ + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "indexTokenPrice", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "longTokenPrice", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "shortTokenPrice", + "type": "tuple" + } + ], + "internalType": "struct MarketUtils.MarketPrices", + "name": "prices", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "longTokenAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortTokenAmount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "uiFeeReceiver", + "type": "address" + } + ], + "name": "getDepositAmountOut", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract DataStore", + "name": "dataStore", + "type": "address" + }, + { + "internalType": "address", + "name": "marketKey", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "indexTokenPrice", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "positionSizeInUsd", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "positionSizeInTokens", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "sizeDeltaUsd", + "type": "int256" + }, + { + "internalType": "bool", + "name": "isLong", + "type": "bool" + } + ], + "name": "getExecutionPrice", + "outputs": [ + { + "components": [ + { + "internalType": "int256", + "name": "priceImpactUsd", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "priceImpactDiffUsd", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "executionPrice", + "type": "uint256" + } + ], + "internalType": "struct ReaderPricingUtils.ExecutionPriceResult", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract DataStore", + "name": "dataStore", + "type": "address" + }, + { + "internalType": "address", + "name": "key", + "type": "address" + } + ], + "name": "getMarket", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "marketToken", + "type": "address" + }, + { + "internalType": "address", + "name": "indexToken", + "type": "address" + }, + { + "internalType": "address", + "name": "longToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortToken", + "type": "address" + } + ], + "internalType": "struct Market.Props", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract DataStore", + "name": "dataStore", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + } + ], + "name": "getMarketBySalt", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "marketToken", + "type": "address" + }, + { + "internalType": "address", + "name": "indexToken", + "type": "address" + }, + { + "internalType": "address", + "name": "longToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortToken", + "type": "address" + } + ], + "internalType": "struct Market.Props", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract DataStore", + "name": "dataStore", + "type": "address" + }, + { + "components": [ + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "indexTokenPrice", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "longTokenPrice", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "shortTokenPrice", + "type": "tuple" + } + ], + "internalType": "struct MarketUtils.MarketPrices", + "name": "prices", + "type": "tuple" + }, + { + "internalType": "address", + "name": "marketKey", + "type": "address" + } + ], + "name": "getMarketInfo", + "outputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "marketToken", + "type": "address" + }, + { + "internalType": "address", + "name": "indexToken", + "type": "address" + }, + { + "internalType": "address", + "name": "longToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortToken", + "type": "address" + } + ], + "internalType": "struct Market.Props", + "name": "market", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "borrowingFactorPerSecondForLongs", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowingFactorPerSecondForShorts", + "type": "uint256" + }, + { + "components": [ + { + "components": [ + { + "components": [ + { + "internalType": "uint256", + "name": "longToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortToken", + "type": "uint256" + } + ], + "internalType": "struct MarketUtils.CollateralType", + "name": "long", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "longToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortToken", + "type": "uint256" + } + ], + "internalType": "struct MarketUtils.CollateralType", + "name": "short", + "type": "tuple" + } + ], + "internalType": "struct MarketUtils.PositionType", + "name": "fundingFeeAmountPerSize", + "type": "tuple" + }, + { + "components": [ + { + "components": [ + { + "internalType": "uint256", + "name": "longToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortToken", + "type": "uint256" + } + ], + "internalType": "struct MarketUtils.CollateralType", + "name": "long", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "longToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortToken", + "type": "uint256" + } + ], + "internalType": "struct MarketUtils.CollateralType", + "name": "short", + "type": "tuple" + } + ], + "internalType": "struct MarketUtils.PositionType", + "name": "claimableFundingAmountPerSize", + "type": "tuple" + } + ], + "internalType": "struct ReaderUtils.BaseFundingValues", + "name": "baseFunding", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bool", + "name": "longsPayShorts", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "fundingFactorPerSecond", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "nextSavedFundingFactorPerSecond", + "type": "int256" + }, + { + "components": [ + { + "components": [ + { + "internalType": "uint256", + "name": "longToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortToken", + "type": "uint256" + } + ], + "internalType": "struct MarketUtils.CollateralType", + "name": "long", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "longToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortToken", + "type": "uint256" + } + ], + "internalType": "struct MarketUtils.CollateralType", + "name": "short", + "type": "tuple" + } + ], + "internalType": "struct MarketUtils.PositionType", + "name": "fundingFeeAmountPerSizeDelta", + "type": "tuple" + }, + { + "components": [ + { + "components": [ + { + "internalType": "uint256", + "name": "longToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortToken", + "type": "uint256" + } + ], + "internalType": "struct MarketUtils.CollateralType", + "name": "long", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "longToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortToken", + "type": "uint256" + } + ], + "internalType": "struct MarketUtils.CollateralType", + "name": "short", + "type": "tuple" + } + ], + "internalType": "struct MarketUtils.PositionType", + "name": "claimableFundingAmountPerSizeDelta", + "type": "tuple" + } + ], + "internalType": "struct MarketUtils.GetNextFundingAmountPerSizeResult", + "name": "nextFunding", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "virtualPoolAmountForLongToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "virtualPoolAmountForShortToken", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "virtualInventoryForPositions", + "type": "int256" + } + ], + "internalType": "struct ReaderUtils.VirtualInventory", + "name": "virtualInventory", + "type": "tuple" + }, + { + "internalType": "bool", + "name": "isDisabled", + "type": "bool" + } + ], + "internalType": "struct ReaderUtils.MarketInfo", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract DataStore", + "name": "dataStore", + "type": "address" + }, + { + "components": [ + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "indexTokenPrice", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "longTokenPrice", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "shortTokenPrice", + "type": "tuple" + } + ], + "internalType": "struct MarketUtils.MarketPrices[]", + "name": "marketPricesList", + "type": "tuple[]" + }, + { + "internalType": "uint256", + "name": "start", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "end", + "type": "uint256" + } + ], + "name": "getMarketInfoList", + "outputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "marketToken", + "type": "address" + }, + { + "internalType": "address", + "name": "indexToken", + "type": "address" + }, + { + "internalType": "address", + "name": "longToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortToken", + "type": "address" + } + ], + "internalType": "struct Market.Props", + "name": "market", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "borrowingFactorPerSecondForLongs", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowingFactorPerSecondForShorts", + "type": "uint256" + }, + { + "components": [ + { + "components": [ + { + "components": [ + { + "internalType": "uint256", + "name": "longToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortToken", + "type": "uint256" + } + ], + "internalType": "struct MarketUtils.CollateralType", + "name": "long", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "longToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortToken", + "type": "uint256" + } + ], + "internalType": "struct MarketUtils.CollateralType", + "name": "short", + "type": "tuple" + } + ], + "internalType": "struct MarketUtils.PositionType", + "name": "fundingFeeAmountPerSize", + "type": "tuple" + }, + { + "components": [ + { + "components": [ + { + "internalType": "uint256", + "name": "longToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortToken", + "type": "uint256" + } + ], + "internalType": "struct MarketUtils.CollateralType", + "name": "long", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "longToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortToken", + "type": "uint256" + } + ], + "internalType": "struct MarketUtils.CollateralType", + "name": "short", + "type": "tuple" + } + ], + "internalType": "struct MarketUtils.PositionType", + "name": "claimableFundingAmountPerSize", + "type": "tuple" + } + ], + "internalType": "struct ReaderUtils.BaseFundingValues", + "name": "baseFunding", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bool", + "name": "longsPayShorts", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "fundingFactorPerSecond", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "nextSavedFundingFactorPerSecond", + "type": "int256" + }, + { + "components": [ + { + "components": [ + { + "internalType": "uint256", + "name": "longToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortToken", + "type": "uint256" + } + ], + "internalType": "struct MarketUtils.CollateralType", + "name": "long", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "longToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortToken", + "type": "uint256" + } + ], + "internalType": "struct MarketUtils.CollateralType", + "name": "short", + "type": "tuple" + } + ], + "internalType": "struct MarketUtils.PositionType", + "name": "fundingFeeAmountPerSizeDelta", + "type": "tuple" + }, + { + "components": [ + { + "components": [ + { + "internalType": "uint256", + "name": "longToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortToken", + "type": "uint256" + } + ], + "internalType": "struct MarketUtils.CollateralType", + "name": "long", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "longToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortToken", + "type": "uint256" + } + ], + "internalType": "struct MarketUtils.CollateralType", + "name": "short", + "type": "tuple" + } + ], + "internalType": "struct MarketUtils.PositionType", + "name": "claimableFundingAmountPerSizeDelta", + "type": "tuple" + } + ], + "internalType": "struct MarketUtils.GetNextFundingAmountPerSizeResult", + "name": "nextFunding", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "virtualPoolAmountForLongToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "virtualPoolAmountForShortToken", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "virtualInventoryForPositions", + "type": "int256" + } + ], + "internalType": "struct ReaderUtils.VirtualInventory", + "name": "virtualInventory", + "type": "tuple" + }, + { + "internalType": "bool", + "name": "isDisabled", + "type": "bool" + } + ], + "internalType": "struct ReaderUtils.MarketInfo[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract DataStore", + "name": "dataStore", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "marketToken", + "type": "address" + }, + { + "internalType": "address", + "name": "indexToken", + "type": "address" + }, + { + "internalType": "address", + "name": "longToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortToken", + "type": "address" + } + ], + "internalType": "struct Market.Props", + "name": "market", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "indexTokenPrice", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "longTokenPrice", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "shortTokenPrice", + "type": "tuple" + }, + { + "internalType": "bytes32", + "name": "pnlFactorType", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "maximize", + "type": "bool" + } + ], + "name": "getMarketTokenPrice", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + }, + { + "components": [ + { + "internalType": "int256", + "name": "poolValue", + "type": "int256" + }, + { + "internalType": "int256", + "name": "longPnl", + "type": "int256" + }, + { + "internalType": "int256", + "name": "shortPnl", + "type": "int256" + }, + { + "internalType": "int256", + "name": "netPnl", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "longTokenAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortTokenAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "longTokenUsd", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortTokenUsd", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalBorrowingFees", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowingFeePoolFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "impactPoolAmount", + "type": "uint256" + } + ], + "internalType": "struct MarketPoolValueInfo.Props", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract DataStore", + "name": "dataStore", + "type": "address" + }, + { + "internalType": "uint256", + "name": "start", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "end", + "type": "uint256" + } + ], + "name": "getMarkets", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "marketToken", + "type": "address" + }, + { + "internalType": "address", + "name": "indexToken", + "type": "address" + }, + { + "internalType": "address", + "name": "longToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortToken", + "type": "address" + } + ], + "internalType": "struct Market.Props[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract DataStore", + "name": "dataStore", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "marketToken", + "type": "address" + }, + { + "internalType": "address", + "name": "indexToken", + "type": "address" + }, + { + "internalType": "address", + "name": "longToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortToken", + "type": "address" + } + ], + "internalType": "struct Market.Props", + "name": "market", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "indexTokenPrice", + "type": "tuple" + }, + { + "internalType": "bool", + "name": "maximize", + "type": "bool" + } + ], + "name": "getNetPnl", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract DataStore", + "name": "dataStore", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "marketToken", + "type": "address" + }, + { + "internalType": "address", + "name": "indexToken", + "type": "address" + }, + { + "internalType": "address", + "name": "longToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortToken", + "type": "address" + } + ], + "internalType": "struct Market.Props", + "name": "market", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "indexTokenPrice", + "type": "tuple" + }, + { + "internalType": "bool", + "name": "isLong", + "type": "bool" + }, + { + "internalType": "bool", + "name": "maximize", + "type": "bool" + } + ], + "name": "getOpenInterestWithPnl", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract DataStore", + "name": "dataStore", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "key", + "type": "bytes32" + } + ], + "name": "getOrder", + "outputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "address", + "name": "callbackContract", + "type": "address" + }, + { + "internalType": "address", + "name": "uiFeeReceiver", + "type": "address" + }, + { + "internalType": "address", + "name": "market", + "type": "address" + }, + { + "internalType": "address", + "name": "initialCollateralToken", + "type": "address" + }, + { + "internalType": "address[]", + "name": "swapPath", + "type": "address[]" + } + ], + "internalType": "struct Order.Addresses", + "name": "addresses", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "enum Order.OrderType", + "name": "orderType", + "type": "uint8" + }, + { + "internalType": "enum Order.DecreasePositionSwapType", + "name": "decreasePositionSwapType", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "sizeDeltaUsd", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialCollateralDeltaAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "triggerPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "acceptablePrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "executionFee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "callbackGasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minOutputAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAtBlock", + "type": "uint256" + } + ], + "internalType": "struct Order.Numbers", + "name": "numbers", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bool", + "name": "isLong", + "type": "bool" + }, + { + "internalType": "bool", + "name": "shouldUnwrapNativeToken", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isFrozen", + "type": "bool" + } + ], + "internalType": "struct Order.Flags", + "name": "flags", + "type": "tuple" + } + ], + "internalType": "struct Order.Props", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract DataStore", + "name": "dataStore", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "marketToken", + "type": "address" + }, + { + "internalType": "address", + "name": "indexToken", + "type": "address" + }, + { + "internalType": "address", + "name": "longToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortToken", + "type": "address" + } + ], + "internalType": "struct Market.Props", + "name": "market", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "indexTokenPrice", + "type": "tuple" + }, + { + "internalType": "bool", + "name": "isLong", + "type": "bool" + }, + { + "internalType": "bool", + "name": "maximize", + "type": "bool" + } + ], + "name": "getPnl", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract DataStore", + "name": "dataStore", + "type": "address" + }, + { + "internalType": "address", + "name": "marketAddress", + "type": "address" + }, + { + "components": [ + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "indexTokenPrice", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "longTokenPrice", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "shortTokenPrice", + "type": "tuple" + } + ], + "internalType": "struct MarketUtils.MarketPrices", + "name": "prices", + "type": "tuple" + }, + { + "internalType": "bool", + "name": "isLong", + "type": "bool" + }, + { + "internalType": "bool", + "name": "maximize", + "type": "bool" + } + ], + "name": "getPnlToPoolFactor", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract DataStore", + "name": "dataStore", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "key", + "type": "bytes32" + } + ], + "name": "getPosition", + "outputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "market", + "type": "address" + }, + { + "internalType": "address", + "name": "collateralToken", + "type": "address" + } + ], + "internalType": "struct Position.Addresses", + "name": "addresses", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "sizeInUsd", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "sizeInTokens", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "collateralAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowingFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fundingFeeAmountPerSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "longTokenClaimableFundingAmountPerSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortTokenClaimableFundingAmountPerSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "increasedAtBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "decreasedAtBlock", + "type": "uint256" + } + ], + "internalType": "struct Position.Numbers", + "name": "numbers", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bool", + "name": "isLong", + "type": "bool" + } + ], + "internalType": "struct Position.Flags", + "name": "flags", + "type": "tuple" + } + ], + "internalType": "struct Position.Props", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract DataStore", + "name": "dataStore", + "type": "address" + }, + { + "internalType": "contract IReferralStorage", + "name": "referralStorage", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "positionKey", + "type": "bytes32" + }, + { + "components": [ + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "indexTokenPrice", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "longTokenPrice", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "shortTokenPrice", + "type": "tuple" + } + ], + "internalType": "struct MarketUtils.MarketPrices", + "name": "prices", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "sizeDeltaUsd", + "type": "uint256" + }, + { + "internalType": "address", + "name": "uiFeeReceiver", + "type": "address" + }, + { + "internalType": "bool", + "name": "usePositionSizeAsSizeDeltaUsd", + "type": "bool" + } + ], + "name": "getPositionInfo", + "outputs": [ + { + "components": [ + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "market", + "type": "address" + }, + { + "internalType": "address", + "name": "collateralToken", + "type": "address" + } + ], + "internalType": "struct Position.Addresses", + "name": "addresses", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "sizeInUsd", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "sizeInTokens", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "collateralAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowingFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fundingFeeAmountPerSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "longTokenClaimableFundingAmountPerSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortTokenClaimableFundingAmountPerSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "increasedAtBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "decreasedAtBlock", + "type": "uint256" + } + ], + "internalType": "struct Position.Numbers", + "name": "numbers", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bool", + "name": "isLong", + "type": "bool" + } + ], + "internalType": "struct Position.Flags", + "name": "flags", + "type": "tuple" + } + ], + "internalType": "struct Position.Props", + "name": "position", + "type": "tuple" + }, + { + "components": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "referralCode", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "affiliate", + "type": "address" + }, + { + "internalType": "address", + "name": "trader", + "type": "address" + }, + { + "internalType": "uint256", + "name": "totalRebateFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "traderDiscountFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalRebateAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "traderDiscountAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "affiliateRewardAmount", + "type": "uint256" + } + ], + "internalType": "struct PositionPricingUtils.PositionReferralFees", + "name": "referral", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "fundingFeeAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "claimableLongTokenAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "claimableShortTokenAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "latestFundingFeeAmountPerSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "latestLongTokenClaimableFundingAmountPerSize", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "latestShortTokenClaimableFundingAmountPerSize", + "type": "uint256" + } + ], + "internalType": "struct PositionPricingUtils.PositionFundingFees", + "name": "funding", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "borrowingFeeUsd", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowingFeeAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowingFeeReceiverFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowingFeeAmountForFeeReceiver", + "type": "uint256" + } + ], + "internalType": "struct PositionPricingUtils.PositionBorrowingFees", + "name": "borrowing", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "uiFeeReceiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "uiFeeReceiverFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "uiFeeAmount", + "type": "uint256" + } + ], + "internalType": "struct PositionPricingUtils.PositionUiFees", + "name": "ui", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "collateralTokenPrice", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "positionFeeFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "protocolFeeAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "positionFeeReceiverFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeReceiverAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeAmountForPool", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "positionFeeAmountForPool", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "positionFeeAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalCostAmountExcludingFunding", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalCostAmount", + "type": "uint256" + } + ], + "internalType": "struct PositionPricingUtils.PositionFees", + "name": "fees", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "int256", + "name": "priceImpactUsd", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "priceImpactDiffUsd", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "executionPrice", + "type": "uint256" + } + ], + "internalType": "struct ReaderPricingUtils.ExecutionPriceResult", + "name": "executionPriceResult", + "type": "tuple" + }, + { + "internalType": "int256", + "name": "basePnlUsd", + "type": "int256" + }, + { + "internalType": "int256", + "name": "uncappedBasePnlUsd", + "type": "int256" + }, + { + "internalType": "int256", + "name": "pnlAfterPriceImpactUsd", + "type": "int256" + } + ], + "internalType": "struct ReaderUtils.PositionInfo", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract DataStore", + "name": "dataStore", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "marketToken", + "type": "address" + }, + { + "internalType": "address", + "name": "indexToken", + "type": "address" + }, + { + "internalType": "address", + "name": "longToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortToken", + "type": "address" + } + ], + "internalType": "struct Market.Props", + "name": "market", + "type": "tuple" + }, + { + "components": [ + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "indexTokenPrice", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "longTokenPrice", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "shortTokenPrice", + "type": "tuple" + } + ], + "internalType": "struct MarketUtils.MarketPrices", + "name": "prices", + "type": "tuple" + }, + { + "internalType": "bytes32", + "name": "positionKey", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "sizeDeltaUsd", + "type": "uint256" + } + ], + "name": "getPositionPnlUsd", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + }, + { + "internalType": "int256", + "name": "", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract DataStore", + "name": "dataStore", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "marketToken", + "type": "address" + }, + { + "internalType": "address", + "name": "indexToken", + "type": "address" + }, + { + "internalType": "address", + "name": "longToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortToken", + "type": "address" + } + ], + "internalType": "struct Market.Props", + "name": "market", + "type": "tuple" + }, + { + "components": [ + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "indexTokenPrice", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "longTokenPrice", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "shortTokenPrice", + "type": "tuple" + } + ], + "internalType": "struct MarketUtils.MarketPrices", + "name": "prices", + "type": "tuple" + }, + { + "internalType": "address", + "name": "tokenIn", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "address", + "name": "uiFeeReceiver", + "type": "address" + } + ], + "name": "getSwapAmountOut", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "", + "type": "int256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "feeReceiverAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeAmountForPool", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountAfterFees", + "type": "uint256" + }, + { + "internalType": "address", + "name": "uiFeeReceiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "uiFeeReceiverFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "uiFeeAmount", + "type": "uint256" + } + ], + "internalType": "struct SwapPricingUtils.SwapFees", + "name": "fees", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract DataStore", + "name": "dataStore", + "type": "address" + }, + { + "internalType": "address", + "name": "marketKey", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenIn", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenOut", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "tokenInPrice", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "tokenOutPrice", + "type": "tuple" + } + ], + "name": "getSwapPriceImpact", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + }, + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract DataStore", + "name": "dataStore", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "key", + "type": "bytes32" + } + ], + "name": "getWithdrawal", + "outputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "address", + "name": "callbackContract", + "type": "address" + }, + { + "internalType": "address", + "name": "uiFeeReceiver", + "type": "address" + }, + { + "internalType": "address", + "name": "market", + "type": "address" + }, + { + "internalType": "address[]", + "name": "longTokenSwapPath", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "shortTokenSwapPath", + "type": "address[]" + } + ], + "internalType": "struct Withdrawal.Addresses", + "name": "addresses", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "marketTokenAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minLongTokenAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minShortTokenAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAtBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "executionFee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "callbackGasLimit", + "type": "uint256" + } + ], + "internalType": "struct Withdrawal.Numbers", + "name": "numbers", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bool", + "name": "shouldUnwrapNativeToken", + "type": "bool" + } + ], + "internalType": "struct Withdrawal.Flags", + "name": "flags", + "type": "tuple" + } + ], + "internalType": "struct Withdrawal.Props", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract DataStore", + "name": "dataStore", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "marketToken", + "type": "address" + }, + { + "internalType": "address", + "name": "indexToken", + "type": "address" + }, + { + "internalType": "address", + "name": "longToken", + "type": "address" + }, + { + "internalType": "address", + "name": "shortToken", + "type": "address" + } + ], + "internalType": "struct Market.Props", + "name": "market", + "type": "tuple" + }, + { + "components": [ + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "indexTokenPrice", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "longTokenPrice", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + } + ], + "internalType": "struct Price.Props", + "name": "shortTokenPrice", + "type": "tuple" + } + ], + "internalType": "struct MarketUtils.MarketPrices", + "name": "prices", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "marketTokenAmount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "uiFeeReceiver", + "type": "address" + } + ], + "name": "getWithdrawalAmountOut", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/packages/composites/gm-token/src/endpoint/index.ts b/packages/composites/gm-token/src/endpoint/index.ts new file mode 100644 index 0000000000..11a44912b4 --- /dev/null +++ b/packages/composites/gm-token/src/endpoint/index.ts @@ -0,0 +1 @@ +export { endpoint as price } from './price' diff --git a/packages/composites/gm-token/src/endpoint/price.ts b/packages/composites/gm-token/src/endpoint/price.ts new file mode 100644 index 0000000000..f52b8d5728 --- /dev/null +++ b/packages/composites/gm-token/src/endpoint/price.ts @@ -0,0 +1,59 @@ +import { AdapterEndpoint } from '@chainlink/external-adapter-framework/adapter' +import { InputParameters } from '@chainlink/external-adapter-framework/validation' +import { config } from '../config' +import { gmTokenTransport } from '../transport/price' + +export const inputParameters = new InputParameters( + { + index: { + required: true, + type: 'string', + options: ['WETH', 'ETH', 'SOL', 'BTC', 'ARB', 'LINK', 'DOGE', 'UNI', 'XRP', 'LTC'], + description: + 'Index token. Long and short tokens will be opened / closed based on this price feed.', + }, + long: { + required: true, + type: 'string', + options: ['SOL', 'WBTC.b', 'WETH', 'ETH', 'ARB', 'LINK', 'UNI'], + description: 'Long token. This is the token that will back long positions.', + }, + short: { + required: true, + type: 'string', + options: ['USDC'], + description: 'Short token. This is the token that will back short positions.', + }, + market: { + required: true, + type: 'string', + description: 'Market address of the market pool.', + }, + }, + [ + { + index: 'LINK', + long: 'LINK', + short: 'USDC', + market: '0x7f1fa204bb700853D36994DA19F830b6Ad18455C', + }, + ], +) + +export type BaseEndpointTypes = { + Parameters: typeof inputParameters.definition + Response: { + Result: number + Data: { + result: number + sources: Record + } + } + Settings: typeof config.settings +} + +export const endpoint = new AdapterEndpoint({ + name: 'price', + transport: gmTokenTransport, + inputParameters, +}) diff --git a/packages/composites/gm-token/src/index.ts b/packages/composites/gm-token/src/index.ts new file mode 100644 index 0000000000..633e5fbedd --- /dev/null +++ b/packages/composites/gm-token/src/index.ts @@ -0,0 +1,13 @@ +import { expose, ServerInstance } from '@chainlink/external-adapter-framework' +import { Adapter } from '@chainlink/external-adapter-framework/adapter' +import { config } from './config' +import { price } from './endpoint' + +export const adapter = new Adapter({ + defaultEndpoint: price.name, + name: 'GM_TOKEN', + config, + endpoints: [price], +}) + +export const server = (): Promise => expose(adapter) diff --git a/packages/composites/gm-token/src/transport/price.ts b/packages/composites/gm-token/src/transport/price.ts new file mode 100644 index 0000000000..09858be5a6 --- /dev/null +++ b/packages/composites/gm-token/src/transport/price.ts @@ -0,0 +1,289 @@ +import { TransportDependencies } from '@chainlink/external-adapter-framework/transports' +import { ResponseCache } from '@chainlink/external-adapter-framework/cache/response' +import { Requester } from '@chainlink/external-adapter-framework/util/requester' + +import { BaseEndpointTypes, inputParameters } from '../endpoint/price' +import { ethers, utils } from 'ethers' +import { SubscriptionTransport } from '@chainlink/external-adapter-framework/transports/abstract/subscription' +import { + EndpointContext, + LwbaResponseDataFields, +} from '@chainlink/external-adapter-framework/adapter' +import { AdapterResponse, makeLogger, sleep } from '@chainlink/external-adapter-framework/util' +import { + decimals, + toFixed, + median, + PriceData, + SIGNED_PRICE_DECIMALS, + tokenAddresses, + Source, +} from './utils' +import abi from './../config/readerAbi.json' +import { AdapterDataProviderError } from '@chainlink/external-adapter-framework/validation/error' + +const logger = makeLogger('GMToken') + +type RequestParams = typeof inputParameters.validated + +export type GmTokenTransportTypes = BaseEndpointTypes + +export class GmTokenTransport extends SubscriptionTransport { + name!: string + responseCache!: ResponseCache + requester!: Requester + provider!: ethers.providers.JsonRpcProvider + readerContract!: ethers.Contract + abiEncoder!: utils.AbiCoder + settings!: GmTokenTransportTypes['Settings'] + + async initialize( + dependencies: TransportDependencies, + adapterSettings: GmTokenTransportTypes['Settings'], + endpointName: string, + transportName: string, + ): Promise { + await super.initialize(dependencies, adapterSettings, endpointName, transportName) + this.settings = adapterSettings + this.provider = new ethers.providers.JsonRpcProvider( + adapterSettings.ARBITRUM_RPC_URL, + adapterSettings.ARBITRUM_CHAIN_ID, + ) + this.readerContract = new ethers.Contract( + adapterSettings.READER_CONTRACT_ADDRESS, + abi, + this.provider, + ) + this.requester = dependencies.requester + } + + async backgroundHandler(context: EndpointContext, entries: RequestParams[]) { + await Promise.all(entries.map(async (param) => this.handleRequest(param))) + await sleep(context.adapterSettings.BACKGROUND_EXECUTE_MS) + } + + async handleRequest(param: RequestParams) { + let response: AdapterResponse + try { + response = await this._handleRequest(param) + } catch (e) { + const errorMessage = e instanceof Error ? e.message : 'Unknown error occurred' + response = { + statusCode: 502, + errorMessage, + timestamps: { + providerDataRequestedUnixMs: 0, + providerDataReceivedUnixMs: 0, + providerIndicatedTimeUnixMs: undefined, + }, + } + } + await this.responseCache.write(this.name, [{ params: param, response }]) + } + + async _handleRequest( + param: RequestParams, + ): Promise> { + const { index, long, short, market } = param + + const assets = [index, long, short] + + const providerDataRequestedUnixMs = Date.now() + + const { + prices: [indexPrices, longPrices, shortPrices], + sources, + } = await this.fetchPrices(assets, providerDataRequestedUnixMs) + + const indexToken = tokenAddresses.arbitrum[index] + const longToken = tokenAddresses.arbitrum[long] + const shortToken = tokenAddresses.arbitrum[short] + + const tokenPriceContractParams = [ + this.settings.DATASTORE_CONTRACT_ADDRESS, + [market, indexToken, longToken, shortToken], + [indexPrices.ask, indexPrices.bid], + [longPrices.ask, longPrices.bid], + [shortPrices.ask, shortPrices.bid], + utils.keccak256(utils.defaultAbiCoder.encode(['string'], [this.settings.PNL_FACTOR_TYPE])), + ] + + // Prices have a spread from min to max. The last param (maximize-true/false) decides whether to maximize the market token price + // or not. We get both values and return the median. + const [[maximizedValue], [minimizedValue]] = await Promise.all([ + this.readerContract.getMarketTokenPrice(...tokenPriceContractParams, true), + this.readerContract.getMarketTokenPrice(...tokenPriceContractParams, false), + ]) + + const maximizedPrice = Number(utils.formatUnits(maximizedValue, SIGNED_PRICE_DECIMALS)) + const minimizedPrice = Number(utils.formatUnits(minimizedValue, SIGNED_PRICE_DECIMALS)) + const result = median([minimizedPrice, maximizedPrice]) + + return { + data: { + result, + sources, + }, + statusCode: 200, + result, + timestamps: { + providerDataRequestedUnixMs, + providerDataReceivedUnixMs: Date.now(), + providerIndicatedTimeUnixMs: undefined, + }, + } + } + + // Fetches the lwba price info from multiple source EAs, calculates the median for bids and asks per asset and fixes the price precision + private async fetchPrices(assets: string[], dataRequestedTimestamp: number) { + // priceData holds raw bid/ask values per asset from source EAs response + const priceData = {} as PriceData + + const sources = [ + { url: this.settings.TIINGO_ADAPTER_URL, name: 'tiingo' }, + { url: this.settings.COINMETRICS_ADAPTER_URL, name: 'coinmetrics' }, + { url: this.settings.NCFX_ADAPTER_URL, name: 'ncfx' }, + ] + + //priceProviders contains assets with a list of sources where asset price was successfully fetched + const priceProviders: Record = {} + + const promises = [] + + for (let i = 0; i < sources.length; i++) { + const source = sources[i] + + const assetPromises = assets.map(async (asset) => { + const base = this.unwrapAsset(asset) + const requestConfig = { + url: source.url, + method: 'POST', + data: { + data: { + endpoint: 'crypto-lwba', + base, + quote: 'USD', + }, + }, + } + + // try/catch is needed in a case if one of source EAs fails to return a response, + // we will still get and calculate the median price based on responses of remaining EAs (based on MIN_REQUIRED_SOURCE_SUCCESS setting) + try { + const response = await this.requester.request<{ data: LwbaResponseDataFields['Data'] }>( + JSON.stringify(requestConfig), + requestConfig, + ) + const { bid, ask } = response.response.data.data + + priceData[asset] = { + bids: [...(priceData[asset]?.bids || []), bid], + asks: [...(priceData[asset]?.asks || []), ask], + } + + priceProviders[base] = priceProviders[base] + ? [...new Set([...priceProviders[base], source.name])] + : [source.name] + } catch (error) { + const e = error as Error + logger.error( + `Error fetching data for ${asset} from ${source.name}, url - ${source.url}: ${e.message}`, + ) + } + }) + + promises.push(...assetPromises) + } + + await Promise.all(promises) + + this.validateRequiredResponses(priceProviders, sources, assets, dataRequestedTimestamp) + + const medianValues = this.calculateMedian(assets, priceData) + + const prices = medianValues.map((v) => ({ + ...v, + ask: toFixed(v.ask, decimals[v.asset as keyof typeof decimals]), + bid: toFixed(v.bid, decimals[v.asset as keyof typeof decimals]), + })) + + return { + prices, + sources: priceProviders, + } + } + + private calculateMedian(assets: string[], priceData: PriceData) { + return assets.map((asset) => { + // Since most of the gm markets have the same long and index tokens, we need to remove duplicate values from duplicate requests + const medianBid = median([...new Set(priceData[asset].bids)]) + const medianAsk = median([...new Set(priceData[asset].asks)]) + return { asset, bid: medianBid, ask: medianAsk } + }) + } + + private unwrapAsset(asset: string) { + if (asset === 'WBTC.b') { + return 'BTC' + } + if (asset === 'WETH') { + return 'ETH' + } + return asset + } + + /* + For every asset check that we received responses from the required number of source EAs to accurately calculate the median price of the asset. + */ + private validateRequiredResponses( + priceProviders: Record = {}, + sources: Source[], + assets: string[], + dataRequestedTimestamp: number, + ) { + const allSource = sources.map((s) => s.name) + if (!Object.entries(priceProviders)?.length) { + throw new AdapterDataProviderError( + { + statusCode: 502, + message: `Missing responses from '${allSource.join(',')}' for all assets.`, + }, + { + providerDataRequestedUnixMs: dataRequestedTimestamp, + providerDataReceivedUnixMs: Date.now(), + providerIndicatedTimeUnixMs: undefined, + }, + ) + } + + assets.forEach((asset) => { + const base = this.unwrapAsset(asset) + const respondedSources = priceProviders[base] + + if (respondedSources.length < this.settings.MIN_REQUIRED_SOURCE_SUCCESS) { + const missingSources = allSource.filter((s) => !respondedSources.includes(s)) + throw new AdapterDataProviderError( + { + statusCode: 502, + message: `Cannot calculate median price for '${asset}'. At least ${ + this.settings.MIN_REQUIRED_SOURCE_SUCCESS + } EAs are required to provide a response but response was received only from ${ + respondedSources.length + } EA(s). Missing responses from '${missingSources.join(',')}'.`, + }, + { + providerDataRequestedUnixMs: dataRequestedTimestamp, + providerDataReceivedUnixMs: Date.now(), + providerIndicatedTimeUnixMs: undefined, + }, + ) + } + }) + } + + getSubscriptionTtlFromConfig(adapterSettings: BaseEndpointTypes['Settings']): number { + return adapterSettings.WARMUP_SUBSCRIPTION_TTL + } +} + +export const gmTokenTransport = new GmTokenTransport() diff --git a/packages/composites/gm-token/src/transport/utils.ts b/packages/composites/gm-token/src/transport/utils.ts new file mode 100644 index 0000000000..55021bf6d3 --- /dev/null +++ b/packages/composites/gm-token/src/transport/utils.ts @@ -0,0 +1,67 @@ +import Decimal from 'decimal.js' + +// The signed prices represent the price of one unit of the token using a value with 30 decimals of precision. +export const SIGNED_PRICE_DECIMALS = 30 + +export type PriceData = { [asset: string]: { bids: number[]; asks: number[] } } + +export type Source = { url: string; name: string } + +export const decimals = { + ETH: 18, + WETH: 18, + BTC: 8, + 'WBTC.b': 8, + SOL: 9, + LINK: 18, + USDC: 6, + ARB: 18, + DOGE: 8, + XRP: 6, + UNI: 18, + LTC: 8, +} + +export const tokenAddresses = { + arbitrum: { + ETH: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', + WETH: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', + BTC: '0x47904963fc8b2340414262125aF798B9655E58Cd', + ARB: '0x912CE59144191C1204E64559FE8253a0e49E6548', + SOL: '0x2bcC6D6CdBbDC0a4071e48bb3B969b06B3330c07', + LINK: '0xf97f4df75117a78c1A5a0DBb814Af92458539FB4', + USDC: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', + 'WBTC.b': '0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f', + DOGE: '0xC4da4c24fd591125c3F47b340b6f4f76111883d8', + XRP: '0xc14e065b0067dE91534e032868f5Ac6ecf2c6868', + UNI: '0xFa7F8980b0f1E64A2062791cc3b0871572f1F7f0', + LTC: '0xB46A094Bc4B0adBD801E14b9DB95e05E28962764', + }, +} + +export const median = (values: number[]): number => { + if (values.length === 0) { + throw new Error('Input array is empty') + } + + values = [...values].sort((a, b) => a - b) + + const half = Math.floor(values.length / 2) + + return values.length % 2 ? values[half] : (values[half - 1] + values[half]) / 2 +} + +/* +Formats a given number with a specified precision without leading zeros and decimal point. +Price decimals = SIGNED_PRICE_DECIMALS - token decimals + +toFixed(14.84329267, 18) -> '14843292670000' +toFixed(0.99999558, 6) -> '999995580000000000000000' + */ +export const toFixed = (number: number, decimals: number): string => { + const n = new Decimal(number) + return n + .toFixed(SIGNED_PRICE_DECIMALS - decimals) + .replace('.', '') + .replace(/^0+/, '') +} diff --git a/packages/composites/gm-token/test-payload.json b/packages/composites/gm-token/test-payload.json new file mode 100644 index 0000000000..fca839bfa7 --- /dev/null +++ b/packages/composites/gm-token/test-payload.json @@ -0,0 +1,8 @@ +{ + "requests": [{ + "index":"LINK", + "long": "LINK", + "short": "USDC", + "market": "0x7f1fa204bb700853D36994DA19F830b6Ad18455C" + }] +} diff --git a/packages/composites/gm-token/test/integration/__snapshots__/adapter.test.ts.snap b/packages/composites/gm-token/test/integration/__snapshots__/adapter.test.ts.snap new file mode 100644 index 0000000000..611915844a --- /dev/null +++ b/packages/composites/gm-token/test/integration/__snapshots__/adapter.test.ts.snap @@ -0,0 +1,38 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`execute price endpoint should return error when fewer than required source EAs respond 1`] = ` +{ + "errorMessage": "Cannot calculate median price for 'ETH'. At least 2 EAs are required to provide a response but response was received only from 1 EA(s). Missing responses from 'coinmetrics,ncfx'.", + "statusCode": 502, + "timestamps": { + "providerDataReceivedUnixMs": 0, + "providerDataRequestedUnixMs": 0, + }, +} +`; + +exports[`execute price endpoint should return success 1`] = ` +{ + "data": { + "result": 1.3774681066355723, + "sources": { + "LINK": [ + "tiingo", + "coinmetrics", + "ncfx", + ], + "USDC": [ + "tiingo", + "coinmetrics", + "ncfx", + ], + }, + }, + "result": 1.3774681066355723, + "statusCode": 200, + "timestamps": { + "providerDataReceivedUnixMs": 978347471111, + "providerDataRequestedUnixMs": 978347471111, + }, +} +`; diff --git a/packages/composites/gm-token/test/integration/adapter.test.ts b/packages/composites/gm-token/test/integration/adapter.test.ts new file mode 100644 index 0000000000..baa26c96c3 --- /dev/null +++ b/packages/composites/gm-token/test/integration/adapter.test.ts @@ -0,0 +1,81 @@ +import { + TestAdapter, + setEnvVariables, +} from '@chainlink/external-adapter-framework/util/testing-utils' +import * as nock from 'nock' +import { + mockCoinmetricsEAResponseFailure, + mockCoinmetricsEAResponseSuccess, + mockNCFXEAResponseFailure, + mockNCFXEAResponseSuccess, + mockRPCResponses, + mockTiingoEAResponseSuccess, +} from './fixtures' + +describe('execute', () => { + let spy: jest.SpyInstance + let testAdapter: TestAdapter + let oldEnv: NodeJS.ProcessEnv + + beforeAll(async () => { + oldEnv = JSON.parse(JSON.stringify(process.env)) + process.env.TIINGO_ADAPTER_URL = process.env.TIINGO_ADAPTER_URL ?? 'http://localhost:8081' + process.env.NCFX_ADAPTER_URL = process.env.NCFX_ADAPTER_URL ?? 'http://localhost:8082' + process.env.COINMETRICS_ADAPTER_URL = + process.env.COINMETRICS_ADAPTER_URL ?? 'http://localhost:8083' + process.env.ARBITRUM_RPC_URL = process.env.ARBITRUM_RPC_URL ?? 'http://localhost:3040' + process.env.RETRY = process.env.RETRY ?? '1' + process.env.BACKGROUND_EXECUTE_MS = '0' + + const mockDate = new Date('2001-01-01T11:11:11.111Z') + spy = jest.spyOn(Date, 'now').mockReturnValue(mockDate.getTime()) + + const adapter = (await import('./../../src')).adapter + adapter.rateLimiting = undefined + testAdapter = await TestAdapter.startWithMockedCache(adapter, { + testAdapter: {} as TestAdapter, + }) + }) + + afterAll(async () => { + setEnvVariables(oldEnv) + await testAdapter.api.close() + nock.restore() + nock.cleanAll() + spy.mockRestore() + }) + + describe('price endpoint', () => { + it('should return success', async () => { + const data = { + index: 'LINK', + long: 'LINK', + short: 'USDC', + market: '0x7f1fa204bb700853D36994DA19F830b6Ad18455C', + } + mockTiingoEAResponseSuccess('LINK') + mockNCFXEAResponseSuccess('LINK') + mockCoinmetricsEAResponseSuccess('LINK') + mockRPCResponses() + const response = await testAdapter.request(data) + expect(response.statusCode).toBe(200) + expect(response.json()).toMatchSnapshot() + }) + + it('should return error when fewer than required source EAs respond', async () => { + const data = { + index: 'ETH', + long: 'WETH', + short: 'USDC', + market: '0x70d95587d40A2caf56bd97485aB3Eec10Bee6336', + } + mockTiingoEAResponseSuccess('ETH') + mockNCFXEAResponseFailure('ETH') + mockCoinmetricsEAResponseFailure('ETH') + mockRPCResponses() + const response = await testAdapter.request(data) + expect(response.statusCode).toBe(502) + expect(response.json()).toMatchSnapshot() + }) + }) +}) diff --git a/packages/composites/gm-token/test/integration/fixtures.ts b/packages/composites/gm-token/test/integration/fixtures.ts new file mode 100644 index 0000000000..586158faef --- /dev/null +++ b/packages/composites/gm-token/test/integration/fixtures.ts @@ -0,0 +1,389 @@ +import nock from 'nock' + +export const mockTiingoEAResponseSuccess = (base): nock.Scope => + nock('http://localhost:8081', { + encodedQueryParams: true, + }) + .post('/', { + data: { + endpoint: 'crypto-lwba', + base, + quote: 'USD', + }, + }) + .reply( + 200, + () => ({ + data: { + ask: 15.694322872166047, + bid: 15.763680197921362, + mid: 15.729001535, + }, + result: null, + statusCode: 200, + timestamps: { + providerDataReceivedUnixMs: 2028, + providerDataStreamEstablishedUnixMs: 2020, + providerIndicatedTimeUnixMs: 1680187094577, + }, + }), + [ + 'Content-Type', + 'application/json', + 'Connection', + 'close', + 'Vary', + 'Accept-Encoding', + 'Vary', + 'Origin', + ], + ) + .persist() + .post('/', { + data: { + endpoint: 'crypto-lwba', + base: 'USDC', + quote: 'USD', + }, + }) + .reply( + 200, + () => ({ + data: { + ask: 1.0012, + bid: 1.01, + mid: 1.0056, + }, + result: null, + statusCode: 200, + timestamps: { + providerDataReceivedUnixMs: 2028, + providerDataStreamEstablishedUnixMs: 2020, + providerIndicatedTimeUnixMs: 1680187094577, + }, + }), + [ + 'Content-Type', + 'application/json', + 'Connection', + 'close', + 'Vary', + 'Accept-Encoding', + 'Vary', + 'Origin', + ], + ) + .persist() + +export const mockNCFXEAResponseSuccess = (base): nock.Scope => + nock('http://localhost:8082', { + encodedQueryParams: true, + }) + .post('/', { + data: { + endpoint: 'crypto-lwba', + base, + quote: 'USD', + }, + }) + .reply( + 200, + () => ({ + data: { + ask: 15.614322872166047, + bid: 15.863680197921362, + mid: 15.739001535, + }, + result: 15.739001535, + statusCode: 200, + timestamps: { + providerDataReceivedUnixMs: 2028, + providerDataStreamEstablishedUnixMs: 2020, + providerIndicatedTimeUnixMs: 1680187094577, + }, + }), + [ + 'Content-Type', + 'application/json', + 'Connection', + 'close', + 'Vary', + 'Accept-Encoding', + 'Vary', + 'Origin', + ], + ) + .persist() + .post('/', { + data: { + endpoint: 'crypto-lwba', + base: 'USDC', + quote: 'USD', + }, + }) + .reply( + 200, + () => ({ + data: { + ask: 1.001, + bid: 1.002, + mid: 1.0015, + }, + result: null, + statusCode: 200, + timestamps: { + providerDataReceivedUnixMs: 2028, + providerDataStreamEstablishedUnixMs: 2020, + providerIndicatedTimeUnixMs: 1680187094577, + }, + }), + [ + 'Content-Type', + 'application/json', + 'Connection', + 'close', + 'Vary', + 'Accept-Encoding', + 'Vary', + 'Origin', + ], + ) + .persist() + +export const mockCoinmetricsEAResponseSuccess = (base): nock.Scope => + nock('http://localhost:8083', { + encodedQueryParams: true, + }) + .post('/', { + data: { + endpoint: 'crypto-lwba', + base, + quote: 'USD', + }, + }) + .reply( + 200, + () => ({ + data: { + ask: 15.59, + bid: 15.64, + mid: 15.61, + }, + result: 15.739001535, + statusCode: 200, + timestamps: { + providerDataReceivedUnixMs: 2028, + providerDataStreamEstablishedUnixMs: 2020, + providerIndicatedTimeUnixMs: 1680187094577, + }, + }), + [ + 'Content-Type', + 'application/json', + 'Connection', + 'close', + 'Vary', + 'Accept-Encoding', + 'Vary', + 'Origin', + ], + ) + .persist() + .post('/', { + data: { + endpoint: 'crypto-lwba', + base: 'USDC', + quote: 'USD', + }, + }) + .reply( + 200, + () => ({ + data: { + ask: 1, + bid: 1.002, + mid: 1.001, + }, + result: null, + statusCode: 200, + timestamps: { + providerDataReceivedUnixMs: 2028, + providerDataStreamEstablishedUnixMs: 2020, + providerIndicatedTimeUnixMs: 1680187094577, + }, + }), + [ + 'Content-Type', + 'application/json', + 'Connection', + 'close', + 'Vary', + 'Accept-Encoding', + 'Vary', + 'Origin', + ], + ) + .persist() + +export const mockNCFXEAResponseFailure = (base): nock.Scope => + nock('http://localhost:8082', { + encodedQueryParams: true, + }) + .post('/', { + data: { + endpoint: 'crypto-lwba', + base, + quote: 'USD', + }, + }) + .reply(500, () => ({}), [ + 'Content-Type', + 'application/json', + 'Connection', + 'close', + 'Vary', + 'Accept-Encoding', + 'Vary', + 'Origin', + ]) + .persist() + .post('/', { + data: { + endpoint: 'crypto-lwba', + base: 'USDC', + quote: 'USD', + }, + }) + .reply(500, () => ({}), [ + 'Content-Type', + 'application/json', + 'Connection', + 'close', + 'Vary', + 'Accept-Encoding', + 'Vary', + 'Origin', + ]) + .persist() + +export const mockCoinmetricsEAResponseFailure = (base): nock.Scope => + nock('http://localhost:8083', { + encodedQueryParams: true, + }) + .post('/', { + data: { + endpoint: 'crypto-lwba', + base, + quote: 'USD', + }, + }) + .reply(500, () => ({}), [ + 'Content-Type', + 'application/json', + 'Connection', + 'close', + 'Vary', + 'Accept-Encoding', + 'Vary', + 'Origin', + ]) + .persist() + .post('/', { + data: { + endpoint: 'crypto-lwba', + base: 'USDC', + quote: 'USD', + }, + }) + .reply(500, () => ({}), [ + 'Content-Type', + 'application/json', + 'Connection', + 'close', + 'Vary', + 'Accept-Encoding', + 'Vary', + 'Origin', + ]) + .persist() + +export const mockRPCResponses = (): nock.Scope => + nock('http://localhost:3040/', { + encodedQueryParams: true, + }) + .persist() + .post('/', { method: 'eth_chainId', params: [], id: /^\d+$/, jsonrpc: '2.0' }) + .reply(200, (_, request) => ({ jsonrpc: '2.0', id: request['id'], result: '0xa4b1' }), [ + 'Content-Type', + 'application/json', + 'Connection', + 'close', + 'Vary', + 'Accept-Encoding', + 'Vary', + 'Origin', + ]) + .post('/', { + method: 'eth_call', + params: [ + { + to: '0xf60becbba223eea9495da3f606753867ec10d139', + data: '0x095ce6c5000000000000000000000000fd70de6b91282d8017aa4e741e9ae325cab992d80000000000000000000000007f1fa204bb700853d36994da19f830b6ad18455c000000000000000000000000f97f4df75117a78c1a5a0dbb814af92458539fb4000000000000000000000000f97f4df75117a78c1a5a0dbb814af92458539fb4000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e583100000000000000000000000000000000000000000000000000000e337e2b0f6600000000000000000000000000000000000000000000000000000e56448efd2100000000000000000000000000000000000000000000000000000e337e2b0f6600000000000000000000000000000000000000000000000000000e56448efd2100000000000000000000000000000000000000000000d3f851987ab37fa0000000000000000000000000000000000000000000000000d5075e88df90d8c00000ab15365d3aa743e766355e2557c230d8f943e195dc84d9b2b05928a07b635ee10000000000000000000000000000000000000000000000000000000000000001', + }, + 'latest', + ], + id: /^\d+$/, + jsonrpc: '2.0', + }) + .reply( + 200, + (_, request) => ({ + jsonrpc: '2.0', + id: request['id'], + result: + '0x000000000000000000000000000000000000001186c3a294738329d44c1fff380000000000000000000000000000000005865a7eaf0180547f38cc539f817d8e00000000000000000000000000000000005650d31a2d4fda491b4b194f487279ffffffffffffffffffffffffffffffffffd8c21aed46a409997ef1809620192700000000000000000000000000000000002f12ee0773f3e3e29a3c99e5688ba00000000000000000000000000000000000000000000034528ae5e9bc615284b30000000000000000000000000000000000000000000000000000036ce62b902f0000000000000000000000000000000002ee2556626f422204f4929e401202130000000000000000000000000000000002d9b4c05b841f06b9f36dc3cb400000000000000000000000000000000000000002ec05e118202507041dcdb02757cb0000000000000000000000000000000000000007f3a3c9314b1b95e2f000000000000000000000000000000000000000000000000000016d5123ea1a77143bcb', + }), + [ + 'Content-Type', + 'application/json', + 'Connection', + 'close', + 'Vary', + 'Accept-Encoding', + 'Vary', + 'Origin', + ], + ) + .persist() + .post('/', { + method: 'eth_call', + params: [ + { + to: '0xf60becbba223eea9495da3f606753867ec10d139', + data: '0x095ce6c5000000000000000000000000fd70de6b91282d8017aa4e741e9ae325cab992d80000000000000000000000007f1fa204bb700853d36994da19f830b6ad18455c000000000000000000000000f97f4df75117a78c1a5a0dbb814af92458539fb4000000000000000000000000f97f4df75117a78c1a5a0dbb814af92458539fb4000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e583100000000000000000000000000000000000000000000000000000e337e2b0f6600000000000000000000000000000000000000000000000000000e56448efd2100000000000000000000000000000000000000000000000000000e337e2b0f6600000000000000000000000000000000000000000000000000000e56448efd2100000000000000000000000000000000000000000000d3f851987ab37fa0000000000000000000000000000000000000000000000000d5075e88df90d8c00000ab15365d3aa743e766355e2557c230d8f943e195dc84d9b2b05928a07b635ee10000000000000000000000000000000000000000000000000000000000000000', + }, + 'latest', + ], + id: /^\d+$/, + jsonrpc: '2.0', + }) + .reply( + 200, + (_, request) => ({ + jsonrpc: '2.0', + id: request['id'], + result: + '0x00000000000000000000000000000000000000113eea3079ed4e1491f09d007900000000000000000000000000000000056fb45dda2d58ae05db60292f9349ef00000000000000000000000000000000005ca061d25c0c5175f5745f15a4f554ffffffffffffffffffffffffffffffffffde2b187969f5b5461e1869f09090ac00000000000000000000000000000000003acb7a4bc60206bc138cc9063586000000000000000000000000000000000000000000000034528ae5e9bc615284b30000000000000000000000000000000000000000000000000000036ce62b902f0000000000000000000000000000000002e709d3abf60a89b18a5a9800b85c520000000000000000000000000000000002d6144c612b298f5853ae9e6e600000000000000000000000000000000000000002ec06a65ba009d478ba62a9444d5e0000000000000000000000000000000000000007f3a3c9314b1b95e2f000000000000000000000000000000000000000000000000000016d5108b35fbc197bcb', + }), + [ + 'Content-Type', + 'application/json', + 'Connection', + 'close', + 'Vary', + 'Accept-Encoding', + 'Vary', + 'Origin', + ], + ) + .persist() diff --git a/packages/composites/gm-token/test/unit/utils.test.ts b/packages/composites/gm-token/test/unit/utils.test.ts new file mode 100644 index 0000000000..ef896a5290 --- /dev/null +++ b/packages/composites/gm-token/test/unit/utils.test.ts @@ -0,0 +1,19 @@ +import { toFixed, median } from '../../src/transport/utils' + +describe('toFixed', () => { + it('should return a string with correct precision', () => { + let res = toFixed(0.62296417, 12) + expect(res).toBe('622964170000000000') + res = toFixed(44.3422343, 8) + expect(res).toBe('443422343000000000000000') + }) +}) + +describe('median', () => { + it('should return correct median value', () => { + let res = median([1, 2, 3, 4, 5]) + expect(res).toBe(3) + res = median([1, 2, 3, 4]) + expect(res).toBe(2.5) + }) +}) diff --git a/packages/composites/gm-token/tsconfig.json b/packages/composites/gm-token/tsconfig.json new file mode 100644 index 0000000000..f59363fd76 --- /dev/null +++ b/packages/composites/gm-token/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src/**/*", "src/**/*.json"], + "exclude": ["dist", "**/*.spec.ts", "**/*.test.ts"] +} diff --git a/packages/composites/gm-token/tsconfig.test.json b/packages/composites/gm-token/tsconfig.test.json new file mode 100644 index 0000000000..e3de28cb5c --- /dev/null +++ b/packages/composites/gm-token/tsconfig.test.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*", "**/test", "src/**/*.json"], + "compilerOptions": { + "noEmit": true + } +} diff --git a/packages/scripts/src/get-changed-adapters/soakTestBlacklist.ts b/packages/scripts/src/get-changed-adapters/soakTestBlacklist.ts index 95c5677343..b95690dcdd 100644 --- a/packages/scripts/src/get-changed-adapters/soakTestBlacklist.ts +++ b/packages/scripts/src/get-changed-adapters/soakTestBlacklist.ts @@ -35,6 +35,7 @@ export const SoakTestBlacklist: string[] = [ 'fmpcloud', 'frxeth-exchange-rate', 'genesis-volatility', + 'gm-token', // Composite EA - Missing underlying source EAs 'google-bigquery', 'google-weather', 'harmony', diff --git a/packages/tsconfig.json b/packages/tsconfig.json index 170e466905..fd3d7d399d 100644 --- a/packages/tsconfig.json +++ b/packages/tsconfig.json @@ -44,6 +44,9 @@ { "path": "./composites/dydx-rewards" }, + { + "path": "./composites/gm-token" + }, { "path": "./composites/google-weather" }, diff --git a/packages/tsconfig.test.json b/packages/tsconfig.test.json index 94506b39a0..272a04b812 100644 --- a/packages/tsconfig.test.json +++ b/packages/tsconfig.test.json @@ -44,6 +44,9 @@ { "path": "./composites/dydx-rewards/tsconfig.test.json" }, + { + "path": "./composites/gm-token/tsconfig.test.json" + }, { "path": "./composites/google-weather/tsconfig.test.json" }, diff --git a/yarn.lock b/yarn.lock index fd97cce085..cbfa2722ae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3806,6 +3806,21 @@ __metadata: languageName: unknown linkType: soft +"@chainlink/gm-token-adapter@workspace:packages/composites/gm-token": + version: 0.0.0-use.local + resolution: "@chainlink/gm-token-adapter@workspace:packages/composites/gm-token" + dependencies: + "@chainlink/external-adapter-framework": 0.33.1 + "@types/jest": 27.5.2 + "@types/node": 16.11.51 + decimal.js: ^10.3.1 + ethers: ^5.4.6 + nock: 13.2.9 + tslib: 2.4.1 + typescript: 5.0.4 + languageName: unknown + linkType: soft + "@chainlink/google-bigquery-adapter@workspace:*, @chainlink/google-bigquery-adapter@workspace:packages/sources/google-bigquery": version: 0.0.0-use.local resolution: "@chainlink/google-bigquery-adapter@workspace:packages/sources/google-bigquery"