289 lines
9.4 KiB
Vue
289 lines
9.4 KiB
Vue
<template>
|
|
<div class="stake-view">
|
|
<div class="getting-started-guide">
|
|
<h4 class="getting-started-guide__title">🚀 Getting Started</h4>
|
|
<div class="getting-started-guide__steps">
|
|
<div class="getting-started-step">
|
|
<div class="step-number">1</div>
|
|
<div class="step-content">
|
|
<div class="step-title">Connect your wallet</div>
|
|
<div class="step-description">Use the button below to connect your Web3 wallet</div>
|
|
</div>
|
|
</div>
|
|
<div class="getting-started-step">
|
|
<div class="step-number">2</div>
|
|
<div class="step-content">
|
|
<div class="step-title">Get KRK</div>
|
|
<div class="step-description">Swap ETH for $KRK on Uniswap (link above)</div>
|
|
</div>
|
|
</div>
|
|
<div class="getting-started-step">
|
|
<div class="step-number">3</div>
|
|
<div class="step-content">
|
|
<div class="step-title">Stake your position</div>
|
|
<div class="step-description">Choose your tax rate and stake to claim owner slots</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stake-view-wrapper">
|
|
<h3>
|
|
Staking Dashboard
|
|
<IconInfo size="20px">
|
|
<template #text>
|
|
Stake your $KRK and claim owner slots. Owner slots give a return on every token buy that increases the liquidity of KrAIken.
|
|
Owner slots are limited to 20,000 slots in total. To enable everyone and anyone to join staking is protected by a "Harberger
|
|
Tax" mechanism.
|
|
</template>
|
|
</IconInfo>
|
|
</h3>
|
|
|
|
<ProtocolStatsCard></ProtocolStatsCard>
|
|
|
|
<div class="stake-view-body">
|
|
<ChartComplete></ChartComplete>
|
|
<div class="hold-stake-wrapper">
|
|
<FCard class="inner-border">
|
|
<template v-if="!isChainSupported"> Chain not supported </template>
|
|
<template v-else-if="status !== 'connected'">
|
|
<FButton @click="showPanel = true" size="large" block>Connect Wallet</FButton>
|
|
</template>
|
|
<template v-else>
|
|
<StakeHolder></StakeHolder>
|
|
</template>
|
|
</FCard>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="statistics-wrapper">
|
|
<h3>Statistics</h3>
|
|
<div class="statistics-outputs-wrapper">
|
|
<StatsOutput headline="Average Slot Tax" :price="`${averageTaxRate.toFixed(2)} %`"></StatsOutput>
|
|
<StatsOutput
|
|
headline="Claimed Owner Slots"
|
|
:price="`${commaNumber(stats.claimedSlots)} / ${commaNumber(stats.maxSlots)}`"
|
|
></StatsOutput>
|
|
<StatsOutput headline="Total Supply Change / 7d" :price="`+ ${stats.totalSupplyChange7d} $KRK`"></StatsOutput>
|
|
<StatsOutput headline="Inflation / 7d" :price="`+${stats.inflation7d}%`"></StatsOutput>
|
|
</div>
|
|
</div>
|
|
<div class="active-positions-wrapper">
|
|
<h3>Active Positions</h3>
|
|
<div v-if="myActivePositions.length === 0 && myClosedPositions.length > 0" class="no-active-positions">
|
|
No active positions — your position history is below.
|
|
</div>
|
|
<div class="active-positions-list">
|
|
<CollapseActive
|
|
v-for="position in myActivePositions"
|
|
:taxRate="position.taxRatePercentage"
|
|
:taxRateIndex="position.taxRateIndex"
|
|
:amount="position.amount"
|
|
:tresholdIndex="tresholdValue"
|
|
:id="position.positionId"
|
|
:position="position"
|
|
:key="position.id"
|
|
></CollapseActive>
|
|
</div>
|
|
</div>
|
|
<div v-if="myClosedPositions.length > 0" class="position-history-wrapper">
|
|
<h3>Position History</h3>
|
|
<div class="position-history-list">
|
|
<CollapseHistory
|
|
v-for="position in myClosedPositions"
|
|
:key="position.id"
|
|
:taxRate="position.taxRatePercentage"
|
|
:taxPaid="weiToNumber(position.taxPaid)"
|
|
:id="position.positionId"
|
|
:amount="position.amount"
|
|
:position="position"
|
|
></CollapseHistory>
|
|
</div>
|
|
</div>
|
|
<!-- <f-button @click="getGraphData">graphql test</f-button>
|
|
<div v-for="position in positions" :key="position.id">
|
|
{{ position }}
|
|
</div> -->
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import StakeHolder from '@/components/StakeHolder.vue';
|
|
import ChartComplete from '@/components/chart/ChartComplete.vue';
|
|
import StatsOutput from '@/components/StatsOutput.vue';
|
|
import CollapseActive from '@/components/collapse/CollapseActive.vue';
|
|
import CollapseHistory from '@/components/collapse/CollapseHistory.vue';
|
|
import ProtocolStatsCard from '@/components/ProtocolStatsCard.vue';
|
|
import { onMounted, computed, inject } from 'vue';
|
|
import { useStatCollection } from '@/composables/useStatCollection';
|
|
import { useChains, useAccount } from '@wagmi/vue';
|
|
import { useWallet } from '@/composables/useWallet';
|
|
import FCard from '@/components/fcomponents/FCard.vue';
|
|
import IconInfo from '@/components/icons/IconInfo.vue';
|
|
import FButton from '@/components/fcomponents/FButton.vue';
|
|
import { DEFAULT_CHAIN_ID } from '@/config';
|
|
|
|
// todo interface positions
|
|
import { usePositions } from '@/composables/usePositions';
|
|
const { status } = useAccount();
|
|
const showPanel = inject('showPanel');
|
|
|
|
import { commaNumber, weiToNumber } from 'kraiken-lib/format';
|
|
const wallet = useWallet();
|
|
const initialChainId = wallet.account.chainId ?? DEFAULT_CHAIN_ID;
|
|
const { myActivePositions, myClosedPositions, tresholdValue, activePositions } = usePositions(initialChainId);
|
|
|
|
const stats = useStatCollection(initialChainId);
|
|
const chains = useChains();
|
|
|
|
function calculateAverageTaxRate(data: Array<{ taxRate: number | string }>): number {
|
|
if (data.length === 0) {
|
|
return 0;
|
|
}
|
|
const totalTaxRate = data.reduce((sum: number, entry: { taxRate: number | string }) => sum + parseFloat(String(entry.taxRate)), 0);
|
|
const averageTaxRate = totalTaxRate / data.length;
|
|
return averageTaxRate * 100;
|
|
}
|
|
|
|
const averageTaxRate = computed(() => calculateAverageTaxRate(activePositions.value));
|
|
const supportedChainIds = computed(() => chains.value.map(chain => chain.id));
|
|
const currentChainId = computed(() => wallet.account.chainId ?? DEFAULT_CHAIN_ID);
|
|
const isChainSupported = computed(() => supportedChainIds.value.includes(currentChainId.value));
|
|
|
|
// Refresh balance when navigating to stake page (e.g., after swapping)
|
|
onMounted(async () => {
|
|
await wallet.loadBalance();
|
|
});
|
|
</script>
|
|
|
|
<style lang="sass">
|
|
.stake-view
|
|
display: flex
|
|
flex-direction: column
|
|
gap: 32px
|
|
padding: 16px
|
|
@media (min-width: 992px)
|
|
padding: 48px
|
|
|
|
.getting-started-guide
|
|
background: linear-gradient(135deg, rgba(117, 80, 174, 0.1) 0%, rgba(117, 80, 174, 0.05) 100%)
|
|
border: 1px solid rgba(117, 80, 174, 0.2)
|
|
border-radius: 16px
|
|
padding: 24px
|
|
margin-bottom: 8px
|
|
|
|
&__title
|
|
margin: 0 0 20px 0
|
|
font-size: 20px
|
|
font-weight: 600
|
|
color: #FFFFFF
|
|
|
|
&__steps
|
|
display: flex
|
|
flex-direction: column
|
|
gap: 16px
|
|
@media (min-width: 768px)
|
|
flex-direction: row
|
|
gap: 20px
|
|
|
|
.getting-started-step
|
|
display: flex
|
|
gap: 12px
|
|
align-items: flex-start
|
|
flex: 1
|
|
|
|
.step-number
|
|
flex-shrink: 0
|
|
width: 32px
|
|
height: 32px
|
|
border-radius: 50%
|
|
background-color: #7550AE
|
|
color: #FFFFFF
|
|
display: flex
|
|
align-items: center
|
|
justify-content: center
|
|
font-weight: 700
|
|
font-size: 16px
|
|
|
|
.step-content
|
|
flex: 1
|
|
|
|
.step-title
|
|
font-weight: 600
|
|
font-size: 15px
|
|
color: #FFFFFF
|
|
margin-bottom: 4px
|
|
|
|
.step-description
|
|
font-size: 13px
|
|
color: #9A9898
|
|
line-height: 1.4
|
|
|
|
.stake-view
|
|
.stake-view-wrapper
|
|
background-color: #07111B
|
|
padding: 12px 0
|
|
border-radius: 24px
|
|
@media (min-width: 992px)
|
|
padding: 24px
|
|
.stake-view-body
|
|
display: flex
|
|
gap: 40px
|
|
position: relative
|
|
flex-direction: column
|
|
@media (min-width: 992px)
|
|
flex-direction: row
|
|
.positions-graph
|
|
flex: 1 1 67%
|
|
.hold-stake-wrapper
|
|
flex: 1 1 33%
|
|
display: flex
|
|
flex-direction: column
|
|
gap: 48px
|
|
.f-card
|
|
overflow: unset
|
|
.f-card__body
|
|
height: 100%
|
|
display: flex
|
|
align-items: center
|
|
justify-content: center
|
|
&.inner-border
|
|
padding: 0
|
|
.statistics-wrapper
|
|
.statistics-outputs-wrapper
|
|
display: flex
|
|
justify-content: center
|
|
flex-direction: column
|
|
gap: 46px
|
|
@media (min-width: 768px)
|
|
flex-direction: row
|
|
flex-wrap: wrap
|
|
.stats-output
|
|
min-width: 280px
|
|
|
|
.active-positions-wrapper
|
|
width: 100%
|
|
margin-left: auto
|
|
margin-right: auto
|
|
@media (min-width: 768px)
|
|
width: 580px
|
|
.no-active-positions
|
|
color: #9A9898
|
|
font-size: 14px
|
|
padding: 16px 0
|
|
.active-positions-list
|
|
display: flex
|
|
flex-direction: column
|
|
gap: 12px
|
|
|
|
.position-history-wrapper
|
|
width: 100%
|
|
margin-left: auto
|
|
margin-right: auto
|
|
@media (min-width: 768px)
|
|
width: 580px
|
|
.position-history-list
|
|
display: flex
|
|
flex-direction: column
|
|
gap: 12px
|
|
</style>
|