Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi delegate prod updates #96

Merged
merged 5 commits into from
Aug 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/actions/stake.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import {
CLAIM_REWARDS_DIALOG_HIDE,
CLAIM_REWARDS_DIALOG_SHOW,
CLAIM_REWARDS_VALIDATOR_SET,
CLAIM_DELEGATE_DIALOG_SHOW,
CLAIM_DELEGATE_DIALOG_HIDE,
CLAIM_DELEGATE_VALIDATOR_SET,
DELEGATE_DIALOG_HIDE,
DELEGATE_DIALOG_SHOW,
DELEGATE_FAILED_DIALOG_HIDE,
Expand Down Expand Up @@ -32,6 +35,7 @@ import {
VALIDATORS_FETCH_ERROR,
VALIDATORS_FETCH_IN_PROGRESS,
VALIDATORS_FETCH_SUCCESS,
SELECTED_MULTI_VALIDATORS,
} from '../constants/stake';
import Axios from 'axios';
import {
Expand Down Expand Up @@ -274,6 +278,25 @@ export const setClaimRewardsValidator = (value) => {
};
};

export const showClaimDelegateDialog = () => {
return {
type: CLAIM_DELEGATE_DIALOG_SHOW,
};
};

export const hideClaimDelegateDialog = () => {
return {
type: CLAIM_DELEGATE_DIALOG_HIDE,
};
};

export const setClaimDelegateValidator = (value) => {
return {
type: CLAIM_DELEGATE_VALIDATOR_SET,
value,
};
};

const fetchValidatorImageInProgress = () => {
return {
type: VALIDATOR_IMAGE_FETCH_IN_PROGRESS,
Expand Down Expand Up @@ -428,3 +451,10 @@ export const fetchAPR = () => (dispatch) => {
}
})();
};

export const selectMultiValidators = (value) => {
return {
type: SELECTED_MULTI_VALIDATORS,
value,
};
};
30 changes: 30 additions & 0 deletions src/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,33 @@ html::-webkit-scrollbar-thumb,
margin: 10px;
}
}

@keyframes fadeInAnimation {
from {
opacity: 0;
}

to {
opacity: 1;
}
}

.stake .stake_content,
.proposals .proposals_content,
.home .card {
animation: fadeInAnimation ease-in-out 1s;
}

@keyframes dialogOpen {
from {
transform: scale(0.5);
}

to {
transform: scale(1);
}
}

.dialog {
animation: dialogOpen 0.1s;
}
7 changes: 6 additions & 1 deletion src/constants/stake.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@ export const DELEGATED_VALIDATORS_FETCH_ERROR = 'DELEGATED_VALIDATORS_FETCH_ERRO

export const CLAIM_REWARDS_DIALOG_SHOW = 'CLAIM_REWARDS_DIALOG_SHOW';
export const CLAIM_REWARDS_DIALOG_HIDE = 'CLAIM_REWARDS_DIALOG_HIDE';

export const CLAIM_REWARDS_VALIDATOR_SET = 'CLAIM_REWARDS_VALIDATOR_SET';

export const CLAIM_DELEGATE_DIALOG_SHOW = 'CLAIM_DELEGATE_DIALOG_SHOW';
export const CLAIM_DELEGATE_DIALOG_HIDE = 'CLAIM_DELEGATE_DIALOG_HIDE';
export const CLAIM_DELEGATE_VALIDATOR_SET = 'CLAIM_DELEGATE_VALIDATOR_SET';

export const VALIDATOR_IMAGE_FETCH_IN_PROGRESS = 'VALIDATOR_IMAGE_FETCH_IN_PROGRESS';
export const VALIDATOR_IMAGE_FETCH_SUCCESS = 'VALIDATOR_IMAGE_FETCH_SUCCESS';
export const VALIDATOR_IMAGE_FETCH_ERROR = 'VALIDATOR_IMAGE_FETCH_ERROR';
Expand All @@ -45,3 +48,5 @@ export const INACTIVE_VALIDATORS_FETCH_ERROR = 'INACTIVE_VALIDATORS_FETCH_ERROR'
export const APR_FETCH_IN_PROGRESS = 'APR_FETCH_IN_PROGRESS';
export const APR_FETCH_SUCCESS = 'APR_FETCH_SUCCESS';
export const APR_FETCH_ERROR = 'APR_FETCH_ERROR';

