parent
66106077ba
commit
db3633425a
17 changed files with 18548 additions and 199 deletions
|
|
@ -16,6 +16,7 @@
|
|||
<SocialButton type="telegram" href="https://t.me/kraikenportal"></SocialButton>
|
||||
<SocialButton type="twitter" href="https://x.com/KrAIkenProtocol"></SocialButton>
|
||||
</div>
|
||||
<WalletButton />
|
||||
</div>
|
||||
<div class="menu-trigger" @click.stop="toggleMenu">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24">
|
||||
|
|
@ -42,6 +43,7 @@
|
|||
<script setup lang="ts">
|
||||
import { RouterLink, useRouter } from 'vue-router';
|
||||
import SocialButton from './SocialButton.vue';
|
||||
import WalletButton from './WalletButton.vue';
|
||||
import { onMounted, onUnmounted, ref, computed } from 'vue';
|
||||
|
||||
const router = useRouter();
|
||||
|
|
|
|||
95
landing/src/components/WalletButton.vue
Normal file
95
landing/src/components/WalletButton.vue
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
<script setup lang="ts">
|
||||
import { useAccount, useConnect, useDisconnect } from '@harb/web3';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const { address, isConnected } = useAccount();
|
||||
const { connectors, connect } = useConnect();
|
||||
const { disconnect } = useDisconnect();
|
||||
const showConnectors = ref(false);
|
||||
|
||||
function shortAddress(addr: string) {
|
||||
return `${addr.slice(0, 6)}…${addr.slice(-4)}`;
|
||||
}
|
||||
|
||||
function handleConnect(connector: (typeof connectors)[number]) {
|
||||
connect({ connector });
|
||||
showConnectors.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="wallet-button">
|
||||
<template v-if="isConnected && address">
|
||||
<button class="wallet-button__connected" @click="disconnect()">
|
||||
{{ shortAddress(address) }}
|
||||
</button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<button class="wallet-button__connect" @click="showConnectors = !showConnectors">
|
||||
Connect Wallet
|
||||
</button>
|
||||
<div v-if="showConnectors" class="wallet-button__dropdown">
|
||||
<button
|
||||
v-for="connector in connectors"
|
||||
:key="connector.uid"
|
||||
class="wallet-button__option"
|
||||
@click="handleConnect(connector)"
|
||||
>
|
||||
{{ connector.name }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.wallet-button
|
||||
position: relative
|
||||
|
||||
&__connect, &__connected
|
||||
padding: 0.5rem 1rem
|
||||
border-radius: 8px
|
||||
border: 1px solid rgba(255, 255, 255, 0.2)
|
||||
background: rgba(255, 255, 255, 0.08)
|
||||
color: #fff
|
||||
cursor: pointer
|
||||
font-size: 0.9rem
|
||||
transition: background 0.2s
|
||||
|
||||
&:hover
|
||||
background: rgba(255, 255, 255, 0.15)
|
||||
|
||||
&__connect
|
||||
background: #3b82f6
|
||||
border-color: #3b82f6
|
||||
|
||||
&:hover
|
||||
background: #2563eb
|
||||
|
||||
&__dropdown
|
||||
position: absolute
|
||||
top: 100%
|
||||
right: 0
|
||||
margin-top: 0.5rem
|
||||
background: #1a1a2e
|
||||
border: 1px solid rgba(255, 255, 255, 0.15)
|
||||
border-radius: 8px
|
||||
padding: 0.5rem
|
||||
min-width: 200px
|
||||
z-index: 100
|
||||
|
||||
&__option
|
||||
display: block
|
||||
width: 100%
|
||||
padding: 0.5rem 0.75rem
|
||||
background: none
|
||||
border: none
|
||||
color: #fff
|
||||
cursor: pointer
|
||||
text-align: left
|
||||
border-radius: 4px
|
||||
font-size: 0.85rem
|
||||
|
||||
&:hover
|
||||
background: rgba(255, 255, 255, 0.1)
|
||||
</style>
|
||||
137
landing/src/components/WalletCard.vue
Normal file
137
landing/src/components/WalletCard.vue
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, watch, computed } from 'vue';
|
||||
import { useAccount } from '@harb/web3';
|
||||
import axios from 'axios';
|
||||
|
||||
const { address, isConnected } = useAccount();
|
||||
|
||||
const holder = ref<{
|
||||
balance: string;
|
||||
totalEthSpent: string;
|
||||
totalTokensAcquired: string;
|
||||
} | null>(null);
|
||||
const stats = ref<{ currentPriceWei: string } | null>(null);
|
||||
const loading = ref(false);
|
||||
|
||||
const endpoint = `${window.location.origin}/api/graphql`;
|
||||
|
||||
async function fetchWalletData(addr: string) {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await axios.post(endpoint, {
|
||||
query: `{
|
||||
holders(address: "${addr.toLowerCase()}") {
|
||||
balance
|
||||
totalEthSpent
|
||||
totalTokensAcquired
|
||||
}
|
||||
protocolStatss(where: { id: "0x01" }) {
|
||||
items { currentPriceWei }
|
||||
}
|
||||
}`,
|
||||
});
|
||||
holder.value = res.data?.data?.holders ?? null;
|
||||
stats.value = res.data?.data?.protocolStatss?.items?.[0] ?? null;
|
||||
} catch {
|
||||
holder.value = null;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
watch([address, isConnected], () => {
|
||||
if (isConnected.value && address.value) {
|
||||
void fetchWalletData(address.value);
|
||||
} else {
|
||||
holder.value = null;
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
function fmt(wei: string, decimals = 4) {
|
||||
const n = Number(wei) / 1e18;
|
||||
return n.toLocaleString('en', { maximumFractionDigits: decimals });
|
||||
}
|
||||
|
||||
const balanceKrk = computed(() => fmt(holder.value?.balance ?? '0', 2));
|
||||
const hasPosition = computed(() => holder.value && BigInt(holder.value.balance) > 0n);
|
||||
|
||||
const avgCost = computed(() => {
|
||||
if (!holder.value) return 0;
|
||||
const spent = Number(holder.value.totalEthSpent) / 1e18;
|
||||
const acquired = Number(holder.value.totalTokensAcquired) / 1e18;
|
||||
return acquired > 0 ? spent / acquired : 0;
|
||||
});
|
||||
|
||||
const currentPrice = computed(() =>
|
||||
stats.value ? Number(stats.value.currentPriceWei) / 1e18 : 0,
|
||||
);
|
||||
|
||||
const pnlPercent = computed(() => {
|
||||
if (avgCost.value === 0) return 0;
|
||||
return (currentPrice.value / avgCost.value - 1) * 100;
|
||||
});
|
||||
|
||||
const pnlClass = computed(() => (pnlPercent.value >= 0 ? 'positive' : 'negative'));
|
||||
|
||||
const appUrl = computed(() => `/app/#/wallet/${address.value}`);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="isConnected && hasPosition" class="wallet-card" :class="pnlClass">
|
||||
<div class="wallet-card__balance">{{ balanceKrk }} KRK</div>
|
||||
<div v-if="avgCost > 0" class="wallet-card__pnl">
|
||||
{{ pnlPercent >= 0 ? '+' : '' }}{{ pnlPercent.toFixed(1) }}%
|
||||
</div>
|
||||
<a :href="appUrl" class="wallet-card__link">View Dashboard →</a>
|
||||
</div>
|
||||
<div v-else-if="isConnected && !loading && !hasPosition" class="wallet-card wallet-card--empty">
|
||||
<span>No KRK yet</span>
|
||||
<a href="#get-krk" class="wallet-card__link">Get KRK →</a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
.wallet-card
|
||||
display: flex
|
||||
align-items: center
|
||||
gap: 1rem
|
||||
padding: 0.75rem 1.25rem
|
||||
border-radius: 12px
|
||||
background: rgba(255, 255, 255, 0.06)
|
||||
border: 1px solid rgba(255, 255, 255, 0.15)
|
||||
margin-top: 1rem
|
||||
|
||||
&.positive
|
||||
border-color: rgba(16, 185, 129, 0.4)
|
||||
background: rgba(16, 185, 129, 0.06)
|
||||
|
||||
&.negative
|
||||
border-color: rgba(239, 68, 68, 0.4)
|
||||
background: rgba(239, 68, 68, 0.06)
|
||||
|
||||
&--empty
|
||||
color: #9a9898
|
||||
font-size: 0.85rem
|
||||
|
||||
&__balance
|
||||
font-weight: 700
|
||||
font-size: 1.1rem
|
||||
|
||||
&__pnl
|
||||
font-weight: 600
|
||||
|
||||
.positive &
|
||||
color: #10b981
|
||||
|
||||
.negative &
|
||||
color: #ef4444
|
||||
|
||||
&__link
|
||||
margin-left: auto
|
||||
color: #3b82f6
|
||||
text-decoration: none
|
||||
font-size: 0.85rem
|
||||
|
||||
&:hover
|
||||
text-decoration: underline
|
||||
</style>
|
||||
Loading…
Add table
Add a link
Reference in a new issue