diff --git a/config-overrides.js b/config-overrides.js index 732893e..d5c098a 100644 --- a/config-overrides.js +++ b/config-overrides.js @@ -1,6 +1,6 @@ const webpack = require('webpack'); const path = require('path'); -const HtmlWebpackPlugin = require("html-webpack-plugin"); +const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { webpack: function override (config, env) { @@ -13,16 +13,16 @@ module.exports = { }; config.resolve.alias = { ...config.resolve.alias, - "../package.json": path.resolve( + '../package.json': path.resolve( __dirname, - "node_modules/@heliaxdev/namada-sdk/package.json", + 'node_modules/@heliaxdev/namada-sdk/package.json', ), }; config.resolve.extensions = [...config.resolve.extensions, '.ts', '.js', '.wasm']; config.plugins = [ ...config.plugins, new HtmlWebpackPlugin({ - template: path.join(__dirname, "./public/index.html"), + template: path.join(__dirname, './public/index.html'), }), new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'], @@ -31,32 +31,32 @@ module.exports = { process: 'process/browser', }), new webpack.DefinePlugin({ - process: { - env: {}, - }, + process: { + env: {}, + }, }), ]; config.ignoreWarnings = [/Failed to parse source map/]; - + return config; }, - // Function to customize the devServer + // Function to customize the devServer devServer: function (configFunction) { return function (proxy, allowedHost) { // Get the default config and modify it - const config = configFunction(proxy, allowedHost); + const config = configFunction(proxy, allowedHost); - // Update devServer settings here - config.static = [ - path.join(__dirname, "public"), - path.join(__dirname, "node_modules", "@heliaxdev", "namada-sdk", "dist"), - ]; - config.compress = true; - config.port = 9000; + // Update devServer settings here + config.static = [ + path.join(__dirname, 'public'), + path.join(__dirname, 'node_modules', '@heliaxdev', 'namada-sdk', 'dist'), + ]; + config.compress = true; + config.port = 9000; - // Return the modified configuration - return config; + // Return the modified configuration + return config; }; }, }; diff --git a/src/actions/accounts/index.js b/src/actions/accounts/index.js index e47f8f5..fbe4ee0 100644 --- a/src/actions/accounts/index.js +++ b/src/actions/accounts/index.js @@ -8,6 +8,9 @@ import { DELEGATIONS_FETCH_IN_PROGRESS, DELEGATIONS_FETCH_SUCCESS, DISCONNECT_SET, + REVEALED_PUB_KEY_FETCH_ERROR, + REVEALED_PUB_KEY_FETCH_IN_PROGRESS, + REVEALED_PUB_KEY_FETCH_SUCCESS, REWARDS_FETCH_ERROR, REWARDS_FETCH_IN_PROGRESS, REWARDS_FETCH_SUCCESS, @@ -22,12 +25,12 @@ import { VESTING_BALANCE_FETCH_SUCCESS, } from '../../constants/accounts'; import Axios from 'axios'; -import { urlFetchRewards, urlFetchUnBondingDelegations, urlFetchVestingBalance } from '../../constants/url'; +import { urlFetchRevealedPubkey, urlFetchRewards, urlFetchUnBondingDelegations, urlFetchVestingBalance } from '../../constants/url'; // import { Query } from '@namada/shared'; import { config } from '../../config'; // import { init as initShared } from '@namada/shared/dist/init-inline'; -import { Sdk, getSdk } from "@heliaxdev/namada-sdk/web"; -import init from "@heliaxdev/namada-sdk/web-init"; +import { Sdk, getSdk } from '@heliaxdev/namada-sdk/web'; +import init from '@heliaxdev/namada-sdk/web-init'; // import { Tokens } from '@namada/types'; export const setAccountAddress = (value) => { @@ -69,11 +72,11 @@ export const getDelegations = (address) => (dispatch) => { (async () => { const { cryptoMemory } = await init(); const sdk = getSdk( - cryptoMemory, - config.RPC_URL, - config.MAPS_REST_URL, - "", - config.TOKEN_ADDRESS + cryptoMemory, + config.RPC_URL, + config.MAPS_REST_URL, + '', + config.TOKEN_ADDRESS, ); const { rpc } = sdk; @@ -134,11 +137,11 @@ export const getBalance = (address, cb) => (dispatch) => { // } const { cryptoMemory } = await init(); const sdk = getSdk( - cryptoMemory, - config.RPC_URL, - config.MAPS_REST_URL, - "", - config.TOKEN_ADDRESS + cryptoMemory, + config.RPC_URL, + config.MAPS_REST_URL, + '', + config.TOKEN_ADDRESS, ); const { rpc } = sdk; @@ -310,6 +313,48 @@ export const fetchRewards = (address) => (dispatch) => { }); }; +const fetchRevealedPubKeyInProgress = () => { + return { + type: REVEALED_PUB_KEY_FETCH_IN_PROGRESS, + }; +}; + +const fetchRevealedPubKeySuccess = (value) => { + return { + type: REVEALED_PUB_KEY_FETCH_SUCCESS, + value, + }; +}; + +const fetchRevealedPubKeyError = (message) => { + return { + type: REVEALED_PUB_KEY_FETCH_ERROR, + message, + }; +}; + +export const fetchRevealedPubKey = (address) => (dispatch) => { + dispatch(fetchRevealedPubKeyInProgress()); + const url = urlFetchRevealedPubkey(address); + Axios.get(url, { + headers: { + Accept: 'application/json, text/plain, */*', + }, + }) + .then((res) => { + dispatch(fetchRevealedPubKeySuccess(res.data)); + }) + .catch((error) => { + dispatch(fetchRevealedPubKeyError( + error.response && + error.response.data && + error.response.data.message + ? error.response.data.message + : 'Failed!', + )); + }); +}; + export const disconnectSet = () => { return { type: DISCONNECT_SET, diff --git a/src/actions/stake.js b/src/actions/stake.js index 4191ea4..b5dfe09 100644 --- a/src/actions/stake.js +++ b/src/actions/stake.js @@ -51,6 +51,7 @@ import { } from '../constants/url'; import { config } from '../config'; import { calculateNominalAPR, calculateRealAPR, getBlocksPerYearReal, getParams } from '../utils/aprCalculation'; +import { randomNoRepeats } from 'utils/array'; const axios = require('axios').default; @@ -84,8 +85,9 @@ export const getValidators = (page, cb) => (dispatch) => { }, }) .then((res) => { - dispatch(fetchValidatorsSuccess(res.data, res.data && res.data.length, page)); - cb(res.data, res.data && res.data.length, page, res.data && res.data.length); + const data = randomNoRepeats(res.data); + dispatch(fetchValidatorsSuccess(data, data && data.length, page)); + cb(data, data && data.length, page, data && data.length); }) .catch((error) => { dispatch(fetchValidatorsError( diff --git a/src/assets/userDetails/rewards.svg b/src/assets/userDetails/rewards.svg index 8a84ab3..82aaa61 100644 --- a/src/assets/userDetails/rewards.svg +++ b/src/assets/userDetails/rewards.svg @@ -1,8 +1,8 @@ + fill="#fff"/> + fill="#fff"/> + fill="#fff"/> diff --git a/src/constants/accounts.js b/src/constants/accounts.js index df165fc..709df53 100644 --- a/src/constants/accounts.js +++ b/src/constants/accounts.js @@ -26,4 +26,8 @@ export const REWARDS_FETCH_IN_PROGRESS = 'REWARDS_FETCH_IN_PROGRESS'; export const REWARDS_FETCH_SUCCESS = 'REWARDS_FETCH_SUCCESS'; export const REWARDS_FETCH_ERROR = 'REWARDS_FETCH_ERROR'; +export const REVEALED_PUB_KEY_FETCH_IN_PROGRESS = 'REVEALED_PUB_KEY_FETCH_IN_PROGRESS'; +export const REVEALED_PUB_KEY_FETCH_SUCCESS = 'REVEALED_PUB_KEY_FETCH_SUCCESS'; +export const REVEALED_PUB_KEY_FETCH_ERROR = 'REVEALED_PUB_KEY_FETCH_ERROR'; + export const DISCONNECT_SET = 'DISCONNECT_SET'; diff --git a/src/constants/url.js b/src/constants/url.js index 5d8e896..d3da034 100644 --- a/src/constants/url.js +++ b/src/constants/url.js @@ -8,8 +8,9 @@ export const urlFetchBalance = (address) => `${REST_URL}/cosmos/bank/v1beta1/bal export const urlFetchVestingBalance = (address) => `${REST_URL}/cosmos/auth/v1beta1/accounts/${address}`; export const urlFetchUnBondingDelegations = (address) => `${REST_URL}/cosmos/staking/v1beta1/delegators/${address}/unbonding_delegations`; -export const urlFetchRewards = (address) => `${REST_URL}/cosmos/distribution/v1beta1/delegators/${address}/rewards`; +export const urlFetchRewards = (address) => `${REST_URL}/api/v1/pos/reward/${address}`; export const urlFetchVoteDetails = (proposalId, address) => `${REST_URL}/cosmos/gov/v1beta1/proposals/${proposalId}/votes/${address}`; +export const urlFetchRevealedPubkey = (address) => `${REST_URL}/api/v1/revealed-public-key/${address}`; export const VALIDATORS_LIST_URL = () => `${REST_URL}/api/v1/pos/validator/all`; export const GENESIS_VALIDATORS_LIST_URL = 'https://namada.info/shielded-expedition.88f17d1d14/output/genesis_tm_address_to_alias.json'; diff --git a/src/containers/Home/ClaimDialog/ValidatorsSelectField.js b/src/containers/Home/ClaimDialog/ValidatorsSelectField.js index 2688f3a..ccbd610 100644 --- a/src/containers/Home/ClaimDialog/ValidatorsSelectField.js +++ b/src/containers/Home/ClaimDialog/ValidatorsSelectField.js @@ -20,12 +20,9 @@ const ValidatorSelectField = (props) => { let total = 0; - const totalRewards = props.rewards && props.rewards.rewards && - props.rewards.rewards.length && - props.rewards.rewards.map((value) => { - let rewards = value.reward && value.reward.length && - value.reward.find((val) => val.denom === config.COIN_MINIMAL_DENOM); - rewards = rewards && rewards.amount ? rewards.amount / 10 ** config.COIN_DECIMALS : 0; + const totalRewards = props.rewards && props.rewards.length && + props.rewards.map((value) => { + const rewards = value && value.minDenomAmount ? value.minDenomAmount / 10 ** config.COIN_DECIMALS : 0; total = rewards + total; return total; @@ -34,19 +31,17 @@ const ValidatorSelectField = (props) => { return ( Select the validator - {props.rewards && props.rewards.rewards && - props.rewards.rewards.length && - props.rewards.rewards.map((item, index) => { - const validator = item && item.validator_address && props.validatorList && props.validatorList.length && - props.validatorList.filter((value) => value.operator_address === item.validator_address); - + {props.rewards && props.rewards.length && + props.rewards.map((item, index) => { + const validator = item && item.validator; + const rewards = item && item.minDenomAmount ? item.minDenomAmount / 10 ** config.COIN_DECIMALS : 0; const image = validator && validator.length && validator[0] && validator[0].description && validator[0].description.identity && props.validatorImages && props.validatorImages.length && @@ -54,8 +49,8 @@ const ValidatorSelectField = (props) => { return ( + key={validator.address} + value={validator.address}> {image && image.length && image[0] && image[0].them && image[0].them.length && image[0].them[0] && image[0].them[0].pictures && image[0].them[0].pictures.primary && image[0].them[0].pictures.primary.url @@ -65,22 +60,12 @@ const ValidatorSelectField = (props) => { className="image" src={image[0].them[0].pictures.primary.url}/> : } - {props.validatorList && props.validatorList.map((value) => { - let rewards = item.reward && item.reward.length && - item.reward.find((val) => val.denom === config.COIN_MINIMAL_DENOM); - rewards = rewards && rewards.amount ? rewards.amount / 10 ** config.COIN_DECIMALS : 0; - - if (value.operator_address === item.validator_address) { - return - {value.description && value.description.moniker} - {rewards && rewards > 0 - ?  ({rewards.toFixed(4)}) - : null} - ; - } - - return null; - })} + + {validator.name || validator.address} + {rewards && rewards > 0 + ?  ({rewards.toFixed(4)}) + : null} + ); }, diff --git a/src/containers/Home/ClaimDialog/index.js b/src/containers/Home/ClaimDialog/index.js index fbb34bd..3569bff 100644 --- a/src/containers/Home/ClaimDialog/index.js +++ b/src/containers/Home/ClaimDialog/index.js @@ -11,13 +11,14 @@ import { import { connect } from 'react-redux'; import '../../Stake/DelegateDialog/index.css'; import ValidatorsSelectField from './ValidatorsSelectField'; -import { cosmoStationSign, signTxAndBroadcast } from '../../../helper'; +import { claimTransaction, cosmoStationSign, signTxAndBroadcast } from '../../../helper'; import { showMessage } from '../../../actions/snackbar'; import { fetchRewards, fetchVestingBalance, getBalance } from '../../../actions/accounts'; import { config } from '../../../config'; import variables from '../../../utils/variables'; import CircularProgress from '../../../components/CircularProgress'; import { gas } from '../../../defaultGasValues'; +import BigNumber from 'bignumber.js'; const ClaimDialog = (props) => { const [inProgress, setInProgress] = useState(false); @@ -77,60 +78,44 @@ const ClaimDialog = (props) => { } if (result) { props.setTokens(tokens); - props.successDialog(result.transactionHash); + props.successDialog(result.hash); props.fetchRewards(props.address); props.getBalance(props.address); - props.fetchVestingBalance(props.address); + // props.fetchVestingBalance(props.address); } }; const handleClaim = () => { setInProgress(true); - const updatedTx = { - msg: { - typeUrl: '/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward', - value: { - delegatorAddress: props.address, - validatorAddress: props.value, - amount: { - denom: config.COIN_MINIMAL_DENOM, - }, - }, - }, - fee: { - amount: [{ - amount: String(gas.claim_reward * config.GAS_PRICE_STEP_AVERAGE), - denom: config.COIN_MINIMAL_DENOM, - }], - gas: String(gas.claim_reward), - }, - memo: '', + + const tx = { + source: props.address, + validator: props.value, }; - if (localStorage.getItem('of_co_wallet') === 'cosmostation') { - cosmoStationSign(updatedTx, props.address, handleFetch); - return; - } + const txs = { + token: config.TOKEN_ADDRESS, + feeAmount: new BigNumber(0.000010), + gasLimit: new BigNumber(50000), + chainId: config.CHAIN_ID, + publicKey: props.details && props.details.publicKey, + }; - signTxAndBroadcast(updatedTx, props.address, handleFetch); + claimTransaction(tx, txs, props.details && props.details.type, handleFetch); }; - const rewards = props.rewards && props.rewards.rewards && - props.rewards.rewards.length && - props.rewards.rewards.filter((value) => value.validator_address === props.value); + const rewards = props.rewards && props.rewards.length && + props.rewards.find((value) => value.validator && value.validator.address === props.value); - let tokens = rewards && rewards.length && rewards[0] && rewards[0].reward && - rewards[0].reward.length && rewards[0].reward.find((val) => val.denom === config.COIN_MINIMAL_DENOM); - tokens = tokens && tokens.amount ? tokens.amount / 10 ** config.COIN_DECIMALS : 0; + let tokens = rewards && rewards.minDenomAmount; + tokens = tokens ? tokens / 10 ** config.COIN_DECIMALS : 0; - if (props.value === 'all' && props.rewards && props.rewards.rewards && - props.rewards.rewards.length) { + if (props.value === 'all' && props.rewards && props.rewards.length) { let total = 0; - props.rewards.rewards.map((value) => { - let rewards = value.reward && value.reward.length && - value.reward.find((val) => val.denom === config.COIN_MINIMAL_DENOM); - rewards = rewards && rewards.amount ? rewards.amount / 10 ** config.COIN_DECIMALS : 0; + props.rewards.map((value) => { + let rewards = value.minDenomAmount; + rewards = rewards ? rewards / 10 ** config.COIN_DECIMALS : 0; total = rewards + total; return total; @@ -172,6 +157,7 @@ const ClaimDialog = (props) => { }; ClaimDialog.propTypes = { + details: PropTypes.object.isRequired, failedDialog: PropTypes.func.isRequired, fetchRewards: PropTypes.func.isRequired, fetchVestingBalance: PropTypes.func.isRequired, @@ -198,6 +184,7 @@ const stateToProps = (state) => { open: state.stake.claimDialog.open, value: state.stake.claimDialog.validator, rewards: state.accounts.rewards.result, + details: state.accounts.address.details, }; }; diff --git a/src/containers/Home/TokenDetails/index.css b/src/containers/Home/TokenDetails/index.css index a1cdd16..5412779 100644 --- a/src/containers/Home/TokenDetails/index.css +++ b/src/containers/Home/TokenDetails/index.css @@ -86,7 +86,7 @@ flex-shrink: 0; .token_details { margin: 40px 0 0; width: 100%; - /* justify-content: space-around; */ + justify-content: space-around; flex-wrap: wrap; } diff --git a/src/containers/Home/TokenDetails/index.js b/src/containers/Home/TokenDetails/index.js index 6dffbdd..b5fed0c 100644 --- a/src/containers/Home/TokenDetails/index.js +++ b/src/containers/Home/TokenDetails/index.js @@ -5,12 +5,12 @@ import variables from '../../../utils/variables'; import totalTokens from '../../../assets/userDetails/totalTokens.png'; import stakedTokens from '../../../assets/userDetails/stakedTokens.png'; // import unStake from '../../../assets/userDetails/unstake.png'; -// import rewardsIcon from '../../../assets/userDetails/rewards.svg'; +import rewardsIcon from '../../../assets/userDetails/rewards.svg'; import { connect } from 'react-redux'; import StakeTokensButton from './StakeTokensButton'; import UnDelegateButton from './UnDelegateButton'; import ReDelegateButton from './ReDelegateButton'; -// import ClaimButton from './ClaimButton'; +import ClaimButton from './ClaimButton'; // import Compound from './Compound'; import { config } from '../../../config'; // import { gas } from '../../../defaultGasValues'; @@ -54,13 +54,13 @@ const TokenDetails = (props) => { }); // const gasValue = (gas.claim_reward + gas.delegate) * config.GAS_PRICE_STEP_AVERAGE; - /* let rewards = props.rewards && props.rewards.total && props.rewards.total.length && - props.rewards.total.find((val) => val.denom === config.COIN_MINIMAL_DENOM); - rewards = rewards && rewards.amount ? rewards.amount / 10 ** config.COIN_DECIMALS : 0; */ - // let tokens = props.rewards && props.rewards.total && props.rewards.total.length && - // props.rewards.total.find((val) => val.amount > gasValue); - // tokens = tokens && tokens.amount ? tokens.amount / 10 ** config.COIN_DECIMALS : 0; - + let rewards = props.rewards && props.rewards.length && + props.rewards.reduce((accumulator, currentValue) => { + if (currentValue && currentValue.minDenomAmount) { + return accumulator + Number(currentValue.minDenomAmount); + } + }, 0); + rewards = rewards ? rewards / 10 ** config.COIN_DECIMALS : 0; return (
@@ -83,18 +83,18 @@ const TokenDetails = (props) => {
- {/*
*/} - {/*

{variables[props.lang].rewards}

*/} - {/*
*/} - {/* total tokens */} - {/*

{rewards > 0 ? rewards.toFixed(4) : 0}

*/} - {/*
*/} - {/*
*/} - {/* */} - {/* /!* *!/ */} - {/* /!* *!/ */} - {/*
*/} - {/*
*/} +
+

{variables[props.lang].rewards}

+
+ total tokens +

{rewards > 0 ? rewards.toFixed(4) : 0}

+
+
+ {/* */} + {/* /!* *!/ */} + {/* /!* *!/ */} +
+
{/*
*/} {/*

{variables[props.lang]['un_staked_tokens']}

*/} {/*
*/} @@ -109,8 +109,8 @@ const TokenDetails = (props) => { TokenDetails.propTypes = { balance: PropTypes.array.isRequired, balanceInProgress: PropTypes.bool.isRequired, - delegations: PropTypes.array.isRequired, delegatedValidatorList: PropTypes.array.isRequired, + delegations: PropTypes.array.isRequired, delegationsInProgress: PropTypes.bool.isRequired, lang: PropTypes.string.isRequired, rewards: PropTypes.shape({ diff --git a/src/containers/Home/index.js b/src/containers/Home/index.js index b7a1b73..73424c4 100644 --- a/src/containers/Home/index.js +++ b/src/containers/Home/index.js @@ -89,12 +89,12 @@ class Home extends Component { NAM Staking & Governance

- {/* developed by OmniFlix for Cosmic Validator + {/* developed by OmniFlix for Cosmic Validator supported with a grant by Mandragora */} {/* {variables[this.props.lang].participate} */} developed by OmniFlix Network in association with -

- cv +

+ cv
{/* */} diff --git a/src/containers/NavBar/ConnectDialog/NamadaConnectButton.js b/src/containers/NavBar/ConnectDialog/NamadaConnectButton.js index 53fbf17..13a5363 100644 --- a/src/containers/NavBar/ConnectDialog/NamadaConnectButton.js +++ b/src/containers/NavBar/ConnectDialog/NamadaConnectButton.js @@ -33,8 +33,8 @@ const KeplrConnectButton = (props) => { return; } - props.setAccountAddress(addressList[0] && addressList[0].address); - props.setAccountDetails(addressList[0]); + props.setAccountAddress(addressList && addressList.address); + props.setAccountDetails(addressList); props.hideConnectDialog(); // if (!props.proposalTab && !props.stake) { // props.getUnBondingDelegations(addressList[0] && addressList[0].address); diff --git a/src/containers/NavBar/index.js b/src/containers/NavBar/index.js index 8366907..0db6bc1 100644 --- a/src/containers/NavBar/index.js +++ b/src/containers/NavBar/index.js @@ -14,6 +14,7 @@ import { decode, encode } from 'js-base64'; import { config, DEFAULT_PAGE } from '../../config'; import { showMessage } from '../../actions/snackbar'; import { + fetchRevealedPubKey, fetchRewards, fetchVestingBalance, getBalance, @@ -223,6 +224,7 @@ class NavBar extends Component { this.props.getDelegations(this.props.address); } }); + this.props.fetchRevealedPubKey(this.props.address); this.props.fetchVestingBalance(this.props.address); this.props.fetchRewards(this.props.address); this.props.getUnBondingDelegations(this.props.address); @@ -308,11 +310,11 @@ class NavBar extends Component { // !this.props.vestingBalanceInProgress) { // this.props.fetchVestingBalance(address); // } - // - // if (!this.props.proposalTab && !this.props.stake) { - // this.props.fetchRewards(address); - // } - // + + if (!this.props.proposalTab && !this.props.stake) { + this.props.fetchRewards(address); + } + this.props.fetchRevealedPubKey(address); // if (this.props.unBondingDelegations && !this.props.unBondingDelegations.length && // !this.props.unBondingDelegationsInProgress && !this.props.proposalTab && !this.props.stake) { // this.props.getUnBondingDelegations(address); @@ -343,12 +345,12 @@ class NavBar extends Component { const previousAddress = localStorage.getItem('of_co_address') && decode(localStorage.getItem('of_co_address')); - this.props.setAccountAddress(addressList[0] && addressList[0].address); + this.props.setAccountAddress(addressList && addressList.address); if (fetch) { - this.handleFetch(addressList[0] && addressList[0].address); + this.handleFetch(addressList && addressList.address); } - if (addressList[0] && previousAddress !== addressList[0].address) { - localStorage.setItem('of_co_address', encode(addressList[0] && addressList[0].address)); + if (addressList && previousAddress !== addressList.address) { + localStorage.setItem('of_co_address', encode(addressList && addressList.address)); } }); } @@ -369,13 +371,13 @@ class NavBar extends Component { const previousAddress = localStorage.getItem('of_co_address') && decode(localStorage.getItem('of_co_address')); - this.props.setAccountAddress(addressList[0] && addressList[0].address); - this.props.setAccountDetails(addressList[0]); + this.props.setAccountAddress(addressList && addressList.address); + this.props.setAccountDetails(addressList); if (fetch) { - this.handleFetch(addressList[0] && addressList[0].address); + this.handleFetch(addressList && addressList.address); } - if (addressList[0] && previousAddress !== addressList[0].address) { - localStorage.setItem('of_co_address', encode(addressList[0] && addressList[0].address)); + if (addressList && previousAddress !== addressList.address) { + localStorage.setItem('of_co_address', encode(addressList && addressList.address)); } }); } @@ -453,6 +455,7 @@ NavBar.propTypes = { fetchGenesisValidators: PropTypes.func.isRequired, fetchProposalDetails: PropTypes.func.isRequired, fetchProposalTally: PropTypes.func.isRequired, + fetchRevealedPubKey: PropTypes.func.isRequired, fetchRewards: PropTypes.func.isRequired, fetchValidatorImage: PropTypes.func.isRequired, fetchValidatorImageSuccess: PropTypes.func.isRequired, @@ -569,6 +572,7 @@ const actionToProps = { fetchVoteDetails, fetchProposalTally, fetchProposalDetails, + fetchRevealedPubKey, getInActiveValidators, showConnectDialog, setAccountDetails, diff --git a/src/containers/Stake/DelegateDialog/index.js b/src/containers/Stake/DelegateDialog/index.js index 943971d..dd775be 100644 --- a/src/containers/Stake/DelegateDialog/index.js +++ b/src/containers/Stake/DelegateDialog/index.js @@ -96,7 +96,7 @@ const DelegateDialog = (props) => { txs.gasLimit = new BigNumber(100000); reDelegateTransaction(tx, txs, props.details && props.details.type, handleFetch); } else { - delegateTransaction(tx, txs, props.details && props.details.type, handleFetch); + delegateTransaction(tx, txs, props.revealPublicKey, handleFetch); } }; @@ -371,6 +371,7 @@ DelegateDialog.propTypes = { vestingBalance: PropTypes.object.isRequired, address: PropTypes.string, amount: PropTypes.any, + revealPublicKey: PropTypes.object, toValidator: PropTypes.string, validator: PropTypes.string, }; @@ -386,6 +387,7 @@ const stateToProps = (state) => { amount: state.stake.tokens, validator: state.stake.validator.value, vestingBalance: state.accounts.vestingBalance.result, + revealPublicKey: state.accounts.revealPublicKey.result, toValidator: state.stake.toValidator.value, selectedMultiValidatorArray: state.stake.selectMultiValidators.list, details: state.accounts.address.details, diff --git a/src/containers/Stake/Table.js b/src/containers/Stake/Table.js index 1fce9e5..4a64223 100644 --- a/src/containers/Stake/Table.js +++ b/src/containers/Stake/Table.js @@ -12,6 +12,7 @@ import ValidatorName from './ValidatorName'; import { config } from '../../config'; import { Button } from '@material-ui/core'; import { showConnectDialog } from '../../actions/navBar'; +import { randomNoRepeats } from 'utils/array'; class Table extends Component { render () { @@ -22,10 +23,10 @@ class Table extends Component { pagination: false, selectableRows: 'none', selectToolbarPlacement: 'none', - sortOrder: { - name: 'voting_power', - direction: 'desc', - }, + // sortOrder: { + // name: 'voting_power', + // direction: 'desc', + // }, textLabels: { body: { noMatch: this.props.inProgress @@ -127,7 +128,7 @@ class Table extends Component { if (value && value.validator && value.validator.address && item && (item.address === value.validator.address)) { address = value.validator.address; - newValue = value.minDenomAmount && value.minDenomAmount / 10 ** config.COIN_DECIMALS;; + newValue = value.minDenomAmount && value.minDenomAmount / 10 ** config.COIN_DECIMALS; } }); // address && this.props.delegations && this.props.delegations.length && @@ -194,8 +195,28 @@ class Table extends Component { }); } - const tableData = dataToMap && dataToMap.length - ? dataToMap.map((item) => + let newData = []; + if (dataToMap && dataToMap.length) { + dataToMap.map((val) => { + if (val && val.name && val.name.toLowerCase() === 'cosmic validator') { + newData.splice(0, 0, val); + } else if (val && val.name && val.name.toLowerCase() === 'mandragora') { + const find = newData.find((val1) => val1 && val1.name && val1.name.toLowerCase() === 'cosmic validator'); + if (!find) { + newData.splice(0, 0, val); + } else { + newData.splice(1, 0, val); + } + } else { + newData.push(val); + } + }); + } else { + newData = dataToMap; + } + + const tableData = newData && newData.length + ? newData.map((item) => [ // item.description && item.description.moniker, item.name, diff --git a/src/containers/Stake/ValidatorName.js b/src/containers/Stake/ValidatorName.js index bbaeb9b..80ed303 100644 --- a/src/containers/Stake/ValidatorName.js +++ b/src/containers/Stake/ValidatorName.js @@ -16,13 +16,11 @@ const ValidatorName = (props) => { return (
- {image && image.length && image[0] && image[0].them && image[0].them.length && - image[0].them[0] && image[0].them[0].pictures && image[0].them[0].pictures.primary && - image[0].them[0].pictures.primary.url + {props.value && props.value.avatar ? {props.value.description + src={props.value.avatar}/> : props.value.description && props.value.description.moniker ?
{ const offlineSigner = namada.getSigner(chainId); let accounts; if (offlineSigner.accounts) { - accounts = await offlineSigner.accounts(); + accounts = await offlineSigner.defaultAccount(); } else { - accounts = await namada.accounts(); + accounts = await namada.defaultAccount(); } cb(null, accounts); } else { @@ -284,7 +285,7 @@ export const initializeNamadaChain = (cb) => { // })(); // }; -export const delegateTransaction = (Tx, txs, type, cb) => { +export const delegateTransaction = (Tx, txs, revealPublicKey, cb) => { (async () => { const isExtensionInstalled = typeof window.namada === 'object'; if (!isExtensionInstalled || !window.namada) { @@ -298,24 +299,24 @@ export const delegateTransaction = (Tx, txs, type, cb) => { const { cryptoMemory } = await init(); const sdk = getSdk( - cryptoMemory, - config.RPC_URL, - config.MAPS_REST_URL, - "", - config.TOKEN_ADDRESS + cryptoMemory, + config.RPC_URL, + config.MAPS_REST_URL, + '', + config.TOKEN_ADDRESS, ); const { rpc, tx } = sdk; - let checksums = await rpc.queryChecksums(); + const checksums = await rpc.queryChecksums(); if (checksums && Object.keys(checksums).length) { Object.keys(checksums).map((key) => { if (key && checksums[key]) { checksums[key] = checksums[key].toLowerCase(); } - }) + }); } - + const bondMsgValue = new BondMsgValue({ source: Tx.source, validator: Tx.validator, @@ -328,12 +329,14 @@ export const delegateTransaction = (Tx, txs, type, cb) => { gasLimit: txs.gasLimit, chainId: txs.chainId, publicKey: txs.publicKey, - memo: "", + memo: '', }; const newTxs = []; - const revealPkTx = await tx.buildRevealPk(wrapperProps); - newTxs.push(revealPkTx); + if (revealPublicKey && !revealPublicKey.publicKey) { + const revealPkTx = await tx.buildRevealPk(wrapperProps); + newTxs.push(revealPkTx); + } const wrapperTxValue = new WrapperTxMsgValue(wrapperProps); const encoded = await tx.buildBond(wrapperTxValue, bondMsgValue); newTxs.push(encoded); @@ -401,24 +404,24 @@ export const unDelegateTransaction = (Tx, txs, type, cb) => { const { cryptoMemory } = await init(); const sdk = getSdk( - cryptoMemory, - config.RPC_URL, - config.MAPS_REST_URL, - "", - config.TOKEN_ADDRESS + cryptoMemory, + config.RPC_URL, + config.MAPS_REST_URL, + '', + config.TOKEN_ADDRESS, ); const { rpc, tx } = sdk; - let checksums = await rpc.queryChecksums(); + const checksums = await rpc.queryChecksums(); if (checksums && Object.keys(checksums).length) { Object.keys(checksums).map((key) => { if (key && checksums[key]) { checksums[key] = checksums[key].toLowerCase(); } - }) + }); } - + const bondMsgValue = new UnbondMsgValue({ source: Tx.source, validator: Tx.validator, @@ -431,7 +434,7 @@ export const unDelegateTransaction = (Tx, txs, type, cb) => { gasLimit: txs.gasLimit, chainId: txs.chainId, publicKey: txs.publicKey, - memo: "", + memo: '', }; const newTxs = []; @@ -486,22 +489,22 @@ export const reDelegateTransaction = (Tx, txs, type, cb) => { const { cryptoMemory } = await init(); const sdk = getSdk( - cryptoMemory, - config.RPC_URL, - config.MAPS_REST_URL, - "", - config.TOKEN_ADDRESS + cryptoMemory, + config.RPC_URL, + config.MAPS_REST_URL, + '', + config.TOKEN_ADDRESS, ); const { rpc, tx } = sdk; - let checksums = await rpc.queryChecksums(); + const checksums = await rpc.queryChecksums(); if (checksums && Object.keys(checksums).length) { Object.keys(checksums).map((key) => { if (key && checksums[key]) { checksums[key] = checksums[key].toLowerCase(); } - }) + }); } const bondMsgValue = new RedelegateMsgValue({ @@ -517,7 +520,7 @@ export const reDelegateTransaction = (Tx, txs, type, cb) => { gasLimit: txs.gasLimit, chainId: txs.chainId, publicKey: txs.publicKey, - memo: "", + memo: '', }; const newTxs = []; @@ -557,3 +560,98 @@ export const reDelegateTransaction = (Tx, txs, type, cb) => { } })(); }; + +export const claimTransaction = (Tx, txs, type, cb) => { + (async () => { + const isExtensionInstalled = typeof window.namada === 'object'; + if (!isExtensionInstalled || !window.namada) { + const error = 'Download the Namada Extension'; + cb(error); + } + + if (window.namada) { + const namada = window.namada; + const client = namada.getSigner(); + + const { cryptoMemory } = await init(); + const sdk = getSdk( + cryptoMemory, + config.RPC_URL, + config.MAPS_REST_URL, + '', + config.TOKEN_ADDRESS, + ); + + const { rpc, tx } = sdk; + + const checksums = await rpc.queryChecksums(); + if (checksums && Object.keys(checksums).length) { + Object.keys(checksums).map((key) => { + if (key && checksums[key]) { + checksums[key] = checksums[key].toLowerCase(); + } + }); + } + + const wrapperProps = { + token: txs.token, + feeAmount: txs.feeAmount, + gasLimit: txs.gasLimit, + chainId: txs.chainId, + publicKey: txs.publicKey, + memo: '', + }; + + const newTxs = []; + const wrapperTxValue = new WrapperTxMsgValue(wrapperProps); + if (Tx && Tx.length > 1) { + Tx.map((newTx) => { + (async () => { + const bondMsgValue = new ClaimRewardsMsgValue({ + validator: newTx.validator, + }); + const encoded = await tx.buildClaimRewards(wrapperTxValue, bondMsgValue); + newTxs.push(encoded); + })(); + }); + } else { + const bondMsgValue = new ClaimRewardsMsgValue({ + // source: Tx.source, + validator: Tx.validator, + }); + const encoded = await tx.buildClaimRewards(wrapperTxValue, bondMsgValue); + newTxs.push(encoded); + } + + const updateDate = tx.buildBatch(newTxs); + + client.sign(updateDate, Tx.source, checksums).then((signedBondTxBytes) => { + rpc.broadcastTx(signedBondTxBytes && signedBondTxBytes.length && signedBondTxBytes[0], wrapperProps).then((result) => { + if (result && result.code !== undefined && result.code !== 0 && result.code !== '0') { + cb(result.info || result.log || result.rawLog); + } else { + cb(null, result); + } + }).catch((error) => { + console.error(`broadcast error: ${error}`); + const message = 'success'; + if (error && error.message === 'Invalid string. Length must be a multiple of 4') { + cb(null, message); + } else { + cb(error && error.message); + } + }); + }).catch((error) => { + console.error(`Transaction was rejected: ${error}`); + const message = 'success'; + if (error && error.message === 'Invalid string. Length must be a multiple of 4') { + cb(null, message); + } else { + cb(error && error.message); + } + }); + } else { + return null; + } + })(); +}; diff --git a/src/reducers/accounts/index.js b/src/reducers/accounts/index.js index 6dd2fe5..c1cfe08 100644 --- a/src/reducers/accounts/index.js +++ b/src/reducers/accounts/index.js @@ -9,6 +9,9 @@ import { DELEGATIONS_FETCH_IN_PROGRESS, DELEGATIONS_FETCH_SUCCESS, DISCONNECT_SET, + REVEALED_PUB_KEY_FETCH_ERROR, + REVEALED_PUB_KEY_FETCH_IN_PROGRESS, + REVEALED_PUB_KEY_FETCH_SUCCESS, REWARDS_FETCH_ERROR, REWARDS_FETCH_IN_PROGRESS, REWARDS_FETCH_SUCCESS, @@ -223,6 +226,37 @@ const rewards = (state = { } }; +const revealPublicKey = (state = { + result: {}, + inProgress: false, +}, action) => { + switch (action.type) { + case REVEALED_PUB_KEY_FETCH_IN_PROGRESS: + return { + ...state, + inProgress: true, + }; + case REVEALED_PUB_KEY_FETCH_SUCCESS: + return { + ...state, + inProgress: false, + result: action.value, + }; + case REVEALED_PUB_KEY_FETCH_ERROR: + return { + ...state, + inProgress: false, + }; + case DISCONNECT_SET: + return { + ...state, + result: {}, + }; + default: + return state; + } +}; + export default combineReducers({ address, delegations, @@ -232,4 +266,5 @@ export default combineReducers({ unBondingDelegations, stakeAccountAddress, rewards, + revealPublicKey, }); diff --git a/src/utils/array.js b/src/utils/array.js new file mode 100644 index 0000000..7886d01 --- /dev/null +++ b/src/utils/array.js @@ -0,0 +1,17 @@ +export const randomNoRepeats = (array) => { + for (let i = array.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [array[i], array[j]] = [array[j], array[i]]; + } + + return array; +}; + +export const getRandomElement = (arr) => { + if (!Array.isArray(arr) || arr.length === 0) { + return arr; + } + + const randomIndex = Math.floor(Math.random() * arr.length); + return arr[randomIndex]; +};