export const SELECTED_MULTI_VALIDATORS = 'SELECTED_MULTI_VALIDATORS';
248 changes: 248 additions & 0 deletions src/containers/Home/ClaimDialog/ClaimDelegateDialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
import React, { useState } from 'react';
import { Button, Dialog, DialogActions, DialogContent } from '@material-ui/core';
import * as PropTypes from 'prop-types';
import {
hideClaimDelegateDialog,
setTokens,
showDelegateFailedDialog,
showDelegateProcessingDialog,
showDelegateSuccessDialog,
} from '../../../actions/stake';
import { connect } from 'react-redux';
import '../../Stake/DelegateDialog/index.css';
import { 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 ClaimDelegateValidatorsSelectField from './ClaimDelegateValidatorSelectField';

const ClaimDelegateDialog = (props) => {
const [inProgress, setInProgress] = useState(false);

const handleClaimAll = () => {
setInProgress(true);
let gasValue = gas.claim_reward + gas.delegate;
let count = 0;
if (props.rewards && props.rewards.rewards && props.rewards.rewards.length > 1) {
props.rewards.rewards.map((item) => {
const tokens = item && item.reward && item.reward.length &&
item.reward.filter((val) => val.amount > gasValue * config.GAS_PRICE_STEP_AVERAGE);
if (tokens) {
count += tokens.length;
}
return null;
});
}
if (count) {
gasValue = count * gasValue / 1.1 + gasValue;
}

const updatedTx = {
msgs: [],
fee: {
amount: [{
amount: String(gasValue * config.GAS_PRICE_STEP_AVERAGE),
denom: config.COIN_MINIMAL_DENOM,
}],
gas: String(gasValue),
},
memo: '',
};
if (props.rewards && props.rewards.rewards &&
props.rewards.rewards.length) {
props.rewards.rewards.map((item) => {
let tokens = item && item.reward && item.reward.length &&
item.reward.find((val) => val.denom === config.COIN_MINIMAL_DENOM);
tokens = tokens && tokens.amount;
if (tokens && tokens > ((gas.claim_reward + gas.delegate) * config.GAS_PRICE_STEP_AVERAGE)) {
updatedTx.msgs.push({
typeUrl: '/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward',
value: {
delegatorAddress: props.address,
validatorAddress: item.validator_address,
},
}, {
typeUrl: '/cosmos.staking.v1beta1.MsgDelegate',
value: {
delegatorAddress: props.address,
validatorAddress: item.validator_address,
amount: {
amount: String(Math.floor(Number(tokens))),
denom: config.COIN_MINIMAL_DENOM,
},
},
});
}
return null;
});
}

if (localStorage.getItem('of_co_wallet') === 'cosmostation') {
cosmoStationSign(updatedTx, props.address, handleFetch);
return;
}

signTxAndBroadcast(updatedTx, props.address, handleFetch);
};

const handleFetch = (error, result) => {
setInProgress(false);
if (error) {
if (error.indexOf('not yet found on the chain') > -1) {
props.pendingDialog();
return;
}
props.failedDialog();
props.showMessage(error);
return;
}
if (result) {
props.setTokens(tokens);
props.successDialog(result.transactionHash);
props.fetchRewards(props.address);
props.getBalance(props.address);
props.fetchVestingBalance(props.address);
}
};

const handleClaim = () => {
setInProgress(true);
const updatedTx = {
msgs: [{
typeUrl: '/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward',
value: {
delegatorAddress: props.address,
validatorAddress: props.value,
},
}, {
typeUrl: '/cosmos.staking.v1beta1.MsgDelegate',
value: {
delegatorAddress: props.address,
validatorAddress: props.value,
amount: {
amount: String(Math.floor(Number(delegateableTokesn))),
denom: config.COIN_MINIMAL_DENOM,
},
},
}],
fee: {
amount: [{
amount: String((gas.claim_reward + gas.delegate) * config.GAS_PRICE_STEP_AVERAGE),
denom: config.COIN_MINIMAL_DENOM,
}],
gas: String(gas.claim_reward + gas.delegate),
},
memo: '',
};

if (localStorage.getItem('of_co_wallet') === 'cosmostation') {
cosmoStationSign(updatedTx, props.address, handleFetch);
return;
}

signTxAndBroadcast(updatedTx, props.address, handleFetch);
};

const rewards = props.rewards && props.rewards.rewards &&
props.rewards.rewards.length &&
props.rewards.rewards.filter((value) => 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);
const delegateableTokesn = tokens && tokens.amount;
tokens = tokens && tokens.amount ? tokens.amount / 10 ** config.COIN_DECIMALS : 0;

if (props.value === 'all' && props.rewards && props.rewards.rewards &&
props.rewards.rewards.length) {
const gasValue = (gas.claim_reward + gas.delegate) * config.GAS_PRICE_STEP_AVERAGE;
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 > gasValue ? rewards.amount / 10 ** config.COIN_DECIMALS : 0;
total = rewards + total;

return total;
});
tokens = total;
}

