547 lines
16 KiB
Vue
547 lines
16 KiB
Vue
<template>
|
|
<div class="sacrifice-wrapper card card--glow">
|
|
<div class="sacrifice-headline">Select Sacrifice {{ splashBlood }}</div>
|
|
<div class="sacrifice-select-tokens">
|
|
<div class="sacrifice-tokens">
|
|
<div
|
|
class="sacrifice-token"
|
|
:class="[
|
|
{
|
|
'sacrifice-token--active': token.contractAddress === activeToken.contractAddress,
|
|
'bloodline-border': token.contractAddress === activeToken.contractAddress,
|
|
'bloodline-border--variant1': token.contractAddress === activeToken.contractAddress,
|
|
},
|
|
]"
|
|
v-for="token in props.data"
|
|
:key="token.contractAddress"
|
|
@click="changeActiveToken(token)"
|
|
>
|
|
<div class="sacrifice-token-logo">
|
|
<img v-if="isMobile" :src="token.logoMobile" alt="token logo" />
|
|
<img v-if="!isMobile" :src="token.logo" alt="token logo" />
|
|
</div>
|
|
<div class="sacrifice-token-name mid-text">{{ token.name }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sacrifice-from-to-wrapper" v-if="activeToken.contractAddress">
|
|
<div class="sacrifice-from-wrapper bloodline-border bloodline-border--variant1 purple">
|
|
<token-input
|
|
:modelValue="sacrificeStore.sacrificeNumber / 10n ** activeToken.decimals"
|
|
@input="
|
|
($event) => {
|
|
const value = BigInt($event.target.value) * 10n ** 18n;
|
|
sacrificeStore.sacrificeNumber = value;
|
|
processChange(value);
|
|
}
|
|
"
|
|
:max="activeWalletToken?.balanceOf"
|
|
:logo="activeToken.logo"
|
|
>
|
|
<template v-slot:message>
|
|
<div
|
|
class="sacrifice-adder-step bloodline-badge"
|
|
v-for="steps in activeToken.addSteps"
|
|
:key="steps.name"
|
|
@click="addTokens(steps.amount)"
|
|
>
|
|
<template
|
|
v-if="
|
|
!walletStore.connected ||
|
|
sacrificeStore.sacrificeNumber < activeWalletToken?.balanceOf
|
|
"
|
|
>
|
|
+{{ steps.name }}
|
|
</template>
|
|
<template v-else> -{{ steps.name }} </template>
|
|
</div>
|
|
</template>
|
|
<template v-slot:message-end v-if="activeWalletToken?.balanceOf">
|
|
<div class="input-balance tiny-text">
|
|
Balance:
|
|
{{ BigInt(activeWalletToken?.balanceOf) / 10n ** activeToken.decimals }}
|
|
</div>
|
|
<div class="input-max tiny-text" @click="addMaxTokens">Max</div>
|
|
</template>
|
|
</token-input>
|
|
</div>
|
|
<div class="bloodline-devider">
|
|
<font-awesome-icon :icon="['fas', 'arrow-down']" />
|
|
</div>
|
|
<div
|
|
:class="{
|
|
'sacrifice-to-wrapper': true,
|
|
'bloodline-border': true,
|
|
'bloodline-border--variant1': true,
|
|
purple: output && output > 0,
|
|
}"
|
|
>
|
|
<div class="sacrifice-to-body">
|
|
<token-name-price name="$BLOOD" :price="`≈${formatNumber(output)}`"></token-name-price>
|
|
<token-logo :img="activeToken.vamp.logo" :size="64" :alt="activeToken.vamp.name"></token-logo>
|
|
</div>
|
|
|
|
<div class="sacrifice-staking-bonus">
|
|
<div
|
|
class="sacrifice-staking-bonus-inner"
|
|
@click="
|
|
(e) => {
|
|
showStakingMenu = !showStakingMenu;
|
|
}
|
|
"
|
|
>
|
|
<div class="mid-mid-text" v-if="!walletStore.connected">+ Staking Bonus</div>
|
|
<div
|
|
class="mid-mid-text"
|
|
v-else-if="walletStore.memeToken?.staked === 0 || cycleStore.currentCycle === 1"
|
|
>
|
|
No Staking Bonus
|
|
</div>
|
|
<div class="mid-mid-text" v-else>+ {{ stakingOutput }} Staking Bonus</div>
|
|
<div class="icon">
|
|
<font-awesome-icon icon="chevron-down" />
|
|
</div>
|
|
</div>
|
|
<stake-component-v1 :variant="1" v-if="showStakingMenu">
|
|
<template v-slot:headline> {{ null }}</template>
|
|
</stake-component-v1>
|
|
</div>
|
|
</div>
|
|
<div class="sacrificed-info" v-if="stateComputed === 4">
|
|
<div class="left low-mid-text">You can claim your $BLOOD when the cycle is finished.</div>
|
|
<div class="right">
|
|
<img src="/assets/img/moon.png" alt="moon" />
|
|
</div>
|
|
</div>
|
|
<div class="sacrifice-buttons-function-wrapper">
|
|
<template v-if="walletStore.connected">
|
|
<div class="btn btn-immortal sub-header" disabled v-if="stateComputed === 0">Enter Amount</div>
|
|
<div
|
|
class="btn btn-immortal sub-header"
|
|
disabled
|
|
v-else-if="sacrificeStore.sacrificeNumber > activeWalletToken?.balanceOf"
|
|
>
|
|
Insufficient Balance
|
|
</div>
|
|
<div
|
|
class="btn btn-immortal sub-header"
|
|
v-else-if="
|
|
activeToken.allowance > 0 &&
|
|
activeToken.allowance >= sacrificeStore.sacrificeNumber &&
|
|
(stateComputed === 1 || stateComputed === 4)
|
|
"
|
|
@click="sacrificeToken"
|
|
>
|
|
Sacrifice 🩸
|
|
</div>
|
|
<div
|
|
class="btn btn-immortal sub-header"
|
|
v-else-if="
|
|
(!activeToken.allowance || activeToken.allowance < sacrificeStore.sacrificeNumber) &&
|
|
(stateComputed === 1 || stateComputed === 4)
|
|
"
|
|
@click="approveToken(sacrificeStore.sacrificeNumber)"
|
|
>
|
|
Approve Token
|
|
</div>
|
|
<div class="btn btn-immortal sub-header" disabled v-else-if="stateComputed === 2">
|
|
Sign Transaction
|
|
</div>
|
|
<div class="btn btn-immortal sub-header" disabled v-else-if="stateComputed === 3">
|
|
<loading-component></loading-component>
|
|
</div>
|
|
</template>
|
|
<template v-else>
|
|
<div class="btn btn-immortal sub-header" @click="showPanel = true">Connect</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, defineProps, watch, inject, nextTick, onMounted, onBeforeUnmount, computed, markRaw } from "vue";
|
|
import { useWalletStoreV2 } from "@/stores/walletV2";
|
|
import { useCycleStore } from "@/stores/cycles";
|
|
import { useTokenStore } from "@/stores/tokens";
|
|
import { useMemeburnerStore } from "@/stores/memeburner";
|
|
import { useUniswapStore } from "@/stores/uniswap";
|
|
import { useSacrificeStore } from "@/stores/sacrifice";
|
|
import StakeComponentV1 from "@/components/StakeComponentV1.vue";
|
|
import LoadingComponent from "@/components/LoadingComponent.vue";
|
|
import TokenInput from "@/components/TokenInput.vue";
|
|
import TokenNamePrice from "@/components/TokenNamePrice.vue";
|
|
import TokenLogo from "@/components/TokenLogo.vue";
|
|
import ApprovedToast from "@/components/toasts/ApprovedToast.vue";
|
|
import { useToast } from "vue-toastification";
|
|
import { storeToRefs } from "pinia";
|
|
import { waitForTransaction, readContract, watchBlockNumber, multicall } from "@wagmi/core";
|
|
|
|
const cycleStore = useCycleStore();
|
|
const sacrificeStore = useSacrificeStore();
|
|
const toast = useToast();
|
|
|
|
import { debounce, formatNumber } from "@/helper/helper.js";
|
|
|
|
const isMobile = inject("isMobile");
|
|
const showPanel = inject("showPanel");
|
|
const splashBlood = inject("splashBlood");
|
|
|
|
const walletStore = useWalletStoreV2();
|
|
|
|
const tokenStore = useTokenStore();
|
|
const { activeToken } = storeToRefs(tokenStore);
|
|
|
|
const props = defineProps({
|
|
data: Array,
|
|
});
|
|
|
|
const output = ref(0);
|
|
const stakingOutput = ref(null);
|
|
const blockEvent = ref(null);
|
|
const approveToast = ref(null);
|
|
const addOperator = ref(true);
|
|
const splashesUsed = ref([]);
|
|
/**
|
|
* state 0 enter amount
|
|
* state 1 rdy to go
|
|
* state 2 signing
|
|
* state 3 loading
|
|
* state 4 sacrificed
|
|
*/
|
|
const showStakingMenu = ref(false);
|
|
const approvalData = ref(null);
|
|
|
|
const activeWalletToken = computed(() => {
|
|
let returnedObject = {};
|
|
if (activeToken.value.contractAddress) {
|
|
returnedObject = walletStore.token.find((obj) => obj.contractAddress === activeToken.value.contractAddress);
|
|
}
|
|
return returnedObject;
|
|
});
|
|
|
|
const stateComputed = computed(() => {
|
|
let returnState = 0;
|
|
if (
|
|
sacrificeStore.sacrificeNumber === null ||
|
|
sacrificeStore.sacrificeNumber === "" ||
|
|
sacrificeStore.sacrificeNumber === 0n
|
|
) {
|
|
returnState = 0;
|
|
} else if (sacrificeStore.sacrificeState === 2) {
|
|
returnState = 2;
|
|
} else if (sacrificeStore.sacrificeState === 3) {
|
|
returnState = 3;
|
|
} else if (
|
|
sacrificeStore.sacrificeNumber !== null ||
|
|
sacrificeStore.sacrificeNumber !== "" ||
|
|
sacrificeStore.sacrificeNumber > 0
|
|
) {
|
|
returnState = 1;
|
|
}
|
|
return returnState;
|
|
});
|
|
const processChange = debounce(async (value) => {
|
|
const rewardValue = await sacrificeStore.getRewardsFor(value, activeToken.value);
|
|
output.value = rewardValue.output;
|
|
stakingOutput.value = rewardValue.staking;
|
|
});
|
|
|
|
onMounted(() => {
|
|
blockEvent.value = watchBlockNumber(
|
|
{
|
|
listen: true,
|
|
},
|
|
async (blockNumber) => {
|
|
console.log("blockNumber", blockNumber);
|
|
var rewardValue = await sacrificeStore.getRewardsFor(sacrificeStore.sacrificeNumber, activeToken.value);
|
|
output.value = rewardValue.output;
|
|
stakingOutput.value = rewardValue.staking;
|
|
}
|
|
);
|
|
});
|
|
|
|
onBeforeUnmount(() => {
|
|
console.log("blockEventunwatch", blockEvent.value);
|
|
blockEvent.value();
|
|
});
|
|
|
|
async function approveToken(amount) {
|
|
try {
|
|
console.log("amount", amount);
|
|
sacrificeStore.sacrificeState = 2;
|
|
const hash = await tokenStore.approveToken(amount);
|
|
sacrificeStore.sacrificeState = 3;
|
|
const data = await waitForTransaction({
|
|
hash: hash,
|
|
});
|
|
console.log("data", data);
|
|
activeToken.value.allowance = await tokenStore.allowance();
|
|
sacrificeStore.sacrificeState = 1;
|
|
approveToast.value = showApprovedToast();
|
|
} catch (error) {
|
|
console.error(error);
|
|
throw new Error(error);
|
|
} finally {
|
|
sacrificeStore.sacrificeState = 0;
|
|
}
|
|
}
|
|
|
|
async function addTokens(value) {
|
|
if (!walletStore.connected || sacrificeStore.sacrificeNumber < activeWalletToken.value?.balanceOf) {
|
|
sacrificeStore.sacrificeNumber = BigInt(sacrificeStore.sacrificeNumber) + value;
|
|
} else {
|
|
sacrificeStore.sacrificeNumber = sacrificeStore.sacrificeNumber - value;
|
|
if (sacrificeStore.sacrificeNumber < 0n) {
|
|
sacrificeStore.sacrificeNumber = 0n;
|
|
}
|
|
}
|
|
|
|
const rewardValue = await sacrificeStore.getRewardsFor(sacrificeStore.sacrificeNumber, activeToken.value);
|
|
output.value = rewardValue.output;
|
|
stakingOutput.value = rewardValue.staking;
|
|
}
|
|
|
|
async function addMaxTokens() {
|
|
addOperator.value = !addOperator.value;
|
|
sacrificeStore.sacrificeNumber = activeWalletToken.value?.balanceOf;
|
|
const rewardValue = await sacrificeStore.getRewardsFor(sacrificeStore.sacrificeNumber, activeToken.value);
|
|
console.log("rewardValue", rewardValue);
|
|
output.value = rewardValue.output;
|
|
stakingOutput.value = rewardValue.staking;
|
|
}
|
|
|
|
function showApprovedToast(value, header, subheader, info) {
|
|
// Define the content object with the component, props and listeners
|
|
const content = {
|
|
component: ApprovedToast,
|
|
// Any prop can be passed, but don't expect them to be reactive
|
|
props: {
|
|
value: value,
|
|
header: header,
|
|
subheader: subheader,
|
|
info: info,
|
|
},
|
|
// Listen and react to events using callbacks. In this case we listen for
|
|
// the "click" event emitted when clicking the toast button
|
|
listeners: {
|
|
sacrificed: showSplash,
|
|
},
|
|
};
|
|
|
|
// Render the toast and its contents
|
|
return toast(content, {
|
|
position: "top-right",
|
|
icon: false,
|
|
closeOnClick: false,
|
|
// toastClassName: "modal-overlay",
|
|
hideProgressBar: true,
|
|
timeout: false,
|
|
toastClassName: "approve-toast toast-card",
|
|
});
|
|
}
|
|
|
|
function showSplash() {
|
|
splashBlood.value++;
|
|
}
|
|
|
|
//call function in the store and remove icon,
|
|
async function sacrificeToken() {
|
|
var result = await sacrificeStore.sacrificeToken(sacrificeStore.sacrificeNumber, activeToken.value);
|
|
if (result) {
|
|
showSplash();
|
|
}
|
|
toast.dismiss(approveToast.value);
|
|
}
|
|
|
|
async function changeActiveToken(token) {
|
|
console.log("changeActiveToken");
|
|
tokenStore.activeToken = token;
|
|
if (walletStore.connected) {
|
|
//TODO, wenn connected, dann all diese Daten laden?
|
|
tokenStore.activeToken.balance = await walletStore.getTokenBalance(token);
|
|
tokenStore.activeToken.allowance = Number(await tokenStore.allowance());
|
|
}
|
|
var rewards = await sacrificeStore.getRewardsFor(sacrificeStore.sacrificeNumber, token);
|
|
console.log("rewards", rewards);
|
|
output.value = rewards.output;
|
|
stakingOutput.value = rewards.staking;
|
|
}
|
|
</script>
|
|
|
|
<style lang="sass">
|
|
.sacrifice-wrapper
|
|
display: flex
|
|
flex-direction: column
|
|
margin-left: auto
|
|
margin-right: auto
|
|
background-color: var(--primary)
|
|
padding: 24px
|
|
align-items: center
|
|
@media (min-width: 992px)
|
|
padding: 48px 64px
|
|
.sacrifice-headline
|
|
text-align: center
|
|
font-size: 24px
|
|
margin-bottom: 32px
|
|
@media (min-width: 992px)
|
|
font-size: 32px
|
|
.sacrifice-select-tokens
|
|
display: flex
|
|
margin-bottom: 32px
|
|
width: 100%
|
|
.sacrifice-tokens
|
|
display: flex
|
|
width: 100%
|
|
justify-content: space-between
|
|
flex-wrap: wrap
|
|
gap: 12px
|
|
|
|
.sacrifice-token
|
|
border-radius: 16px
|
|
padding: 9px
|
|
cursor: pointer
|
|
background-color: #0F0F0F
|
|
text-align: center
|
|
user-select: none
|
|
border: 4px solid transparent
|
|
.sacrifice-token-logo
|
|
pointer-events: none
|
|
@media (min-width: 992px)
|
|
img
|
|
height: 100px
|
|
width: 100px
|
|
// padding: 0
|
|
// background-color: unset
|
|
&.sacrifice-token--active
|
|
border-color: var(--purple)
|
|
|
|
.sacrifice-token-name
|
|
@media (min-width: 992px)
|
|
padding: 5px 26px
|
|
|
|
.sacrifice-buttons-function-wrapper
|
|
text-align: center
|
|
justify-content: center
|
|
display: block
|
|
@media (min-width: 992px)
|
|
display: flex
|
|
.btn
|
|
padding: 12px 48px
|
|
.sacrifice-fees-info-wrapper
|
|
display: flex
|
|
border: 1px dashed var(--grey)
|
|
color: var(--grey)
|
|
border-radius: 16px
|
|
padding: 12px
|
|
gap: 12px
|
|
.sacrifice-fees-img
|
|
img
|
|
height: 46px
|
|
.sacrifice-fees-body
|
|
flex: 1 1 auto
|
|
display: flex
|
|
flex-direction: column
|
|
justify-content: space-between
|
|
.sacrifice-fees-body-header
|
|
display: flex
|
|
flex-direction: row
|
|
justify-content: space-between
|
|
margin-bottom: 8px
|
|
@media (min-width: 992px)
|
|
margin-bottom: 0
|
|
|
|
.sacrifice-from-to-wrapper
|
|
display: flex
|
|
flex-direction: column
|
|
gap: 16px
|
|
width: 100%
|
|
@media (min-width: 992px)
|
|
gap: 32px
|
|
.sacrifice-from-wrapper
|
|
.sacrifice-logo
|
|
margin-left: 16px
|
|
img
|
|
width: 45px
|
|
height: 45px
|
|
border-radius: 50%
|
|
pointer-events: none
|
|
@media (min-width: 992px)
|
|
height: 100%
|
|
width: auto
|
|
.sacrifice-to-output
|
|
flex: 1 1 auto
|
|
text-align: left
|
|
.sacrifice-to-output-coin
|
|
font-size: 16px
|
|
@media (min-width: 992px)
|
|
font-size: 24px
|
|
.sacrifice-to-output-rewards
|
|
font-size: 24px
|
|
@media (min-width: 992px)
|
|
font-size: 42px
|
|
.value-adder
|
|
display: flex
|
|
gap: 6px
|
|
.sacrifice-adder-step
|
|
font-size: 12px
|
|
align-items: center
|
|
background: var(--grey, #858585)
|
|
color: var(--midnight-black, #0F0F0F)
|
|
&:hover,
|
|
&:active,
|
|
&:focus
|
|
background-color: var(--grey-light)
|
|
cursor: pointer
|
|
// @media (min-width: 992px)
|
|
// font-size: 12px
|
|
// .sacrifice-adder-balance
|
|
// margin-left: auto
|
|
// .sacrifice-adder-max
|
|
// margin-left: 12px
|
|
// color: red
|
|
// &:hover,
|
|
// &:focus,
|
|
// &:active
|
|
// color: darken(red, 10%)
|
|
// transform: scale(1.1)
|
|
// transition: transform 0s
|
|
.sacrifice-to-wrapper
|
|
display: flex
|
|
flex-direction: column
|
|
gap: 18px
|
|
.sacrifice-to-body
|
|
display: flex
|
|
justify-content: space-between
|
|
height: 56px
|
|
.sacrificed-info
|
|
display: flex
|
|
padding: 8px 24px
|
|
justify-content: space-between
|
|
align-items: center
|
|
align-self: stretch
|
|
border-radius: 16px
|
|
background: var(--midnight-black, #0F0F0F)
|
|
text-align: left
|
|
|
|
.sacrifice-staking-bonus
|
|
display: flex
|
|
flex-direction: column
|
|
padding: 8px 16px
|
|
row-gap: 8px
|
|
border-radius: 10px
|
|
background: var(--midnight-black, #0F0F0F)
|
|
.sacrifice-staking-bonus-inner
|
|
display: flex
|
|
justify-content: space-between
|
|
.icon
|
|
margin-left: auto
|
|
|
|
.input-max
|
|
color: var(--grey, #858585)
|
|
font-weight: bold
|
|
&:hover, &:active, &:focus
|
|
color: var(--grey-light, #858585)
|
|
cursor: pointer
|
|
</style>
|