From 620715b4b93ff426d40a677106accee0c7561f90 Mon Sep 17 00:00:00 2001 From: xionyk <82797250+xionyk@users.noreply.github.com> Date: Mon, 14 Nov 2022 17:23:19 +0800 Subject: [PATCH 1/4] feat(mux): Add lev-trades ContractPositionFetcher and add BalanceFetcher --- src/apps/mux/arbitrum/mux.balance-fetcher.ts | 35 ++ ...ux.lev-trades.contract-position-fetcher.ts | 25 + src/apps/mux/avalanche/mux.balance-fetcher.ts | 35 ++ ...ux.lev-trades.contract-position-fetcher.ts | 25 + .../mux.balance-fetcher.ts | 35 ++ ...ux.lev-trades.contract-position-fetcher.ts | 25 + src/apps/mux/contracts/abis/mux-reader.json | 565 +++++++++++++++++ .../contracts/abis/mux-reward-tracker.json | 2 +- src/apps/mux/contracts/ethers/MuxReader.ts | 525 ++++++++++++++++ .../ethers/factories/MuxReader__factory.ts | 583 ++++++++++++++++++ .../mux/contracts/ethers/factories/index.ts | 1 + src/apps/mux/contracts/ethers/index.ts | 2 + src/apps/mux/contracts/index.ts | 5 + src/apps/mux/fantom/mux.balance-fetcher.ts | 35 ++ ...ux.lev-trades.contract-position-fetcher.ts | 25 + src/apps/mux/helpers/common.ts | 150 +++++ .../helpers/mux.lev-trades.balance-helper.ts | 97 +++ ...mux.lev-trades.contract-position-helper.ts | 89 +++ src/apps/mux/helpers/mux.mlp.token-helper.ts | 2 +- src/apps/mux/mux.definition.ts | 5 + src/apps/mux/mux.module.ts | 20 + 21 files changed, 2284 insertions(+), 2 deletions(-) create mode 100644 src/apps/mux/arbitrum/mux.balance-fetcher.ts create mode 100644 src/apps/mux/arbitrum/mux.lev-trades.contract-position-fetcher.ts create mode 100644 src/apps/mux/avalanche/mux.balance-fetcher.ts create mode 100644 src/apps/mux/avalanche/mux.lev-trades.contract-position-fetcher.ts create mode 100644 src/apps/mux/binance-smart-chain/mux.balance-fetcher.ts create mode 100644 src/apps/mux/binance-smart-chain/mux.lev-trades.contract-position-fetcher.ts create mode 100644 src/apps/mux/contracts/abis/mux-reader.json create mode 100644 src/apps/mux/contracts/ethers/MuxReader.ts create mode 100644 src/apps/mux/contracts/ethers/factories/MuxReader__factory.ts create mode 100644 src/apps/mux/fantom/mux.balance-fetcher.ts create mode 100644 src/apps/mux/fantom/mux.lev-trades.contract-position-fetcher.ts create mode 100644 src/apps/mux/helpers/common.ts create mode 100644 src/apps/mux/helpers/mux.lev-trades.balance-helper.ts create mode 100644 src/apps/mux/helpers/mux.lev-trades.contract-position-helper.ts diff --git a/src/apps/mux/arbitrum/mux.balance-fetcher.ts b/src/apps/mux/arbitrum/mux.balance-fetcher.ts new file mode 100644 index 000000000..388b07bd4 --- /dev/null +++ b/src/apps/mux/arbitrum/mux.balance-fetcher.ts @@ -0,0 +1,35 @@ +import { Inject } from '@nestjs/common'; + +import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface'; +import { Register } from '~app-toolkit/decorators'; +import { presentBalanceFetcherResponse } from '~app-toolkit/helpers/presentation/balance-fetcher-response.present'; +import { MuxLevTradesBalanceHelper } from '~apps/mux/helpers/mux.lev-trades.balance-helper'; +import { BalanceFetcher } from '~balance/balance-fetcher.interface'; +import { Network } from '~types/network.interface'; + +import { MUX_DEFINITION } from '../mux.definition'; + +const network = Network.ARBITRUM_MAINNET; + +@Register.BalanceFetcher(MUX_DEFINITION.id, network) +export class ArbitrumMuxBalanceFetcher implements BalanceFetcher { + constructor( + @Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit, + @Inject(MuxLevTradesBalanceHelper) private readonly muxLevTradesBalanceHelper: MuxLevTradesBalanceHelper, + ) {} + + private async getLevTradesBalances(address: string) { + return this.muxLevTradesBalanceHelper.getBalance({ address, network }); + } + + async getBalances(address: string) { + const levTradesBalances = await this.getLevTradesBalances(address); + + return presentBalanceFetcherResponse([ + { + label: 'Leveraged trades', + assets: [...levTradesBalances], + }, + ]); + } +} diff --git a/src/apps/mux/arbitrum/mux.lev-trades.contract-position-fetcher.ts b/src/apps/mux/arbitrum/mux.lev-trades.contract-position-fetcher.ts new file mode 100644 index 000000000..a92dd575f --- /dev/null +++ b/src/apps/mux/arbitrum/mux.lev-trades.contract-position-fetcher.ts @@ -0,0 +1,25 @@ +import { Inject } from '@nestjs/common'; + +import { Register } from '~app-toolkit/decorators'; +import { PositionFetcher } from '~position/position-fetcher.interface'; +import { ContractPosition } from '~position/position.interface'; +import { Network } from '~types/network.interface'; + +import { MuxLevTradesContractPositionHelper } from '../helpers/mux.lev-trades.contract-position-helper'; +import { MUX_DEFINITION } from '../mux.definition'; + +const appId = MUX_DEFINITION.id; +const groupId = MUX_DEFINITION.groups.levTrades.id; +const network = Network.ARBITRUM_MAINNET; + +@Register.ContractPositionFetcher({ appId, groupId, network }) +export class ArbitrumMuxLevTradesContractPositionFetcher implements PositionFetcher { + constructor( + @Inject(MuxLevTradesContractPositionHelper) + private readonly muxLevTradesContractPositionHelper: MuxLevTradesContractPositionHelper, + ) {} + + async getPositions() { + return this.muxLevTradesContractPositionHelper.getPosition({ network }); + } +} diff --git a/src/apps/mux/avalanche/mux.balance-fetcher.ts b/src/apps/mux/avalanche/mux.balance-fetcher.ts new file mode 100644 index 000000000..2df1fc70b --- /dev/null +++ b/src/apps/mux/avalanche/mux.balance-fetcher.ts @@ -0,0 +1,35 @@ +import { Inject } from '@nestjs/common'; + +import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface'; +import { Register } from '~app-toolkit/decorators'; +import { presentBalanceFetcherResponse } from '~app-toolkit/helpers/presentation/balance-fetcher-response.present'; +import { MuxLevTradesBalanceHelper } from '~apps/mux/helpers/mux.lev-trades.balance-helper'; +import { BalanceFetcher } from '~balance/balance-fetcher.interface'; +import { Network } from '~types/network.interface'; + +import { MUX_DEFINITION } from '../mux.definition'; + +const network = Network.AVALANCHE_MAINNET; + +@Register.BalanceFetcher(MUX_DEFINITION.id, network) +export class AvalancheMuxBalanceFetcher implements BalanceFetcher { + constructor( + @Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit, + @Inject(MuxLevTradesBalanceHelper) private readonly muxLevTradesBalanceHelper: MuxLevTradesBalanceHelper, + ) {} + + private async getLevTradesBalances(address: string) { + return this.muxLevTradesBalanceHelper.getBalance({ address, network }); + } + + async getBalances(address: string) { + const levTradesBalances = await this.getLevTradesBalances(address); + + return presentBalanceFetcherResponse([ + { + label: 'Leveraged trades', + assets: [...levTradesBalances], + }, + ]); + } +} diff --git a/src/apps/mux/avalanche/mux.lev-trades.contract-position-fetcher.ts b/src/apps/mux/avalanche/mux.lev-trades.contract-position-fetcher.ts new file mode 100644 index 000000000..efbfdc6c9 --- /dev/null +++ b/src/apps/mux/avalanche/mux.lev-trades.contract-position-fetcher.ts @@ -0,0 +1,25 @@ +import { Inject } from '@nestjs/common'; + +import { Register } from '~app-toolkit/decorators'; +import { MuxLevTradesContractPositionHelper } from '~apps/mux/helpers/mux.lev-trades.contract-position-helper'; +import { PositionFetcher } from '~position/position-fetcher.interface'; +import { ContractPosition } from '~position/position.interface'; +import { Network } from '~types/network.interface'; + +import { MUX_DEFINITION } from '../mux.definition'; + +const appId = MUX_DEFINITION.id; +const groupId = MUX_DEFINITION.groups.levTrades.id; +const network = Network.AVALANCHE_MAINNET; + +@Register.ContractPositionFetcher({ appId, groupId, network }) +export class AvalancheMuxLevTradesContractPositionFetcher implements PositionFetcher { + constructor( + @Inject(MuxLevTradesContractPositionHelper) + private readonly muxLevTradesContractPositionHelper: MuxLevTradesContractPositionHelper, + ) {} + + async getPositions() { + return this.muxLevTradesContractPositionHelper.getPosition({ network }); + } +} diff --git a/src/apps/mux/binance-smart-chain/mux.balance-fetcher.ts b/src/apps/mux/binance-smart-chain/mux.balance-fetcher.ts new file mode 100644 index 000000000..142acc51e --- /dev/null +++ b/src/apps/mux/binance-smart-chain/mux.balance-fetcher.ts @@ -0,0 +1,35 @@ +import { Inject } from '@nestjs/common'; + +import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface'; +import { Register } from '~app-toolkit/decorators'; +import { presentBalanceFetcherResponse } from '~app-toolkit/helpers/presentation/balance-fetcher-response.present'; +import { MuxLevTradesBalanceHelper } from '~apps/mux/helpers/mux.lev-trades.balance-helper'; +import { BalanceFetcher } from '~balance/balance-fetcher.interface'; +import { Network } from '~types/network.interface'; + +import { MUX_DEFINITION } from '../mux.definition'; + +const network = Network.BINANCE_SMART_CHAIN_MAINNET; + +@Register.BalanceFetcher(MUX_DEFINITION.id, network) +export class BinanceSmartChainMuxBalanceFetcher implements BalanceFetcher { + constructor( + @Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit, + @Inject(MuxLevTradesBalanceHelper) private readonly muxLevTradesBalanceHelper: MuxLevTradesBalanceHelper, + ) {} + + private async getLevTradesBalances(address: string) { + return this.muxLevTradesBalanceHelper.getBalance({ address, network }); + } + + async getBalances(address: string) { + const levTradesBalances = await this.getLevTradesBalances(address); + + return presentBalanceFetcherResponse([ + { + label: 'Leveraged trades', + assets: [...levTradesBalances], + }, + ]); + } +} diff --git a/src/apps/mux/binance-smart-chain/mux.lev-trades.contract-position-fetcher.ts b/src/apps/mux/binance-smart-chain/mux.lev-trades.contract-position-fetcher.ts new file mode 100644 index 000000000..2c3fdf62c --- /dev/null +++ b/src/apps/mux/binance-smart-chain/mux.lev-trades.contract-position-fetcher.ts @@ -0,0 +1,25 @@ +import { Inject } from '@nestjs/common'; + +import { Register } from '~app-toolkit/decorators'; +import { MuxLevTradesContractPositionHelper } from '~apps/mux/helpers/mux.lev-trades.contract-position-helper'; +import { PositionFetcher } from '~position/position-fetcher.interface'; +import { ContractPosition } from '~position/position.interface'; +import { Network } from '~types/network.interface'; + +import { MUX_DEFINITION } from '../mux.definition'; + +const appId = MUX_DEFINITION.id; +const groupId = MUX_DEFINITION.groups.levTrades.id; +const network = Network.BINANCE_SMART_CHAIN_MAINNET; + +@Register.ContractPositionFetcher({ appId, groupId, network }) +export class BinanceSmartChainMuxLevTradesContractPositionFetcher implements PositionFetcher { + constructor( + @Inject(MuxLevTradesContractPositionHelper) + private readonly muxLevTradesContractPositionHelper: MuxLevTradesContractPositionHelper, + ) {} + + async getPositions() { + return this.muxLevTradesContractPositionHelper.getPosition({ network }); + } +} diff --git a/src/apps/mux/contracts/abis/mux-reader.json b/src/apps/mux/contracts/abis/mux-reader.json new file mode 100644 index 000000000..c5830beee --- /dev/null +++ b/src/apps/mux/contracts/abis/mux-reader.json @@ -0,0 +1,565 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "pool_", + "type": "address" + }, + { + "internalType": "address", + "name": "mlp_", + "type": "address" + }, + { + "internalType": "address", + "name": "dex_", + "type": "address" + }, + { + "internalType": "address", + "name": "orderBook_", + "type": "address" + }, + { + "internalType": "address[]", + "name": "deductWhiteList_", + "type": "address[]" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "deductWhiteList", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "dex", + "outputs": [ + { + "internalType": "contract ILiquidityManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getChainStorage", + "outputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "uint32", + "name": "shortFundingBaseRate8H", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "shortFundingLimitRate8H", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "fundingInterval", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "liquidityBaseFeeRate", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "liquidityDynamicFeeRate", + "type": "uint32" + }, + { + "internalType": "uint96", + "name": "mlpPriceLowerBound", + "type": "uint96" + }, + { + "internalType": "uint96", + "name": "mlpPriceUpperBound", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "lastFundingTime", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "sequence", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "strictStableDeviation", + "type": "uint32" + } + ], + "internalType": "struct Reader.PoolStorage", + "name": "pool", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "symbol", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "muxTokenAddress", + "type": "address" + }, + { + "internalType": "uint8", + "name": "id", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + }, + { + "internalType": "uint56", + "name": "flags", + "type": "uint56" + }, + { + "internalType": "uint32", + "name": "initialMarginRate", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "maintenanceMarginRate", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "positionFeeRate", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "minProfitRate", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "minProfitTime", + "type": "uint32" + }, + { + "internalType": "uint96", + "name": "maxLongPositionSize", + "type": "uint96" + }, + { + "internalType": "uint96", + "name": "maxShortPositionSize", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "spotWeight", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "longFundingBaseRate8H", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "longFundingLimitRate8H", + "type": "uint32" + }, + { + "internalType": "uint8", + "name": "referenceOracleType", + "type": "uint8" + }, + { + "internalType": "address", + "name": "referenceOracle", + "type": "address" + }, + { + "internalType": "uint32", + "name": "referenceDeviation", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "halfSpread", + "type": "uint32" + }, + { + "internalType": "uint128", + "name": "longCumulativeFundingRate", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "shortCumulativeFunding", + "type": "uint128" + }, + { + "internalType": "uint96", + "name": "spotLiquidity", + "type": "uint96" + }, + { + "internalType": "uint96", + "name": "totalLongPosition", + "type": "uint96" + }, + { + "internalType": "uint96", + "name": "totalShortPosition", + "type": "uint96" + }, + { + "internalType": "uint96", + "name": "averageLongPrice", + "type": "uint96" + }, + { + "internalType": "uint96", + "name": "averageShortPrice", + "type": "uint96" + }, + { + "internalType": "uint128", + "name": "collectedFee", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "deduct", + "type": "uint256" + } + ], + "internalType": "struct Reader.AssetStorage[]", + "name": "assets", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "uint8", + "name": "dexId", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "dexType", + "type": "uint8" + }, + { + "internalType": "uint8[]", + "name": "assetIds", + "type": "uint8[]" + }, + { + "internalType": "uint32[]", + "name": "assetWeightInDEX", + "type": "uint32[]" + }, + { + "internalType": "uint256[]", + "name": "totalSpotInDEX", + "type": "uint256[]" + }, + { + "internalType": "uint32", + "name": "dexWeight", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "dexLPBalance", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "liquidityBalance", + "type": "uint256[]" + } + ], + "internalType": "struct Reader.DexStorage[]", + "name": "dexes", + "type": "tuple[]" + }, + { + "internalType": "uint32", + "name": "liquidityLockPeriod", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "marketOrderTimeout", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "maxLimitOrderTimeout", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "lpDeduct", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "stableDeduct", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isPositionOrderPaused", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isLiquidityOrderPaused", + "type": "bool" + } + ], + "internalType": "struct Reader.ChainStorage", + "name": "chain", + "type": "tuple" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "tokens", + "type": "address[]" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "getErc20Balances", + "outputs": [ + { + "internalType": "uint256[]", + "name": "balances", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64[]", + "name": "orderIds", + "type": "uint64[]" + } + ], + "name": "getOrders", + "outputs": [ + { + "internalType": "bytes32[3][]", + "name": "orders", + "type": "bytes32[3][]" + }, + { + "internalType": "bool[]", + "name": "isExist", + "type": "bool[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32[]", + "name": "subAccountIds", + "type": "bytes32[]" + } + ], + "name": "getSubAccounts", + "outputs": [ + { + "components": [ + { + "internalType": "uint96", + "name": "collateral", + "type": "uint96" + }, + { + "internalType": "uint96", + "name": "size", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "lastIncreasedTime", + "type": "uint32" + }, + { + "internalType": "uint96", + "name": "entryPrice", + "type": "uint96" + }, + { + "internalType": "uint128", + "name": "entryFunding", + "type": "uint128" + } + ], + "internalType": "struct Reader.SubAccountState[]", + "name": "subAccounts", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32[]", + "name": "subAccountIds", + "type": "bytes32[]" + }, + { + "internalType": "uint64[]", + "name": "orderIds", + "type": "uint64[]" + } + ], + "name": "getSubAccountsAndOrders", + "outputs": [ + { + "components": [ + { + "internalType": "uint96", + "name": "collateral", + "type": "uint96" + }, + { + "internalType": "uint96", + "name": "size", + "type": "uint96" + }, + { + "internalType": "uint32", + "name": "lastIncreasedTime", + "type": "uint32" + }, + { + "internalType": "uint96", + "name": "entryPrice", + "type": "uint96" + }, + { + "internalType": "uint128", + "name": "entryFunding", + "type": "uint128" + } + ], + "internalType": "struct Reader.SubAccountState[]", + "name": "subAccounts", + "type": "tuple[]" + }, + { + "internalType": "bytes32[3][]", + "name": "orders", + "type": "bytes32[3][]" + }, + { + "internalType": "bool[]", + "name": "isOrderExist", + "type": "bool[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "mlp", + "outputs": [ + { + "internalType": "contract IERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "orderBook", + "outputs": [ + { + "internalType": "contract IOrderBook", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pool", + "outputs": [ + { + "internalType": "contract ILiquidityPool", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/src/apps/mux/contracts/abis/mux-reward-tracker.json b/src/apps/mux/contracts/abis/mux-reward-tracker.json index 75c6c9fa5..2e9c9b6d4 100644 --- a/src/apps/mux/contracts/abis/mux-reward-tracker.json +++ b/src/apps/mux/contracts/abis/mux-reward-tracker.json @@ -902,4 +902,4 @@ "stateMutability": "nonpayable", "type": "function" } -] \ No newline at end of file +] diff --git a/src/apps/mux/contracts/ethers/MuxReader.ts b/src/apps/mux/contracts/ethers/MuxReader.ts new file mode 100644 index 000000000..f8265132a --- /dev/null +++ b/src/apps/mux/contracts/ethers/MuxReader.ts @@ -0,0 +1,525 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +import type { + BaseContract, + BigNumber, + BigNumberish, + BytesLike, + CallOverrides, + ContractTransaction, + Overrides, + PopulatedTransaction, + Signer, + utils, +} from 'ethers'; +import type { FunctionFragment, Result } from '@ethersproject/abi'; +import type { Listener, Provider } from '@ethersproject/providers'; +import type { TypedEventFilter, TypedEvent, TypedListener, OnEvent, PromiseOrValue } from './common'; + +export declare namespace Reader { + export type PoolStorageStruct = { + shortFundingBaseRate8H: PromiseOrValue; + shortFundingLimitRate8H: PromiseOrValue; + fundingInterval: PromiseOrValue; + liquidityBaseFeeRate: PromiseOrValue; + liquidityDynamicFeeRate: PromiseOrValue; + mlpPriceLowerBound: PromiseOrValue; + mlpPriceUpperBound: PromiseOrValue; + lastFundingTime: PromiseOrValue; + sequence: PromiseOrValue; + strictStableDeviation: PromiseOrValue; + }; + + export type PoolStorageStructOutput = [ + number, + number, + number, + number, + number, + BigNumber, + BigNumber, + number, + number, + number, + ] & { + shortFundingBaseRate8H: number; + shortFundingLimitRate8H: number; + fundingInterval: number; + liquidityBaseFeeRate: number; + liquidityDynamicFeeRate: number; + mlpPriceLowerBound: BigNumber; + mlpPriceUpperBound: BigNumber; + lastFundingTime: number; + sequence: number; + strictStableDeviation: number; + }; + + export type AssetStorageStruct = { + symbol: PromiseOrValue; + tokenAddress: PromiseOrValue; + muxTokenAddress: PromiseOrValue; + id: PromiseOrValue; + decimals: PromiseOrValue; + flags: PromiseOrValue; + initialMarginRate: PromiseOrValue; + maintenanceMarginRate: PromiseOrValue; + positionFeeRate: PromiseOrValue; + minProfitRate: PromiseOrValue; + minProfitTime: PromiseOrValue; + maxLongPositionSize: PromiseOrValue; + maxShortPositionSize: PromiseOrValue; + spotWeight: PromiseOrValue; + longFundingBaseRate8H: PromiseOrValue; + longFundingLimitRate8H: PromiseOrValue; + referenceOracleType: PromiseOrValue; + referenceOracle: PromiseOrValue; + referenceDeviation: PromiseOrValue; + halfSpread: PromiseOrValue; + longCumulativeFundingRate: PromiseOrValue; + shortCumulativeFunding: PromiseOrValue; + spotLiquidity: PromiseOrValue; + totalLongPosition: PromiseOrValue; + totalShortPosition: PromiseOrValue; + averageLongPrice: PromiseOrValue; + averageShortPrice: PromiseOrValue; + collectedFee: PromiseOrValue; + deduct: PromiseOrValue; + }; + + export type AssetStorageStructOutput = [ + string, + string, + string, + number, + number, + BigNumber, + number, + number, + number, + number, + number, + BigNumber, + BigNumber, + number, + number, + number, + number, + string, + number, + number, + BigNumber, + BigNumber, + BigNumber, + BigNumber, + BigNumber, + BigNumber, + BigNumber, + BigNumber, + BigNumber, + ] & { + symbol: string; + tokenAddress: string; + muxTokenAddress: string; + id: number; + decimals: number; + flags: BigNumber; + initialMarginRate: number; + maintenanceMarginRate: number; + positionFeeRate: number; + minProfitRate: number; + minProfitTime: number; + maxLongPositionSize: BigNumber; + maxShortPositionSize: BigNumber; + spotWeight: number; + longFundingBaseRate8H: number; + longFundingLimitRate8H: number; + referenceOracleType: number; + referenceOracle: string; + referenceDeviation: number; + halfSpread: number; + longCumulativeFundingRate: BigNumber; + shortCumulativeFunding: BigNumber; + spotLiquidity: BigNumber; + totalLongPosition: BigNumber; + totalShortPosition: BigNumber; + averageLongPrice: BigNumber; + averageShortPrice: BigNumber; + collectedFee: BigNumber; + deduct: BigNumber; + }; + + export type DexStorageStruct = { + dexId: PromiseOrValue; + dexType: PromiseOrValue; + assetIds: PromiseOrValue[]; + assetWeightInDEX: PromiseOrValue[]; + totalSpotInDEX: PromiseOrValue[]; + dexWeight: PromiseOrValue; + dexLPBalance: PromiseOrValue; + liquidityBalance: PromiseOrValue[]; + }; + + export type DexStorageStructOutput = [ + number, + number, + number[], + number[], + BigNumber[], + number, + BigNumber, + BigNumber[], + ] & { + dexId: number; + dexType: number; + assetIds: number[]; + assetWeightInDEX: number[]; + totalSpotInDEX: BigNumber[]; + dexWeight: number; + dexLPBalance: BigNumber; + liquidityBalance: BigNumber[]; + }; + + export type ChainStorageStruct = { + pool: Reader.PoolStorageStruct; + assets: Reader.AssetStorageStruct[]; + dexes: Reader.DexStorageStruct[]; + liquidityLockPeriod: PromiseOrValue; + marketOrderTimeout: PromiseOrValue; + maxLimitOrderTimeout: PromiseOrValue; + lpDeduct: PromiseOrValue; + stableDeduct: PromiseOrValue; + isPositionOrderPaused: PromiseOrValue; + isLiquidityOrderPaused: PromiseOrValue; + }; + + export type ChainStorageStructOutput = [ + Reader.PoolStorageStructOutput, + Reader.AssetStorageStructOutput[], + Reader.DexStorageStructOutput[], + number, + number, + number, + BigNumber, + BigNumber, + boolean, + boolean, + ] & { + pool: Reader.PoolStorageStructOutput; + assets: Reader.AssetStorageStructOutput[]; + dexes: Reader.DexStorageStructOutput[]; + liquidityLockPeriod: number; + marketOrderTimeout: number; + maxLimitOrderTimeout: number; + lpDeduct: BigNumber; + stableDeduct: BigNumber; + isPositionOrderPaused: boolean; + isLiquidityOrderPaused: boolean; + }; + + export type SubAccountStateStruct = { + collateral: PromiseOrValue; + size: PromiseOrValue; + lastIncreasedTime: PromiseOrValue; + entryPrice: PromiseOrValue; + entryFunding: PromiseOrValue; + }; + + export type SubAccountStateStructOutput = [BigNumber, BigNumber, number, BigNumber, BigNumber] & { + collateral: BigNumber; + size: BigNumber; + lastIncreasedTime: number; + entryPrice: BigNumber; + entryFunding: BigNumber; + }; +} + +export interface MuxReaderInterface extends utils.Interface { + functions: { + 'deductWhiteList(uint256)': FunctionFragment; + 'dex()': FunctionFragment; + 'getChainStorage()': FunctionFragment; + 'getErc20Balances(address[],address)': FunctionFragment; + 'getOrders(uint64[])': FunctionFragment; + 'getSubAccounts(bytes32[])': FunctionFragment; + 'getSubAccountsAndOrders(bytes32[],uint64[])': FunctionFragment; + 'mlp()': FunctionFragment; + 'orderBook()': FunctionFragment; + 'pool()': FunctionFragment; + }; + + getFunction( + nameOrSignatureOrTopic: + | 'deductWhiteList' + | 'dex' + | 'getChainStorage' + | 'getErc20Balances' + | 'getOrders' + | 'getSubAccounts' + | 'getSubAccountsAndOrders' + | 'mlp' + | 'orderBook' + | 'pool', + ): FunctionFragment; + + encodeFunctionData(functionFragment: 'deductWhiteList', values: [PromiseOrValue]): string; + encodeFunctionData(functionFragment: 'dex', values?: undefined): string; + encodeFunctionData(functionFragment: 'getChainStorage', values?: undefined): string; + encodeFunctionData( + functionFragment: 'getErc20Balances', + values: [PromiseOrValue[], PromiseOrValue], + ): string; + encodeFunctionData(functionFragment: 'getOrders', values: [PromiseOrValue[]]): string; + encodeFunctionData(functionFragment: 'getSubAccounts', values: [PromiseOrValue[]]): string; + encodeFunctionData( + functionFragment: 'getSubAccountsAndOrders', + values: [PromiseOrValue[], PromiseOrValue[]], + ): string; + encodeFunctionData(functionFragment: 'mlp', values?: undefined): string; + encodeFunctionData(functionFragment: 'orderBook', values?: undefined): string; + encodeFunctionData(functionFragment: 'pool', values?: undefined): string; + + decodeFunctionResult(functionFragment: 'deductWhiteList', data: BytesLike): Result; + decodeFunctionResult(functionFragment: 'dex', data: BytesLike): Result; + decodeFunctionResult(functionFragment: 'getChainStorage', data: BytesLike): Result; + decodeFunctionResult(functionFragment: 'getErc20Balances', data: BytesLike): Result; + decodeFunctionResult(functionFragment: 'getOrders', data: BytesLike): Result; + decodeFunctionResult(functionFragment: 'getSubAccounts', data: BytesLike): Result; + decodeFunctionResult(functionFragment: 'getSubAccountsAndOrders', data: BytesLike): Result; + decodeFunctionResult(functionFragment: 'mlp', data: BytesLike): Result; + decodeFunctionResult(functionFragment: 'orderBook', data: BytesLike): Result; + decodeFunctionResult(functionFragment: 'pool', data: BytesLike): Result; + + events: {}; +} + +export interface MuxReader extends BaseContract { + connect(signerOrProvider: Signer | Provider | string): this; + attach(addressOrName: string): this; + deployed(): Promise; + + interface: MuxReaderInterface; + + queryFilter( + event: TypedEventFilter, + fromBlockOrBlockhash?: string | number | undefined, + toBlock?: string | number | undefined, + ): Promise>; + + listeners(eventFilter?: TypedEventFilter): Array>; + listeners(eventName?: string): Array; + removeAllListeners(eventFilter: TypedEventFilter): this; + removeAllListeners(eventName?: string): this; + off: OnEvent; + on: OnEvent; + once: OnEvent; + removeListener: OnEvent; + + functions: { + deductWhiteList(arg0: PromiseOrValue, overrides?: CallOverrides): Promise<[string]>; + + dex(overrides?: CallOverrides): Promise<[string]>; + + getChainStorage(overrides?: Overrides & { from?: PromiseOrValue }): Promise; + + getErc20Balances( + tokens: PromiseOrValue[], + owner: PromiseOrValue, + overrides?: CallOverrides, + ): Promise<[BigNumber[]] & { balances: BigNumber[] }>; + + getOrders( + orderIds: PromiseOrValue[], + overrides?: CallOverrides, + ): Promise< + [[string, string, string][], boolean[]] & { + orders: [string, string, string][]; + isExist: boolean[]; + } + >; + + getSubAccounts( + subAccountIds: PromiseOrValue[], + overrides?: CallOverrides, + ): Promise< + [Reader.SubAccountStateStructOutput[]] & { + subAccounts: Reader.SubAccountStateStructOutput[]; + } + >; + + getSubAccountsAndOrders( + subAccountIds: PromiseOrValue[], + orderIds: PromiseOrValue[], + overrides?: CallOverrides, + ): Promise< + [Reader.SubAccountStateStructOutput[], [string, string, string][], boolean[]] & { + subAccounts: Reader.SubAccountStateStructOutput[]; + orders: [string, string, string][]; + isOrderExist: boolean[]; + } + >; + + mlp(overrides?: CallOverrides): Promise<[string]>; + + orderBook(overrides?: CallOverrides): Promise<[string]>; + + pool(overrides?: CallOverrides): Promise<[string]>; + }; + + deductWhiteList(arg0: PromiseOrValue, overrides?: CallOverrides): Promise; + + dex(overrides?: CallOverrides): Promise; + + getChainStorage(overrides?: Overrides & { from?: PromiseOrValue }): Promise; + + getErc20Balances( + tokens: PromiseOrValue[], + owner: PromiseOrValue, + overrides?: CallOverrides, + ): Promise; + + getOrders( + orderIds: PromiseOrValue[], + overrides?: CallOverrides, + ): Promise< + [[string, string, string][], boolean[]] & { + orders: [string, string, string][]; + isExist: boolean[]; + } + >; + + getSubAccounts( + subAccountIds: PromiseOrValue[], + overrides?: CallOverrides, + ): Promise; + + getSubAccountsAndOrders( + subAccountIds: PromiseOrValue[], + orderIds: PromiseOrValue[], + overrides?: CallOverrides, + ): Promise< + [Reader.SubAccountStateStructOutput[], [string, string, string][], boolean[]] & { + subAccounts: Reader.SubAccountStateStructOutput[]; + orders: [string, string, string][]; + isOrderExist: boolean[]; + } + >; + + mlp(overrides?: CallOverrides): Promise; + + orderBook(overrides?: CallOverrides): Promise; + + pool(overrides?: CallOverrides): Promise; + + callStatic: { + deductWhiteList(arg0: PromiseOrValue, overrides?: CallOverrides): Promise; + + dex(overrides?: CallOverrides): Promise; + + getChainStorage(overrides?: CallOverrides): Promise; + + getErc20Balances( + tokens: PromiseOrValue[], + owner: PromiseOrValue, + overrides?: CallOverrides, + ): Promise; + + getOrders( + orderIds: PromiseOrValue[], + overrides?: CallOverrides, + ): Promise< + [[string, string, string][], boolean[]] & { + orders: [string, string, string][]; + isExist: boolean[]; + } + >; + + getSubAccounts( + subAccountIds: PromiseOrValue[], + overrides?: CallOverrides, + ): Promise; + + getSubAccountsAndOrders( + subAccountIds: PromiseOrValue[], + orderIds: PromiseOrValue[], + overrides?: CallOverrides, + ): Promise< + [Reader.SubAccountStateStructOutput[], [string, string, string][], boolean[]] & { + subAccounts: Reader.SubAccountStateStructOutput[]; + orders: [string, string, string][]; + isOrderExist: boolean[]; + } + >; + + mlp(overrides?: CallOverrides): Promise; + + orderBook(overrides?: CallOverrides): Promise; + + pool(overrides?: CallOverrides): Promise; + }; + + filters: {}; + + estimateGas: { + deductWhiteList(arg0: PromiseOrValue, overrides?: CallOverrides): Promise; + + dex(overrides?: CallOverrides): Promise; + + getChainStorage(overrides?: Overrides & { from?: PromiseOrValue }): Promise; + + getErc20Balances( + tokens: PromiseOrValue[], + owner: PromiseOrValue, + overrides?: CallOverrides, + ): Promise; + + getOrders(orderIds: PromiseOrValue[], overrides?: CallOverrides): Promise; + + getSubAccounts(subAccountIds: PromiseOrValue[], overrides?: CallOverrides): Promise; + + getSubAccountsAndOrders( + subAccountIds: PromiseOrValue[], + orderIds: PromiseOrValue[], + overrides?: CallOverrides, + ): Promise; + + mlp(overrides?: CallOverrides): Promise; + + orderBook(overrides?: CallOverrides): Promise; + + pool(overrides?: CallOverrides): Promise; + }; + + populateTransaction: { + deductWhiteList(arg0: PromiseOrValue, overrides?: CallOverrides): Promise; + + dex(overrides?: CallOverrides): Promise; + + getChainStorage(overrides?: Overrides & { from?: PromiseOrValue }): Promise; + + getErc20Balances( + tokens: PromiseOrValue[], + owner: PromiseOrValue, + overrides?: CallOverrides, + ): Promise; + + getOrders(orderIds: PromiseOrValue[], overrides?: CallOverrides): Promise; + + getSubAccounts( + subAccountIds: PromiseOrValue[], + overrides?: CallOverrides, + ): Promise; + + getSubAccountsAndOrders( + subAccountIds: PromiseOrValue[], + orderIds: PromiseOrValue[], + overrides?: CallOverrides, + ): Promise; + + mlp(overrides?: CallOverrides): Promise; + + orderBook(overrides?: CallOverrides): Promise; + + pool(overrides?: CallOverrides): Promise; + }; +} diff --git a/src/apps/mux/contracts/ethers/factories/MuxReader__factory.ts b/src/apps/mux/contracts/ethers/factories/MuxReader__factory.ts new file mode 100644 index 000000000..33e31864d --- /dev/null +++ b/src/apps/mux/contracts/ethers/factories/MuxReader__factory.ts @@ -0,0 +1,583 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ + +import { Contract, Signer, utils } from 'ethers'; +import type { Provider } from '@ethersproject/providers'; +import type { MuxReader, MuxReaderInterface } from '../MuxReader'; + +const _abi = [ + { + inputs: [ + { + internalType: 'address', + name: 'pool_', + type: 'address', + }, + { + internalType: 'address', + name: 'mlp_', + type: 'address', + }, + { + internalType: 'address', + name: 'dex_', + type: 'address', + }, + { + internalType: 'address', + name: 'orderBook_', + type: 'address', + }, + { + internalType: 'address[]', + name: 'deductWhiteList_', + type: 'address[]', + }, + ], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + name: 'deductWhiteList', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'dex', + outputs: [ + { + internalType: 'contract ILiquidityManager', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getChainStorage', + outputs: [ + { + components: [ + { + components: [ + { + internalType: 'uint32', + name: 'shortFundingBaseRate8H', + type: 'uint32', + }, + { + internalType: 'uint32', + name: 'shortFundingLimitRate8H', + type: 'uint32', + }, + { + internalType: 'uint32', + name: 'fundingInterval', + type: 'uint32', + }, + { + internalType: 'uint32', + name: 'liquidityBaseFeeRate', + type: 'uint32', + }, + { + internalType: 'uint32', + name: 'liquidityDynamicFeeRate', + type: 'uint32', + }, + { + internalType: 'uint96', + name: 'mlpPriceLowerBound', + type: 'uint96', + }, + { + internalType: 'uint96', + name: 'mlpPriceUpperBound', + type: 'uint96', + }, + { + internalType: 'uint32', + name: 'lastFundingTime', + type: 'uint32', + }, + { + internalType: 'uint32', + name: 'sequence', + type: 'uint32', + }, + { + internalType: 'uint32', + name: 'strictStableDeviation', + type: 'uint32', + }, + ], + internalType: 'struct Reader.PoolStorage', + name: 'pool', + type: 'tuple', + }, + { + components: [ + { + internalType: 'bytes32', + name: 'symbol', + type: 'bytes32', + }, + { + internalType: 'address', + name: 'tokenAddress', + type: 'address', + }, + { + internalType: 'address', + name: 'muxTokenAddress', + type: 'address', + }, + { + internalType: 'uint8', + name: 'id', + type: 'uint8', + }, + { + internalType: 'uint8', + name: 'decimals', + type: 'uint8', + }, + { + internalType: 'uint56', + name: 'flags', + type: 'uint56', + }, + { + internalType: 'uint32', + name: 'initialMarginRate', + type: 'uint32', + }, + { + internalType: 'uint32', + name: 'maintenanceMarginRate', + type: 'uint32', + }, + { + internalType: 'uint32', + name: 'positionFeeRate', + type: 'uint32', + }, + { + internalType: 'uint32', + name: 'minProfitRate', + type: 'uint32', + }, + { + internalType: 'uint32', + name: 'minProfitTime', + type: 'uint32', + }, + { + internalType: 'uint96', + name: 'maxLongPositionSize', + type: 'uint96', + }, + { + internalType: 'uint96', + name: 'maxShortPositionSize', + type: 'uint96', + }, + { + internalType: 'uint32', + name: 'spotWeight', + type: 'uint32', + }, + { + internalType: 'uint32', + name: 'longFundingBaseRate8H', + type: 'uint32', + }, + { + internalType: 'uint32', + name: 'longFundingLimitRate8H', + type: 'uint32', + }, + { + internalType: 'uint8', + name: 'referenceOracleType', + type: 'uint8', + }, + { + internalType: 'address', + name: 'referenceOracle', + type: 'address', + }, + { + internalType: 'uint32', + name: 'referenceDeviation', + type: 'uint32', + }, + { + internalType: 'uint32', + name: 'halfSpread', + type: 'uint32', + }, + { + internalType: 'uint128', + name: 'longCumulativeFundingRate', + type: 'uint128', + }, + { + internalType: 'uint128', + name: 'shortCumulativeFunding', + type: 'uint128', + }, + { + internalType: 'uint96', + name: 'spotLiquidity', + type: 'uint96', + }, + { + internalType: 'uint96', + name: 'totalLongPosition', + type: 'uint96', + }, + { + internalType: 'uint96', + name: 'totalShortPosition', + type: 'uint96', + }, + { + internalType: 'uint96', + name: 'averageLongPrice', + type: 'uint96', + }, + { + internalType: 'uint96', + name: 'averageShortPrice', + type: 'uint96', + }, + { + internalType: 'uint128', + name: 'collectedFee', + type: 'uint128', + }, + { + internalType: 'uint256', + name: 'deduct', + type: 'uint256', + }, + ], + internalType: 'struct Reader.AssetStorage[]', + name: 'assets', + type: 'tuple[]', + }, + { + components: [ + { + internalType: 'uint8', + name: 'dexId', + type: 'uint8', + }, + { + internalType: 'uint8', + name: 'dexType', + type: 'uint8', + }, + { + internalType: 'uint8[]', + name: 'assetIds', + type: 'uint8[]', + }, + { + internalType: 'uint32[]', + name: 'assetWeightInDEX', + type: 'uint32[]', + }, + { + internalType: 'uint256[]', + name: 'totalSpotInDEX', + type: 'uint256[]', + }, + { + internalType: 'uint32', + name: 'dexWeight', + type: 'uint32', + }, + { + internalType: 'uint256', + name: 'dexLPBalance', + type: 'uint256', + }, + { + internalType: 'uint256[]', + name: 'liquidityBalance', + type: 'uint256[]', + }, + ], + internalType: 'struct Reader.DexStorage[]', + name: 'dexes', + type: 'tuple[]', + }, + { + internalType: 'uint32', + name: 'liquidityLockPeriod', + type: 'uint32', + }, + { + internalType: 'uint32', + name: 'marketOrderTimeout', + type: 'uint32', + }, + { + internalType: 'uint32', + name: 'maxLimitOrderTimeout', + type: 'uint32', + }, + { + internalType: 'uint256', + name: 'lpDeduct', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'stableDeduct', + type: 'uint256', + }, + { + internalType: 'bool', + name: 'isPositionOrderPaused', + type: 'bool', + }, + { + internalType: 'bool', + name: 'isLiquidityOrderPaused', + type: 'bool', + }, + ], + internalType: 'struct Reader.ChainStorage', + name: 'chain', + type: 'tuple', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address[]', + name: 'tokens', + type: 'address[]', + }, + { + internalType: 'address', + name: 'owner', + type: 'address', + }, + ], + name: 'getErc20Balances', + outputs: [ + { + internalType: 'uint256[]', + name: 'balances', + type: 'uint256[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint64[]', + name: 'orderIds', + type: 'uint64[]', + }, + ], + name: 'getOrders', + outputs: [ + { + internalType: 'bytes32[3][]', + name: 'orders', + type: 'bytes32[3][]', + }, + { + internalType: 'bool[]', + name: 'isExist', + type: 'bool[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32[]', + name: 'subAccountIds', + type: 'bytes32[]', + }, + ], + name: 'getSubAccounts', + outputs: [ + { + components: [ + { + internalType: 'uint96', + name: 'collateral', + type: 'uint96', + }, + { + internalType: 'uint96', + name: 'size', + type: 'uint96', + }, + { + internalType: 'uint32', + name: 'lastIncreasedTime', + type: 'uint32', + }, + { + internalType: 'uint96', + name: 'entryPrice', + type: 'uint96', + }, + { + internalType: 'uint128', + name: 'entryFunding', + type: 'uint128', + }, + ], + internalType: 'struct Reader.SubAccountState[]', + name: 'subAccounts', + type: 'tuple[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32[]', + name: 'subAccountIds', + type: 'bytes32[]', + }, + { + internalType: 'uint64[]', + name: 'orderIds', + type: 'uint64[]', + }, + ], + name: 'getSubAccountsAndOrders', + outputs: [ + { + components: [ + { + internalType: 'uint96', + name: 'collateral', + type: 'uint96', + }, + { + internalType: 'uint96', + name: 'size', + type: 'uint96', + }, + { + internalType: 'uint32', + name: 'lastIncreasedTime', + type: 'uint32', + }, + { + internalType: 'uint96', + name: 'entryPrice', + type: 'uint96', + }, + { + internalType: 'uint128', + name: 'entryFunding', + type: 'uint128', + }, + ], + internalType: 'struct Reader.SubAccountState[]', + name: 'subAccounts', + type: 'tuple[]', + }, + { + internalType: 'bytes32[3][]', + name: 'orders', + type: 'bytes32[3][]', + }, + { + internalType: 'bool[]', + name: 'isOrderExist', + type: 'bool[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'mlp', + outputs: [ + { + internalType: 'contract IERC20', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'orderBook', + outputs: [ + { + internalType: 'contract IOrderBook', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'pool', + outputs: [ + { + internalType: 'contract ILiquidityPool', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, +]; + +export class MuxReader__factory { + static readonly abi = _abi; + static createInterface(): MuxReaderInterface { + return new utils.Interface(_abi) as MuxReaderInterface; + } + static connect(address: string, signerOrProvider: Signer | Provider): MuxReader { + return new Contract(address, _abi, signerOrProvider) as MuxReader; + } +} diff --git a/src/apps/mux/contracts/ethers/factories/index.ts b/src/apps/mux/contracts/ethers/factories/index.ts index 3c5562dbf..99b6c5781 100644 --- a/src/apps/mux/contracts/ethers/factories/index.ts +++ b/src/apps/mux/contracts/ethers/factories/index.ts @@ -1,5 +1,6 @@ /* Autogenerated file. Do not edit manually. */ /* tslint:disable */ /* eslint-disable */ +export { MuxReader__factory } from './MuxReader__factory'; export { MuxRewardRouter__factory } from './MuxRewardRouter__factory'; export { MuxRewardTracker__factory } from './MuxRewardTracker__factory'; diff --git a/src/apps/mux/contracts/ethers/index.ts b/src/apps/mux/contracts/ethers/index.ts index 549bea0a4..6cdce8bfc 100644 --- a/src/apps/mux/contracts/ethers/index.ts +++ b/src/apps/mux/contracts/ethers/index.ts @@ -1,8 +1,10 @@ /* Autogenerated file. Do not edit manually. */ /* tslint:disable */ /* eslint-disable */ +export type { MuxReader } from './MuxReader'; export type { MuxRewardRouter } from './MuxRewardRouter'; export type { MuxRewardTracker } from './MuxRewardTracker'; export * as factories from './factories'; +export { MuxReader__factory } from './factories/MuxReader__factory'; export { MuxRewardRouter__factory } from './factories/MuxRewardRouter__factory'; export { MuxRewardTracker__factory } from './factories/MuxRewardTracker__factory'; diff --git a/src/apps/mux/contracts/index.ts b/src/apps/mux/contracts/index.ts index 621b75eab..1850a513f 100644 --- a/src/apps/mux/contracts/index.ts +++ b/src/apps/mux/contracts/index.ts @@ -4,6 +4,7 @@ import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface'; import { ContractFactory } from '~contract/contracts'; import { Network } from '~types/network.interface'; +import { MuxReader__factory } from './ethers'; import { MuxRewardRouter__factory } from './ethers'; import { MuxRewardTracker__factory } from './ethers'; @@ -16,6 +17,9 @@ export class MuxContractFactory extends ContractFactory { super((network: Network) => appToolkit.getNetworkProvider(network)); } + muxReader({ address, network }: ContractOpts) { + return MuxReader__factory.connect(address, this.appToolkit.getNetworkProvider(network)); + } muxRewardRouter({ address, network }: ContractOpts) { return MuxRewardRouter__factory.connect(address, this.appToolkit.getNetworkProvider(network)); } @@ -24,5 +28,6 @@ export class MuxContractFactory extends ContractFactory { } } +export type { MuxReader } from './ethers'; export type { MuxRewardRouter } from './ethers'; export type { MuxRewardTracker } from './ethers'; diff --git a/src/apps/mux/fantom/mux.balance-fetcher.ts b/src/apps/mux/fantom/mux.balance-fetcher.ts new file mode 100644 index 000000000..3d179f8d5 --- /dev/null +++ b/src/apps/mux/fantom/mux.balance-fetcher.ts @@ -0,0 +1,35 @@ +import { Inject } from '@nestjs/common'; + +import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface'; +import { Register } from '~app-toolkit/decorators'; +import { presentBalanceFetcherResponse } from '~app-toolkit/helpers/presentation/balance-fetcher-response.present'; +import { MuxLevTradesBalanceHelper } from '~apps/mux/helpers/mux.lev-trades.balance-helper'; +import { BalanceFetcher } from '~balance/balance-fetcher.interface'; +import { Network } from '~types/network.interface'; + +import { MUX_DEFINITION } from '../mux.definition'; + +const network = Network.FANTOM_OPERA_MAINNET; + +@Register.BalanceFetcher(MUX_DEFINITION.id, network) +export class FantomMuxBalanceFetcher implements BalanceFetcher { + constructor( + @Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit, + @Inject(MuxLevTradesBalanceHelper) private readonly muxLevTradesBalanceHelper: MuxLevTradesBalanceHelper, + ) {} + + private async getLevTradesBalances(address: string) { + return this.muxLevTradesBalanceHelper.getBalance({ address, network }); + } + + async getBalances(address: string) { + const levTradesBalances = await this.getLevTradesBalances(address); + + return presentBalanceFetcherResponse([ + { + label: 'Leveraged trades', + assets: [...levTradesBalances], + }, + ]); + } +} diff --git a/src/apps/mux/fantom/mux.lev-trades.contract-position-fetcher.ts b/src/apps/mux/fantom/mux.lev-trades.contract-position-fetcher.ts new file mode 100644 index 000000000..73f5f5a13 --- /dev/null +++ b/src/apps/mux/fantom/mux.lev-trades.contract-position-fetcher.ts @@ -0,0 +1,25 @@ +import { Inject } from '@nestjs/common'; + +import { Register } from '~app-toolkit/decorators'; +import { MuxLevTradesContractPositionHelper } from '~apps/mux/helpers/mux.lev-trades.contract-position-helper'; +import { PositionFetcher } from '~position/position-fetcher.interface'; +import { ContractPosition } from '~position/position.interface'; +import { Network } from '~types/network.interface'; + +import { MUX_DEFINITION } from '../mux.definition'; + +const appId = MUX_DEFINITION.id; +const groupId = MUX_DEFINITION.groups.levTrades.id; +const network = Network.FANTOM_OPERA_MAINNET; + +@Register.ContractPositionFetcher({ appId, groupId, network }) +export class FantomMuxLevTradesContractPositionFetcher implements PositionFetcher { + constructor( + @Inject(MuxLevTradesContractPositionHelper) + private readonly muxLevTradesContractPositionHelper: MuxLevTradesContractPositionHelper, + ) {} + + async getPositions() { + return this.muxLevTradesContractPositionHelper.getPosition({ network }); + } +} diff --git a/src/apps/mux/helpers/common.ts b/src/apps/mux/helpers/common.ts new file mode 100644 index 000000000..1a2874da1 --- /dev/null +++ b/src/apps/mux/helpers/common.ts @@ -0,0 +1,150 @@ +import Axios, { AxiosInstance } from 'axios'; +import { ethers } from 'ethers'; + +import { IAppToolkit } from '~app-toolkit/app-toolkit.interface'; +import { MuxContractFactory } from '~apps/mux'; +import { LiquidityAsset } from '~apps/mux/helpers/mux.mlp.token-helper'; +import { ContractType } from '~position/contract.interface'; +import { BaseToken } from '~position/token.interface'; +import { Network } from '~types/network.interface'; + +interface ReaderAssets { + symbol: string; + tokenAddress: string; + decimals: number; + isStable: boolean; + isTradable: boolean; + isOpenable: boolean; + useStableTokenForProfit: boolean; + isEnabled: boolean; +} + +export const DECIMALS = 18; +export const RATIO_DECIMALS = 5; +export const ASSET_IS_STABLE = 0x00000000000001; // is a usdt, usdc, ... +export const ASSET_CAN_ADD_REMOVE_LIQUIDITY = 0x00000000000002; // can call addLiquidity and removeLiquidity with this token +export const ASSET_IS_TRADABLE = 0x00000000000100; // allowed to be assetId +export const ASSET_IS_OPENABLE = 0x00000000010000; // can open position +export const ASSET_IS_SHORTABLE = 0x00000001000000; // allow shorting this asset +export const ASSET_USE_STABLE_TOKEN_FOR_PROFIT = 0x00000100000000; // take profit will get stable coin +export const ASSET_IS_ENABLED = 0x00010000000000; // allowed to be assetId and collateralId +export const ASSET_IS_STRICT_STABLE = 0x01000000000000; // assetPrice is always 1 unless volatility exceeds strictStableDeviation + +const invalidAddress = '0x0000000000000000000000000000000000000000'; + +export const READER_ADDRESS = { + [Network.ARBITRUM_MAINNET]: '0x6e29c4e8095b2885b8d30b17790924f33ecd7b33', + [Network.BINANCE_SMART_CHAIN_MAINNET]: '0xeab5b06a1ea173674601dd54c612542b563beca1', + [Network.AVALANCHE_MAINNET]: '0x5996d4545ee59d96cb1fe8661a028bef0f4744b0', + [Network.FANTOM_OPERA_MAINNET]: '0x29f4dc996a0219838afecf868362e4df28a70a7b', +}; + +const muxAxios: AxiosInstance = Axios.create({ + baseURL: 'https://app.mux.network', +}); + +export function formatMetaBaseData(cols: Array, rows: Array>) { + const keys = cols.map(col => { + return col.display_name; + }); + + return rows.map(row => { + const obj: any = {}; + row.map((item, index) => { + obj[keys[index]] = item; + }); + return obj; + }); +} + +async function getLiquidityAssetPriceMap() { + const priceMap: Map = new Map(); + const { data: liquidityAsset } = await muxAxios.get('/api/liquidityAsset'); + liquidityAsset.assets.map(asset => { + priceMap.set(asset.symbol, Number(asset.price)); + }); + return priceMap; +} + +function and64(v1: number, v2: number): number { + const hi = 0x80000000; + const low = 0x7fffffff; + const hi1 = ~~(v1 / hi); + const hi2 = ~~(v2 / hi); + const low1 = v1 & low; + const low2 = v2 & low; + const h = hi1 & hi2; + const l = low1 & low2; + return h * hi + l; +} + +function test64(v1: number, mask: number): boolean { + return and64(v1, mask) !== 0; +} + +async function getReaderAssets(network: Network, appToolkit: IAppToolkit): Promise { + const multicall = appToolkit.getMulticall(network); + const readerContract = new MuxContractFactory(appToolkit).muxReader({ address: READER_ADDRESS[network], network }); + const storage = await multicall.wrap(readerContract).callStatic.getChainStorage(); + + return storage[1] + .filter(item => item.tokenAddress !== invalidAddress) + .map(a => { + return { + symbol: ethers.utils.parseBytes32String(a.symbol), + tokenAddress: a.tokenAddress, + decimals: a.decimals, + isStable: test64(a.flags.toNumber(), ASSET_IS_STABLE), + isTradable: test64(a.flags.toNumber(), ASSET_IS_TRADABLE), + isOpenable: test64(a.flags.toNumber(), ASSET_IS_OPENABLE), + useStableTokenForProfit: test64(a.flags.toNumber(), ASSET_USE_STABLE_TOKEN_FOR_PROFIT), + isEnabled: test64(a.flags.toNumber(), ASSET_IS_ENABLED), + }; + }); +} + +export async function getMarketTokensByNetwork(network: Network, appToolkit: IAppToolkit): Promise { + const baseTokens = await appToolkit.getBaseTokenPrices(network); + const priceMap = await getLiquidityAssetPriceMap(); + const assets = await getReaderAssets(network, appToolkit); + + return assets + .filter(item => !item.isStable && item.isTradable && item.isOpenable && item.isEnabled) + .map(token => { + let marketToken = baseTokens.find(x => x.address === token.tokenAddress); + if (!marketToken) { + marketToken = { + address: token.tokenAddress, + symbol: token.symbol, + decimals: token.decimals, + price: priceMap.get(token.symbol) || 0, + type: ContractType.BASE_TOKEN, + network: network, + }; + } + return marketToken; + }); +} + +export async function getCollateralTokensByNetwork(network: Network, appToolkit: IAppToolkit): Promise { + const baseTokens = await appToolkit.getBaseTokenPrices(network); + const priceMap = await getLiquidityAssetPriceMap(); + const assets = await getReaderAssets(network, appToolkit); + + return assets + .filter(item => !item.useStableTokenForProfit && item.isEnabled) + .map(token => { + let collateralToken = baseTokens.find(x => x.address === token.tokenAddress); + if (!collateralToken) { + collateralToken = { + address: token.tokenAddress, + symbol: token.symbol, + decimals: token.decimals, + price: priceMap.get(token.symbol) || 0, + type: ContractType.BASE_TOKEN, + network: network, + }; + } + return collateralToken; + }); +} diff --git a/src/apps/mux/helpers/mux.lev-trades.balance-helper.ts b/src/apps/mux/helpers/mux.lev-trades.balance-helper.ts new file mode 100644 index 000000000..7ffea6271 --- /dev/null +++ b/src/apps/mux/helpers/mux.lev-trades.balance-helper.ts @@ -0,0 +1,97 @@ +import { Inject, Injectable } from '@nestjs/common'; +import Axios, { AxiosInstance } from 'axios'; +import _ from 'lodash'; + +import { drillBalance } from '~app-toolkit'; +import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface'; +import { formatMetaBaseData, getCollateralTokensByNetwork, getMarketTokensByNetwork } from '~apps/mux/helpers/common'; +import { Network, NETWORK_IDS } from '~types/network.interface'; + +import { MuxContractFactory } from '../contracts'; +import MUX_DEFINITION from '../mux.definition'; + +export type MuxLevTradesContractPositionDataProps = { + collateralTokenSymbol: string; + marketTokenSymbol: string; + isLong: boolean; +}; + +type GetLevTradesContractPositionHelperParams = { + network: Network; + address: string; +}; + +@Injectable() +export class MuxLevTradesBalanceHelper { + private axios: AxiosInstance = Axios.create({ + baseURL: 'https://app.mux.network', + }); + + constructor( + @Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit, + @Inject(MuxContractFactory) private readonly contractFactory: MuxContractFactory, + ) {} + + async getBalance({ address, network }: GetLevTradesContractPositionHelperParams) { + const parameter = `[{"type":"id","value":"${address.toLowerCase()}","target":["variable",["template-tag","trader"]],"id":"14ef4983"}]` + const pathUrl = `/metabase/api/public/dashboard/5fe8cebe-328e-4334-8fb3-52c98fc8ef71/dashcard/128/card/92?parameters=${encodeURIComponent( + parameter, + )}`; + const { data } = await this.axios.get(pathUrl); + const positions = formatMetaBaseData(data.data.cols, data.data.rows); + + const levTrades = await this.appToolkit.getAppContractPositions({ + appId: MUX_DEFINITION.id, + groupIds: [MUX_DEFINITION.groups.levTrades.id], + network, + }); + + const baseTokens = await this.appToolkit.getBaseTokenPrices(network); + const marketTokens = await getMarketTokensByNetwork(network, this.appToolkit); + const collateralTokens = await getCollateralTokensByNetwork(network, this.appToolkit); + const contractPositions = levTrades.map(levTrade => { + const collateralTokenSymbol = levTrade.dataProps.collateralTokenSymbol; + const marketTokenSymbol = levTrade.dataProps.marketTokenSymbol; + const isLong = levTrade.dataProps.isLong; + + const position = positions.find( + item => + item.asset === marketTokenSymbol && + item.collateral_token === collateralTokenSymbol && + item.is_long === isLong && + item.chain_id === NETWORK_IDS[network], + ); + if (!position) return; + const collateralToken = collateralTokens.find(x => x.symbol == collateralTokenSymbol); + const hasProfit = position.upnl > 0; + const profitToken = isLong + ? marketTokens.find(x => x.symbol == marketTokenSymbol) + : baseTokens.find(x => x.symbol == 'USDC'); + if (!profitToken || !collateralToken) return; + + let balanceInCollateralToken = position.collateral_usd / collateralToken.price; + let balanceInCollateralTokenRaw = balanceInCollateralToken * 10 ** collateralToken.decimals; + let balanceInProfitToken = position.upnl / profitToken.price; + let balanceInProfitTokenRaw = balanceInProfitToken * 10 ** profitToken.decimals; + + if (!hasProfit) { + balanceInCollateralToken = (position.collateral_usd + position.upnl) / collateralToken.price; + balanceInCollateralTokenRaw = balanceInCollateralToken * 10 ** collateralToken.decimals; + balanceInProfitToken = 0; + balanceInProfitTokenRaw = 0; + } + + const tokenBalance = [ + drillBalance(collateralToken, balanceInCollateralTokenRaw.toString()), + hasProfit ? drillBalance(profitToken, balanceInProfitTokenRaw.toString()) : null, + ]; + + return { + ...levTrade, + tokens: _.compact(tokenBalance), + balanceUSD: position.collateral_usd + position.upnl, + }; + }); + return _.compact(contractPositions); + } +} diff --git a/src/apps/mux/helpers/mux.lev-trades.contract-position-helper.ts b/src/apps/mux/helpers/mux.lev-trades.contract-position-helper.ts new file mode 100644 index 000000000..b8f4f38d4 --- /dev/null +++ b/src/apps/mux/helpers/mux.lev-trades.contract-position-helper.ts @@ -0,0 +1,89 @@ +import { Inject, Injectable } from '@nestjs/common'; +import _ from 'lodash'; + +import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface'; +import { getTokenImg } from '~app-toolkit/helpers/presentation/image.present'; +import { getCollateralTokensByNetwork, getMarketTokensByNetwork, READER_ADDRESS } from '~apps/mux/helpers/common'; +import { ContractType } from '~position/contract.interface'; +import { ContractPosition } from '~position/position.interface'; +import { supplied } from '~position/position.utils'; +import { Network } from '~types/network.interface'; + +import { MuxContractFactory } from '../contracts'; +import MUX_DEFINITION from '../mux.definition'; + +export type MuxLevTradesContractPositionDataProps = { + collateralTokenSymbol: string; + marketTokenSymbol: string; + isLong: boolean; +}; + +type GetLevTradesContractPositionHelperParams = { + network: Network; +}; + +const appId = MUX_DEFINITION.id; +const groupId = MUX_DEFINITION.groups.levTrades.id; + +@Injectable() +export class MuxLevTradesContractPositionHelper { + constructor( + @Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit, + @Inject(MuxContractFactory) private readonly contractFactory: MuxContractFactory, + ) {} + + async getPosition({ network }: GetLevTradesContractPositionHelperParams) { + const marketTokensList = await getMarketTokensByNetwork(network, this.appToolkit); + const collateralTokensList = await getCollateralTokensByNetwork(network, this.appToolkit); + + const positions = await Promise.all( + collateralTokensList.map(collateralToken => { + const positionsForGivenPair = marketTokensList.map(marketToken => { + const shortPosition: ContractPosition = { + type: ContractType.POSITION, + appId, + groupId, + address: READER_ADDRESS[network], + key: `${collateralToken.symbol}:${marketToken.symbol}:short`, + network, + tokens: [supplied(collateralToken), marketToken], + dataProps: { + collateralTokenSymbol: collateralToken.symbol, + marketTokenSymbol: marketToken.symbol, + isLong: false, + }, + displayProps: { + label: `Short ${marketToken.symbol}`, + images: [getTokenImg(collateralToken.address, network), getTokenImg(marketToken.address, network)], + statsItems: [], + }, + }; + const longPosition: ContractPosition = { + type: ContractType.POSITION, + appId, + groupId, + address: READER_ADDRESS[network], + key: `${collateralToken.symbol}:${marketToken.symbol}:long`, + network, + tokens: [supplied(collateralToken), marketToken], + dataProps: { + collateralTokenSymbol: collateralToken.symbol, + marketTokenSymbol: marketToken.symbol, + isLong: true, + }, + displayProps: { + label: `Long ${marketToken.symbol}`, + images: [getTokenImg(collateralToken.address, network), getTokenImg(marketToken.address, network)], + statsItems: [], + }, + }; + return [shortPosition, longPosition]; + }); + + return _.compact(positionsForGivenPair).flat(); + }), + ); + + return positions.flat(); + } +} diff --git a/src/apps/mux/helpers/mux.mlp.token-helper.ts b/src/apps/mux/helpers/mux.mlp.token-helper.ts index 2d5c5beac..3bd37877a 100644 --- a/src/apps/mux/helpers/mux.mlp.token-helper.ts +++ b/src/apps/mux/helpers/mux.mlp.token-helper.ts @@ -21,7 +21,7 @@ type GetMuxMlpTokenParams = { mlpTokenAddress: string; }; -type LiquidityAsset = { +export type LiquidityAsset = { muxLPPrice: number; assets: Asset[]; }; diff --git a/src/apps/mux/mux.definition.ts b/src/apps/mux/mux.definition.ts index 1ebb2ed8b..964f63257 100644 --- a/src/apps/mux/mux.definition.ts +++ b/src/apps/mux/mux.definition.ts @@ -26,6 +26,11 @@ export const MUX_DEFINITION = appDefinition({ type: GroupType.POSITION, label: 'Farms', }, + levTrades: { + id: 'lev-trades', + type: GroupType.POSITION, + label: 'Leveraged trades', + }, }, tags: [AppTag.MARGIN_TRADING], keywords: [], diff --git a/src/apps/mux/mux.module.ts b/src/apps/mux/mux.module.ts index ef90fb4a4..ecf7c7736 100644 --- a/src/apps/mux/mux.module.ts +++ b/src/apps/mux/mux.module.ts @@ -1,13 +1,23 @@ import { Register } from '~app-toolkit/decorators'; import { AbstractApp } from '~app/app.dynamic-module'; +import { MuxLevTradesBalanceHelper } from '~apps/mux/helpers/mux.lev-trades.balance-helper'; +import { MuxLevTradesContractPositionHelper } from '~apps/mux/helpers/mux.lev-trades.contract-position-helper'; import { MuxMlpTokenHelper } from '~apps/mux/helpers/mux.mlp.token-helper'; +import { ArbitrumMuxBalanceFetcher } from './arbitrum/mux.balance-fetcher'; import { ArbitrumMuxFarmContractPositionFetcher } from './arbitrum/mux.farm.contract-position-fetcher'; +import { ArbitrumMuxLevTradesContractPositionFetcher } from './arbitrum/mux.lev-trades.contract-position-fetcher'; import { ArbitrumMuxMlpTokenFetcher } from './arbitrum/mux.mlp.token-fetcher'; import { ArbitrumMuxMuxTokenFetcher } from './arbitrum/mux.mux.token-fetcher'; +import { AvalancheMuxBalanceFetcher } from './avalanche/mux.balance-fetcher'; +import { AvalancheMuxLevTradesContractPositionFetcher } from './avalanche/mux.lev-trades.contract-position-fetcher'; import { AvalancheMuxMlpTokenFetcher } from './avalanche/mux.mlp.token-fetcher'; +import { BinanceSmartChainMuxBalanceFetcher } from './binance-smart-chain/mux.balance-fetcher'; +import { BinanceSmartChainMuxLevTradesContractPositionFetcher } from './binance-smart-chain/mux.lev-trades.contract-position-fetcher'; import { BinanceSmartChainMuxMlpTokenFetcher } from './binance-smart-chain/mux.mlp.token-fetcher'; import { MuxContractFactory } from './contracts'; +import { FantomMuxBalanceFetcher } from './fantom/mux.balance-fetcher'; +import { FantomMuxLevTradesContractPositionFetcher } from './fantom/mux.lev-trades.contract-position-fetcher'; import { FantomMuxMlpTokenFetcher } from './fantom/mux.mlp.token-fetcher'; import { MuxAppDefinition, MUX_DEFINITION } from './mux.definition'; @@ -17,16 +27,26 @@ import { MuxAppDefinition, MUX_DEFINITION } from './mux.definition'; MuxAppDefinition, MuxContractFactory, // Helper + MuxLevTradesBalanceHelper, MuxMlpTokenHelper, + MuxLevTradesContractPositionHelper, // Arbitrum + ArbitrumMuxBalanceFetcher, ArbitrumMuxFarmContractPositionFetcher, + ArbitrumMuxLevTradesContractPositionFetcher, ArbitrumMuxMlpTokenFetcher, ArbitrumMuxMuxTokenFetcher, // Avalanche + AvalancheMuxBalanceFetcher, + AvalancheMuxLevTradesContractPositionFetcher, AvalancheMuxMlpTokenFetcher, // Binance-smart-chain + BinanceSmartChainMuxBalanceFetcher, + BinanceSmartChainMuxLevTradesContractPositionFetcher, BinanceSmartChainMuxMlpTokenFetcher, // Fantom + FantomMuxBalanceFetcher, + FantomMuxLevTradesContractPositionFetcher, FantomMuxMlpTokenFetcher, ], }) From 570a1c9d83aa88d75a11066658feb14517d18796 Mon Sep 17 00:00:00 2001 From: xionyk <82797250+xionyk@users.noreply.github.com> Date: Tue, 13 Dec 2022 21:02:10 +0800 Subject: [PATCH 2/4] feat(mux): Add lev-trades ContractPositionFetcher and add BalanceFetcher --- src/apps/mux/helpers/common.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/apps/mux/helpers/common.ts b/src/apps/mux/helpers/common.ts index 1a2874da1..842a38cb9 100644 --- a/src/apps/mux/helpers/common.ts +++ b/src/apps/mux/helpers/common.ts @@ -2,6 +2,7 @@ import Axios, { AxiosInstance } from 'axios'; import { ethers } from 'ethers'; import { IAppToolkit } from '~app-toolkit/app-toolkit.interface'; +import { ZERO_ADDRESS } from '~app-toolkit/constants/address'; import { MuxContractFactory } from '~apps/mux'; import { LiquidityAsset } from '~apps/mux/helpers/mux.mlp.token-helper'; import { ContractType } from '~position/contract.interface'; @@ -30,8 +31,6 @@ export const ASSET_USE_STABLE_TOKEN_FOR_PROFIT = 0x00000100000000; // take profi export const ASSET_IS_ENABLED = 0x00010000000000; // allowed to be assetId and collateralId export const ASSET_IS_STRICT_STABLE = 0x01000000000000; // assetPrice is always 1 unless volatility exceeds strictStableDeviation -const invalidAddress = '0x0000000000000000000000000000000000000000'; - export const READER_ADDRESS = { [Network.ARBITRUM_MAINNET]: '0x6e29c4e8095b2885b8d30b17790924f33ecd7b33', [Network.BINANCE_SMART_CHAIN_MAINNET]: '0xeab5b06a1ea173674601dd54c612542b563beca1', @@ -88,7 +87,7 @@ async function getReaderAssets(network: Network, appToolkit: IAppToolkit): Promi const storage = await multicall.wrap(readerContract).callStatic.getChainStorage(); return storage[1] - .filter(item => item.tokenAddress !== invalidAddress) + .filter(item => item.tokenAddress !== ZERO_ADDRESS) .map(a => { return { symbol: ethers.utils.parseBytes32String(a.symbol), From 317147180c09b58d9788fc6f32424d9b9fe6fda9 Mon Sep 17 00:00:00 2001 From: xionyk <82797250+xionyk@users.noreply.github.com> Date: Fri, 16 Dec 2022 09:45:54 +0800 Subject: [PATCH 3/4] feat(mux): Add lev-trades ContractPositionFetcher and add BalanceFetcher --- src/apps/mux/helpers/common.ts | 144 ++++++++++++------ .../helpers/mux.lev-trades.balance-helper.ts | 135 ++++++++-------- ...mux.lev-trades.contract-position-helper.ts | 14 +- 3 files changed, 173 insertions(+), 120 deletions(-) diff --git a/src/apps/mux/helpers/common.ts b/src/apps/mux/helpers/common.ts index 842a38cb9..45fb3ad02 100644 --- a/src/apps/mux/helpers/common.ts +++ b/src/apps/mux/helpers/common.ts @@ -1,16 +1,16 @@ -import Axios, { AxiosInstance } from 'axios'; +import BigNumber from 'bignumber.js'; import { ethers } from 'ethers'; +import _ from 'lodash'; import { IAppToolkit } from '~app-toolkit/app-toolkit.interface'; import { ZERO_ADDRESS } from '~app-toolkit/constants/address'; import { MuxContractFactory } from '~apps/mux'; -import { LiquidityAsset } from '~apps/mux/helpers/mux.mlp.token-helper'; -import { ContractType } from '~position/contract.interface'; import { BaseToken } from '~position/token.interface'; import { Network } from '~types/network.interface'; interface ReaderAssets { symbol: string; + id: number; tokenAddress: string; decimals: number; isStable: boolean; @@ -18,8 +18,19 @@ interface ReaderAssets { isOpenable: boolean; useStableTokenForProfit: boolean; isEnabled: boolean; + minProfitTime: number; + minProfitRate: BigNumber; } +interface BaseTokenWithMuxTokenId { + muxTokenId: number; + minProfitTime: number; + minProfitRate: BigNumber; +} + +type MuxBaseToken = BaseToken & BaseTokenWithMuxTokenId; + +export const _0: BigNumber = new BigNumber('0'); export const DECIMALS = 18; export const RATIO_DECIMALS = 5; export const ASSET_IS_STABLE = 0x00000000000001; // is a usdt, usdc, ... @@ -38,10 +49,6 @@ export const READER_ADDRESS = { [Network.FANTOM_OPERA_MAINNET]: '0x29f4dc996a0219838afecf868362e4df28a70a7b', }; -const muxAxios: AxiosInstance = Axios.create({ - baseURL: 'https://app.mux.network', -}); - export function formatMetaBaseData(cols: Array, rows: Array>) { const keys = cols.map(col => { return col.display_name; @@ -56,15 +63,6 @@ export function formatMetaBaseData(cols: Array, rows: Array>) { }); } -async function getLiquidityAssetPriceMap() { - const priceMap: Map = new Map(); - const { data: liquidityAsset } = await muxAxios.get('/api/liquidityAsset'); - liquidityAsset.assets.map(asset => { - priceMap.set(asset.symbol, Number(asset.price)); - }); - return priceMap; -} - function and64(v1: number, v2: number): number { const hi = 0x80000000; const low = 0x7fffffff; @@ -81,6 +79,14 @@ function test64(v1: number, mask: number): boolean { return and64(v1, mask) !== 0; } +function fromRate(n: number): BigNumber { + return new BigNumber(n.toString()).shiftedBy(-RATIO_DECIMALS); +} + +export function fromWei(n: ethers.BigNumber): BigNumber { + return new BigNumber(n.toString()).shiftedBy(-DECIMALS); +} + async function getReaderAssets(network: Network, appToolkit: IAppToolkit): Promise { const multicall = appToolkit.getMulticall(network); const readerContract = new MuxContractFactory(appToolkit).muxReader({ address: READER_ADDRESS[network], network }); @@ -91,6 +97,7 @@ async function getReaderAssets(network: Network, appToolkit: IAppToolkit): Promi .map(a => { return { symbol: ethers.utils.parseBytes32String(a.symbol), + id: a.id, tokenAddress: a.tokenAddress, decimals: a.decimals, isStable: test64(a.flags.toNumber(), ASSET_IS_STABLE), @@ -98,52 +105,89 @@ async function getReaderAssets(network: Network, appToolkit: IAppToolkit): Promi isOpenable: test64(a.flags.toNumber(), ASSET_IS_OPENABLE), useStableTokenForProfit: test64(a.flags.toNumber(), ASSET_USE_STABLE_TOKEN_FOR_PROFIT), isEnabled: test64(a.flags.toNumber(), ASSET_IS_ENABLED), + minProfitTime: a.minProfitTime, + minProfitRate: fromRate(a.minProfitRate), }; }); } -export async function getMarketTokensByNetwork(network: Network, appToolkit: IAppToolkit): Promise { +export async function getMarketTokensByNetwork(network: Network, appToolkit: IAppToolkit): Promise { const baseTokens = await appToolkit.getBaseTokenPrices(network); - const priceMap = await getLiquidityAssetPriceMap(); const assets = await getReaderAssets(network, appToolkit); - return assets - .filter(item => !item.isStable && item.isTradable && item.isOpenable && item.isEnabled) - .map(token => { - let marketToken = baseTokens.find(x => x.address === token.tokenAddress); - if (!marketToken) { - marketToken = { - address: token.tokenAddress, - symbol: token.symbol, - decimals: token.decimals, - price: priceMap.get(token.symbol) || 0, - type: ContractType.BASE_TOKEN, - network: network, + return _.compact( + assets + .filter(item => !item.isStable && item.isTradable && item.isOpenable && item.isEnabled) + .map(token => { + const marketToken = baseTokens.find(x => x.address === token.tokenAddress.toLowerCase()); + if (!marketToken) { + return; + } + return { + ...marketToken, + muxTokenId: token.id, + minProfitTime: token.minProfitTime, + minProfitRate: token.minProfitRate, }; - } - return marketToken; - }); + }), + ); } -export async function getCollateralTokensByNetwork(network: Network, appToolkit: IAppToolkit): Promise { +export async function getCollateralTokensByNetwork(network: Network, appToolkit: IAppToolkit): Promise { const baseTokens = await appToolkit.getBaseTokenPrices(network); - const priceMap = await getLiquidityAssetPriceMap(); const assets = await getReaderAssets(network, appToolkit); - return assets - .filter(item => !item.useStableTokenForProfit && item.isEnabled) - .map(token => { - let collateralToken = baseTokens.find(x => x.address === token.tokenAddress); - if (!collateralToken) { - collateralToken = { - address: token.tokenAddress, - symbol: token.symbol, - decimals: token.decimals, - price: priceMap.get(token.symbol) || 0, - type: ContractType.BASE_TOKEN, - network: network, + return _.compact( + assets + .filter(item => !item.useStableTokenForProfit && item.isEnabled) + .map(token => { + const collateralToken = baseTokens.find(x => x.address === token.tokenAddress.toLowerCase()); + if (!collateralToken) { + return; + } + return { + ...collateralToken, + muxTokenId: token.id, + minProfitTime: token.minProfitTime, + minProfitRate: token.minProfitRate, }; - } - return collateralToken; - }); + }), + ); +} + +export function computePositionPnlUsd( + asset: MuxBaseToken, + amount: BigNumber, + entryPrice: BigNumber, + lastIncreasedTime: number, + isLong: boolean, +): { pendingPnlUsd: BigNumber; pnlUsd: BigNumber } { + if (amount.eq(_0)) { + return { pendingPnlUsd: _0, pnlUsd: _0 }; + } + const priceDelta = isLong ? new BigNumber(asset.price).minus(entryPrice) : entryPrice.minus(asset.price); + const pendingPnlUsd = priceDelta.times(amount); + if ( + priceDelta.gt(_0) && + Math.ceil(Date.now() / 1000) < lastIncreasedTime + asset.minProfitTime && + priceDelta.abs().lt(asset.minProfitRate.times(entryPrice)) + ) { + return { pendingPnlUsd, pnlUsd: _0 }; + } + return { pendingPnlUsd, pnlUsd: pendingPnlUsd }; +} + +export function encodeSubAccountId( + account: string, + collateralId: number, + assetId: number, + isLong: boolean, +): string | null { + if (ethers.utils.arrayify(account).length !== 20) { + return null; + } + return ( + ethers.utils.solidityPack(['address', 'uint8', 'uint8', 'bool'], [account, collateralId, assetId, isLong]) + + '000000000000000000' + ); } diff --git a/src/apps/mux/helpers/mux.lev-trades.balance-helper.ts b/src/apps/mux/helpers/mux.lev-trades.balance-helper.ts index 7ffea6271..7dc44daf4 100644 --- a/src/apps/mux/helpers/mux.lev-trades.balance-helper.ts +++ b/src/apps/mux/helpers/mux.lev-trades.balance-helper.ts @@ -1,21 +1,23 @@ import { Inject, Injectable } from '@nestjs/common'; -import Axios, { AxiosInstance } from 'axios'; import _ from 'lodash'; import { drillBalance } from '~app-toolkit'; import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface'; -import { formatMetaBaseData, getCollateralTokensByNetwork, getMarketTokensByNetwork } from '~apps/mux/helpers/common'; -import { Network, NETWORK_IDS } from '~types/network.interface'; +import { MuxContractFactory } from '~apps/mux'; +import { + _0, + computePositionPnlUsd, + encodeSubAccountId, + fromWei, + getCollateralTokensByNetwork, + getMarketTokensByNetwork, + READER_ADDRESS, +} from '~apps/mux/helpers/common'; +import { MuxLevTradesContractPositionDataProps } from '~apps/mux/helpers/mux.lev-trades.contract-position-helper'; +import { Network } from '~types/network.interface'; -import { MuxContractFactory } from '../contracts'; import MUX_DEFINITION from '../mux.definition'; -export type MuxLevTradesContractPositionDataProps = { - collateralTokenSymbol: string; - marketTokenSymbol: string; - isLong: boolean; -}; - type GetLevTradesContractPositionHelperParams = { network: Network; address: string; @@ -23,22 +25,16 @@ type GetLevTradesContractPositionHelperParams = { @Injectable() export class MuxLevTradesBalanceHelper { - private axios: AxiosInstance = Axios.create({ - baseURL: 'https://app.mux.network', - }); - constructor( @Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit, @Inject(MuxContractFactory) private readonly contractFactory: MuxContractFactory, ) {} async getBalance({ address, network }: GetLevTradesContractPositionHelperParams) { - const parameter = `[{"type":"id","value":"${address.toLowerCase()}","target":["variable",["template-tag","trader"]],"id":"14ef4983"}]` - const pathUrl = `/metabase/api/public/dashboard/5fe8cebe-328e-4334-8fb3-52c98fc8ef71/dashcard/128/card/92?parameters=${encodeURIComponent( - parameter, - )}`; - const { data } = await this.axios.get(pathUrl); - const positions = formatMetaBaseData(data.data.cols, data.data.rows); + const muxReaderContract = this.contractFactory.muxReader({ + address: READER_ADDRESS[network], + network, + }); const levTrades = await this.appToolkit.getAppContractPositions({ appId: MUX_DEFINITION.id, @@ -49,49 +45,62 @@ export class MuxLevTradesBalanceHelper { const baseTokens = await this.appToolkit.getBaseTokenPrices(network); const marketTokens = await getMarketTokensByNetwork(network, this.appToolkit); const collateralTokens = await getCollateralTokensByNetwork(network, this.appToolkit); - const contractPositions = levTrades.map(levTrade => { - const collateralTokenSymbol = levTrade.dataProps.collateralTokenSymbol; - const marketTokenSymbol = levTrade.dataProps.marketTokenSymbol; - const isLong = levTrade.dataProps.isLong; - - const position = positions.find( - item => - item.asset === marketTokenSymbol && - item.collateral_token === collateralTokenSymbol && - item.is_long === isLong && - item.chain_id === NETWORK_IDS[network], - ); - if (!position) return; - const collateralToken = collateralTokens.find(x => x.symbol == collateralTokenSymbol); - const hasProfit = position.upnl > 0; - const profitToken = isLong - ? marketTokens.find(x => x.symbol == marketTokenSymbol) - : baseTokens.find(x => x.symbol == 'USDC'); - if (!profitToken || !collateralToken) return; - - let balanceInCollateralToken = position.collateral_usd / collateralToken.price; - let balanceInCollateralTokenRaw = balanceInCollateralToken * 10 ** collateralToken.decimals; - let balanceInProfitToken = position.upnl / profitToken.price; - let balanceInProfitTokenRaw = balanceInProfitToken * 10 ** profitToken.decimals; - - if (!hasProfit) { - balanceInCollateralToken = (position.collateral_usd + position.upnl) / collateralToken.price; - balanceInCollateralTokenRaw = balanceInCollateralToken * 10 ** collateralToken.decimals; - balanceInProfitToken = 0; - balanceInProfitTokenRaw = 0; - } - - const tokenBalance = [ - drillBalance(collateralToken, balanceInCollateralTokenRaw.toString()), - hasProfit ? drillBalance(profitToken, balanceInProfitTokenRaw.toString()) : null, - ]; - - return { - ...levTrade, - tokens: _.compact(tokenBalance), - balanceUSD: position.collateral_usd + position.upnl, - }; - }); + const contractPositions = await Promise.all( + levTrades.map(async levTrade => { + const collateralTokenId = levTrade.dataProps.collateralTokenId; + const marketTokenId = levTrade.dataProps.marketTokenId; + const isLong = levTrade.dataProps.isLong; + + const collateralToken = collateralTokens.find(x => x.muxTokenId == collateralTokenId); + const marketToken = marketTokens.find(x => x.muxTokenId == marketTokenId); + const subAccountId = encodeSubAccountId(address, collateralTokenId, marketTokenId, isLong); + if (!subAccountId || !marketToken || !collateralToken) return; + + const { collateral, size, entryPrice, lastIncreasedTime } = ( + await muxReaderContract.getSubAccounts([subAccountId]) + )[0]; + + const { pnlUsd } = computePositionPnlUsd( + marketToken, + fromWei(size), + fromWei(entryPrice), + lastIncreasedTime, + isLong, + ); + + const collateralAmount = fromWei(collateral); + const collateralAmountUsd = collateralAmount.times(collateralToken.price); + + const hasProfit = pnlUsd.gt(0); + const profitToken = isLong + ? marketTokens.find(x => x.muxTokenId == marketTokenId) + : baseTokens.find(x => x.symbol == 'USDC'); + if (!profitToken || !collateralToken) return; + + let balanceInCollateralToken = collateralAmount; + let balanceInCollateralTokenRaw = balanceInCollateralToken.shiftedBy(collateralToken.decimals); + let balanceInProfitToken = pnlUsd.div(profitToken.price); + let balanceInProfitTokenRaw = balanceInProfitToken.shiftedBy(profitToken.decimals); + + if (!hasProfit) { + balanceInCollateralToken = collateralAmountUsd.plus(pnlUsd).div(collateralToken.price); + balanceInCollateralTokenRaw = balanceInCollateralToken.shiftedBy(collateralToken.decimals); + balanceInProfitToken = _0; + balanceInProfitTokenRaw = _0; + } + + const tokenBalance = [ + drillBalance(collateralToken, balanceInCollateralTokenRaw.toString()), + hasProfit ? drillBalance(profitToken, balanceInProfitTokenRaw.toString()) : null, + ]; + + return { + ...levTrade, + tokens: _.compact(tokenBalance), + balanceUSD: collateralAmountUsd.plus(pnlUsd), + }; + }), + ); return _.compact(contractPositions); } } diff --git a/src/apps/mux/helpers/mux.lev-trades.contract-position-helper.ts b/src/apps/mux/helpers/mux.lev-trades.contract-position-helper.ts index b8f4f38d4..78f227cea 100644 --- a/src/apps/mux/helpers/mux.lev-trades.contract-position-helper.ts +++ b/src/apps/mux/helpers/mux.lev-trades.contract-position-helper.ts @@ -3,18 +3,18 @@ import _ from 'lodash'; import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface'; import { getTokenImg } from '~app-toolkit/helpers/presentation/image.present'; +import { MuxContractFactory } from '~apps/mux'; import { getCollateralTokensByNetwork, getMarketTokensByNetwork, READER_ADDRESS } from '~apps/mux/helpers/common'; import { ContractType } from '~position/contract.interface'; import { ContractPosition } from '~position/position.interface'; import { supplied } from '~position/position.utils'; import { Network } from '~types/network.interface'; -import { MuxContractFactory } from '../contracts'; import MUX_DEFINITION from '../mux.definition'; export type MuxLevTradesContractPositionDataProps = { - collateralTokenSymbol: string; - marketTokenSymbol: string; + collateralTokenId: number; + marketTokenId: number; isLong: boolean; }; @@ -48,8 +48,8 @@ export class MuxLevTradesContractPositionHelper { network, tokens: [supplied(collateralToken), marketToken], dataProps: { - collateralTokenSymbol: collateralToken.symbol, - marketTokenSymbol: marketToken.symbol, + collateralTokenId: collateralToken.muxTokenId, + marketTokenId: marketToken.muxTokenId, isLong: false, }, displayProps: { @@ -67,8 +67,8 @@ export class MuxLevTradesContractPositionHelper { network, tokens: [supplied(collateralToken), marketToken], dataProps: { - collateralTokenSymbol: collateralToken.symbol, - marketTokenSymbol: marketToken.symbol, + collateralTokenId: collateralToken.muxTokenId, + marketTokenId: marketToken.muxTokenId, isLong: true, }, displayProps: { From 4f834e01821cb460b7c7a85c9b12be37756eb816 Mon Sep 17 00:00:00 2001 From: xionyk <82797250+xionyk@users.noreply.github.com> Date: Fri, 16 Dec 2022 15:46:18 +0800 Subject: [PATCH 4/4] feat(mux): Add lev-trades ContractPositionFetcher and add BalanceFetcher --- src/apps/mux/helpers/mux.lev-trades.balance-helper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apps/mux/helpers/mux.lev-trades.balance-helper.ts b/src/apps/mux/helpers/mux.lev-trades.balance-helper.ts index 7dc44daf4..6ea0735b1 100644 --- a/src/apps/mux/helpers/mux.lev-trades.balance-helper.ts +++ b/src/apps/mux/helpers/mux.lev-trades.balance-helper.ts @@ -97,7 +97,7 @@ export class MuxLevTradesBalanceHelper { return { ...levTrade, tokens: _.compact(tokenBalance), - balanceUSD: collateralAmountUsd.plus(pnlUsd), + balanceUSD: collateralAmountUsd.plus(pnlUsd).toNumber(), }; }), );