From 966768d1f1338cc073863280b70a8066e72f5141 Mon Sep 17 00:00:00 2001 From: Lukasz Kosiak <103423117+lukozill@users.noreply.github.com> Date: Wed, 22 Jan 2025 13:14:48 +0100 Subject: [PATCH] APT-1653: ZQ2 protomainnet connected #738 * zq2 protomainnet connected * staging set to protomainnet --------- Co-authored-by: Lukasz Kosiak --- products/zillion/README.md | 1 - products/zillion/cd/base/configmap.yaml | 10 +- .../cd/overlays/production/kustomization.yaml | 10 +- .../cd/overlays/staging/kustomization.yaml | 58 ++++++ products/zillion/public/config.js | 24 ++- .../contract-calls/complete-withdraw.tsx | 3 +- .../components/contract-calls/swap-deleg.tsx | 3 +- .../contract-calls/update-commission-rate.tsx | 3 +- .../update-receiver-address.tsx | 3 +- .../contract-calls/withdraw-comm.tsx | 3 +- .../contract-calls/withdraw-stake.tsx | 1 + products/zillion/src/components/dashboard.tsx | 44 ++--- products/zillion/src/components/explorer.tsx | 33 ++-- products/zillion/src/components/home.tsx | 67 +++---- .../src/components/landing-stats-table.tsx | 6 + products/zillion/src/components/ssn-table.tsx | 5 + .../src/components/wallet-keystore.tsx | 2 +- products/zillion/src/index.tsx | 8 +- products/zillion/src/saga/stakingSaga.ts | 181 +++++++++++------- products/zillion/src/saga/userSaga.ts | 30 ++- products/zillion/src/setupTests.ts | 5 - products/zillion/src/store/blockchainSlice.ts | 13 +- products/zillion/src/util/api-randomizer.ts | 80 -------- products/zillion/src/util/calculator.ts | 15 +- products/zillion/src/util/config-helper.ts | 64 +++++++ .../zillion/src/util/config-json-helper.ts | 44 +++-- products/zillion/src/util/enum.ts | 14 +- .../zillion/src/util/reward-calculator.ts | 16 +- .../zillion/src/util/use-local-storage.ts | 10 +- products/zillion/src/util/utils.ts | 59 ++---- products/zillion/src/zilliqa-api.ts | 47 +++-- 31 files changed, 457 insertions(+), 405 deletions(-) delete mode 100644 products/zillion/src/setupTests.ts delete mode 100644 products/zillion/src/util/api-randomizer.ts create mode 100644 products/zillion/src/util/config-helper.ts diff --git a/products/zillion/README.md b/products/zillion/README.md index 895ce7f6a..04699ebfb 100644 --- a/products/zillion/README.md +++ b/products/zillion/README.md @@ -39,7 +39,6 @@ ] } }, - blockchain_explorer_config: "viewblock", refresh_rate_config: 3000, api_max_retry_attempt: 10, environment_config: "dev" diff --git a/products/zillion/cd/base/configmap.yaml b/products/zillion/cd/base/configmap.yaml index 45d93c5bb..289d31a70 100644 --- a/products/zillion/cd/base/configmap.yaml +++ b/products/zillion/cd/base/configmap.yaml @@ -29,6 +29,15 @@ data: "https://staking-zil.kucoin.com/api", ] }, + zq2_protomainnet: { + proxy: "0x62A9d5D611CDCaE8D78005F31635898330e06B93", + impl: "0xa7C67D49C82c7dc1B73D231640B2e4d0661D37c1", + blockchain: "https://api.zq2-protomainnet.zilliqa.com", + node_status: "https://staking-viewer.zilliqa.com", + api_list : [ + "https://api.zq2-protomainnet.zilliqa.com", + ] + }, isolated_server: { proxy: "0x0578B8e9D9c2493D4a2E98f364c7ed311F7a0d71", impl: "", @@ -39,7 +48,6 @@ data: ] } }, - blockchain_explorer_config: "viewblock", refresh_rate_config: 10000, api_max_retry_attempt: 10, environment_config: "stage" diff --git a/products/zillion/cd/overlays/production/kustomization.yaml b/products/zillion/cd/overlays/production/kustomization.yaml index c98e595af..8d5343583 100644 --- a/products/zillion/cd/overlays/production/kustomization.yaml +++ b/products/zillion/cd/overlays/production/kustomization.yaml @@ -58,6 +58,15 @@ patches: "https://staking-zil.kucoin.com/api", ] }, + zq2_protomainnet: { + proxy: "0x62A9d5D611CDCaE8D78005F31635898330e06B93", + impl: "0xa7C67D49C82c7dc1B73D231640B2e4d0661D37c1", + blockchain: "https://api.zq2-protomainnet.zilliqa.com", + node_status: "https://staking-viewer.zilliqa.com", + api_list : [ + "https://api.zq2-protomainnet.zilliqa.com", + ] + }, isolated_server: { proxy: "0x0578B8e9D9c2493D4a2E98f364c7ed311F7a0d71", impl: "", @@ -68,7 +77,6 @@ patches: ] } }, - blockchain_explorer_config: "viewblock", refresh_rate_config: 300000, api_max_retry_attempt: 10, environment_config: "prod" diff --git a/products/zillion/cd/overlays/staging/kustomization.yaml b/products/zillion/cd/overlays/staging/kustomization.yaml index 114c9ee8f..230dce0f3 100644 --- a/products/zillion/cd/overlays/staging/kustomization.yaml +++ b/products/zillion/cd/overlays/staging/kustomization.yaml @@ -21,4 +21,62 @@ patches: kubernetes.io/ingress.global-static-ip-name: zillion-zilstg-dev networking.gke.io/managed-certificates: zillion +- target: + kind: ConfigMap + name: zillion-config + patch: |- + - op: replace + path: "/data/config.js" + value: | + window['config'] = { + networks_config: { + testnet: { + proxy: "", + impl: "", + blockchain: "https://dev-api.zilliqa.com", + node_status: "https://testnet-viewer.zilliqa.com", + api_list: [ + "https://bumblebee-api.zilliqa.network", + "https://dev-api.zilliqa.com", + ] + }, + mainnet: { + proxy: "0x62A9d5D611CDCaE8D78005F31635898330e06B93", + impl: "0xa7C67D49C82c7dc1B73D231640B2e4d0661D37c1", + blockchain: "https://api.zilliqa.com", + node_status: "https://staking-viewer.zilliqa.com", + api_list : [ + "https://api.zilliqa.com", + "https://ssn-zilliqa.cex.io/api", + "https://ssn.ignitedao.io/api", + "https://ssn.zillet.io", + "https://zil-staking.ezil.me/api", + "https://staking-zil.kucoin.com/api", + ] + }, + zq2_protomainnet: { + proxy: "0x62A9d5D611CDCaE8D78005F31635898330e06B93", + impl: "0xa7C67D49C82c7dc1B73D231640B2e4d0661D37c1", + blockchain: "https://api.zq2-protomainnet.zilliqa.com", + node_status: "https://staking-viewer.zilliqa.com", + api_list : [ + "https://api.zq2-protomainnet.zilliqa.com", + ] + }, + isolated_server: { + proxy: "0x0578B8e9D9c2493D4a2E98f364c7ed311F7a0d71", + impl: "", + blockchain: "https://zilliqa-isolated-server.zilliqa.com", + node_status: "", + api_list : [ + "https://zilliqa-isolated-server.zilliqa.com" + ] + } + }, + refresh_rate_config: 300000, + api_max_retry_attempt: 10, + environment_config: "stage_zq2_protomainnet" + } + + namespace: zillion-stg diff --git a/products/zillion/public/config.js b/products/zillion/public/config.js index 2799856d3..d93c329c0 100644 --- a/products/zillion/public/config.js +++ b/products/zillion/public/config.js @@ -1,7 +1,4 @@ /* config file - * - * blockchain_explorer_config: [viewblock (default) | devex] - * - domain link to blockchain explorer * * node_status * - link to staking viewer @@ -45,18 +42,26 @@ window['config'] = { ] }, mainnet: { - proxy: "", - impl: "", + proxy: "0x62A9d5D611CDCaE8D78005F31635898330e06B93", + impl: "0xa7C67D49C82c7dc1B73D231640B2e4d0661D37c1", blockchain: "https://api.zilliqa.com", node_status: "https://staking-viewer.zilliqa.com", api_list : [ - "https://ssn.zillacracy.com/api", + "https://api.zilliqa.com", "https://ssn-zilliqa.cex.io/api", + // "https://ssn.ignitedao.io/api", "https://ssn.zillet.io", "https://zil-staking.ezil.me/api", "https://staking-zil.kucoin.com/api", - "https://stakingseed-api.seed.zilliqa.com", - "https://api.zilliqa.com", + ] + }, + zq2_protomainnet: { + proxy: "0x62A9d5D611CDCaE8D78005F31635898330e06B93", + impl: "0xa7C67D49C82c7dc1B73D231640B2e4d0661D37c1", + blockchain: "https://api.zq2-protomainnet.zilliqa.com", + node_status: "https://staking-viewer.zilliqa.com", + api_list : [ + "https://api.zq2-protomainnet.zilliqa.com", ] }, isolated_server: { @@ -69,8 +74,7 @@ window['config'] = { ] } }, - blockchain_explorer_config: "viewblock", refresh_rate_config: 300000, api_max_retry_attempt: 10, - environment_config: "stage" + environment_config: "stage_zq2_protomainnet" } diff --git a/products/zillion/src/components/contract-calls/complete-withdraw.tsx b/products/zillion/src/components/contract-calls/complete-withdraw.tsx index b075d522b..5d0f59556 100644 --- a/products/zillion/src/components/contract-calls/complete-withdraw.tsx +++ b/products/zillion/src/components/contract-calls/complete-withdraw.tsx @@ -10,11 +10,10 @@ import ModalPending from '../contract-calls-modal/modal-pending'; import ModalSent from '../contract-calls-modal/modal-sent'; import { useAppSelector } from '../../store/hooks'; import { ZilSigner } from '../../zilliqa-signer'; -import { units } from '@zilliqa-js/zilliqa'; import BigNumber from 'bignumber.js'; import GasSettings from './gas-settings'; -import { BN } from '@zilliqa-js/util'; +import { BN, units } from '@zilliqa-js/util'; function CompleteWithdrawModal(props: any) { const proxy = useAppSelector(state => state.blockchain.proxy); diff --git a/products/zillion/src/components/contract-calls/swap-deleg.tsx b/products/zillion/src/components/contract-calls/swap-deleg.tsx index 9d74c6c97..c5f75d924 100644 --- a/products/zillion/src/components/contract-calls/swap-deleg.tsx +++ b/products/zillion/src/components/contract-calls/swap-deleg.tsx @@ -27,10 +27,9 @@ import { useAppSelector } from '../../store/hooks'; import { SwapDelegModalData } from '../../util/interface'; import { ZilSigner } from '../../zilliqa-signer'; import { ZilSdk } from '../../zilliqa-api'; -import { units } from '@zilliqa-js/zilliqa'; import BigNumber from 'bignumber.js'; import GasSettings from './gas-settings'; -import { BN, validation } from '@zilliqa-js/util'; +import { BN, validation, units } from '@zilliqa-js/util'; function SwapDelegModal(props: any) { diff --git a/products/zillion/src/components/contract-calls/update-commission-rate.tsx b/products/zillion/src/components/contract-calls/update-commission-rate.tsx index 6314a1f73..1174aff05 100644 --- a/products/zillion/src/components/contract-calls/update-commission-rate.tsx +++ b/products/zillion/src/components/contract-calls/update-commission-rate.tsx @@ -10,12 +10,11 @@ import ModalPending from '../contract-calls-modal/modal-pending'; import ModalSent from '../contract-calls-modal/modal-sent'; import { useAppSelector } from '../../store/hooks'; import { ZilSigner } from '../../zilliqa-signer'; -import { units } from '@zilliqa-js/zilliqa'; import BigNumber from 'bignumber.js'; import GasSettings from './gas-settings'; import { logger } from '../../util/logger'; -import { BN } from '@zilliqa-js/util'; +import { BN, units } from '@zilliqa-js/util'; function UpdateCommRateModal(props: any) { const proxy = useAppSelector(state => state.blockchain.proxy); diff --git a/products/zillion/src/components/contract-calls/update-receiver-address.tsx b/products/zillion/src/components/contract-calls/update-receiver-address.tsx index a4dc57fbc..0cf68dc3c 100644 --- a/products/zillion/src/components/contract-calls/update-receiver-address.tsx +++ b/products/zillion/src/components/contract-calls/update-receiver-address.tsx @@ -10,11 +10,10 @@ import ModalPending from '../contract-calls-modal/modal-pending'; import ModalSent from '../contract-calls-modal/modal-sent'; import { useAppSelector } from '../../store/hooks'; import { ZilSigner } from '../../zilliqa-signer'; -import { units } from '@zilliqa-js/zilliqa'; import BigNumber from 'bignumber.js'; import GasSettings from './gas-settings'; -import { BN } from '@zilliqa-js/util'; +import { BN, units } from '@zilliqa-js/util'; function UpdateReceiverAddress(props: any) { diff --git a/products/zillion/src/components/contract-calls/withdraw-comm.tsx b/products/zillion/src/components/contract-calls/withdraw-comm.tsx index 68e93dc6b..60bd1c4e6 100644 --- a/products/zillion/src/components/contract-calls/withdraw-comm.tsx +++ b/products/zillion/src/components/contract-calls/withdraw-comm.tsx @@ -10,10 +10,9 @@ import ModalPending from '../contract-calls-modal/modal-pending'; import ModalSent from '../contract-calls-modal/modal-sent'; import { useAppSelector } from '../../store/hooks'; import { ZilSigner } from '../../zilliqa-signer'; -import { units } from '@zilliqa-js/zilliqa'; import BigNumber from 'bignumber.js'; import GasSettings from './gas-settings'; -import { BN } from '@zilliqa-js/util'; +import { BN, units } from '@zilliqa-js/util'; function WithdrawCommModal(props: any) { const proxy = useAppSelector(state => state.blockchain.proxy); diff --git a/products/zillion/src/components/contract-calls/withdraw-stake.tsx b/products/zillion/src/components/contract-calls/withdraw-stake.tsx index f42af1984..39a1ce249 100644 --- a/products/zillion/src/components/contract-calls/withdraw-stake.tsx +++ b/products/zillion/src/components/contract-calls/withdraw-stake.tsx @@ -136,6 +136,7 @@ function WithdrawStakeModal(props: any) { // check if deleg has unwithdrawn rewards or buffered deposits for this ssn address const hasRewards = await hasRewardToWithdraw(); if (hasRewards) { + Alert('info', "Withdraw rewards first", "Withdraw your rewards before withdrawing your staked amount."); setIsPending(''); return null; } diff --git a/products/zillion/src/components/dashboard.tsx b/products/zillion/src/components/dashboard.tsx index 923ab2b42..5e6b85371 100644 --- a/products/zillion/src/components/dashboard.tsx +++ b/products/zillion/src/components/dashboard.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react'; import { ToastContainer, toast } from 'react-toastify'; import ReactTooltip from 'react-tooltip'; -import { Role, NetworkURL, Network as NetworkLabel, AccountType, Environment, Constants, TransactionType, ButtonText, ContractState, OperationStatus } from '../util/enum'; +import { Role, NetworkURL, Network, AccountType, Environment, Constants, TransactionType, ButtonText, ContractState, OperationStatus } from '../util/enum'; import { convertQaToCommaStr, getAddressLink } from '../util/utils'; import StakingPortfolio from './staking-portfolio'; import SsnTable from './ssn-table'; @@ -49,10 +49,11 @@ import WarningDashboardBanner from './warning-dashboard-banner'; import { POLL_USER_DATA_STOP, QUERY_AND_UPDATE_USER_STATS, RESET_USER_STATE, UPDATE_ADDRESS } from '../store/userSlice'; import { useAppDispatch, useAppSelector } from '../store/hooks'; import { logger } from '../util/logger'; -import { getEnvironment, getNetworks, NetworkConfig, Networks } from '../util/config-json-helper'; +import { getEnvironment, getNetworks, NetworkConfig, Networks, tryGetNetworkLabelByApiUrl } from '../util/config-json-helper'; import { RESET_BLOCKCHAIN_STATE, UPDATE_CHAIN_INFO } from '../store/blockchainSlice'; import { ZilSigner } from '../zilliqa-signer'; import { QUERY_AND_UPDATE_STAKING_STATS } from '../store/stakingSlice'; +import { networkStringToNetwork } from '../util/config-helper'; function Dashboard(props: any) { @@ -146,32 +147,12 @@ function Dashboard(props: any) { // for zilpay to toggle different network const networkChanger = (net: string) => { - let label; - - switch (net) { - case NetworkLabel.MAINNET: - // do nothing - Alert("info", "Info", "You are on Mainnet."); - label = NetworkLabel.MAINNET; - break; - case NetworkLabel.TESTNET: - label = NetworkLabel.TESTNET; - if (env === Environment.PROD) { - // warn users not to switch to testnet on production - Alert("warn", "Testnet not supported", "Please switch to Mainnet via ZilPay."); - } - break; - case NetworkLabel.ISOLATED_SERVER: - case NetworkLabel.PRIVATE: - label = NetworkLabel.ISOLATED_SERVER; - if (env === Environment.PROD) { - // warn users not to switch to testnet on production - Alert("warn", "Private network not supported", "Please switch to Mainnet via ZilPay."); - } - break; - default: - label = NetworkLabel.TESTNET; - break; + const label = networkStringToNetwork(net); + + if (label === Network.MAINNET) { + Alert("info", "Info", "You are on Mainnet."); + } else if (env === Environment.PROD) { + Alert("warn", "Warning", "Currently connected network is not supported. Please switch to Mainnet via ZilPay."); } const networkConfig: NetworkConfig = networks[label]; @@ -191,9 +172,12 @@ function Dashboard(props: any) { if (userState.account_type === AccountType.ZILPAY) { const zilPay = (window as any).zilPay; + console.log({ zilpay_data: zilPay.wallet }); + if (zilPay) { - // switch to the zilpay network on load - networkChanger(zilPay.wallet.net); + networkChanger( + tryGetNetworkLabelByApiUrl(zilPay.wallet.http) || zilPay.wallet.net + ); const accountStreamChanged = zilPay.wallet.observableAccount().subscribe((account: any) => { console.log("zil pay account changing..."); diff --git a/products/zillion/src/components/explorer.tsx b/products/zillion/src/components/explorer.tsx index f68e69bd2..a30ba7566 100644 --- a/products/zillion/src/components/explorer.tsx +++ b/products/zillion/src/components/explorer.tsx @@ -1,31 +1,31 @@ -import React, { useEffect, useRef, useState, useCallback } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { trackPromise } from 'react-promise-tracker'; import DisclaimerModal from './disclaimer'; import Footer from './footer'; -import { Environment, Network, PromiseArea, ContractState, OperationStatus } from '../util/enum'; -import { DelegStats, DelegStakingPortfolioStats, initialDelegStats, PendingWithdrawStats } from '../util/interface'; +import { ContractState, OperationStatus, PromiseArea } from '../util/enum'; +import { DelegStakingPortfolioStats, DelegStats, initialDelegStats, PendingWithdrawStats } from '../util/interface'; import { fromBech32Address, toBech32Address } from '@zilliqa-js/crypto'; import { validation } from "@zilliqa-js/util"; import { computeDelegRewards } from '../util/reward-calculator'; +import useDarkMode from '../util/use-dark-mode'; import { convertQaToCommaStr, isRespOk } from '../util/utils'; import Spinner from './spinner'; -import useDarkMode from '../util/use-dark-mode'; -import ZillionLogo from '../static/zillion.svg'; +import { BigNumber } from 'bignumber.js'; import ZillionLightLogo from '../static/light/zillion.svg'; -import IconSun from './icons/sun'; +import ZillionLogo from '../static/zillion.svg'; +import { useAppSelector } from '../store/hooks'; +import { getDefaultNetworkForCurrentEnv } from '../util/config-helper'; +import { ZilSdk } from '../zilliqa-api'; +import ExplorerPendingWithdrawalTable from './explorer-pending-withdrawal-table'; +import ExplorerStakingPortfolio from './explorer-staking-portfolio'; import IconMoon from './icons/moon'; import IconSearch from './icons/search'; -import ExplorerStakingPortfolio from './explorer-staking-portfolio'; -import WarningBanner from './warning-banner'; -import ExplorerPendingWithdrawalTable from './explorer-pending-withdrawal-table'; +import IconSun from './icons/sun'; import RewardCountdownTable from './reward-countdown-table'; -import { getEnvironment } from '../util/config-json-helper'; -import { useAppSelector } from '../store/hooks'; -import { ZilSdk } from '../zilliqa-api'; -import { BigNumber } from 'bignumber.js'; +import WarningBanner from './warning-banner'; function Explorer(props: any) { const address = props.match.params.address; // bech32 wallet address; @@ -39,8 +39,8 @@ function Explorer(props: any) { // config.js from public folder const impl = useAppSelector(state => state.blockchain.impl); - const env = getEnvironment(); - const network = env === Environment.PROD ? Network.MAINNET : Network.TESTNET; + + const network = getDefaultNetworkForCurrentEnv(); const mountedRef = useRef(true); @@ -268,8 +268,7 @@ function Explorer(props: any) { { - ( env === Environment.STAGE || env === Environment.PROD ) && - {network} + network && {network} } diff --git a/products/zillion/src/components/home.tsx b/products/zillion/src/components/home.tsx index abf02357f..101c55ced 100644 --- a/products/zillion/src/components/home.tsx +++ b/products/zillion/src/components/home.tsx @@ -1,11 +1,11 @@ -import React, { useState, useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; import { withRouter } from "react-router-dom"; import ReactTooltip from "react-tooltip"; -import { AccountType, Environment, Network, Role, ContractState } from '../util/enum'; +import { AccountType, ContractState, Role } from '../util/enum'; import DisclaimerModal from './disclaimer'; -import SsnTable from './ssn-table'; import Footer from './footer'; +import SsnTable from './ssn-table'; import WalletKeystore from './wallet-keystore'; import WalletLedger from './wallet-ledger'; @@ -14,29 +14,28 @@ import WalletZilPay from './wallet-zilpay'; import IconKeystoreLine from './icons/keystore-line'; import IconLedgerLine from './icons/ledger-line'; import IconZilPayLine from './icons/zil-pay-line'; -import IconSun from './icons/sun'; -import IconMoon from './icons/moon'; -import ZillionLogo from '../static/zillion.svg'; import ZillionLightLogo from '../static/light/zillion.svg'; +import ZillionLogo from '../static/zillion.svg'; import LandingStatsTable from './landing-stats-table'; import AvelyLogo from '../static/avely.svg'; -import TorchWalletLogo from '../static/torch_wallet.png'; import PlunderswapLogo from '../static/plunderswap_dao.png'; +import TorchWalletLogo from '../static/torch_wallet.png'; -import useDarkMode from '../util/use-dark-mode'; import { ToastContainer } from 'react-toastify'; +import useDarkMode from '../util/use-dark-mode'; import IconSearch from './icons/search'; import WarningBanner from './warning-banner'; -import RewardCountdownTable from './reward-countdown-table'; import { useAppDispatch, useAppSelector } from '../store/hooks'; -import { getEnvironment } from '../util/config-json-helper'; import { QUERY_AND_UPDATE_STAKING_STATS } from '../store/stakingSlice'; -import { logger } from '../util/logger'; import { INIT_USER, QUERY_AND_UPDATE_USER_STATS, UPDATE_LEDGER_INDEX } from '../store/userSlice'; +import { envStringToEnv, getDefaultNetworkForEnv, networkToNetworkName } from '../util/config-helper'; +import { getEnvironment } from '../util/config-json-helper'; +import { logger } from '../util/logger'; import { ZilSigner } from '../zilliqa-signer'; +import RewardCountdownTable from './reward-countdown-table'; function Home(props: any) { @@ -51,14 +50,9 @@ function Home(props: any) { const [explorerSearchAddress, setExplorerSearchAddress] = useState(''); const [role, setRole] = useState(''); const [accessMethod, setAccessMethod] = useState(''); - const [selectedNetwork, setSelectedNetwork] = useState(() => { - if (env === Environment.PROD) { - return Network.MAINNET; - } else { - // default to testnet - return Network.TESTNET; - } - }); + const [selectedNetwork, setSelectedNetwork] = useState( + () => getDefaultNetworkForEnv(envStringToEnv(env)) + ); const darkMode = useDarkMode(true); @@ -146,13 +140,13 @@ function Home(props: any) { ); } - const toggleTheme = () => { - if (darkMode.value === true) { - darkMode.disable(); - } else { - darkMode.enable(); - } - } + // const toggleTheme = () => { + // if (darkMode.value === true) { + // darkMode.disable(); + // } else { + // darkMode.enable(); + // } + // } const toggleZillionLogo = () => { if (darkMode.value === true) { @@ -180,13 +174,9 @@ function Home(props: any) { }; useEffect(() => { - if (env === Environment.PROD) { - setSelectedNetwork(Network.MAINNET); - } else { - setSelectedNetwork(Network.TESTNET); - } + setSelectedNetwork(getDefaultNetworkForEnv(envStringToEnv(env))); dispatch(QUERY_AND_UPDATE_STAKING_STATS()); - }, [env, selectedNetwork, dispatch]); + }, [env]); // eslint-disable-line react-hooks/exhaustive-deps useEffect(() => { window.onbeforeunload = null; @@ -219,8 +209,8 @@ function Home(props: any) { */} { - (env === Environment.STAGE || env === Environment.PROD) && - {selectedNetwork} + selectedNetwork && + {networkToNetworkName(selectedNetwork)} } @@ -234,21 +224,21 @@ function Home(props: any) { className='btn-logos d-flex justify-content-center align-items-center mx-2' onClick={() => window.location.href = 'https://dapp.avely.fi/'} > - + Avely logo Avely
window.location.href = 'https://instantunstaking.torchwallet.io/'} > - + Torch wallet logo Torch Wallet
window.location.href = 'https://stake.plunderswap.com/'} > - + Plunderswap logo PlunderSwap
@@ -329,7 +319,8 @@ function Home(props: any) {
handleAccessMethod(AccountType.ZILPAY)} - data-tip={env === Environment.PROD ? "Ensure your ZilPay is on Mainnet network" : "Ensure your ZilPay is on Testnet network"}> + data-tip={`Ensure your ZilPay is on ${getDefaultNetworkForEnv(envStringToEnv(env))} network`} + > ZilPay
diff --git a/products/zillion/src/components/landing-stats-table.tsx b/products/zillion/src/components/landing-stats-table.tsx index 4217392e8..1174408a6 100644 --- a/products/zillion/src/components/landing-stats-table.tsx +++ b/products/zillion/src/components/landing-stats-table.tsx @@ -19,6 +19,12 @@ function LandingStatsTable(props: any) {
{ loading === OperationStatus.PENDING && } + + { loading === OperationStatus.ERROR && +
+ Error fetching statistics. Please refresh the page. +
+ } { loading === OperationStatus.COMPLETE && diff --git a/products/zillion/src/components/ssn-table.tsx b/products/zillion/src/components/ssn-table.tsx index a50c01bb8..579e705d7 100644 --- a/products/zillion/src/components/ssn-table.tsx +++ b/products/zillion/src/components/ssn-table.tsx @@ -251,6 +251,11 @@ function SsnTable(props: any) { loading === OperationStatus.PENDING && } + { loading === OperationStatus.ERROR && +
+ Error fetching available SSNs. Please refresh the page. +
+ } { loading === OperationStatus.COMPLETE && Number(b.stakeAmt) - Number(a.stakeAmt)); + + for (const ssnStats of ssnListOrderedBystakeAmt) { + try { + const hexAddress = fromBech32Address(ssnStats.address).toLowerCase(); + const responseSsnDelegAmt: Object = yield call(ZilSdk.getSmartContractSubStateBatch, [[checksumImpl, 'ssn_deleg_amt', [hexAddress]]]); + const ssn_deleg_amt = (responseSsnDelegAmt as any)[0]["result"]?.["ssn_deleg_amt"]?.[hexAddress] || [] + + ssnStatsList = ssnStatsList.map( + ssn => ssn.address === ssnStats.address ? { ...ssn, delegNum: Object.keys(ssn_deleg_amt).length.toString() } : ssn + ) + + yield put(UPDATE_SSN_LIST({ ssn_list: ssnStatsList })); + } catch (e) { + console.log("fetch ssn deleg num failed", e); + } } } @@ -186,8 +228,7 @@ function* pollStakingSaga() { try { yield call(pollStakingData); } catch (e) { - console.warn("poll staking data failed"); - console.warn(e); + console.error("poll staking data failed", e); } finally { yield delay(REFRESH_INTERVAL); } diff --git a/products/zillion/src/saga/userSaga.ts b/products/zillion/src/saga/userSaga.ts index 130a9d9f2..0731d89c1 100644 --- a/products/zillion/src/saga/userSaga.ts +++ b/products/zillion/src/saga/userSaga.ts @@ -1,4 +1,4 @@ -import { toBech32Address } from '@zilliqa-js/zilliqa'; +import { toBech32Address } from '@zilliqa-js/crypto'; import { BigNumber } from 'bignumber.js'; import { all, call, delay, fork, put, race, select, take, takeLatest } from 'redux-saga/effects'; import { PRELOAD_INFO_READY, UPDATE_REWARD_BLK_COUNTDOWN } from '../store/stakingSlice'; @@ -25,7 +25,7 @@ function* queryAndUpdateBalance() { logger("user balance: ", balance); yield put(UPDATE_BALANCE({ balance: balance })); } catch (e) { - console.warn("query and update balance failed"); + console.error("query and update balance failed", e); yield put(UPDATE_BALANCE({ balance: "0" })); } } @@ -52,8 +52,7 @@ function* queryAndUpdateBalance() { } yield put(UPDATE_ROLE({ role: role })); } catch (e) { - console.warn("query and update role failed"); - console.warn(e); + console.error("query and update role failed", e); } } @@ -82,11 +81,10 @@ function* queryAndUpdateGzil() { if (!isRespOk(response)) { yield put(UPDATE_GZIL_BALANCE({ gzil_balance: "0" })); } else { - yield put(UPDATE_GZIL_BALANCE({ gzil_balance: (response as any)['balances'][address_base16] })); + yield put(UPDATE_GZIL_BALANCE({ gzil_balance: (response as any)['balances']?.[address_base16] || "0" })); } } catch (e) { - console.warn("query and update gzil failed"); - console.warn(e); + console.error("query and update gzil failed", e); yield put(UPDATE_GZIL_BALANCE({ gzil_balance: "0" })); } } @@ -107,7 +105,7 @@ function* populateStakingPortfolio() { let staking_portfolio_list: DelegStakingPortfolioStats[] = []; let totalDeposits = new BigNumber(0); let totalRewards = new BigNumber(0); - + for (const ssnAddress in depositAmtDeleg) { // compute total deposits const deposit = new BigNumber(depositAmtDeleg[ssnAddress]); @@ -141,8 +139,7 @@ function* populateStakingPortfolio() { yield put(UPDATE_DELEG_STATS({ deleg_stats: delegStats })); yield put(UPDATE_DELEG_PORTFOLIO({ portfolio_list: staking_portfolio_list })); } catch (e) { - console.warn("populate staking portfolio failed"); - console.warn(e); + console.error("populate staking portfolio failed", e); yield put(UPDATE_DELEG_STATS({ deleg_stats: initialDelegStats })); yield put(UPDATE_DELEG_PORTFOLIO({ portfolio_list: [] })); } @@ -185,8 +182,7 @@ function* populateSwapRequests() { yield put(UPDATE_SWAP_DELEG_MODAL({ swap_deleg_modal: swapDelegModal })); } catch (e) { - console.warn("populate swap requests failed"); - console.warn(e); + console.error("populate swap requests failed", e); yield put(UPDATE_SWAP_DELEG_MODAL({ swap_deleg_modal: initialSwapDelegModalData })); } } @@ -210,7 +206,7 @@ function* populatePendingWithdrawal() { let progress = '0'; let pendingWithdrawList: PendingWithdrawStats[] = []; let totalWithdrawAmt = new BigNumber(0); - const pendingWithdrawal = (response as any)['withdrawal_pending'][address_base16]; + const pendingWithdrawal = (response as any)['withdrawal_pending']?.[address_base16] || []; const { min_bnum_req } = yield select(getStakingState); const numTxBlk: string = yield call(ZilSdk.getNumTxBlocks); const currBlkNum = isRespOk(numTxBlk) === true ? new BigNumber(numTxBlk).minus(1) : new BigNumber(0); @@ -250,7 +246,7 @@ function* populatePendingWithdrawal() { yield put(UPDATE_PENDING_WITHDRAWAL_LIST({ pending_withdraw_list: pendingWithdrawList })); yield put(UPDATE_COMPLETE_WITHDRAWAL_AMT({ complete_withdrawal_amt: `${totalWithdrawAmt}` })); } catch (e) { - console.warn("populate pending withdrawal failed"); + console.error("populate pending withdrawal failed", e); yield put(UPDATE_PENDING_WITHDRAWAL_LIST({ pending_withdraw_list: [] })); yield put(UPDATE_COMPLETE_WITHDRAWAL_AMT({ complete_withdrawal_amt: "0" })); } @@ -272,7 +268,7 @@ function* populateRewardBlkCountdown() { } yield put(UPDATE_REWARD_BLK_COUNTDOWN({ reward_blk_countdown: `${rewardBlkCountdown}` })) } catch (e) { - console.warn("populate reward blk countdown failed"); + console.error("populate reward blk countdown failed", e); yield put(UPDATE_REWARD_BLK_COUNTDOWN({ reward_blk_countdown: "0" })) } } @@ -316,7 +312,7 @@ function* populateOperatorStats() { yield put(UPDATE_OPERATOR_STATS({ operator_stats: operatorStats })); } catch (e) { - console.warn("populate operator stats failed"); + console.error("populate operator stats failed", e); yield put(UPDATE_OPERATOR_STATS({ operator_stats: initialOperatorStats })); } finally { yield put(UPDATE_FETCH_OPERATOR_STATS_STATUS(OperationStatus.COMPLETE)); @@ -366,7 +362,7 @@ function* pollUserSaga() { yield call(pollDelegatorData) } } catch (e) { - console.warn("poll user data failed"); + console.error("poll user data failed", e); } finally { yield put(UPDATE_FETCH_DELEG_STATS_STATUS(OperationStatus.COMPLETE)); yield delay(REFRESH_INTERVAL); diff --git a/products/zillion/src/setupTests.ts b/products/zillion/src/setupTests.ts deleted file mode 100644 index 74b1a275a..000000000 --- a/products/zillion/src/setupTests.ts +++ /dev/null @@ -1,5 +0,0 @@ -// jest-dom adds custom jest matchers for asserting on DOM nodes. -// allows you to do things like: -// expect(element).toHaveTextContent(/react/i) -// learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom/extend-expect'; diff --git a/products/zillion/src/store/blockchainSlice.ts b/products/zillion/src/store/blockchainSlice.ts index 1ba220d60..1238535b7 100644 --- a/products/zillion/src/store/blockchainSlice.ts +++ b/products/zillion/src/store/blockchainSlice.ts @@ -1,13 +1,12 @@ import { createSlice } from '@reduxjs/toolkit' -import { getNetworkConfigByEnv } from '../util/config-json-helper' +import { getNetworkConfigForCurrentEnv } from '../util/config-helper' export interface BlockchainState { proxy: string, impl: string, blockchain: string, staking_viewer: string, - api_list: [], - blockchain_explorer: string, + api_list: string[], refresh_rate: number, api_max_retry_attempt: number, } @@ -18,7 +17,6 @@ const initialState: BlockchainState = { blockchain: '', staking_viewer: '', api_list: [], - blockchain_explorer: '', refresh_rate: 300000, api_max_retry_attempt: 10, } @@ -35,10 +33,6 @@ const blockchainSlice = createSlice({ state.staking_viewer = staking_viewer state.api_list = api_list }, - UPDATE_BLOCKCHAIN_EXPLORER(state, action) { - const { blockchain_explorer } = action.payload - state.blockchain_explorer = blockchain_explorer - }, UPDATE_REFRESH_RATE(state, action) { const { refresh_rate } = action.payload state.refresh_rate = refresh_rate @@ -49,7 +43,7 @@ const blockchainSlice = createSlice({ }, RESET_BLOCKCHAIN_STATE(state) { // reset config on logout - const networkConfig = getNetworkConfigByEnv() + const networkConfig = getNetworkConfigForCurrentEnv() state.proxy = networkConfig.proxy state.impl = networkConfig.impl state.blockchain = networkConfig.blockchain @@ -62,7 +56,6 @@ const blockchainSlice = createSlice({ export const { UPDATE_API_MAX_ATTEMPT, - UPDATE_BLOCKCHAIN_EXPLORER, UPDATE_CHAIN_INFO, UPDATE_REFRESH_RATE, RESET_BLOCKCHAIN_STATE, diff --git a/products/zillion/src/util/api-randomizer.ts b/products/zillion/src/util/api-randomizer.ts deleted file mode 100644 index 56efbab84..000000000 --- a/products/zillion/src/util/api-randomizer.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { NetworkURL } from "./enum"; -import { Random, MersenneTwister19937 } from "random-js"; - -const randomJS = new Random(MersenneTwister19937.autoSeed()); - -/** - * Randomize the API - * Generate an array of random index of 1...LIST_SIZE from the api list - * Shuffle the array and retrieve the current index - */ -export class ApiRandomizer { - private static instance: ApiRandomizer; - - testnetIndex: number; - mainnetIndex: number; - randTestnetIndexList: number[]; - randMainnetIndexList: number[]; - testnetApiList: string[]; - mainnetApiList: string[]; - - private constructor() { - this.testnetIndex = 0; - this.mainnetIndex = 0; - this.testnetApiList = []; - this.mainnetApiList = []; - this.randTestnetIndexList = []; - this.randMainnetIndexList = []; - } - - public static getInstance(): ApiRandomizer { - if (!ApiRandomizer.instance) { - ApiRandomizer.instance = new ApiRandomizer() - } - return ApiRandomizer.instance; - } - - /** - * sets the correct api list and fetch a random one according to the network - * @param url blockchan api url to determine which network; url is from config.json => store.blockchain - * @param apiList list of api urls for the specific network for retry purposes; list is from config.json => store.blockchain - * @returns a random api from the apiList - */ - public fetchApi(url: NetworkURL, apiList: string[]) { - if (url === NetworkURL.MAINNET && this.mainnetApiList.length === 0) { - this.mainnetApiList = (apiList.length > 0) ? [...apiList] : ["https://api.zilliqa.com"]; - this.randMainnetIndexList = randomJS.shuffle(Array.from(Array(this.mainnetApiList.length).keys())); - } else if (url === NetworkURL.TESTNET && this.testnetApiList.length === 0) { - this.testnetApiList = (apiList.length > 0) ? [...apiList] : ["https://dev-api.zilliqa.com"]; - this.randTestnetIndexList = randomJS.shuffle(Array.from(Array(this.testnetApiList.length).keys())); - } - - let maxLen = 0; - let randomIndex = 0; - let api = ""; - - if (url === NetworkURL.MAINNET) { - randomIndex = this.randMainnetIndexList[this.mainnetIndex]; - api = this.mainnetApiList[randomIndex]; - maxLen = this.mainnetApiList.length; - - // prepare the index for the next try - this.mainnetIndex = this.mainnetIndex + 1; - if (this.mainnetIndex >= maxLen) { - this.mainnetIndex = 0; - } - } else { - randomIndex = this.randTestnetIndexList[this.testnetIndex]; - api = this.testnetApiList[randomIndex]; - maxLen = this.testnetApiList.length; - - // prepare the index for the next try - this.testnetIndex = this.testnetIndex + 1; - if (this.testnetIndex >= maxLen) { - this.testnetIndex = 0; - } - } - - return api; - } -} \ No newline at end of file diff --git a/products/zillion/src/util/calculator.ts b/products/zillion/src/util/calculator.ts index d071d81b9..f8b4da97c 100644 --- a/products/zillion/src/util/calculator.ts +++ b/products/zillion/src/util/calculator.ts @@ -1,5 +1,6 @@ import { BN } from "@zilliqa-js/util"; -import { Zilliqa } from "@zilliqa-js/zilliqa"; +import { Zilliqa, Contract, State } from "@zilliqa-js/zilliqa"; + const KEY_LAST_REWARD_CYCLE = 'lastrewardcycle'; const KEY_DIRECT_DEPOSIT_DELEG = 'direct_deposit_deleg'; const KEY_BUFF_DEPOSIT_DELEG = 'buff_deposit_deleg'; @@ -8,12 +9,12 @@ const KEY_LAST_WITHDRAW_CYCLE = 'last_withdraw_cycle_deleg' const KEY_DELEG_PER_CYCLE = 'deleg_stake_per_cycle' export class RewardCalculator { - zilliqa: any; - contract: any; - last_reward_cycle_json: any; + zilliqa: Zilliqa; + contract: Contract; + last_reward_cycle_json: State; - constructor(url: string, ssnlist: string) { - this.zilliqa = new Zilliqa(url); + constructor(zilliqa: Zilliqa, ssnlist: string) { + this.zilliqa = zilliqa; this.contract = this.zilliqa.contracts.at(ssnlist); } @@ -33,7 +34,7 @@ export class RewardCalculator { // get reward cycle list - async get_reward_cycle_list(last_withdraw_cycle_map: any, last_reward_cycle_json: any, ssnaddr: string, delegator: string) { + async get_reward_cycle_list(last_withdraw_cycle_map: any, last_reward_cycle_json: State, ssnaddr: string, delegator: string) { // remove this // console.log(last_withdraw_cycle_map); diff --git a/products/zillion/src/util/config-helper.ts b/products/zillion/src/util/config-helper.ts new file mode 100644 index 000000000..66c0f9810 --- /dev/null +++ b/products/zillion/src/util/config-helper.ts @@ -0,0 +1,64 @@ +import { getEnvironment, getNetworks, NetworkConfig } from "./config-json-helper"; +import { Environment, Network } from "./enum"; + +export function envStringToEnv(env: string): Environment { + const selectedEnv = Object.entries(Environment).find(([key, val]) => val === env); + + if (!selectedEnv) { + const environmentKeys = Object.values(Environment).join(', '); + throw new Error(`Invalid environment: ${env}. Available environments: ${environmentKeys}`); + } + + return selectedEnv[1] +} + +export function getDefaultNetworkForEnv(env: Environment): Network { + return { + [Environment.PROD]: Network.MAINNET, + [Environment.STAGE]: Network.TESTNET, + [Environment.DEV]: Network.TESTNET, + [Environment.STAGE_ZQ2_PROTOMAINNET]: Network.ZQ2_PROTOMAINNET + }[env]; +} + +export function getDefaultNetworkForCurrentEnv(): Network { + return getDefaultNetworkForEnv( + envStringToEnv( + getEnvironment() + ) + ); +} + +export function networkStringToNetwork(network: string): Network { + const selectedNetwork = Object.entries(Network).find(([key, val]) => val === network); + + if (!selectedNetwork) { + const networkKeys = Object.values(Network).join(', '); + throw new Error(`Invalid network: ${network}. Available networks: ${networkKeys}`); + } + + return selectedNetwork[1] +} + +export function getNetworkConfigByEnv(env: Environment): NetworkConfig { + const defaultEnvNetwork = getDefaultNetworkForEnv(env); + return getNetworks()[defaultEnvNetwork]; +} + +export function getNetworkConfigForCurrentEnv(): NetworkConfig { + return getNetworkConfigByEnv( + envStringToEnv( + getEnvironment() + ) + ); +} + +export function networkToNetworkName(network: Network): string { + return { + [Network.TESTNET]: 'Testnet', + [Network.MAINNET]: 'Mainnet', + [Network.ISOLATED_SERVER]: 'Isolated Server', + [Network.PRIVATE]: 'Private', + [Network.ZQ2_PROTOMAINNET]: 'ZQ2 Protomainnet' + }[network]; +} diff --git a/products/zillion/src/util/config-json-helper.ts b/products/zillion/src/util/config-json-helper.ts index 51d162f5e..328f39d19 100644 --- a/products/zillion/src/util/config-json-helper.ts +++ b/products/zillion/src/util/config-json-helper.ts @@ -2,15 +2,21 @@ * helper file to extract info from public/config.js */ -import { Environment, Network } from "./enum"; +import { Network } from "./enum"; const { api_max_retry_attempt, - blockchain_explorer_config, environment_config, networks_config, refresh_rate_config -} = (window as { [key: string]: any })['config']; +} = (window as any as { + config: { + api_max_retry_attempt: number, + environment_config: string, + networks_config: Networks, + refresh_rate_config: number + } + })['config']; export interface NetworkConfig { @@ -18,13 +24,14 @@ export interface NetworkConfig { impl: string blockchain: string node_status: string - api_list: [] + api_list: string[] } export interface Networks { [Network.TESTNET]: NetworkConfig [Network.MAINNET]: NetworkConfig [Network.ISOLATED_SERVER]: NetworkConfig + [Network.ZQ2_PROTOMAINNET]: NetworkConfig [Network.PRIVATE]: NetworkConfig // not in used in config.json } @@ -32,10 +39,6 @@ export const getEnvironment = () => { return environment_config; } -export const getBlockchainExplorer = () => { - return blockchain_explorer_config; -} - export const getRefreshRate = (): number => { return refresh_rate_config; } @@ -44,19 +47,18 @@ export const getApiMaxRetry = () => { return api_max_retry_attempt || 10; } -// returns entire networks_config json -// e.g. networks_config : { testnet: { ... } , mainnet: { ... } } -export const getNetworks = (): Networks => { +export const getNetworks = () => { return networks_config; } -// return only specfic networks_config section -// according to environment_config -// e.g. mainnet : { proxy: "", impl: "" } -export const getNetworkConfigByEnv = (): NetworkConfig => { - if (environment_config === Environment.PROD) { - return networks_config[Network.MAINNET] - } - // defaults to testnet - return networks_config[Network.TESTNET] -} \ No newline at end of file +export const tryGetNetworkLabelByApiUrl = (api: string): Network | undefined => { + const availableNetworks = Object.entries(getNetworks()) as [string, NetworkConfig][]; + + const formattedApiName = api.startsWith("https://") ? api : `https://${api}`; + + const networkWithApi = availableNetworks.find( + ([label, config]) => config.api_list.includes(formattedApiName) + ); + + return networkWithApi ? networkWithApi[0] as Network : undefined; +} diff --git a/products/zillion/src/util/enum.ts b/products/zillion/src/util/enum.ts index 21134bdfd..530ad4cc1 100644 --- a/products/zillion/src/util/enum.ts +++ b/products/zillion/src/util/enum.ts @@ -30,12 +30,8 @@ export enum Constants { export enum Environment { DEV = "dev", PROD = "prod", - STAGE = "stage" -} - -export enum Explorer { - DEVEX = "devex", - VIEWBLOCK = "viewblock" + STAGE = "stage", + STAGE_ZQ2_PROTOMAINNET = "stage_zq2_protomainnet" } export enum LedgerIndex { @@ -46,13 +42,15 @@ export enum Network { TESTNET = "testnet", MAINNET = "mainnet", ISOLATED_SERVER = "isolated_server", - PRIVATE = "private" + PRIVATE = "private", + ZQ2_PROTOMAINNET = "zq2_protomainnet", } export enum NetworkURL { TESTNET = "https://dev-api.zilliqa.com", MAINNET = "https://api.zilliqa.com", - ISOLATED_SERVER = "https://zilliqa-isolated-server.zilliqa.com" + ISOLATED_SERVER = "https://zilliqa-isolated-server.zilliqa.com", + ZQ2_PROTOMAINNET = "https://api.zq2-protomainnet.zilliqa.com" } export enum OperationStatus { diff --git a/products/zillion/src/util/reward-calculator.ts b/products/zillion/src/util/reward-calculator.ts index b925cf9f4..7d950b350 100644 --- a/products/zillion/src/util/reward-calculator.ts +++ b/products/zillion/src/util/reward-calculator.ts @@ -1,19 +1,16 @@ -import store from "../store/store"; -import { ApiRandomizer } from "./api-randomizer"; +import { Zilliqa } from "@zilliqa-js/zilliqa"; import { getApiMaxRetry } from "./config-json-helper"; -import { NetworkURL } from "./enum"; import { RewardCalculator } from "./calculator"; - -const apiRandomizer = ApiRandomizer.getInstance(); +import { ZilSdk } from "../zilliqa-api"; // config.js from public folder const API_MAX_ATTEMPT = getApiMaxRetry(); let rewardCalculator: RewardCalculator | null = null; -export const computeDelegRewardsExec = async (impl: string, networkURL: string, ssn: string, delegator: string) => { +export const computeDelegRewardsExec = async (impl: string, zilliqa: Zilliqa, ssn: string, delegator: string) => { if (!rewardCalculator) { - rewardCalculator = new RewardCalculator(networkURL, impl); + rewardCalculator = new RewardCalculator(zilliqa, impl); try { await rewardCalculator.compute_maps(); } catch (err) { @@ -32,9 +29,8 @@ export const computeDelegRewards = async (impl: string, ssn: string, delegator: for (let attempt = 0; attempt < API_MAX_ATTEMPT; attempt++) { try { - const { blockchain, api_list } = store.getState().blockchain - const randomAPI = apiRandomizer.fetchApi(blockchain as NetworkURL, api_list); - result = await computeDelegRewardsExec(impl, randomAPI, ssn, delegator); + const zilliqa = ZilSdk.getZilliqaApi(); + result = await computeDelegRewardsExec(impl, zilliqa, ssn, delegator); break; } catch (err) { // error with querying api diff --git a/products/zillion/src/util/use-local-storage.ts b/products/zillion/src/util/use-local-storage.ts index 3adc66b5c..edf4b26f5 100644 --- a/products/zillion/src/util/use-local-storage.ts +++ b/products/zillion/src/util/use-local-storage.ts @@ -1,4 +1,4 @@ -import { convertNetworkUrlToLabel } from "./utils"; +import { tryGetNetworkLabelByApiUrl } from "./config-json-helper"; /** * https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API#Feature-detecting_localStorage @@ -36,13 +36,13 @@ export function isStorageAvailable(type: string) { * value: storage value * */ -export function storeLocalItem(wallet: string, proxy: string, network: string, key: string, value: any) { +export function storeLocalItem(wallet: string, proxy: string, networkUrl: string, key: string, value: any) { if (!isStorageAvailable("localStorage")) { return null; } - network = convertNetworkUrlToLabel(network); + const network = tryGetNetworkLabelByApiUrl(networkUrl); const storedValue: any = window.localStorage.getItem(wallet); let currStorageMap: any; @@ -91,12 +91,12 @@ export function storeLocalItem(wallet: string, proxy: string, network: string, k * network: blockchain url will convert to label later * key: storage key */ -export function getLocalItem(wallet: string, proxy: string, network: string, key: string, defaultValue: any) { +export function getLocalItem(wallet: string, proxy: string, networkUrl: string, key: string, defaultValue: any) { if (!isStorageAvailable("localStorage")) { return defaultValue; } - network = convertNetworkUrlToLabel(network); + const network = tryGetNetworkLabelByApiUrl(networkUrl); const storedValue: any = window.localStorage.getItem(wallet); if (proxy === null || proxy === "") { diff --git a/products/zillion/src/util/utils.ts b/products/zillion/src/util/utils.ts index e1f98d1f7..9362c8502 100644 --- a/products/zillion/src/util/utils.ts +++ b/products/zillion/src/util/utils.ts @@ -1,17 +1,13 @@ import { toBech32Address, fromBech32Address } from '@zilliqa-js/crypto'; -import { Explorer, NetworkURL, Network, TransactionType, AccountType, Constants, OperationStatus } from './enum'; +import { NetworkURL, Network, TransactionType, AccountType, Constants, OperationStatus } from './enum'; import Alert from '../components/alert'; import { ZilSigner } from '../zilliqa-signer'; -import { getBlockchainExplorer } from './config-json-helper'; +import { tryGetNetworkLabelByApiUrl } from './config-json-helper'; import { SsnStats } from './interface'; import { ZilSdk } from '../zilliqa-api'; import { BN, validation, units } from '@zilliqa-js/util'; import BigNumber from 'bignumber.js'; - -// config.js from public folder -const blockchain_explorer_config = getBlockchainExplorer(); - export const bech32ToChecksum = (address: string) => { if (validation.isAddress(address)) { // convert to checksum format @@ -126,34 +122,17 @@ export const convertGzilToCommaStr = (inputVal: string) => { return frontAmt + "." + backAmt; } -export const convertNetworkUrlToLabel = (url: string) => { - let label = ''; - switch (url) { - case NetworkURL.MAINNET: - label = Network.MAINNET; - break; - case NetworkURL.TESTNET: - label = Network.TESTNET; - break; - default: - label = Network.TESTNET; - break; - } - return label; -} - export const getTxnLink = (txnId: string, networkURL: string) => { let link = ""; - if (blockchain_explorer_config === Explorer.VIEWBLOCK) { - if (networkURL === NetworkURL.MAINNET) { - link = "https://viewblock.io/zilliqa/tx/0x" + txnId - } else { - link = "https://viewblock.io/zilliqa/tx/0x" + txnId + "?network=testnet" - } - } else { - // devex - link = "https://devex.zilliqa.com/tx/0x" + txnId + "?network=" + networkURL; + const network = tryGetNetworkLabelByApiUrl(networkURL); + + switch (network) { + case Network.MAINNET: link = "https://viewblock.io/zilliqa/tx/0x" + txnId; break; + case Network.PRIVATE: + case Network.ISOLATED_SERVER: + case Network.TESTNET: link = "https://viewblock.io/zilliqa/tx/0x" + txnId + "?network=testnet"; break; + case Network.ZQ2_PROTOMAINNET: link = "https://explorer.zq2-protomainnet.zilliqa.com/tx/" + txnId; break; } return link; @@ -161,16 +140,14 @@ export const getTxnLink = (txnId: string, networkURL: string) => { export const getAddressLink = (address: string, networkURL: string) => { let link = ""; - - if (blockchain_explorer_config === Explorer.VIEWBLOCK) { - if (networkURL === NetworkURL.MAINNET) { - link = "https://viewblock.io/zilliqa/address/" + address - } else { - link = "https://viewblock.io/zilliqa/address/" + address + "?network=testnet" - } - } else { - // devex - link = "https://devex.zilliqa.com/address/" + address + "?network=" + networkURL; + const network = tryGetNetworkLabelByApiUrl(networkURL); + + switch (network) { + case Network.MAINNET: link = "https://viewblock.io/zilliqa/address/" + address; break; + case Network.PRIVATE: + case Network.ISOLATED_SERVER: + case Network.TESTNET: link = "https://viewblock.io/zilliqa/address/" + address + "?network=testnet"; break; + case Network.ZQ2_PROTOMAINNET: link = "https://explorer.zq2-protomainnet.zilliqa.com/address/" + address; break; } return link; diff --git a/products/zillion/src/zilliqa-api.ts b/products/zillion/src/zilliqa-api.ts index ea7bb2e27..f034b7d3a 100644 --- a/products/zillion/src/zilliqa-api.ts +++ b/products/zillion/src/zilliqa-api.ts @@ -1,15 +1,36 @@ import { Zilliqa } from "@zilliqa-js/zilliqa"; import store from "./store/store"; -import { ApiRandomizer } from "./util/api-randomizer"; import { getApiMaxRetry } from "./util/config-json-helper"; -import { NetworkURL, OperationStatus } from "./util/enum"; +import { OperationStatus } from "./util/enum"; import { logger } from "./util/logger"; const API_MAX_ATTEMPT = getApiMaxRetry(); -const API_RANDOMIZER = ApiRandomizer.getInstance(); export class ZilSdk { + private static currentApiIndextoCallForNetwork: Record = {}; + + /** + * return the zilliqa api object based on the selected url from the networks_config.api_list + * It follows according to round robbin through the api list starting at random index + */ + public static getZilliqaApi() { + const { blockchain, api_list } = store.getState().blockchain + + if (ZilSdk.currentApiIndextoCallForNetwork[blockchain] === undefined) { + // we start counter from random index to avoid all clients calling the same api at start + ZilSdk.currentApiIndextoCallForNetwork[blockchain] = Math.floor(Math.random() * api_list.length) + } + + // we could do that after creating object, but if for some reason the config changes + // then with doing this first we never go out of bound + ZilSdk.currentApiIndextoCallForNetwork[blockchain] += 1 % api_list.length; + + const currentApi = api_list[ZilSdk.currentApiIndextoCallForNetwork[blockchain]]; + + return new Zilliqa(currentApi); + } + /** * query the contract state using random api via batch JSON-RPC * @param queryList an array of array in the form [ [impl_address, contract_field, [indices], [impl_address, contract_field2, [indices]] ] ] @@ -27,9 +48,7 @@ export class ZilSdk { private static getActualSmartContractSubStateBatch = async (queryList: any[]): Promise => { try { - const { blockchain, api_list } = store.getState().blockchain - const randomAPI = API_RANDOMIZER.fetchApi(blockchain as NetworkURL, api_list); - const zilliqa = new Zilliqa(randomAPI); + const zilliqa = ZilSdk.getZilliqaApi() let response: any = await zilliqa.blockchain.getSmartContractSubStateBatch(queryList); @@ -134,9 +153,7 @@ export class ZilSdk { private static getActualNumTxBlocks = async () => { try { - const { blockchain, api_list } = store.getState().blockchain - const randomAPI = API_RANDOMIZER.fetchApi(blockchain as NetworkURL, api_list); - const zilliqa = new Zilliqa(randomAPI); + const zilliqa = ZilSdk.getZilliqaApi() const response = await zilliqa.blockchain.getBlockChainInfo(); if (!response.hasOwnProperty("result") || response.result === undefined) { @@ -150,9 +167,7 @@ export class ZilSdk { private static getActualTotalCoinSupply = async () => { try { - const { blockchain, api_list } = store.getState().blockchain - const randomAPI = API_RANDOMIZER.fetchApi(blockchain as NetworkURL, api_list); - const zilliqa = new Zilliqa(randomAPI); + const zilliqa = ZilSdk.getZilliqaApi() const response = await zilliqa.blockchain.getTotalCoinSupply(); if (!response.hasOwnProperty("result") || response.result === undefined) { @@ -166,9 +181,7 @@ export class ZilSdk { private static getActualBalance = async (address: string) => { try { - const { blockchain, api_list } = store.getState().blockchain - const randomAPI = API_RANDOMIZER.fetchApi(blockchain as NetworkURL, api_list); - const zilliqa = new Zilliqa(randomAPI); + const zilliqa = ZilSdk.getZilliqaApi() const response = await zilliqa.blockchain.getBalance(address); if (!response.hasOwnProperty("result") || response.result.balance === undefined) { @@ -196,9 +209,7 @@ export class ZilSdk { } try { - const { blockchain, api_list } = store.getState().blockchain - const randomAPI = API_RANDOMIZER.fetchApi(blockchain as NetworkURL, api_list); - const zilliqa = new Zilliqa(randomAPI); + const zilliqa = ZilSdk.getZilliqaApi() let response: any = null; if (indices !== null) {