diff --git a/.env.example b/.env.example index ad812e2..2dd5ece 100644 --- a/.env.example +++ b/.env.example @@ -6,7 +6,8 @@ VITE_PORT=8095 VITE_APP_NAME='Morpheus Dashboard' VITE_APP_DESCRIPTION='Morpheus Dashboard: capital, community, coders, compute' VITE_APP_URL='https://morpheus.206.189.243.3.sslip.io' -VITE_APP_GRAPHQL_API_URL='https://api.thegraph.com/subgraphs/name/ruslanprogrammer/morpheus-dashboard' +VITE_APP_TESTNET_GRAPHQL_API_URL='https://api.studio.thegraph.com/query/73688/kkk/version/latest' +VITE_APP_MAINNET_GRAPHQL_API_URL='https://api.studio.thegraph.com/query/67225/morpheus-dashboard/version/latest' VITE_APP_WALLET_CONNECT_PROJECT_ID='abcdefghijklmnopqrstuvwxyz' #VITE_APP_BUILD_VERSION='1.0.0' diff --git a/package.json b/package.json index 00a04df..6988bbc 100644 --- a/package.json +++ b/package.json @@ -21,10 +21,12 @@ "dependencies": { "@apollo/client": "^3.9.10", "@distributedlab/tools": "^1.0.0-rc.13", + "@ethersproject/abi": "^5.7.0", + "@ethersproject/providers": "^5.7.2", "@vuelidate/core": "^2.0.0", "@vuelidate/validators": "^2.0.0", "@vueuse/core": "^10.1.2", - "@web3modal/ethers5": "^4.0.13", + "@web3modal/ethers5": "4.0.13", "chart.js": "^4.4.2", "detect-browser": "^5.3.0", "ethers": "^5.7.2", @@ -69,7 +71,7 @@ "postcss-html": "^1.5.0", "prettier": "^2.8.1", "rollup-plugin-visualizer": "^5.9.0", - "sass": "^1.57.1", + "sass": "1.57.1", "stylelint": "^15.6.1", "stylelint-config-recommended-vue": "^1.4.0", "stylelint-config-standard-scss": "^9.0.0", @@ -85,6 +87,6 @@ "yorkie": "^2.0.0" }, "engines": { - "node": "18" + "node": "22" } } diff --git a/src/abi/MOR1967.json b/src/abi/MOR1967.json new file mode 100644 index 0000000..a63a0f6 --- /dev/null +++ b/src/abi/MOR1967.json @@ -0,0 +1,855 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "uniqueId", + "type": "bytes" + } + ], + "name": "OverplusBridged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "poolId", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint128", + "name": "payoutStart", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "decreaseInterval", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "withdrawLockPeriod", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "claimLockPeriod", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "withdrawLockPeriodAfterStake", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "initialReward", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rewardDecrease", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minimalStake", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isPublic", + "type": "bool" + } + ], + "indexed": false, + "internalType": "struct IDistribution.Pool", + "name": "pool", + "type": "tuple" + } + ], + "name": "PoolCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "poolId", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint128", + "name": "payoutStart", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "decreaseInterval", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "withdrawLockPeriod", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "claimLockPeriod", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "withdrawLockPeriodAfterStake", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "initialReward", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rewardDecrease", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minimalStake", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isPublic", + "type": "bool" + } + ], + "indexed": false, + "internalType": "struct IDistribution.Pool", + "name": "pool", + "type": "tuple" + } + ], + "name": "PoolEdited", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "poolId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "UserClaimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "poolId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "UserStaked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "poolId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "UserWithdrawn", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "depositToken_", + "type": "address" + }, + { + "internalType": "address", + "name": "l1Sender_", + "type": "address" + }, + { + "internalType": "address", + "name": "feeConfig_", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint128", + "name": "payoutStart", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "decreaseInterval", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "withdrawLockPeriod", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "claimLockPeriod", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "withdrawLockPeriodAfterStake", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "initialReward", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rewardDecrease", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minimalStake", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isPublic", + "type": "bool" + } + ], + "internalType": "struct IDistribution.Pool[]", + "name": "poolsInfo_", + "type": "tuple[]" + } + ], + "name": "Distribution_init", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "gasLimit_", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxFeePerGas_", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxSubmissionCost_", + "type": "uint256" + } + ], + "name": "bridgeOverplus", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "poolId_", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receiver_", + "type": "address" + } + ], + "name": "claim", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint128", + "name": "payoutStart", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "decreaseInterval", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "withdrawLockPeriod", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "claimLockPeriod", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "withdrawLockPeriodAfterStake", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "initialReward", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rewardDecrease", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minimalStake", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isPublic", + "type": "bool" + } + ], + "internalType": "struct IDistribution.Pool", + "name": "pool_", + "type": "tuple" + } + ], + "name": "createPool", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "depositToken", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "poolId_", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint128", + "name": "payoutStart", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "decreaseInterval", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "withdrawLockPeriod", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "claimLockPeriod", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "withdrawLockPeriodAfterStake", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "initialReward", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rewardDecrease", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minimalStake", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isPublic", + "type": "bool" + } + ], + "internalType": "struct IDistribution.Pool", + "name": "pool_", + "type": "tuple" + } + ], + "name": "editPool", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "feeConfig", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "poolId_", + "type": "uint256" + }, + { + "internalType": "address", + "name": "user_", + "type": "address" + } + ], + "name": "getCurrentUserReward", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "poolId_", + "type": "uint256" + }, + { + "internalType": "uint128", + "name": "startTime_", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "endTime_", + "type": "uint128" + } + ], + "name": "getPeriodReward", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "l1Sender", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "poolId_", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "users_", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "amounts_", + "type": "uint256[]" + } + ], + "name": "manageUsersInPrivatePool", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "overplus", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "pools", + "outputs": [ + { + "internalType": "uint128", + "name": "payoutStart", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "decreaseInterval", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "withdrawLockPeriod", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "claimLockPeriod", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "withdrawLockPeriodAfterStake", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "initialReward", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rewardDecrease", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minimalStake", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isPublic", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "poolsData", + "outputs": [ + { + "internalType": "uint128", + "name": "lastUpdate", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "rate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalDeposited", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "poolId_", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount_", + "type": "uint256" + } + ], + "name": "stake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "totalDepositedInPublicPools", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "usersData", + "outputs": [ + { + "internalType": "uint128", + "name": "lastStake", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "deposited", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pendingRewards", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "poolId_", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount_", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/src/common/AppButton.vue b/src/common/AppButton.vue index f23d7c9..80b9e87 100644 --- a/src/common/AppButton.vue +++ b/src/common/AppButton.vue @@ -390,7 +390,7 @@ $button-transition: var(--transition-duration-fast) --app-button-flat-text-focused: var(--primary-main); --app-button-flat-text-active: var(--primary-main); - --app-button-flat-border: #{toRem(1)} solid #494949; + --app-button-flat-border: #{toRem(1)} solid var(--border-tertiary-main); --app-button-flat-border-hover: #{toRem(1)} solid var(--primary-main); --app-button-flat-border-focused: var(--app-button-flat-border-hover); --app-button-flat-border-active: var(--app-button-flat-border-hover); diff --git a/src/common/AppTabs.vue b/src/common/AppTabs.vue index 346f97e..9b79636 100644 --- a/src/common/AppTabs.vue +++ b/src/common/AppTabs.vue @@ -54,7 +54,7 @@ const updateTab = (tab: Tab) => { display: grid; place-items: center; padding: toRem(16) toRem(24); - border: toRem(1) solid #494949; + border: toRem(1) solid var(--border-tertiary-main); color: $color; cursor: pointer; transition: var(--transition-duration-fast) var(--transition-timing-default); diff --git a/src/common/InfoBar.vue b/src/common/InfoBar.vue index 8fc00da..1e14f57 100644 --- a/src/common/InfoBar.vue +++ b/src/common/InfoBar.vue @@ -52,11 +52,11 @@ withDefaults( subtitle: string status: 'public' | 'private' title: string - description: string indicators: InfoBarType.Indicator[] isLoading?: boolean + description?: string }>(), - { isLoading: false }, + { isLoading: false, description: '' }, ) @@ -114,7 +114,7 @@ withDefaults( display: grid; grid-gap: toRem(8); padding-top: toRem(16); - border-top: toRem(2) solid #494949; + border-top: toRem(2) solid var(--border-tertiary-main); @include respond-to(medium) { margin-top: toRem(16); diff --git a/src/common/InfoCard.vue b/src/common/InfoCard.vue index 0c1230f..a24c061 100644 --- a/src/common/InfoCard.vue +++ b/src/common/InfoCard.vue @@ -163,7 +163,7 @@ withDefaults( .info-card__footer { display: flex; align-items: center; - border-top: toRem(1) solid #494949; + border-top: toRem(1) solid var(--border-tertiary-main); } .info-card .info-card__show-more-btn { @@ -173,7 +173,7 @@ withDefaults( .info-card__edit-btn-wrp { flex-basis: toRem(111); - border-left: toRem(1) solid #494949; + border-left: toRem(1) solid var(--border-tertiary-main); } .info-card .info-card__edit-btn { diff --git a/src/common/InfoDashboard/const.ts b/src/common/InfoDashboard/const.ts index 93709c0..d986e80 100644 --- a/src/common/InfoDashboard/const.ts +++ b/src/common/InfoDashboard/const.ts @@ -40,7 +40,11 @@ export const CHART_CONFIG: Readonly = Object.freeze({ }, ticks: { color: '#ffffff', - callback: (val: unknown) => String(val).replace(/000$/, 'K'), + callback: (val: unknown) => { + let fixedVal = Number(val).toFixed(3) + fixedVal = parseFloat(fixedVal).toString() + return fixedVal.replace(/000$/, 'K') + }, stepSize: 5000, }, }, diff --git a/src/common/InfoDashboard/helpers.ts b/src/common/InfoDashboard/helpers.ts index a481409..41e1298 100644 --- a/src/common/InfoDashboard/helpers.ts +++ b/src/common/InfoDashboard/helpers.ts @@ -1,25 +1,37 @@ -import { Time, hexlify, BigNumber } from '@/utils' +import { BigNumber, hexlify, Time } from '@/utils' import { gql } from '@apollo/client' -import { config } from '@config' +import { config, NETWORK_IDS } from '@config' import { mapKeys, mapValues } from 'lodash' - -type ChartData = Record +import { + PoolInteraction, + YieldQueryData, + ChartQueryData, + ChartData, +} from '@/types' const ONE_DAY_TIMESTAMP = 24 * 60 * 60 +const DECIMAL = BigNumber.from(10).pow(25) export async function getChartData( poolId: number, poolStartedAt: BigNumber, month: number, + type: NETWORK_IDS, ): Promise { - type QueryData = Record<`r${number}`, { totalStaked: string }[]> - const { data } = await config.apolloClient.query({ + const query = { query: _generateTotalStakedPerDayGraphqlQuery(poolId, poolStartedAt, month), - }) + } + + const apolloClient = + type === NETWORK_IDS.mainnet + ? config.mainnetApolloClient + : config.testnetApolloClient + + const { data } = await apolloClient.query(query) return mapValues( mapKeys(data, (_, key) => key.slice(1)), - value => BigNumber.from(value[0].totalStaked), + value => BigNumber.from(value[0]?.totalStaked ?? 0), ) } @@ -54,11 +66,10 @@ function _generateTotalStakedPerDayGraphqlQuery( for (let date = startDate; date <= endDate; date++) { const timestamp = monthTime.timestamp + date * ONE_DAY_TIMESTAMP - // eslint-disable-next-line prettier/prettier - const request = REQUEST_PATTERN - .replace('{{date}}', date.toString()) - // eslint-disable-next-line prettier/prettier - .replace('{{timestamp}}', timestamp.toString()) + const request = REQUEST_PATTERN.replace( + '{{date}}', + date.toString(), + ).replace('{{timestamp}}', timestamp.toString()) requests.push(request) } @@ -67,3 +78,151 @@ function _generateTotalStakedPerDayGraphqlQuery( ${'{\n' + requests.join('\n') + '\n}'} ` } + +export async function getUserYieldPerDayChartData( + poolId: number, + user: string, + month: number, + type: NETWORK_IDS, +): Promise { + const query = { + query: _generateUserYieldPerDayGraphqlQuery(poolId, user, month), + } + + const apolloClient = + type === NETWORK_IDS.mainnet + ? config.mainnetApolloClient + : config.testnetApolloClient + + const { data } = await apolloClient.query(query) + + const poolInteractions: PoolInteraction[] = Object.keys(data) + .filter(prop => prop.includes('day') && data[prop].length > 0) + .map(prop => data[prop][0]) + + const userInteractions = [ + ...data.initialUserInteractions.slice(0, 1), + ...data.userInteractions, + ] + + const fromDate = new Time(String(month + 1), 'M').toDate() + const fromTimestamp = fromDate.getTime() / 1000 + + const yields: ChartData = {} + + userInteractions.forEach((userInteraction, index) => { + const nextUserInteraction = userInteractions[index + 1] + + const userInteractionValue = BigNumber.from( + userInteraction.claimedRewards, + ).add(userInteraction.pendingRewards) + + if (Number(userInteraction.timestamp) > fromTimestamp) { + yields[Number(userInteraction.timestamp)] = userInteractionValue + } + + const periodPoolInteractions = poolInteractions.filter( + poolInteraction => + Number(poolInteraction.timestamp) > Number(userInteraction.timestamp) && + (!nextUserInteraction || + Number(poolInteraction.timestamp) < + Number(nextUserInteraction.timestamp)), + ) + + periodPoolInteractions.forEach(poolInteraction => { + const rateDiff = BigNumber.from(poolInteraction.rate).sub( + userInteraction.rate, + ) + const periodReward = BigNumber.from(userInteraction.deposited) + .mul(rateDiff) + .div(DECIMAL) + + yields[Number(poolInteraction.timestamp)] = + userInteractionValue.add(periodReward) + }) + }) + + return yields +} + +function _generateUserYieldPerDayGraphqlQuery( + poolId: number, + user: string, + // TODO: add month + month: number, +) { + const fromDate = new Time(String(month + 1), 'M').toDate() + const toDate = new Time(String(month + 2), 'M').toDate() + const fromTimestamp = fromDate.getTime() / 1000 + const toTimestamp = toDate.getTime() / 1000 + + const userInteractionsPattern = ` + initialUserInteractions: userInteractions ( + orderBy: timestamp + orderDirection: desc + where: { + user: "${user}" + poolId: "${poolId.toString()}" + timestamp_lt: ${fromTimestamp} + } + first: 1 + ) { + timestamp + rate + deposited + claimedRewards + pendingRewards + } + userInteractions ( + orderBy: timestamp + orderDirection: asc + where: { + user: "${user}" + poolId: "${poolId.toString()}" + timestamp_gt: ${fromTimestamp} + timestamp_lt: ${toTimestamp} + } + ) { + timestamp + rate + deposited + claimedRewards + pendingRewards + } + ` + + const year = new Date().getFullYear() + const currentMonth = new Date().getMonth() + + const dateInMonth = + currentMonth === month + ? new Date().getDate() + : new Date(year, month + 1, 0).getDate() + + let poolInteractionsPattern = '' + for (let i = 1; i <= dateInMonth; i++) { + poolInteractionsPattern += ` + day${i}: poolInteractions ( + orderBy: timestamp + orderDirection: desc + where: { + rate_gt: 0 + timestamp_lt: ${fromTimestamp + i * 86400} + pool_: { + id: "${hexlify(poolId)}" + } + } + first: 1 + ) { + timestamp + rate + } + ` + } + + const requests = [userInteractionsPattern, poolInteractionsPattern] + + return gql` + ${'{\n' + requests.join('\n') + '\n}'} + ` +} diff --git a/src/common/InfoDashboard/index.vue b/src/common/InfoDashboard/index.vue index 44b56c4..49e7664 100644 --- a/src/common/InfoDashboard/index.vue +++ b/src/common/InfoDashboard/index.vue @@ -2,11 +2,11 @@
-
-
+ +
  • ({ ...CHART_CONFIG }) + const monthOptions = computed[]>(() => { const allMonthOptions = Array.from({ length: 12 }).map((_, idx) => ({ title: t(`months.${idx}`), @@ -127,30 +174,75 @@ const monthOptions = computed[]>(() => { const selectedMonth = ref(monthOptions.value[monthOptions.value.length - 1]) -const isChartDataUpdating = ref(false) +const chartTitle = computed(() => + chartType.value === CHART_TYPE.circulingSupply + ? t('info-dashboard.header-supply-title') + : t('info-dashboard.header-earned-title'), +) -const chartConfig = reactive({ ...CHART_CONFIG }) +const chartSubtitle = computed(() => + chartType.value === CHART_TYPE.circulingSupply + ? t('info-dashboard.header-supply-subtitle') + : t('info-dashboard.header-earned-subtitle'), +) + +const isChartShown = computed( + () => route.name !== ROUTE_NAMES.appDashboardCapital, +) + +const updateSupplyChartData = async (month: number) => { + const chartData = await getChartData( + props.poolId, + props.poolData!.payoutStart, + month, + route.query.network, + ) + + const monthTime = new Time(String(month + 1), 'M') + + chartConfig.data.labels = Object.keys(chartData).map( + day => `${monthTime.format('MMMM')} ${day}`, + ) + chartConfig.data.datasets[0].data = Object.values(chartData).map(amount => + Math.ceil(Number(formatEther(amount))), + ) + + chartConfig.data.datasets[0].borderColor = + CHART_COLORS[CHART_TYPE.circulingSupply] + chartConfig.data.datasets[0].pointBackgroundColor = + CHART_COLORS[CHART_TYPE.circulingSupply] +} + +const updateEarnedMorChartData = async (month: number) => { + const chartData = await getUserYieldPerDayChartData( + props.poolId, + web3ProvidersStore.address, + month, + route.query.network, + ) + + chartConfig.data.labels = Object.keys(chartData).map(timestamp => { + return new Time(Number(timestamp)).format('DD MMMM') + }) + + chartConfig.data.datasets[0].data = Object.values(chartData).map(amount => + Number(formatEther(amount)), + ) + + chartConfig.data.datasets[0].borderColor = CHART_COLORS[CHART_TYPE.earnedMor] + chartConfig.data.datasets[0].pointBackgroundColor = + CHART_COLORS[CHART_TYPE.earnedMor] +} const updateChartData = async (month: number) => { isChartDataUpdating.value = true try { - if (!props.poolData) throw new Error('poolData unavailable') - - const chartData = await getChartData( - props.poolId, - props.poolData.payoutStart, - month, - ) - - const monthTime = new Time(String(month + 1), 'M') + if (!props.poolData) throw new errors.PoolDataNotFoundError() - chartConfig.data.labels = Object.keys(chartData).map( - day => `${monthTime.format('MMMM')} ${day}`, - ) - chartConfig.data.datasets[0].data = Object.values(chartData).map(amount => - Math.ceil(Number(formatEther(amount))), - ) + chartType.value === CHART_TYPE.circulingSupply + ? await updateSupplyChartData(month) + : await updateEarnedMorChartData(month) } catch (error) { ErrorHandler.process(error) } @@ -158,13 +250,20 @@ const updateChartData = async (month: number) => { isChartDataUpdating.value = false } +const changeChartType = (chartToSet: CHART_TYPE) => { + chartType.value = chartToSet +} + onMounted(() => { if (props.poolData) updateChartData(selectedMonth.value.value) }) -watch([selectedMonth, () => props.poolData], async ([newSelectedMonth]) => { - await updateChartData(newSelectedMonth.value) -}) +watch( + [selectedMonth, () => props.poolData, chartType], + async ([newSelectedMonth]) => { + await updateChartData(newSelectedMonth.value) + }, +)