From beead3f5ba88a5756244a56cdc7031593c289907 Mon Sep 17 00:00:00 2001 From: Korniichuk Oleksandr Date: Wed, 5 Jan 2022 13:00:22 +0200 Subject: [PATCH] add loading tokens --- .../components/staking/components/Staking.js | 18 +++++-- .../staking/components/Validator.js | 28 ++++++++--- .../staking/components/ValidatorBox.js | 39 +++++++++------ .../staking/components/Validators.js | 34 +++++++++---- .../frontend/src/redux/actions/staking.js | 38 ++++++++++---- .../src/redux/slices/staking/index.js | 2 +- .../frontend/src/redux/slices/tokens/index.js | 27 ++++++++-- .../frontend/src/translations/en.global.json | 10 +++- .../frontend/src/translations/pt.global.json | 5 ++ .../frontend/src/translations/ru.global.json | 7 ++- .../frontend/src/translations/vi.global.json | 7 ++- .../src/translations/zh-hans.global.json | 7 ++- .../src/translations/zh-hant.global.json | 7 ++- packages/frontend/src/utils/constants.js | 50 +++++++++++++++++++ packages/frontend/src/utils/staking.js | 8 +-- 15 files changed, 227 insertions(+), 60 deletions(-) create mode 100644 packages/frontend/src/utils/constants.js diff --git a/packages/frontend/src/components/staking/components/Staking.js b/packages/frontend/src/components/staking/components/Staking.js index f2f1199750..317d64a657 100644 --- a/packages/frontend/src/components/staking/components/Staking.js +++ b/packages/frontend/src/components/staking/components/Staking.js @@ -35,7 +35,7 @@ export default function Staking({ {multipleAccounts &&
- +
} {!loading && !loadingDetails && @@ -51,8 +51,8 @@ export default function Staking({ show={loading || loadingDetails} className='account-loader' /> - + : null}

- {!loadingDetails + {!loadingDetails ? currentValidators.length ? {currentValidators.map((validator, i) => diff --git a/packages/frontend/src/components/staking/components/Validator.js b/packages/frontend/src/components/staking/components/Validator.js index 7e3d95644a..68cbb9b4c7 100644 --- a/packages/frontend/src/components/staking/components/Validator.js +++ b/packages/frontend/src/components/staking/components/Validator.js @@ -27,14 +27,14 @@ export default function Validator({ const handleStakeAction = async () => { if (showConfirmModal && !loading) { - await onWithdraw('withdraw', selectedValidator || validator.accountId); - setConfirm('done'); + await onWithdraw('withdraw', selectedValidator || validator.accountId); + setConfirm('done'); } }; return ( <> - {stakeNotAllowed + {stakeNotAllowed ? - - {validator && } + {validator && } {validator && !stakeNotAllowed && !actionsPending('UPDATE_STAKING') && <> + { + dispatch(redirectTo(`/staking/${match.params.validator}/claim`)); + Mixpanel.track("CLAIM Click claim button"); + }} + button='staking.balanceBox.farm.button' + buttonColor='gray-red' + loading={loading} + /> { setConfirm('withdraw'); Mixpanel.track("WITHDRAW Click withdraw button"); diff --git a/packages/frontend/src/components/staking/components/ValidatorBox.js b/packages/frontend/src/components/staking/components/ValidatorBox.js index 6f32d411d9..254e675277 100644 --- a/packages/frontend/src/components/staking/components/ValidatorBox.js +++ b/packages/frontend/src/components/staking/components/ValidatorBox.js @@ -5,8 +5,10 @@ import styled from 'styled-components'; import { Mixpanel } from '../../../mixpanel/index'; import { redirectTo } from '../../../redux/actions/account'; +import { PROJECT_VALIDATOR_VERSION, ValidatorVersion } from '../../../utils/constants'; import Balance from '../../common/balance/Balance'; import FormButton from '../../common/FormButton'; +import Tooltip from '../../common/Tooltip'; import ChevronIcon from '../../svg/ChevronIcon'; import UserIcon from '../../svg/UserIcon'; @@ -47,6 +49,8 @@ const Container = styled.div` div { text-align: left; color: #A7A29E; + display: flex; + flex-direction: row; &:first-of-type { color: #24272a; max-width: 165px; @@ -128,7 +132,7 @@ export default function ValidatorBox({ style, label = false, stakeAction, - showBalanceInUSD + showBalanceInUSD, }) { const dispatch = useDispatch(); const { accountId: validatorId, active } = validator; @@ -152,31 +156,36 @@ export default function ValidatorBox({ dispatch(redirectTo(`/staking/${validatorId}${stakeAction ? `/${stakeAction}` : ``}`)); } }; - + const isProjectValidator = validator.version === ValidatorVersion[PROJECT_VALIDATOR_VERSION]; return ( - {label &&
} - +
{validatorId} + {isProjectValidator && }
{typeof fee === 'number' && -
- {fee}% - +
+ { + isProjectValidator + ? {fee}% - + : {fee}% - + } { - active ? - - : - - } + active ? + + : + + }
} @@ -185,7 +194,7 @@ export default function ValidatorBox({
{staking &&
}
- +
} diff --git a/packages/frontend/src/components/staking/components/Validators.js b/packages/frontend/src/components/staking/components/Validators.js index 5795a2ed29..efeae7103a 100644 --- a/packages/frontend/src/components/staking/components/Validators.js +++ b/packages/frontend/src/components/staking/components/Validators.js @@ -1,15 +1,25 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { Translate } from 'react-localize-redux'; +import { useDispatch } from 'react-redux'; +import { actions as tokensActions } from '../../../redux/slices/tokens'; +import { getValidatorRegExp } from '../../../utils/constants'; +import { wallet } from '../../../utils/wallet'; import ListWrapper from './ListWrapper'; import ValidatorBox from './ValidatorBox'; +const { fetchToken } = tokensActions; export default function Validators({ validators, stakeFromAccount }) { - + // const dispatch = useDispatch(); const [validator, setValidator] = useState(''); - + const networkId = wallet.connection.networkId; + const validatorPrefix = getValidatorRegExp(networkId); const validValidator = validators.map(validator => validator.accountId).includes(validator); + // useEffect(() => { + // dispatch(fetchToken({ contractName: 'token.solniechniy.testnet', accountId: stakeFromAccount })); + // }, []); + return ( <>

@@ -22,7 +32,7 @@ export default function Validators({ validators, stakeFromAccount }) { placeholder={translate('staking.validators.inputPlaceholder')} value={validator} onChange={e => setValidator(e.target.value)} - autoFocus + autoFocus spellCheck='false' autoCapitalize='off' data-test-id="stakingSearchForValidator" @@ -38,12 +48,16 @@ export default function Validators({ validators, stakeFromAccount }) {
)} - {validators.filter(v => v.accountId.includes(validator)).map((validator, i) => - - )} + {validators.filter(v => v.accountId.includes(validator)) + .sort((first, second) => + (second.accountId.includes(validatorPrefix) - first.accountId.includes(validatorPrefix)) + ) + .map((validator, i) => + + )} ); diff --git a/packages/frontend/src/redux/actions/staking.js b/packages/frontend/src/redux/actions/staking.js index 1dbd7cd5a9..a8539a33f1 100644 --- a/packages/frontend/src/redux/actions/staking.js +++ b/packages/frontend/src/redux/actions/staking.js @@ -10,13 +10,21 @@ import { import { getLockupAccountId, getLockupMinBalanceForStorage } from '../../utils/account-with-lockup'; import { showAlert } from '../../utils/alerts'; import { setStakingAccountSelected } from '../../utils/localStorage'; -import { +import { + PROJECT_VALIDATOR_VERSION, + ValidatorVersion, + MAINNET, + getValidatorRegExp, + getValidationVersion, + TESTNET +} from '../../utils/constants'; +import { STAKING_AMOUNT_DEVIATION, MIN_DISPLAY_YOCTO, ZERO, EXPLORER_DELAY, ACCOUNT_DEFAULTS, - getStakingDeposits, + getStakingDeposits, lockupMethods, updateStakedBalance, signAndSendTransaction, @@ -25,12 +33,12 @@ import { } from '../../utils/staking'; import { wallet } from '../../utils/wallet'; import { WalletError } from '../../utils/walletError'; -import { +import { selectAccountId, selectAccountSlice } from '../slices/account'; import { selectAllAccountsByAccountId } from '../slices/allAccounts'; -import { +import { selectStakingAccountsMain, selectStakingMainAccountId, selectStakingLockupAccountId, @@ -259,12 +267,23 @@ export const { staking } = createActions({ totalStaked = totalStaked.add(new BN(validator.staked)); totalUnclaimed = totalUnclaimed.add(new BN(validator.unclaimed)); + const networkId = wallet.connection.provider.connection.url.indexOf(MAINNET) > -1 ? MAINNET : TESTNET; + + console.log(networkId, validator.accountId); + validator.version = getValidationVersion(networkId, validator.accountId); } catch (e) { if (e.message.indexOf('cannot find contract code') === -1) { console.warn('Error getting data for validator', validator.accountId, e); } } })); + const projectValidators = validators.filter(v => v.version === ValidatorVersion[PROJECT_VALIDATOR_VERSION]); + await Promise.all(projectValidators.map(async () => { + const allFarms = await projectValidators.contract.get_farms({ from_index: 0, limit: 1000 }); + const allFarmsData = Promise.all(allFarms.map((_, index) => projectValidators.contract.get_farm({ farm_id: index }))); + console.log(allFarmsData); + })); + return { accountId, @@ -290,7 +309,7 @@ export const { staking } = createActions({ if (totalUnstaked.lt(new BN(parseNearAmount('0.002')))) { totalUnstaked = ZERO.clone(); } - + // validator specific const selectedValidator = await contract.get_staking_pool_account_id(); if (!selectedValidator) { @@ -301,7 +320,7 @@ export const { staking } = createActions({ mainAccountId: exAccountId }; } - let validator = validators.find((validator) => + let validator = validators.find((validator) => validator.accountId === selectedValidator ); @@ -377,15 +396,14 @@ export const { staking } = createActions({ GET_VALIDATORS: async (accountIds, accountId) => { const { current_validators, next_validators, current_proposals } = await wallet.connection.provider.validators(); const currentValidators = shuffle(current_validators).map(({ account_id }) => account_id); - if (!accountIds) { const rpcValidators = [...current_validators, ...next_validators, ...current_proposals].map(({ account_id }) => account_id); - const networkId = wallet.connection.provider.connection.url.indexOf('mainnet') > -1 ? 'mainnet' : 'testnet'; + const networkId = wallet.connection.provider.connection.url.indexOf(MAINNET) > -1 ? MAINNET : TESTNET; const allStakingPools = (await fetch(`${ACCOUNT_HELPER_URL}/stakingPools`).then((r) => r.json())); - + const prefix = getValidatorRegExp(networkId); accountIds = [...new Set([...rpcValidators, ...allStakingPools])] - .filter((v) => v.indexOf('nfvalidator') === -1 && v.indexOf(networkId === 'mainnet' ? '.near' : '.m0') > -1); + .filter((v) => v.indexOf('nfvalidator') === -1 && v.match(prefix)); } const currentAccount = wallet.getAccountBasic(accountId); diff --git a/packages/frontend/src/redux/slices/staking/index.js b/packages/frontend/src/redux/slices/staking/index.js index c014983f91..b654b15744 100644 --- a/packages/frontend/src/redux/slices/staking/index.js +++ b/packages/frontend/src/redux/slices/staking/index.js @@ -14,7 +14,7 @@ export const selectStakingSlice = (state) => state[SLICE_NAME] || {}; export const selectStakingAccounts = createSelector(selectStakingSlice, (staking) => staking.accounts || []); export const selectStakingCurrentAccountbyAccountId = createSelector( - [selectStakingAccounts, getAccountIdParam], + [selectStakingAccounts, getAccountIdParam], (accounts, accountId) => accounts.find((account) => account.accountId === accountId) ); diff --git a/packages/frontend/src/redux/slices/tokens/index.js b/packages/frontend/src/redux/slices/tokens/index.js index 9a69a42ee6..fbd463ea00 100644 --- a/packages/frontend/src/redux/slices/tokens/index.js +++ b/packages/frontend/src/redux/slices/tokens/index.js @@ -58,7 +58,7 @@ const fetchTokens = createAsyncThunk( async ({ accountId }, thunkAPI) => { const { dispatch, getState } = thunkAPI; - const likelyContracts = [...new Set([...(await FungibleTokens.getLikelyTokenContracts({ accountId })), ...WHITELISTED_CONTRACTS])]; + const likelyContracts = [...new Set([...(await FungibleTokens.getLikelyTokenContracts({ accountId })), ...WHITELISTED_CONTRACTS])]; await Promise.all(likelyContracts.map(async contractName => { const { actions: { setContractMetadata } } = tokensSlice; @@ -76,6 +76,24 @@ const fetchTokens = createAsyncThunk( } ); +const fetchToken = createAsyncThunk( + `${SLICE_NAME}/fetchTokens`, + async ({ contractName, accountId }, thunkAPI) => { + const { dispatch, getState } = thunkAPI; + const { actions: { setContractMetadata } } = tokensSlice; + try { + const contractMetadata = await getCachedContractMetadataOrFetch(contractName, accountId, getState()); + if (!selectOneContractMetadata(getState(), { contractName })) { + dispatch(setContractMetadata({ contractName, metadata: contractMetadata })); + } + await dispatch(fetchOwnedTokensForContract({ accountId, contractName, contractMetadata })); + } catch (e) { + // Continue loading other likely contracts on failures + console.warn(`Failed to load FT for ${contractName}`, e); + } + } +); + const tokensSlice = createSlice({ name: SLICE_NAME, initialState, @@ -102,9 +120,9 @@ const tokensSlice = createSlice({ set(state, ['ownedTokens', 'byAccountId', accountId, contractName, 'loading'], false); set(state, ['ownedTokens', 'byAccountId', accountId, contractName, 'error'], initialErrorState); }); - builder.addCase(fetchOwnedTokensForContract.rejected, (state, { meta, error }) => { + builder.addCase(fetchOwnedTokensForContract.rejected, (state, { meta, error }) => { const { accountId, contractName } = meta.arg; - + set(state, ['ownedTokens', 'byAccountId', accountId, contractName, 'loading'], false); set(state, ['ownedTokens', 'byAccountId', accountId, contractName, 'error'], { message: error?.message || 'An error was encountered.', @@ -117,10 +135,11 @@ const tokensSlice = createSlice({ export default tokensSlice; export const actions = { + fetchToken, fetchTokens, ...tokensSlice.actions }; -export const reducer = tokensSlice.reducer; +export const reducer = tokensSlice.reducer; const getAccountIdParam = createParameterSelector((params) => params.accountId); diff --git a/packages/frontend/src/translations/en.global.json b/packages/frontend/src/translations/en.global.json index 4eda68b983..757f7a407c 100644 --- a/packages/frontend/src/translations/en.global.json +++ b/packages/frontend/src/translations/en.global.json @@ -1133,7 +1133,10 @@ "networkFees": "Network Fees", "nothingHasBeenTransferred": "Nothing has been transferred.", "retry": { + "estimatedFees": "Estimated Fees", + "feeLimit": "Fee Limit", "link": "What is the fee limit?", + "networkFees": "Network Fees", "text": "The default network fee was not enough to cover the cost of your transaction.

You may resubmit the transaction to have its fee limit automatically increased.", "title": "Insufficient Network Fee" }, @@ -1194,6 +1197,11 @@ "info": "These tokens have been unstaked, and are ready to be withdrawn.", "title": "Available for withdrawal" }, + "farm": { + "button": "Claim", + "info": "This is project validator. You’ll get the farming reward while staking on it.", + "title": "Total token earned" + }, "pending": { "info": "These tokens have been unstaked, but are not yet ready to withdraw. Tokens are ready to withdraw 52 to 65 hours after unstaking.", "title": "Pending release" @@ -1494,4 +1502,4 @@ } }, "warning": "Warning" -} +} \ No newline at end of file diff --git a/packages/frontend/src/translations/pt.global.json b/packages/frontend/src/translations/pt.global.json index 48455afb75..049adbb531 100644 --- a/packages/frontend/src/translations/pt.global.json +++ b/packages/frontend/src/translations/pt.global.json @@ -1108,6 +1108,11 @@ "pending": { "title": "Aguardando liberação", "info": "Esses tokens foram removidos de stake, mas ainda não estão disponíveis para serem sacados. Tokens ficam disponíveis entre 52 a 65 horas após a remoção." + }, + "farm": { + "title": "Total token earned", + "info": "It's project validator", + "button": "Claim" } }, "validatorBox": { diff --git a/packages/frontend/src/translations/ru.global.json b/packages/frontend/src/translations/ru.global.json index e817413737..5bf9cd60cc 100644 --- a/packages/frontend/src/translations/ru.global.json +++ b/packages/frontend/src/translations/ru.global.json @@ -1033,6 +1033,11 @@ "info": "Эти токены сняты со стейкинга и теперь могут быть выведены.", "title": "Доступно для вывода" }, + "farm": { + "button": "Claim", + "info": "This is project validator. You’ll get the farming reward while staking on it.", + "title": "Total token earned" + }, "pending": { "info": "Эти токены сняты со стейкинга, но еще не могут быть выведены. Токенам требуется от 52 до 65 часов на вывод со стейкинга.", "title": "Ожидается снятие" @@ -1242,4 +1247,4 @@ } }, "warning": "Предупреждение" -} +} \ No newline at end of file diff --git a/packages/frontend/src/translations/vi.global.json b/packages/frontend/src/translations/vi.global.json index ade6eecaeb..b6099ca0a1 100644 --- a/packages/frontend/src/translations/vi.global.json +++ b/packages/frontend/src/translations/vi.global.json @@ -1102,6 +1102,11 @@ "info": "Các token này đã được unstake và sẵn sàng để rút.", "title": "Khả dụng để rút tiền" }, + "farm": { + "button": "Claim", + "info": "This is project validator. You’ll get the farming reward while staking on it.", + "title": "Total token earned" + }, "pending": { "info": "Các token này đã được unstake, nhưng vẫn chưa sẵn sàng để rút. Việc rút sẽ khả dụng từ 52 đến 65 giờ sau khi unstake.", "title": "Đang chờ phát hành" @@ -1324,4 +1329,4 @@ } }, "warning": "Cảnh báo" -} +} \ No newline at end of file diff --git a/packages/frontend/src/translations/zh-hans.global.json b/packages/frontend/src/translations/zh-hans.global.json index 40431eceeb..70789ad149 100644 --- a/packages/frontend/src/translations/zh-hans.global.json +++ b/packages/frontend/src/translations/zh-hans.global.json @@ -1173,6 +1173,11 @@ "info": "这些通证已经赎回质押,并可以提现。", "title": "可提现" }, + "farm": { + "button": "Claim", + "info": "This is project validator. You’ll get the farming reward while staking on it.", + "title": "Total token earned" + }, "pending": { "info": "这些通证并未质押,但还不可提现。通常在赎回质押后 36 - 48 小时变为可提现。", "title": "请求释放" @@ -1467,4 +1472,4 @@ } }, "warning": "警告" -} +} \ No newline at end of file diff --git a/packages/frontend/src/translations/zh-hant.global.json b/packages/frontend/src/translations/zh-hant.global.json index c73a824287..bed5f95478 100644 --- a/packages/frontend/src/translations/zh-hant.global.json +++ b/packages/frontend/src/translations/zh-hant.global.json @@ -1173,6 +1173,11 @@ "info": "這些通證已經贖回質押,並可以提現。", "title": "可提現" }, + "farm": { + "button": "Claim", + "info": "This is project validator. You’ll get the farming reward while staking on it.", + "title": "Total token earned" + }, "pending": { "info": "這些通證並未質押,但還不可提現。通常在贖回質押後 36 - 48 小時變為可提現。", "title": "請求釋放" @@ -1467,4 +1472,4 @@ } }, "warning": "警告" -} +} \ No newline at end of file diff --git a/packages/frontend/src/utils/constants.js b/packages/frontend/src/utils/constants.js new file mode 100644 index 0000000000..368c9d6423 --- /dev/null +++ b/packages/frontend/src/utils/constants.js @@ -0,0 +1,50 @@ +export const VALIDATOR_VERSION = "VALIDATOR"; +export const PROJECT_VALIDATOR_VERSION = "PROJECT_VALIDATOR"; + +export const ValidatorVersion = { + [VALIDATOR_VERSION]: 1, + [PROJECT_VALIDATOR_VERSION]: 2 +}; + +export const MAINNET = "mainnet"; +export const TESTNET = "testnet"; +export const PROJECT_VALIDATOR_PREFIX_MAINNET = ".near"; +export const VALIDATOR_PREFIX_MAINNET = ".pool.near"; +export const PROJECT_VALIDATOR_PREFIX_TESTNET = ".factory01.littlefarm.testnet"; +export const VALIDATOR_PREFIX_TESTNET = ".m0"; + +export const PROJECT_VALIDATOR_REGEXP_TESTNET = new RegExp(`.*(${PROJECT_VALIDATOR_PREFIX_TESTNET}|${VALIDATOR_PREFIX_TESTNET})`); +export const PROJECT_VALIDATOR_REGEXP_MAINNET = new RegExp(`.*(${PROJECT_VALIDATOR_PREFIX_MAINNET}|${VALIDATOR_PREFIX_MAINNET})`); + +export const getValidatorRegExp = (networkId) => { + switch (networkId) { + case (MAINNET): { + return PROJECT_VALIDATOR_REGEXP_MAINNET; + } + case (TESTNET): { + return PROJECT_VALIDATOR_REGEXP_TESTNET; + } + default: { + return PROJECT_VALIDATOR_PREFIX_TESTNET; + } + } +}; + +export const getValidationVersion = (networkId, accountId) => { + const prefix = getValidationNetworkPrefix(networkId); + return accountId.indexOf(prefix) === -1 ? VALIDATOR_VERSION : PROJECT_VALIDATOR_VERSION; +}; + +export const getValidationNetworkPrefix = (networkId) => { + switch (networkId) { + case (MAINNET): { + return PROJECT_VALIDATOR_PREFIX_MAINNET; + } + case (TESTNET): { + return PROJECT_VALIDATOR_PREFIX_TESTNET; + } + default: { + return PROJECT_VALIDATOR_PREFIX_TESTNET; + } + } +}; diff --git a/packages/frontend/src/utils/staking.js b/packages/frontend/src/utils/staking.js index dad97c387a..8f81fe55fd 100644 --- a/packages/frontend/src/utils/staking.js +++ b/packages/frontend/src/utils/staking.js @@ -24,7 +24,7 @@ export const ACCOUNT_DEFAULTS = { totalPending: '0', // pending withdrawal totalAvailable: '0', // available for withdrawal totalUnstaked: '0', // available to be staked - totalStaked: '0', + totalStaked: '0', totalUnclaimed: '0', // total rewards paid out - staking deposits made validators: [], }; @@ -38,6 +38,8 @@ export const stakingMethods = { 'get_total_staked_balance', 'get_owner_id', 'get_reward_fee_fraction', + 'get_farms', + 'get_farm' ], changeMethods: [ 'ping', @@ -71,13 +73,13 @@ export async function updateStakedBalance(validatorId, account_id, contract) { } export async function getStakingDeposits(accountId) { - let stakingDeposits = await fetch(ACCOUNT_HELPER_URL + '/staking-deposits/' + accountId).then((r) => r.json()); + let stakingDeposits = await fetch(ACCOUNT_HELPER_URL + '/staking-deposits/' + accountId).then((r) => r.json()); const validatorDepositMap = {}; stakingDeposits.forEach(({ validator_id, deposit }) => { validatorDepositMap[validator_id] = deposit; }); - + return validatorDepositMap; }