resolves #47 Co-authored-by: johba <johba@harb.eth> Reviewed-on: https://codeberg.org/johba/harb/pulls/54
249 lines
8.8 KiB
Vue
249 lines
8.8 KiB
Vue
<template>
|
|
<div class="hold-inner">
|
|
<div class="stake-inner">
|
|
<template v-if="!statCollection.initialized">
|
|
<div>
|
|
<FLoader></FLoader>
|
|
</div>
|
|
</template>
|
|
|
|
<template v-else>
|
|
<div class="subheader2">Token Amount</div>
|
|
<FSlider :min="minStakeAmount" :max="maxStakeAmount" v-model="stake.stakingAmountNumber"></FSlider>
|
|
<div class="formular">
|
|
<div class="row row-1">
|
|
<FInput label="Staking Amount" class="staking-amount" v-model="stake.stakingAmountNumber">
|
|
<template v-slot:details>
|
|
<div class="balance">Balance: {{ maxStakeAmount.toFixed(2) }} $KRK</div>
|
|
<div @click="setMaxAmount" class="staking-amount-max">
|
|
<b>Max</b>
|
|
</div>
|
|
</template>
|
|
</FInput>
|
|
<Icon class="stake-arrow" icon="mdi:chevron-triple-right"></Icon>
|
|
<FInput label="Owner Slots" class="staking-amount" disabled :modelValue="`${stakeSlots}(${supplyFreeze?.toFixed(4)})`">
|
|
<template #info>
|
|
Slots correspond to a percentage of ownership in the protocol.<br /><br />1,000 Slots = 1% Ownership<br /><br />When you
|
|
unstake you get the exact percentage of the current $KRK total supply. When the total supply increased since you staked you
|
|
get more tokens back than before.
|
|
</template>
|
|
</FInput>
|
|
</div>
|
|
<div class="row row-2">
|
|
<FSelect :items="adjustTaxRate.taxRates" label="Tax" v-model="taxRate">
|
|
<template v-slot:info>
|
|
The yearly tax you have to pay to keep your slots open. The tax is paid when unstaking or manually in the dashboard. If
|
|
someone pays a higher tax they can buy you out.
|
|
</template>
|
|
</FSelect>
|
|
<FInput label="Floor Tax" disabled :modelValue="String(snatchSelection.floorTax)">
|
|
<template v-slot:info> This is the current minimum tax you have to pay to claim owner slots from other owners. </template>
|
|
</FInput>
|
|
<FInput label="Positions Buyout" disabled :modelValue="String(snatchSelection.snatchablePositions.value.length)">
|
|
<template v-slot:info>
|
|
This shows you the numbers of staking positions you buy out from current owners by paying a higher tax. If you get bought
|
|
out yourself by new owners you get paid out the current market value of your position incl. your profits.
|
|
</template>
|
|
</FInput>
|
|
</div>
|
|
</div>
|
|
<FButton size="large" disabled block v-if="stake.state === 'NoBalance'">Insufficient Balance</FButton>
|
|
<FButton size="large" disabled block v-else-if="stake.stakingAmountNumber < minStakeAmount">Stake amount too low</FButton>
|
|
<FButton
|
|
size="large"
|
|
disabled
|
|
block
|
|
v-else-if="
|
|
!snatchSelection.openPositionsAvailable && stake.state === 'StakeAble' && snatchSelection.snatchablePositions.value.length === 0
|
|
"
|
|
>taxRate too low to snatch</FButton
|
|
>
|
|
<FButton
|
|
size="large"
|
|
block
|
|
v-else-if="stake.state === 'StakeAble' && snatchSelection.snatchablePositions.value.length === 0"
|
|
@click="stakeSnatch"
|
|
>Stake</FButton
|
|
>
|
|
<FButton
|
|
size="large"
|
|
block
|
|
v-else-if="stake.state === 'StakeAble' && snatchSelection.snatchablePositions.value.length > 0"
|
|
@click="stakeSnatch"
|
|
>Snatch and Stake</FButton
|
|
>
|
|
<FButton size="large" outlined block v-else-if="stake.state === 'SignTransaction'">Sign Transaction ...</FButton>
|
|
<FButton size="large" outlined block v-else-if="stake.state === 'Waiting'">Waiting ...</FButton>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import FButton from '@/components/fcomponents/FButton.vue';
|
|
import FInput from '@/components/fcomponents/FInput.vue';
|
|
import FSelect from '@/components/fcomponents/FSelect.vue';
|
|
import FLoader from '@/components/fcomponents/FLoader.vue';
|
|
import FSlider from '@/components/fcomponents/FSlider.vue';
|
|
import { Icon } from '@iconify/vue';
|
|
import { bigInt2Number } from '@/utils/helper';
|
|
import { loadPositions, usePositions, type Position } from '@/composables/usePositions';
|
|
import { useStake } from '@/composables/useStake';
|
|
import { useClaim } from '@/composables/useClaim';
|
|
import { useAdjustTaxRate } from '@/composables/useAdjustTaxRates';
|
|
import { useSnatchSelection } from '@/composables/useSnatchSelection';
|
|
import { assetsToShares } from '@/contracts/stake';
|
|
import { getMinStake } from '@/contracts/harb';
|
|
import { useWallet } from '@/composables/useWallet';
|
|
import { ref, onMounted, watch, computed, watchEffect } from 'vue';
|
|
import { useStatCollection, loadStats } from '@/composables/useStatCollection';
|
|
import { useRoute } from 'vue-router';
|
|
|
|
const demo = sessionStorage.getItem('demo') === 'true';
|
|
const route = useRoute();
|
|
|
|
const adjustTaxRate = useAdjustTaxRate();
|
|
|
|
const StakeMenuOpen = ref(false);
|
|
const taxRate = ref<number>(1.0);
|
|
const loading = ref<boolean>(true);
|
|
const stakeSnatchLoading = ref<boolean>(false);
|
|
const stake = useStake();
|
|
const _claim = useClaim();
|
|
const wallet = useWallet();
|
|
const statCollection = useStatCollection();
|
|
|
|
const { activePositions: _activePositions } = usePositions();
|
|
|
|
const minStake = ref(0n);
|
|
const stakeSlots = ref();
|
|
const supplyFreeze = ref<number>(0);
|
|
let debounceTimer: ReturnType<typeof setTimeout>;
|
|
watchEffect(() => {
|
|
if (!stake.stakingAmount) {
|
|
supplyFreeze.value = 0;
|
|
return;
|
|
}
|
|
|
|
clearTimeout(debounceTimer);
|
|
debounceTimer = setTimeout(async () => {
|
|
stake.stakingAmountShares = await assetsToShares(stake.stakingAmount);
|
|
const stakingAmountSharesNumber = bigInt2Number(stake.stakingAmountShares, 18);
|
|
const stakeableSupplyNumber = bigInt2Number(statCollection.stakeableSupply, 18);
|
|
minStake.value = await getMinStake();
|
|
|
|
supplyFreeze.value = stakingAmountSharesNumber / stakeableSupplyNumber;
|
|
}, 500);
|
|
});
|
|
|
|
watchEffect(() => {
|
|
stakeSlots.value = (supplyFreeze.value * 1000)?.toFixed(2);
|
|
});
|
|
|
|
const _tokenIssuance = computed(() => {
|
|
if (statCollection.kraikenTotalSupply === 0n) {
|
|
return 0n;
|
|
}
|
|
|
|
return (statCollection.nettoToken7d / statCollection.kraikenTotalSupply) * 100n;
|
|
});
|
|
|
|
async function stakeSnatch() {
|
|
if (snatchSelection.snatchablePositions.value.length === 0) {
|
|
await stake.snatch(stake.stakingAmount, taxRate.value);
|
|
} else {
|
|
const snatchAblePositionsIds = snatchSelection.snatchablePositions.value.map((p: Position) => p.positionId);
|
|
await stake.snatch(stake.stakingAmount, taxRate.value, snatchAblePositionsIds);
|
|
}
|
|
stakeSnatchLoading.value = true;
|
|
await new Promise(resolve => setTimeout(resolve, 10000));
|
|
await loadPositions();
|
|
await loadStats();
|
|
stakeSnatchLoading.value = false;
|
|
}
|
|
|
|
watch(
|
|
route,
|
|
async to => {
|
|
if (to.hash === '#stake') {
|
|
StakeMenuOpen.value = true;
|
|
}
|
|
},
|
|
{ flush: 'pre', immediate: true, deep: true }
|
|
);
|
|
|
|
onMounted(async () => {
|
|
try {
|
|
minStake.value = await getMinStake();
|
|
stake.stakingAmountNumber = minStakeAmount.value;
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
});
|
|
|
|
const minStakeAmount = computed(() => {
|
|
return bigInt2Number(minStake.value, 18);
|
|
});
|
|
|
|
const maxStakeAmount = computed(() => {
|
|
if (wallet.balance?.value) {
|
|
return bigInt2Number(wallet.balance.value, 18);
|
|
} else {
|
|
return 0;
|
|
}
|
|
});
|
|
|
|
watch(
|
|
minStakeAmount,
|
|
async newValue => {
|
|
if (newValue > stake.stakingAmountNumber && stake.stakingAmountNumber === 0) {
|
|
stake.stakingAmountNumber = minStakeAmount.value;
|
|
}
|
|
},
|
|
{ immediate: true }
|
|
);
|
|
|
|
function setMaxAmount() {
|
|
stake.stakingAmountNumber = maxStakeAmount.value;
|
|
}
|
|
|
|
const snatchSelection = useSnatchSelection(demo, taxRate);
|
|
</script>
|
|
|
|
<style lang="sass">
|
|
|
|
.hold-inner
|
|
.stake-inner
|
|
display: flex
|
|
flex-direction: column
|
|
gap: 24px
|
|
.formular
|
|
display: flex
|
|
flex-direction: column
|
|
gap: 8px
|
|
.row
|
|
>*
|
|
flex: 1 1 auto
|
|
.row-1
|
|
gap: 12px
|
|
>:nth-child(2)
|
|
flex: 0 0 auto
|
|
.staking-amount
|
|
.f-input--details
|
|
display: flex
|
|
gap: 8px
|
|
justify-content: flex-end
|
|
color: #9A9898
|
|
font-size: 14px
|
|
.staking-amount-max
|
|
font-weight: 600
|
|
&:hover, &:active, &:focus
|
|
cursor: pointer
|
|
.row-2
|
|
justify-content: space-between
|
|
>*
|
|
flex: 0 0 30%
|
|
.stake-arrow
|
|
align-self: center
|
|
font-size: 30px
|
|
</style>
|