From 74910606ecd99af8b76180d833aa3a1ea752a457 Mon Sep 17 00:00:00 2001 From: Stas Leshchina <66851165+Sorizen@users.noreply.github.com> Date: Fri, 16 Aug 2024 15:34:13 +0300 Subject: [PATCH] feature/dashboard page (#59) * feature dashboard page * wallet balance fix * Update yarn.lock * fixes after review --- src/abi/MOR1967.json | 855 +++++++++++++ src/common/InfoDashboard/index.vue | 128 +- src/common/WalletBalances.vue | 39 +- .../modals/compositions/ChangeLockModal.vue | 6 +- src/composables/use-contract.ts | 3 +- src/composables/use-form-validation.ts | 5 +- src/composables/use-pool.ts | 94 +- src/const/time.const.ts | 2 + src/enums/errors.enum.ts | 7 + src/enums/index.ts | 1 + src/enums/route-names.enum.ts | 2 + src/errors/custom.errors.ts | 36 + src/errors/index.ts | 2 + src/forms/DepositForm.vue | 65 +- src/helpers/error-handler.ts | 15 + src/localization/resources/en.json | 27 +- .../components/ZeroPoolDescription.vue | 41 +- src/pages/HomePage/index.vue | 8 +- src/pages/HomePage/views/PrivatePoolView.vue | 2 +- src/pages/HomePage/views/PublicPoolView.vue | 136 ++- src/router/index.ts | 15 + src/store/modules/web3-providers.module.ts | 171 ++- src/types/contracts/MOR1967.ts | 1060 +++++++++++++++++ .../contracts/factories/MOR1967__factory.ts | 876 ++++++++++++++ src/types/contracts/factories/index.ts | 1 + src/types/contracts/index.ts | 2 + src/types/index.ts | 1 + src/types/info-dashboard.types.ts | 9 + src/types/mor1967-proxy.types.ts | 27 + 29 files changed, 3427 insertions(+), 209 deletions(-) create mode 100644 src/abi/MOR1967.json create mode 100644 src/enums/errors.enum.ts create mode 100644 src/errors/custom.errors.ts create mode 100644 src/types/contracts/MOR1967.ts create mode 100644 src/types/contracts/factories/MOR1967__factory.ts create mode 100644 src/types/mor1967-proxy.types.ts 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/InfoDashboard/index.vue b/src/common/InfoDashboard/index.vue index 3bd5652..63bcd59 100644 --- a/src/common/InfoDashboard/index.vue +++ b/src/common/InfoDashboard/index.vue @@ -2,55 +2,57 @@
-
-
-
- {{ chartTitle }} -
- -
-
- - +
  • - chartType.value === CHART_TYPE.circulingSupply - ? t('info-dashboard.header-supply-title') - : t('info-dashboard.header-earned-title'), -) +const isChartDataUpdating = ref(false) -const chartSubtitle = computed(() => - chartType.value === CHART_TYPE.circulingSupply - ? t('info-dashboard.header-supply-subtitle') - : t('info-dashboard.header-earned-subtitle'), -) +const chartConfig = reactive({ ...CHART_CONFIG }) const monthOptions = computed[]>(() => { const allMonthOptions = Array.from({ length: 12 }).map((_, idx) => ({ @@ -172,9 +170,21 @@ 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( @@ -221,7 +231,7 @@ const updateChartData = async (month: number) => { isChartDataUpdating.value = true try { - if (!props.poolData) throw new Error('poolData unavailable') + if (!props.poolData) throw new errors.PoolDataNotFoundError() chartType.value === CHART_TYPE.circulingSupply ? await updateSupplyChartData(month) diff --git a/src/common/WalletBalances.vue b/src/common/WalletBalances.vue index 6390398..36717c0 100644 --- a/src/common/WalletBalances.vue +++ b/src/common/WalletBalances.vue @@ -61,6 +61,7 @@ import { useWeb3ProvidersStore } from '@/store' import { formatEther } from '@/utils' import { onClickOutside } from '@vueuse/core' import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue' +import { errors } from '@/errors' import AppIcon from './AppIcon.vue' type Balance = { @@ -68,8 +69,7 @@ type Balance = { value: string tokenIconName: ICON_NAMES } - -let _morUpdateIntervalId: Parameters[0] +let _morUpdateIntervalId: NodeJS.Timeout const rootElement = ref(null) const isDropMenuShown = ref(false) @@ -80,15 +80,19 @@ const web3ProvidersStore = useWeb3ProvidersStore() const balances = computed(() => [ { logoIconName: ICON_NAMES.steth, - value: web3ProvidersStore.balances.stEth - ? `${formatEther(web3ProvidersStore.balances.stEth)} stETH` + value: web3ProvidersStore.balances.depositToken + ? `${formatEther(web3ProvidersStore.balances.depositToken)} ${ + web3ProvidersStore.depositTokenSymbol + }` : '', tokenIconName: ICON_NAMES.ethereum, }, { logoIconName: ICON_NAMES.morpheus, - value: web3ProvidersStore.balances.mor - ? `${formatEther(web3ProvidersStore.balances.mor)} MOR` + value: web3ProvidersStore.balances.rewardsToken + ? `${formatEther(web3ProvidersStore.balances.rewardsToken)} ${ + web3ProvidersStore.rewardsTokenSymbol + }` : '', tokenIconName: ICON_NAMES.arbitrum, }, @@ -104,19 +108,21 @@ const onSelectBtnClick = (balanceIdx: number) => { isDropMenuShown.value = false } -const updateBalances = async (): Promise => { +const updateBalances = async () => { if (!web3ProvidersStore.provider.selectedAddress) - throw new Error('user address unavailable') - - const address = web3ProvidersStore.provider.selectedAddress + throw new errors.UserAddressError() const [stEthValue, morValue] = await Promise.all([ - web3ProvidersStore.stEthContract.providerBased.value.balanceOf(address), - web3ProvidersStore.morContract.providerBased.value.balanceOf(address), + web3ProvidersStore.depositContract.providerBased.value.balanceOf( + web3ProvidersStore.provider.selectedAddress, + ), + web3ProvidersStore.rewardsContract.providerBased.value.balanceOf( + web3ProvidersStore.provider.selectedAddress, + ), ]) - web3ProvidersStore.balances.stEth = stEthValue - web3ProvidersStore.balances.mor = morValue + web3ProvidersStore.balances.depositToken = stEthValue + web3ProvidersStore.balances.rewardsToken = morValue } const init = async (): Promise => { @@ -164,8 +170,8 @@ onMounted(() => { const address = web3ProvidersStore.provider.selectedAddress try { - web3ProvidersStore.balances.mor = - await web3ProvidersStore.morContract.providerBased.value.balanceOf( + web3ProvidersStore.balances.rewardsToken = + await web3ProvidersStore.rewardsContract.providerBased.value.balanceOf( address, ) } catch (error) { @@ -181,7 +187,6 @@ onBeforeUnmount(() => { clearInterval(_morUpdateIntervalId) }) -watch(() => web3ProvidersStore.provider.selectedAddress, onChangeBalances) watch(() => web3ProvidersStore.networkId, init) diff --git a/src/common/modals/compositions/ChangeLockModal.vue b/src/common/modals/compositions/ChangeLockModal.vue index 65bf14e..b1d7a70 100644 --- a/src/common/modals/compositions/ChangeLockModal.vue +++ b/src/common/modals/compositions/ChangeLockModal.vue @@ -52,7 +52,7 @@ diff --git a/src/composables/use-contract.ts b/src/composables/use-contract.ts index 2da0a0f..f1a9a68 100644 --- a/src/composables/use-contract.ts +++ b/src/composables/use-contract.ts @@ -1,6 +1,7 @@ import { factories, type Provider } from '@/types' import { providers } from 'ethers' import { computed, unref, type ComputedRef, type MaybeRef } from 'vue' +import { errors } from '@/errors' type ContractFactoryKey = keyof typeof factories type ContractFactoryClass = @@ -50,7 +51,7 @@ export function useContract( {}, { get: () => { - throw new Error('FallbackProvider does not have a signer') + throw new errors.FallbackProviderError() }, }, ) as Contract diff --git a/src/composables/use-form-validation.ts b/src/composables/use-form-validation.ts index 5dda4de..6b4b750 100644 --- a/src/composables/use-form-validation.ts +++ b/src/composables/use-form-validation.ts @@ -5,6 +5,7 @@ import useVuelidate, { } from '@vuelidate/core' import get from 'lodash/get' import { ComputedRef, UnwrapNestedRefs, computed, unref } from 'vue' +import { errors } from '@/errors' export interface FormValidation { getFieldErrorMessage: (fieldPath: string) => string @@ -46,9 +47,7 @@ export const useFormValidation = ( const field = get(validationController.value, fieldPath) if (!field || !Object.keys(field).length) { - throw new Error( - `getFieldErrorMessage: Cannot find vuelidate field by '${fieldPath}'`, - ) + throw new errors.FieldNotFoundError() } if (!field.$dirty) errorMessage = '' diff --git a/src/composables/use-pool.ts b/src/composables/use-pool.ts index a744db2..d59dabc 100644 --- a/src/composables/use-pool.ts +++ b/src/composables/use-pool.ts @@ -1,21 +1,33 @@ import { bus, BUS_EVENTS, ErrorHandler } from '@/helpers' -import { storeToRefs, useWeb3ProvidersStore } from '@/store' -import { type BigNumber, type Erc1967ProxyType } from '@/types' +import { useWeb3ProvidersStore } from '@/store' +import { + type BigNumber, + type Erc1967ProxyType, + type Mor1967ProxyType, +} from '@/types' import { useTimestamp } from '@vueuse/core' -import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue' +import { computed, onBeforeUnmount, onMounted, Ref, ref, watch } from 'vue' +import { useRoute } from 'vue-router' import { ethers } from 'ethers' import { Time } from '@distributedlab/tools' +import { errors } from '@/errors' const MULTIPLIER_SCALE = 21 //digits const REWARDS_DIVIDER = 10000 -export const usePool = (poolId: number) => { +export const usePool = (poolId: Ref) => { let _currentUserRewardUpdateIntervalId: NodeJS.Timeout + const route = useRoute() + const currentUserReward = ref(null) const dailyReward = ref(null) - const poolData = ref(null) - const userPoolData = ref(null) + const poolData = ref< + Erc1967ProxyType.PoolData | Mor1967ProxyType.PoolData | null + >(null) + const userPoolData = ref< + Erc1967ProxyType.UserData | Mor1967ProxyType.UserData | null + >(null) const rewardsMultiplier = ref('1') const expectedRewardsMultiplier = ref('1') @@ -47,8 +59,8 @@ export const usePool = (poolId: number) => { }) const isDepositDisabled = computed(() => { - if (!web3ProvidersStore.balances.stEth) return true - return web3ProvidersStore.balances.stEth.isZero() + if (!web3ProvidersStore.balances.depositToken) return true + return web3ProvidersStore.balances.depositToken.isZero() }) const isWithdrawDisabled = computed(() => { @@ -76,20 +88,22 @@ export const usePool = (poolId: number) => { const currentTimestampMs = useTimestamp() const web3ProvidersStore = useWeb3ProvidersStore() - const { erc1967ProxyContract } = storeToRefs(web3ProvidersStore) + const erc1967ProxyContract = computed( + () => web3ProvidersStore.erc1967ProxyContract.providerBased.value, + ) const fetchCurrentUserReward = async (): Promise => { if (!web3ProvidersStore.provider.selectedAddress) - throw new Error('user address unavailable') + throw new errors.UserAddressError() - return erc1967ProxyContract.value.providerBased.value.getCurrentUserReward( - poolId, + return erc1967ProxyContract.value.getCurrentUserReward( + poolId.value, web3ProvidersStore.provider.selectedAddress, ) } const getDailyReward = (): BigNumber => { - if (!poolData.value) throw new Error('poolData unavailable') + if (!poolData.value) throw new errors.PoolDataNotFoundError() const payoutStartTimestamp = poolData.value.payoutStart.toNumber() @@ -108,12 +122,13 @@ export const usePool = (poolId: number) => { const fetchPoolData = async (): Promise => { const poolDataResponses = await Promise.all([ - erc1967ProxyContract.value.providerBased.value.poolsData(poolId), - erc1967ProxyContract.value.providerBased.value.pools(poolId), - // eslint-disable-next-line max-len - erc1967ProxyContract.value.providerBased.value.totalDepositedInPublicPools(), + erc1967ProxyContract.value.poolsData(poolId.value), + erc1967ProxyContract.value.pools(poolId.value), + erc1967ProxyContract.value.totalDepositedInPublicPools(), ]) + // TODO: refactor + return { claimLockPeriod: poolDataResponses[1].claimLockPeriod, decreaseInterval: poolDataResponses[1].decreaseInterval, @@ -124,7 +139,8 @@ export const usePool = (poolId: number) => { rate: poolDataResponses[0].rate, payoutStart: poolDataResponses[1].payoutStart, rewardDecrease: poolDataResponses[1].rewardDecrease, - totalDeposited: poolDataResponses[2], + totalDeposited: + poolDataResponses[2] ?? poolDataResponses[0].totalVirtualDeposited, withdrawLockPeriod: poolDataResponses[1].withdrawLockPeriod, withdrawLockPeriodAfterStake: poolDataResponses[1].withdrawLockPeriodAfterStake, @@ -133,22 +149,21 @@ export const usePool = (poolId: number) => { const fetchUserPoolData = async (): Promise => { if (!web3ProvidersStore.provider.selectedAddress) - throw new Error('user address unavailable') + throw new errors.UserAddressError() - const response = - await erc1967ProxyContract.value.providerBased.value.usersData( - web3ProvidersStore.provider.selectedAddress, - poolId, - ) + const response = await erc1967ProxyContract.value.usersData( + web3ProvidersStore.provider.selectedAddress, + poolId.value, + ) return { - claimLockEnd: response.claimLockEnd, - claimLockStart: response.claimLockStart, + claimLockEnd: response?.claimLockEnd ?? ethers.BigNumber.from(0), + claimLockStart: response?.claimLockStart ?? ethers.BigNumber.from(0), deposited: response.deposited, lastStake: response.lastStake, pendingRewards: response.pendingRewards, rate: response.rate, - virtualDeposited: response.virtualDeposited, + virtualDeposited: response?.virtualDeposited ?? ethers.BigNumber.from(0), } } @@ -160,15 +175,16 @@ export const usePool = (poolId: number) => { const fetchExpectedMultiplier = async (lockPeriod: string) => { try { + if (!lockPeriod) return const lockStart = new Time().isAfter( - userPoolData.value?.claimLockStart.toString(), + userPoolData.value?.claimLockStart?.toString(), ) ? new Time().timestamp - : userPoolData.value?.claimLockStart.toString() ?? 0 + : userPoolData.value?.claimLockStart?.toString() const multiplier = //eslint-disable-next-line max-len - await web3ProvidersStore.erc1967ProxyContract.providerBased.value.getClaimLockPeriodMultiplier( - poolId, + await erc1967ProxyContract.value?.getClaimLockPeriodMultiplier( + poolId.value, lockStart, lockPeriod || 0, ) @@ -195,13 +211,15 @@ export const usePool = (poolId: number) => { } const updateUserReward = async (): Promise => { + if (route.query.address) return + isUserDataUpdating.value = true try { const response = // eslint-disable-next-line max-len - await erc1967ProxyContract.value.providerBased.value.getCurrentUserMultiplier( - poolId, + await erc1967ProxyContract.value?.getCurrentUserMultiplier( + poolId.value, web3ProvidersStore.provider.selectedAddress, ) @@ -268,17 +286,23 @@ export const usePool = (poolId: number) => { } } + watch(() => [poolId, web3ProvidersStore.dashboardInfo], init, { deep: true }) + watch( () => [ web3ProvidersStore.provider.selectedAddress, web3ProvidersStore.isConnected, web3ProvidersStore.networkId, + web3ProvidersStore.dashboardInfo, ], - async ([newAddress, isConnected]) => { + async () => { currentUserReward.value = null userPoolData.value = null - if (newAddress && isConnected) { + if ( + (route.query.address || web3ProvidersStore.provider.selectedAddress) && + web3ProvidersStore.isConnected + ) { try { await Promise.all([updateUserData(), updateUserReward()]) } catch (error) { diff --git a/src/const/time.const.ts b/src/const/time.const.ts index c09212b..e0b73b8 100644 --- a/src/const/time.const.ts +++ b/src/const/time.const.ts @@ -1 +1,3 @@ export const DEFAULT_TIME_FORMAT = 'D MMM YYYY [at] HH:mm' + +export const DAY_MONTH_TIME_FORMAT = 'D MMMM' diff --git a/src/enums/errors.enum.ts b/src/enums/errors.enum.ts new file mode 100644 index 0000000..cfbc0e2 --- /dev/null +++ b/src/enums/errors.enum.ts @@ -0,0 +1,7 @@ +export enum CUSTOM_ERRORS { + unknownCurrency = 'UnknownCurrencyError', + fieldNotFound = 'FieldNotFoundError', + userAddress = 'UserAddressError', + fallbackProvider = 'FallbackProviderError', + poolDataNotFound = 'PoolDataNotFoundError', +} diff --git a/src/enums/index.ts b/src/enums/index.ts index 16c27f4..d662c9b 100644 --- a/src/enums/index.ts +++ b/src/enums/index.ts @@ -4,5 +4,6 @@ export * from './icon-names.enum' export * from './layer-zero.enum' export * from './window-breakpoints.enum' export * from './time.enum' +export * from './errors.enum' export { CONTRACT_IDS, NETWORK_IDS } from '@config' diff --git a/src/enums/route-names.enum.ts b/src/enums/route-names.enum.ts index ce9691d..d47ccd4 100644 --- a/src/enums/route-names.enum.ts +++ b/src/enums/route-names.enum.ts @@ -1,5 +1,7 @@ export enum ROUTE_NAMES { app = 'app', + appDashboard = 'app-dashboard', + appDashboardCapital = 'app-dashboard-capital', appCapital = 'app-capital', appCommunity = 'app-community', appMor20EcosystemMain = 'app-mor20-ecosystem-main', diff --git a/src/errors/custom.errors.ts b/src/errors/custom.errors.ts new file mode 100644 index 0000000..e63f7fc --- /dev/null +++ b/src/errors/custom.errors.ts @@ -0,0 +1,36 @@ +import { CUSTOM_ERRORS } from '@/enums' + +export class UnknownCurrencyError extends Error { + constructor() { + super() + this.name = CUSTOM_ERRORS.unknownCurrency + } +} + +export class FieldNotFoundError extends Error { + constructor() { + super() + this.name = CUSTOM_ERRORS.fieldNotFound + } +} + +export class UserAddressError extends Error { + constructor() { + super() + this.name = CUSTOM_ERRORS.userAddress + } +} + +export class FallbackProviderError extends Error { + constructor() { + super() + this.name = CUSTOM_ERRORS.fallbackProvider + } +} + +export class PoolDataNotFoundError extends Error { + constructor() { + super() + this.name = CUSTOM_ERRORS.poolDataNotFound + } +} diff --git a/src/errors/index.ts b/src/errors/index.ts index 48c6736..4cb813c 100644 --- a/src/errors/index.ts +++ b/src/errors/index.ts @@ -1,5 +1,7 @@ import * as runtimeErrors from './runtime.errors' +import * as customErrors from './custom.errors' export const errors = { ...runtimeErrors, + ...customErrors, } diff --git a/src/forms/DepositForm.vue b/src/forms/DepositForm.vue index 35165e8..e18ae18 100644 --- a/src/forms/DepositForm.vue +++ b/src/forms/DepositForm.vue @@ -22,7 +22,7 @@ @blur="touchField('balanceOptionIdx')" />
-
+
{{ $t('deposit-form.expected-multiplier-lbl') }} @@ -36,7 +36,7 @@ class="deposit-form__input-field" :placeholder=" $t('deposit-form.amount-placeholder', { - currency: balanceOfForm?.value.currency || CURRENCIES.stEth, + currency: balanceOfForm?.value.currency || CURRENCIES.depositToken, }) " :error-message="getFieldErrorMessage('amount')" @@ -95,7 +95,10 @@ import { BigNumber, formatEther, parseUnits, Time, toEther } from '@/utils' import { ether, maxEther, minEther, minValue, required } from '@/validators' import { config } from '@config' import { v4 as uuidv4 } from 'uuid' -import { computed, onMounted, reactive, ref, watch } from 'vue' +import { computed, reactive, ref, toRef, watch } from 'vue' +import { ROUTE_NAMES } from '@/enums' +import { useRoute } from 'vue-router' +import { errors } from '@/errors' enum ACTIONS { approve = 'approve', @@ -103,7 +106,7 @@ enum ACTIONS { } enum CURRENCIES { - stEth = 'stETH', + depositToken = 'depositToken', } type BalanceOptionValue = { @@ -126,12 +129,14 @@ const isInitializing = ref(true) const isSubmitting = ref(false) const allowances = reactive>({ - [CURRENCIES.stEth]: null, + [CURRENCIES.depositToken]: null, }) const { t } = useI18n() +const route = useRoute() + const { expectedRewardsMultiplier, fetchExpectedMultiplier, userPoolData } = - usePool(props.poolId) + usePool(toRef(props.poolId)) const web3ProvidersStore = useWeb3ProvidersStore() const action = computed(() => { @@ -150,13 +155,15 @@ const action = computed(() => { }) const balanceOptions = computed[]>(() => [ - ...(web3ProvidersStore.balances.stEth + ...(web3ProvidersStore.balances.depositToken ? [ { - title: `${formatEther(web3ProvidersStore.balances.stEth)} stETH`, + title: `${formatEther(web3ProvidersStore.balances.depositToken)} ${ + web3ProvidersStore.depositTokenSymbol + }`, value: { - amount: toEther(web3ProvidersStore.balances.stEth), - currency: CURRENCIES.stEth, + amount: toEther(web3ProvidersStore.balances.depositToken), + currency: CURRENCIES.depositToken, }, }, ] @@ -169,6 +176,10 @@ const form = reactive({ lockPeriod: '', }) +const isMultiplierShown = computed( + () => route.name !== ROUTE_NAMES.appDashboardCapital, +) + const balanceOfForm = computed | null>( () => balanceOptions.value[form.balanceOptionIdx] || null, ) @@ -202,33 +213,31 @@ const fetchAllowanceByCurrency = async ( ): Promise => { let contract switch (currency) { - case CURRENCIES.stEth: - contract = web3ProvidersStore.stEthContract + case CURRENCIES.depositToken: + contract = web3ProvidersStore.depositContract break default: - throw new Error('unknown currency') + throw new errors.UnknownCurrencyError() } return contract.providerBased.value.allowance( web3ProvidersStore.provider.selectedAddress, - config.networksMap[web3ProvidersStore.networkId].contractAddressesMap - .erc1967Proxy, + web3ProvidersStore.erc1967ProxyContract.providerBased.value.address, ) } const approveByCurrency = async (currency: CURRENCIES) => { let contract switch (currency) { - case CURRENCIES.stEth: - contract = web3ProvidersStore.stEthContract + case CURRENCIES.depositToken: + contract = web3ProvidersStore.depositContract break default: - throw new Error('unknown currency') + throw new errors.UnknownCurrencyError() } return contract.signerBased.value.approve( - config.networksMap[web3ProvidersStore.networkId].contractAddressesMap - .erc1967Proxy, + web3ProvidersStore.erc1967ProxyContract.providerBased.value.address, MAX_UINT_256, ) } @@ -295,15 +304,15 @@ const init = async (): Promise => { isInitializing.value = true try { - allowances[CURRENCIES.stEth] = await fetchAllowanceByCurrency( - CURRENCIES.stEth, + allowances[CURRENCIES.depositToken] = await fetchAllowanceByCurrency( + CURRENCIES.depositToken, ) } catch (error) { emit('cancel') ErrorHandler.process(error) } - form.lockPeriod = String(userPoolData.value?.claimLockEnd.toNumber() || '') + form.lockPeriod = String(userPoolData.value?.claimLockEnd?.toNumber() || '') isInitializing.value = false } @@ -318,16 +327,16 @@ watch( async () => { if (!form.lockPeriod) { form.lockPeriod = String( - userPoolData.value?.claimLockEnd.toNumber() || '', + userPoolData.value?.claimLockEnd?.toNumber() || '', ) } - await fetchExpectedMultiplier(form.lockPeriod) + if (isMultiplierShown.value) { + await fetchExpectedMultiplier(form.lockPeriod) + } }, ) -onMounted(() => { - init() -}) +init()