const disable = props.value === 'none' || inProgress;

return (
<Dialog
aria-describedby="claim-dialog-description"
aria-labelledby="claim-dialog-title"
className="dialog delegate_dialog claim_dialog"
open={props.open}
onClose={props.handleClose}>
{inProgress && <CircularProgress className="full_screen"/>}
<DialogContent className="content">
<h1>Claim and Delegate Rewards</h1>
<p>Select validator</p>
<ClaimDelegateValidatorsSelectField/>
{tokens && tokens > 0
? <p>rewards: {tokens.toFixed(4)}</p>
: null}
</DialogContent>
<DialogActions className="footer">
<Button
disabled={disable}
variant="contained"
onClick={props.value === 'all' ? handleClaimAll : handleClaim}>
{inProgress
? variables[props.lang]['approval_pending']
: variables[props.lang].compound}
</Button>
</DialogActions>
</Dialog>
);
};

ClaimDelegateDialog.propTypes = {
failedDialog: PropTypes.func.isRequired,
fetchRewards: PropTypes.func.isRequired,
fetchVestingBalance: PropTypes.func.isRequired,
getBalance: PropTypes.func.isRequired,
handleClose: PropTypes.func.isRequired,
lang: PropTypes.string.isRequired,
open: PropTypes.bool.isRequired,
pendingDialog: PropTypes.func.isRequired,
rewards: PropTypes.shape({
rewards: PropTypes.array,
total: PropTypes.array,
}).isRequired,
setTokens: PropTypes.func.isRequired,
showMessage: PropTypes.func.isRequired,
successDialog: PropTypes.func.isRequired,
value: PropTypes.string.isRequired,
address: PropTypes.string,
};

const stateToProps = (state) => {
return {
address: state.accounts.address.value,
lang: state.language,
open: state.stake.claimDelegateDialog.open,
value: state.stake.claimDelegateDialog.validator,
rewards: state.accounts.rewards.result,
};
};

const actionToProps = {
handleClose: hideClaimDelegateDialog,
failedDialog: showDelegateFailedDialog,
successDialog: showDelegateSuccessDialog,
pendingDialog: showDelegateProcessingDialog,
getBalance,
fetchVestingBalance,
showMessage,
fetchRewards,
setTokens,
};

export default connect(stateToProps, actionToProps)(ClaimDelegateDialog);
Loading