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

feat: reinvest-feature (WIP) #1839

Draft
wants to merge 1 commit into
base: production
Choose a base branch
from
Draft
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
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -83,7 +83,7 @@
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7",
"@babel/plugin-proposal-optional-chaining": "^7.16.7",
"@babel/preset-typescript": "^7.16.7",
"@emeris/types": "^0.3.0",
"@emeris/types": "^0.3.1",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add the PR here

"@esbuild-plugins/node-globals-polyfill": "^0.1.1",
"@keplr-wallet/types": "0.8.8",
"@pinia/testing": "^0.0.10",
8 changes: 8 additions & 0 deletions src/actionhandler/index.ts
Original file line number Diff line number Diff line change
@@ -259,6 +259,14 @@ export async function actionHandler(action: Actions.UserAction): Promise<Array<A
transactions: [{ type: 'claim', status: 'pending', data: action.params }],
});
break;
case 'reinvest':
steps.push({
name: 'reinvest',
description: 'reinvest',
memo: '',
transactions: [{ type: 'reinvest', status: 'pending', data: action.params }],
});
break;
case 'unstake':
steps.push({
name: 'unstake',
33 changes: 33 additions & 0 deletions src/components/asset/StakeTableContents.vue
Original file line number Diff line number Diff line change
@@ -42,6 +42,36 @@
</div>
</td>
</tr>
<!-- claim and reinvest rewards -->
<tr
v-if="totalRewardsAmount && totalRewardsAmount > 1"
class="group cursor-pointer shadow-card hover:shadow-dropdown transition-shadow rounded-xl"
@click="goStakeActionPage(StakingActions.REINVEST)"
>
<td class="py-6 flex items-center rounded-l-xl bg-surface">
<div class="inline-flex items-center ml-6 mr-4">
<img src="@/assets/svg/icons/reward.svg" />
</div>
<span class="text-left overflow-hidden text-ellipsis whitespace-nowrap font-medium">
{{ $t('components.stakeTable.claimAndReinvest') }}
</span>
</td>
<td class="text-right text-muted bg-surface">{{ totalRewardsDisplayAmount }} <Ticker :name="denom" /></td>
<td class="text-right font-medium bg-surface">
<div class="flex justify-end">
+<Price :amount="{ denom: denom, amount: totalRewardsAmount + '' }" :show-dash="false" />
</div>
</td>
<td class="text-right rounded-r-xl bg-surface">
<div class="flex justify-end">
<Icon
name="CaretRightIcon"
:icon-size="1"
class="ml-1.5 mr-1 px-1 self-stretch text-muted group-hover:text-text transition-colors"
/>
</div>
</td>
</tr>

<!-- staked validators -->
<tr v-for="validator of stakingBalances" :key="validator.validator_address" data-cy="validator-row">
@@ -255,6 +285,9 @@ const goStakeActionPage = (action: string, valAddress = '') => {
case StakingActions.SWITCH:
router.push(`/staking/${props.denom}/${action}${validatorAddress ? `/${validatorAddress}` : ''}`);
return;
case StakingActions.REINVEST:
router.push(`/staking/${props.denom}/${StakingActions.REINVEST}`);
return;
default:
router.push(`/staking/${props.denom}/${StakingActions.CLAIM}`);
return;
91 changes: 91 additions & 0 deletions src/components/stake/ReinvestForm/ReinvestForm.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<template>
<div class="w-full mx-auto">
<template v-if="['review', 'stake'].includes(step)">
<TransactionProcessCreator
v-if="steps.length"
:steps="steps"
action="reinvest"
@pending="closeModal"
@close="closeModal"
@previous="emit('previous')"
/>
</template>
</div>
</template>
<script setup lang="ts">
import { EmerisAPI } from '@emeris/types';
import { computed, onMounted, ref, toRefs } from 'vue';
import { useRouter } from 'vue-router';
import { useStore } from 'vuex';

import { actionHandler } from '@/actionhandler';
import useChains from '@/composables/useChains';
import useStaking from '@/composables/useStaking';
import TransactionProcessCreator from '@/features/transactions/components/TransactionProcessCreator.vue';
import { GlobalGetterTypes, RootStoreTyped } from '@/store';
import { ReinvestAction } from '@/types/actions';
import { event } from '@/utils/analytics';

type Step = 'validator' | 'amount' | 'review' | 'stake';

interface Props {
step?: Step;
validators: EmerisAPI.Validator[];
}

const props = withDefaults(defineProps<Props>(), {
step: undefined,
validators: () => {
return [];
},
});

const emit = defineEmits<{
(e: 'update:step', value: any): void;
(e: 'previous'): void;
}>();

const steps = ref([]);
const store = useStore() as RootStoreTyped;
const router = useRouter();

const { getChainNameByBaseDenom } = useChains();
const { getStakingRewardsByBaseDenom, getValidatorMoniker } = useStaking();

const propsRef = toRefs(props);
const chain = computed(() => {
return store.getters[GlobalGetterTypes.API.getChain]({
chain_name: propsRef.validators.value[0].chain_name,
});
});
const baseDenom = chain.value?.denoms.find((x) => x.stakable).name;

const step = computed({
get: () => props.step,
set: (value) => emit('update:step', value),
});

const closeModal = () => {
router.push('/');
};
onMounted(async () => {
const rewardsData = (await getStakingRewardsByBaseDenom(baseDenom)) as any;
const chainName = await getChainNameByBaseDenom(baseDenom);
const rewardsDataWithMoniker = rewardsData.rewards.map((reward) => {
reward.moniker = getValidatorMoniker(reward.validator_address, propsRef.validators.value);
return reward;
});
const action = {
name: 'reinvest',
params: { total: rewardsData.total, rewards: rewardsDataWithMoniker, chainName },
} as ReinvestAction;
event('review_tx', { event_label: 'Reviewing reinvest tx', event_category: 'transactions' });
steps.value = await actionHandler(action);
});

if (!props.step) {
step.value = 'review';
}
</script>

<style lang="scss"></style>
3 changes: 3 additions & 0 deletions src/components/stake/ReinvestForm/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import ReinvestForm from './ReinvestForm.vue';

export default ReinvestForm;
108 changes: 108 additions & 0 deletions src/components/wizard/previews/PreviewReinvest.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<template>
<List>
<!-- Total rewards -->
<ListItem :label="$t(`components.previews.claim.totalRewards`)">
<div class="flex justify-end items-center">
<div class="text-right">
<AmountDisplay
class="font-medium"
:class="context === 'widget' ? 'text-0' : 'text-1'"
:amount="{ amount: rewardsAmount, denom: baseDenom }"
/>
<div class="block text-muted -text-1" :class="{ 'mt-0.5': context !== 'widget' }">
<Price :amount="{ denom: baseDenom, amount: rewardsAmount }" />
</div>
</div>
<CircleSymbol :denom="baseDenom" size="md" class="ml-3" />
</div>
</ListItem>

<!-- Validator list -->
<ListItem :label="$t(`components.previews.claim.validators`)">
<div
v-for="vali of validators"
:key="vali.validator_address"
class="flex justify-end items-center mb-4 last:mb-0"
>
<div class="text-right">
<div class="block text-muted -text-1" :class="{ 'mt-0.5': context !== 'widget' }">
<div class="text-text text-0">
{{ vali.moniker }}
</div>
<template v-if="validators.length > 1">
<AmountDisplay
class="font-medium"
:amount="{ amount: parseInt(vali.reward).toString(), denom: baseDenom }"
/>
{{ $t(`components.previews.claim.rewards`) }}
</template>
</div>
</div>
<ValidatorBadge :validator="getValidator(vali.validator_address)" class="ml-3" />
</div>
</ListItem>

<!-- Transaction fees -->
<ListItem :label="$t(`components.previews.claim.transactionFee`)">
<div class="flex justify-end items-center">
<div class="text-right">
<template v-for="(fee, chain) in fees" :key="'fee_' + chain">
<template v-for="(feeAmount, denom) in fee" :key="'fee' + chain + denom">
<AmountDisplay :amount="{ amount: feeAmount.toString(), denom }" />
</template>
</template>
</div>
</div>
</ListItem>
</List>
</template>

<script setup lang="ts">
import { computed, onMounted, ref, toRefs } from 'vue';
import { useRoute } from 'vue-router';

import AmountDisplay from '@/components/common/AmountDisplay.vue';
import CircleSymbol from '@/components/common/CircleSymbol.vue';
import Price from '@/components/common/Price.vue';
import ValidatorBadge from '@/components/common/ValidatorBadge.vue';
import { List, ListItem } from '@/components/ui/List';
import useStaking from '@/composables/useStaking';
import * as Actions from '@/types/actions';
import { getSumOfRewards } from '@/utils/basic';

interface Props {
step?: Actions.Step;
response?: Actions.Step;
fees: Actions.FeeTotals;
context?: 'default' | 'widget';
isReceipt: boolean;
}

const props = withDefaults(defineProps<Props>(), {
step: undefined,
response: undefined,
context: 'default',
isReceipt: false,
});

const route = useRoute();
const { getValidatorsByBaseDenom } = useStaking();

const baseDenom = route.params.denom as string;
const validatorList = ref([]);
const propsRef = toRefs(props);
const rewardsAmount = computed(() => {
return getSumOfRewards(propsRef.step.value.transactions[0].data.total, baseDenom);
});
const validators = computed(() => {
return propsRef.step.value.transactions[0].data.rewards;
});
onMounted(async () => {
validatorList.value = await getValidatorsByBaseDenom(baseDenom);
});
const getValidator = (val_address) => {
return validatorList.value.find((x) => x.operator_address == val_address);
};
</script>

<style lang="scss" scoped></style>
Original file line number Diff line number Diff line change
@@ -87,6 +87,16 @@
/>
</p>
</template>
<template v-if="transaction.type === 'reinvest'">
<p class="font-medium text-1">
<AmountDisplay
:amount="{
amount: getStakedAmount(),
denom: getStakableBaseDenomFromChainName(transaction.data?.chainName),
}"
/>
</p>
</template>
<template v-if="transaction.type === 'swap'">
<template v-if="isSwapComponent">
<i18n-t
@@ -271,6 +281,7 @@ import CurrencyDisplay from '@/components/ui/CurrencyDisplay.vue';
import Icon from '@/components/ui/Icon.vue';
import PreviewAddLiquidity from '@/components/wizard/previews/PreviewAddLiquidity.vue';
import PreviewClaim from '@/components/wizard/previews/PreviewClaim.vue';
import PreviewReinvest from '@/components/wizard/previews/PreviewReinvest.vue';
import PreviewStake from '@/components/wizard/previews/PreviewStake.vue';
import PreviewSwap from '@/components/wizard/previews/PreviewSwap.vue';
import PreviewSwitch from '@/components/wizard/previews/PreviewSwitch.vue';
@@ -309,6 +320,7 @@ const titleMap = {
withdrawLiquidity: t('components.txHandlingModal.withdrawLiqActionComplete'),
createPool: t('components.txHandlingModal.createPoolActionComplete'),
claim: t('components.txHandlingModal.claimActionComplete'),
reinvest: t('components.txHandlingModal.reinvestActionComplete'),
switch: t('components.txHandlingModal.switchActionComplete'),
stake: t('components.txHandlingModal.stakeActionComplete'),
multistake: t('components.txHandlingModal.stakeActionComplete'),
@@ -324,6 +336,7 @@ const previewComponentMap = {
multistake: PreviewStake,
unstake: PreviewUnstake,
claim: PreviewClaim,
reinvest: PreviewReinvest,
switch: PreviewSwitch,
addLiquidity: PreviewAddLiquidity,
withdrawLiquidity: PreviewWithdrawLiquidity,
@@ -375,6 +388,10 @@ const getStakedAmount = () => {
const baseDenom = getStakableBaseDenomFromChainName(transaction.value.data.chainName);
return getSumOfRewards(transaction.value.data.total, baseDenom).toString();
}
if (transaction.value.type == 'reinvest') {
const baseDenom = getStakableBaseDenomFromChainName(transaction.value.data.chainName);
return getSumOfRewards(transaction.value.data.total, baseDenom).toString();
}
};

const getDepositTotal = () => {
Original file line number Diff line number Diff line change
@@ -57,6 +57,7 @@ import Alert from '@/components/ui/Alert.vue';
import Button from '@/components/ui/Button.vue';
import PreviewAddLiquidity from '@/components/wizard/previews/PreviewAddLiquidity.vue';
import PreviewClaim from '@/components/wizard/previews/PreviewClaim.vue';
import PreviewReinvest from '@/components/wizard/previews/PreviewReinvest.vue';
import PreviewStake from '@/components/wizard/previews/PreviewStake.vue';
import PreviewSwap from '@/components/wizard/previews/PreviewSwap.vue';
import PreviewSwitch from '@/components/wizard/previews/PreviewSwitch.vue';
@@ -90,6 +91,7 @@ const previewComponentMap = {
withdrawliquidity: PreviewWithdrawLiquidity,
createpool: PreviewAddLiquidity,
claim: PreviewClaim,
reinvest: PreviewReinvest,
switch: PreviewSwitch,
};

@@ -104,6 +106,7 @@ const titleMap = {
withdrawliquidity: t('context.transactions.review.withdrawliquidity'),
createpool: t('context.transactions.review.createpool'),
claim: t('context.transactions.review.claim'),
reinvest: t('context.transactions.review.reinvest'),
switch: t('context.transactions.review.switch'),
};

Loading