webapp - ESLint + Prettier with pre-commit hooks (#54)
resolves #47 Co-authored-by: johba <johba@harb.eth> Reviewed-on: https://codeberg.org/johba/harb/pulls/54
This commit is contained in:
parent
2acb619a11
commit
f8927b426e
83 changed files with 7137 additions and 5113 deletions
|
|
@ -1,3 +1,10 @@
|
||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
cd kraiken-lib || exit 1
|
set -e
|
||||||
npx lint-staged
|
|
||||||
|
if [ -d "kraiken-lib" ]; then
|
||||||
|
(cd kraiken-lib && npx lint-staged)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d "web-app" ]; then
|
||||||
|
(cd web-app && npx lint-staged)
|
||||||
|
fi
|
||||||
|
|
|
||||||
|
|
@ -2,5 +2,8 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.55.1",
|
"@playwright/test": "^1.55.1",
|
||||||
"playwright-mcp": "^0.0.12"
|
"playwright-mcp": "^0.0.12"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"prepare": "husky"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
9
web-app/.lintstagedrc.json
Normal file
9
web-app/.lintstagedrc.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"src/**/*.{vue,ts,tsx}": [
|
||||||
|
"eslint --fix",
|
||||||
|
"prettier --write"
|
||||||
|
],
|
||||||
|
"src/**/*.scss": [
|
||||||
|
"prettier --write"
|
||||||
|
]
|
||||||
|
}
|
||||||
9
web-app/.prettierrc
Normal file
9
web-app/.prettierrc
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"printWidth": 140,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"arrowParens": "avoid",
|
||||||
|
"vueIndentScriptAndStyle": false
|
||||||
|
}
|
||||||
10
web-app/env.d.ts
vendored
10
web-app/env.d.ts
vendored
|
|
@ -1 +1,11 @@
|
||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
import type { EIP1193Provider } from 'viem';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
ethereum?: EIP1193Provider;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {};
|
||||||
|
|
|
||||||
95
web-app/eslint.config.js
Normal file
95
web-app/eslint.config.js
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
import { dirname, resolve } from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
import pluginVue from 'eslint-plugin-vue';
|
||||||
|
import vueTsEslintConfig from '@vue/eslint-config-typescript';
|
||||||
|
import tsParser from '@typescript-eslint/parser';
|
||||||
|
import stylistic from '@stylistic/eslint-plugin';
|
||||||
|
import prettier from 'eslint-config-prettier';
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = dirname(__filename);
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
name: 'app/files-to-lint',
|
||||||
|
files: ['**/*.{ts,mts,tsx,vue}'],
|
||||||
|
languageOptions: {
|
||||||
|
parser: tsParser,
|
||||||
|
parserOptions: {
|
||||||
|
projectService: true,
|
||||||
|
project: [resolve(__dirname, 'tsconfig.app.json')],
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
sourceType: 'module',
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
allowDefaultProject: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
'@stylistic': stylistic,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'app/files-to-ignore',
|
||||||
|
ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**', '**/node_modules/**', '**/.ponder/**', '**/__tests__/**'],
|
||||||
|
},
|
||||||
|
|
||||||
|
...pluginVue.configs['flat/essential'],
|
||||||
|
...vueTsEslintConfig(),
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'app/custom-rules',
|
||||||
|
rules: {
|
||||||
|
// TypeScript rules
|
||||||
|
'@typescript-eslint/no-explicit-any': 'error',
|
||||||
|
'@typescript-eslint/no-unused-vars': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
argsIgnorePattern: '^_',
|
||||||
|
varsIgnorePattern: '^_',
|
||||||
|
caughtErrorsIgnorePattern: '^_',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
// General code quality
|
||||||
|
'no-console': 'error',
|
||||||
|
'@stylistic/indent': ['error', 2, { SwitchCase: 1 }],
|
||||||
|
'max-len': ['error', { code: 140 }],
|
||||||
|
|
||||||
|
// Vue specific rules
|
||||||
|
'vue/multi-word-component-names': 'error',
|
||||||
|
'vue/component-name-in-template-casing': ['error', 'PascalCase'],
|
||||||
|
'vue/require-prop-types': 'error',
|
||||||
|
'vue/require-default-prop': 'error',
|
||||||
|
'vue/html-indent': ['error', 2],
|
||||||
|
'vue/no-unused-vars': 'error',
|
||||||
|
|
||||||
|
// Disable rules that conflict with Prettier
|
||||||
|
'vue/max-attributes-per-line': 'off',
|
||||||
|
'vue/singleline-html-element-content-newline': 'off',
|
||||||
|
'vue/html-self-closing': 'off',
|
||||||
|
|
||||||
|
// Naming conventions
|
||||||
|
camelcase: ['error', { properties: 'never', ignoreDestructuring: true, allow: ['^UNSAFE_'] }],
|
||||||
|
|
||||||
|
// Complexity rules (disabled as per requirements)
|
||||||
|
complexity: 'off',
|
||||||
|
'max-depth': 'off',
|
||||||
|
'max-lines': 'off',
|
||||||
|
'max-params': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'app/tests-override',
|
||||||
|
files: ['src/**/__tests__/**/*.ts', 'src/**/__tests__/**/*.tsx'],
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
projectService: false,
|
||||||
|
project: undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
prettier,
|
||||||
|
];
|
||||||
2094
web-app/package-lock.json
generated
2094
web-app/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -10,7 +10,12 @@
|
||||||
"build-only": "vite build",
|
"build-only": "vite build",
|
||||||
"type-check": "vue-tsc --build",
|
"type-check": "vue-tsc --build",
|
||||||
"subtree": "git subtree push --prefix dist origin gh-pages",
|
"subtree": "git subtree push --prefix dist origin gh-pages",
|
||||||
"test": "vitest"
|
"test": "vitest",
|
||||||
|
"lint": "eslint . --ext .vue,.ts,.tsx",
|
||||||
|
"lint:fix": "eslint . --ext .vue,.ts,.tsx --fix",
|
||||||
|
"format": "prettier --write \"src/**/*.{vue,ts,tsx,scss}\"",
|
||||||
|
"format:check": "prettier --check \"src/**/*.{vue,ts,tsx,scss}\"",
|
||||||
|
"prepare": "husky install"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tanstack/vue-query": "^5.64.2",
|
"@tanstack/vue-query": "^5.64.2",
|
||||||
|
|
@ -31,13 +36,23 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@iconify/vue": "^4.3.0",
|
"@iconify/vue": "^4.3.0",
|
||||||
|
"@stylistic/eslint-plugin": "^2.8.0",
|
||||||
"@tsconfig/node22": "^22.0.0",
|
"@tsconfig/node22": "^22.0.0",
|
||||||
"@types/node": "^22.10.7",
|
"@types/node": "^22.10.7",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^8.45.0",
|
||||||
|
"@typescript-eslint/parser": "^8.45.0",
|
||||||
"@vitejs/plugin-vue": "^5.2.1",
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
|
"@vue/eslint-config-typescript": "^14.6.0",
|
||||||
"@vue/tsconfig": "^0.7.0",
|
"@vue/tsconfig": "^0.7.0",
|
||||||
|
"eslint": "^9.36.0",
|
||||||
|
"eslint-config-prettier": "^10.1.8",
|
||||||
|
"eslint-plugin-vue": "^10.5.0",
|
||||||
"gh-pages": "^6.1.1",
|
"gh-pages": "^6.1.1",
|
||||||
|
"husky": "^9.1.7",
|
||||||
"jsdom": "^27.0.0",
|
"jsdom": "^27.0.0",
|
||||||
|
"lint-staged": "^16.2.3",
|
||||||
"npm-run-all2": "^7.0.2",
|
"npm-run-all2": "^7.0.2",
|
||||||
|
"prettier": "^3.6.2",
|
||||||
"typescript": "~5.7.3",
|
"typescript": "~5.7.3",
|
||||||
"vite": "^6.0.11",
|
"vite": "^6.0.11",
|
||||||
"vite-plugin-vue-devtools": "^7.7.0",
|
"vite-plugin-vue-devtools": "^7.7.0",
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,32 @@
|
||||||
<template>
|
<template>
|
||||||
|
<component :is="layoutComponent">
|
||||||
<component :is="layoutComponent">
|
<RouterView />
|
||||||
<router-view />
|
</component>
|
||||||
</component>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { RouterView, useRoute, useRouter } from "vue-router";
|
import { RouterView, useRoute } from 'vue-router';
|
||||||
import type { LayoutName } from '@/types/router';
|
import type { LayoutName } from '@/types/router';
|
||||||
|
|
||||||
|
|
||||||
import DefaultLayout from '@/layouts/DefaultLayout.vue';
|
import DefaultLayout from '@/layouts/DefaultLayout.vue';
|
||||||
import NavbarLayout from '@/layouts/NavbarLayout.vue';
|
import NavbarLayout from '@/layouts/NavbarLayout.vue';
|
||||||
|
|
||||||
const layouts = {
|
const layouts = {
|
||||||
DefaultLayout,
|
DefaultLayout,
|
||||||
NavbarLayout
|
NavbarLayout,
|
||||||
};
|
};
|
||||||
|
|
||||||
import { computed, defineAsyncComponent, provide, ref } from "vue";
|
import { computed } from 'vue';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
|
|
||||||
const layoutComponent = computed(() => {
|
const layoutComponent = computed(() => {
|
||||||
const layoutName: LayoutName = route.meta.layout ?? 'DefaultLayout';
|
const layoutName: LayoutName = route.meta.layout ?? 'DefaultLayout';
|
||||||
return layouts[layoutName]; // Jetzt kennt TypeScript den Typ!
|
return layouts[layoutName]; // Jetzt kennt TypeScript den Typ!
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
footer
|
footer
|
||||||
margin-top: auto
|
margin-top: auto
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
defineProps<{
|
defineProps<{
|
||||||
msg: string
|
msg: string;
|
||||||
}>()
|
}>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
||||||
55
web-app/src/components/SocialButton.vue
Normal file
55
web-app/src/components/SocialButton.vue
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
<template>
|
||||||
|
<a :href="props.href" target="_blank">
|
||||||
|
<div class="social-badge" :style="{ color: color, 'border-color': color }" :class="{ 'social-badge--dark': props.dark }">
|
||||||
|
<div class="social-badge-icon">
|
||||||
|
<component :color="color" :is="img" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import IconDiscord from '@/components/icons/IconDiscord.vue';
|
||||||
|
import IconTwitter from '@/components/icons/IconTwitter.vue';
|
||||||
|
import IconTelegram from '@/components/icons/IconTelegram.vue';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
type?: string;
|
||||||
|
dark?: boolean;
|
||||||
|
href?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
type: 'discord',
|
||||||
|
dark: false,
|
||||||
|
href: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const color = computed(() => (props.dark ? 'white' : 'black'));
|
||||||
|
|
||||||
|
const icons = {
|
||||||
|
discord: IconDiscord,
|
||||||
|
twitter: IconTwitter,
|
||||||
|
telegram: IconTelegram,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const img = computed(() => icons[props.type as keyof typeof icons] ?? null);
|
||||||
|
</script>
|
||||||
|
<style lang="sass">
|
||||||
|
.social-badge
|
||||||
|
border-radius: 14px
|
||||||
|
display: flex
|
||||||
|
border: 1px solid var(--color-social-border)
|
||||||
|
padding: 6px 20px
|
||||||
|
align-items: center
|
||||||
|
flex: 0 1 0
|
||||||
|
color: black
|
||||||
|
&:hover,&:active,&:focus
|
||||||
|
background-color: var(--color-white-hovered)
|
||||||
|
cursor: pointer
|
||||||
|
&.social-badge--dark
|
||||||
|
&:hover, &:active, &:focus
|
||||||
|
background-color: var(--color-black-hovered)
|
||||||
|
// font-size: 0
|
||||||
|
</style>
|
||||||
|
|
@ -1,241 +1,213 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="hold-inner">
|
<div class="hold-inner">
|
||||||
<div class="stake-inner">
|
<div class="stake-inner">
|
||||||
<template v-if="!statCollection.initialized">
|
<template v-if="!statCollection.initialized">
|
||||||
<div>
|
<div>
|
||||||
<f-loader></f-loader>
|
<FLoader></FLoader>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="subheader2">Token Amount</div>
|
<div class="subheader2">Token Amount</div>
|
||||||
<FSlider :min="minStakeAmount" :max="maxStakeAmount" v-model="stake.stakingAmountNumber"></FSlider>
|
<FSlider :min="minStakeAmount" :max="maxStakeAmount" v-model="stake.stakingAmountNumber"></FSlider>
|
||||||
<div class="formular">
|
<div class="formular">
|
||||||
<div class="row row-1">
|
<div class="row row-1">
|
||||||
<f-input label="Staking Amount" class="staking-amount" v-model="stake.stakingAmountNumber">
|
<FInput label="Staking Amount" class="staking-amount" v-model="stake.stakingAmountNumber">
|
||||||
<template v-slot:details>
|
<template v-slot:details>
|
||||||
<div class="balance">Balance: {{ maxStakeAmount.toFixed(2) }} $KRK</div>
|
<div class="balance">Balance: {{ maxStakeAmount.toFixed(2) }} $KRK</div>
|
||||||
<div @click="setMaxAmount" class="staking-amount-max">
|
<div @click="setMaxAmount" class="staking-amount-max">
|
||||||
<b>Max</b>
|
<b>Max</b>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</f-input>
|
</FInput>
|
||||||
<Icon class="stake-arrow" icon="mdi:chevron-triple-right"></Icon>
|
<Icon class="stake-arrow" icon="mdi:chevron-triple-right"></Icon>
|
||||||
<f-input
|
<FInput label="Owner Slots" class="staking-amount" disabled :modelValue="`${stakeSlots}(${supplyFreeze?.toFixed(4)})`">
|
||||||
label="Owner Slots"
|
<template #info>
|
||||||
class="staking-amount"
|
Slots correspond to a percentage of ownership in the protocol.<br /><br />1,000 Slots = 1% Ownership<br /><br />When you
|
||||||
disabled
|
unstake you get the exact percentage of the current $KRK total supply. When the total supply increased since you staked you
|
||||||
:modelValue="`${stakeSlots}(${supplyFreeze?.toFixed(4)})`"
|
get more tokens back than before.
|
||||||
>
|
</template>
|
||||||
<template #info>
|
</FInput>
|
||||||
Slots correspond to a percentage of ownership in the protocol.<br /><br />1,000 Slots =
|
</div>
|
||||||
1% Ownership<br /><br />When you unstake you get the exact percentage of the current
|
<div class="row row-2">
|
||||||
$KRK total supply. When the total supply increased since you staked you get more tokens
|
<FSelect :items="adjustTaxRate.taxRates" label="Tax" v-model="taxRate">
|
||||||
back than before.
|
<template v-slot:info>
|
||||||
</template>
|
The yearly tax you have to pay to keep your slots open. The tax is paid when unstaking or manually in the dashboard. If
|
||||||
</f-input>
|
someone pays a higher tax they can buy you out.
|
||||||
</div>
|
</template>
|
||||||
<div class="row row-2">
|
</FSelect>
|
||||||
<f-select :items="adjustTaxRate.taxRates" label="Tax" v-model="taxRate">
|
<FInput label="Floor Tax" disabled :modelValue="String(snatchSelection.floorTax)">
|
||||||
<template v-slot:info>
|
<template v-slot:info> This is the current minimum tax you have to pay to claim owner slots from other owners. </template>
|
||||||
The yearly tax you have to pay to keep your slots open. The tax is paid when unstaking
|
</FInput>
|
||||||
or manually in the dashboard. If someone pays a higher tax they can buy you out.
|
<FInput label="Positions Buyout" disabled :modelValue="String(snatchSelection.snatchablePositions.value.length)">
|
||||||
</template>
|
<template v-slot:info>
|
||||||
</f-select>
|
This shows you the numbers of staking positions you buy out from current owners by paying a higher tax. If you get bought
|
||||||
<f-input label="Floor Tax" disabled :modelValue="snatchSelection.floorTax">
|
out yourself by new owners you get paid out the current market value of your position incl. your profits.
|
||||||
<template v-slot:info>
|
</template>
|
||||||
This is the current minimum tax you have to pay to claim owner slots from other owners.
|
</FInput>
|
||||||
</template>
|
</div>
|
||||||
</f-input>
|
</div>
|
||||||
<f-input label="Positions Buyout" disabled :modelValue="snatchSelection.snatchablePositions.length">
|
<FButton size="large" disabled block v-if="stake.state === 'NoBalance'">Insufficient Balance</FButton>
|
||||||
<template v-slot:info>
|
<FButton size="large" disabled block v-else-if="stake.stakingAmountNumber < minStakeAmount">Stake amount too low</FButton>
|
||||||
This shows you the numbers of staking positions you buy out from current owners by
|
<FButton
|
||||||
paying a higher tax. If you get bought out yourself by new owners you get paid out the
|
size="large"
|
||||||
current market value of your position incl. your profits.
|
disabled
|
||||||
</template>
|
block
|
||||||
</f-input>
|
v-else-if="
|
||||||
</div>
|
!snatchSelection.openPositionsAvailable && stake.state === 'StakeAble' && snatchSelection.snatchablePositions.value.length === 0
|
||||||
</div>
|
"
|
||||||
<f-button size="large" disabled block v-if="stake.state === 'NoBalance'">Insufficient Balance</f-button>
|
>taxRate too low to snatch</FButton
|
||||||
<f-button size="large" disabled block v-else-if="stake.stakingAmountNumber < minStakeAmount"
|
>
|
||||||
>Stake amount too low</f-button
|
<FButton
|
||||||
>
|
size="large"
|
||||||
<f-button
|
block
|
||||||
size="large"
|
v-else-if="stake.state === 'StakeAble' && snatchSelection.snatchablePositions.value.length === 0"
|
||||||
disabled
|
@click="stakeSnatch"
|
||||||
block
|
>Stake</FButton
|
||||||
v-else-if="
|
>
|
||||||
!snatchSelection.openPositionsAvailable && stake.state === 'StakeAble' && snatchSelection.snatchablePositions.length === 0
|
<FButton
|
||||||
"
|
size="large"
|
||||||
>taxRate too low to snatch</f-button
|
block
|
||||||
>
|
v-else-if="stake.state === 'StakeAble' && snatchSelection.snatchablePositions.value.length > 0"
|
||||||
<f-button
|
@click="stakeSnatch"
|
||||||
size="large"
|
>Snatch and Stake</FButton
|
||||||
block
|
>
|
||||||
v-else-if="stake.state === 'StakeAble' && snatchSelection.snatchablePositions.length === 0"
|
<FButton size="large" outlined block v-else-if="stake.state === 'SignTransaction'">Sign Transaction ...</FButton>
|
||||||
@click="stakeSnatch"
|
<FButton size="large" outlined block v-else-if="stake.state === 'Waiting'">Waiting ...</FButton>
|
||||||
>Stake</f-button
|
</template>
|
||||||
>
|
</div>
|
||||||
<f-button
|
</div>
|
||||||
size="large"
|
|
||||||
block
|
|
||||||
v-else-if="stake.state === 'StakeAble' && snatchSelection.snatchablePositions.length > 0"
|
|
||||||
@click="stakeSnatch"
|
|
||||||
>Snatch and Stake</f-button
|
|
||||||
>
|
|
||||||
<f-button size="large" outlined block v-else-if="stake.state === 'SignTransaction'"
|
|
||||||
>Sign Transaction ...</f-button
|
|
||||||
>
|
|
||||||
<f-button size="large" outlined block v-else-if="stake.state === 'Waiting'">Waiting ...</f-button>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import FButton from "@/components/fcomponents/FButton.vue";
|
import FButton from '@/components/fcomponents/FButton.vue';
|
||||||
import FInput from "@/components/fcomponents/FInput.vue";
|
import FInput from '@/components/fcomponents/FInput.vue';
|
||||||
import FSelect from "@/components/fcomponents/FSelect.vue";
|
import FSelect from '@/components/fcomponents/FSelect.vue';
|
||||||
import FLoader from "@/components/fcomponents/FLoader.vue";
|
import FLoader from '@/components/fcomponents/FLoader.vue';
|
||||||
import FSlider from "@/components/fcomponents/FSlider.vue";
|
import FSlider from '@/components/fcomponents/FSlider.vue';
|
||||||
import FOutput from "@/components/fcomponents/FOutput.vue";
|
import { Icon } from '@iconify/vue';
|
||||||
import { Icon } from "@iconify/vue";
|
import { bigInt2Number } from '@/utils/helper';
|
||||||
import { formatBigIntDivision, InsertCommaNumber, formatBigNumber, bigInt2Number } from "@/utils/helper";
|
import { loadPositions, usePositions, type Position } from '@/composables/usePositions';
|
||||||
import { formatUnits } from "viem";
|
import { useStake } from '@/composables/useStake';
|
||||||
import { loadPositions, usePositions } from "@/composables/usePositions";
|
import { useClaim } from '@/composables/useClaim';
|
||||||
import { useStake } from "@/composables/useStake";
|
import { useAdjustTaxRate } from '@/composables/useAdjustTaxRates';
|
||||||
import { useClaim } from "@/composables/useClaim";
|
import { useSnatchSelection } from '@/composables/useSnatchSelection';
|
||||||
import { useAdjustTaxRate } from "@/composables/useAdjustTaxRates";
|
import { assetsToShares } from '@/contracts/stake';
|
||||||
import { useSnatchSelection } from "@/composables/useSnatchSelection";
|
import { getMinStake } from '@/contracts/harb';
|
||||||
import { getMinStake } from "@/contracts/harb";
|
import { useWallet } from '@/composables/useWallet';
|
||||||
import { useWallet } from "@/composables/useWallet";
|
import { ref, onMounted, watch, computed, watchEffect } from 'vue';
|
||||||
import { ref, onMounted, watch, computed, inject, watchEffect } from "vue";
|
import { useStatCollection, loadStats } from '@/composables/useStatCollection';
|
||||||
import { useStatCollection, loadStats } from "@/composables/useStatCollection";
|
import { useRoute } from 'vue-router';
|
||||||
import { useRoute } from "vue-router";
|
|
||||||
|
|
||||||
const demo = sessionStorage.getItem("demo") === "true";
|
const demo = sessionStorage.getItem('demo') === 'true';
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
const adjustTaxRate = useAdjustTaxRate();
|
const adjustTaxRate = useAdjustTaxRate();
|
||||||
|
|
||||||
const activeTab = ref("stake");
|
|
||||||
const StakeMenuOpen = ref(false);
|
const StakeMenuOpen = ref(false);
|
||||||
const taxRate = ref<number>(1.0);
|
const taxRate = ref<number>(1.0);
|
||||||
const loading = ref<boolean>(true);
|
const loading = ref<boolean>(true);
|
||||||
const stakeSnatchLoading = ref<boolean>(false);
|
const stakeSnatchLoading = ref<boolean>(false);
|
||||||
const stake = useStake();
|
const stake = useStake();
|
||||||
const claim = useClaim();
|
const _claim = useClaim();
|
||||||
const wallet = useWallet();
|
const wallet = useWallet();
|
||||||
const statCollection = useStatCollection();
|
const statCollection = useStatCollection();
|
||||||
|
|
||||||
const { activePositions } = usePositions();
|
const { activePositions: _activePositions } = usePositions();
|
||||||
|
|
||||||
const minStake = ref(0n);
|
const minStake = ref(0n);
|
||||||
const stakeSlots = ref();
|
const stakeSlots = ref();
|
||||||
const supplyFreeze = ref<number>(0);
|
const supplyFreeze = ref<number>(0);
|
||||||
let debounceTimer: ReturnType<typeof setTimeout>;
|
let debounceTimer: ReturnType<typeof setTimeout>;
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
console.log("supplyFreeze");
|
if (!stake.stakingAmount) {
|
||||||
|
supplyFreeze.value = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!stake.stakingAmount) {
|
clearTimeout(debounceTimer);
|
||||||
supplyFreeze.value = 0;
|
debounceTimer = setTimeout(async () => {
|
||||||
return;
|
stake.stakingAmountShares = await assetsToShares(stake.stakingAmount);
|
||||||
}
|
const stakingAmountSharesNumber = bigInt2Number(stake.stakingAmountShares, 18);
|
||||||
|
const stakeableSupplyNumber = bigInt2Number(statCollection.stakeableSupply, 18);
|
||||||
|
minStake.value = await getMinStake();
|
||||||
|
|
||||||
clearTimeout(debounceTimer);
|
supplyFreeze.value = stakingAmountSharesNumber / stakeableSupplyNumber;
|
||||||
debounceTimer = setTimeout(async () => {
|
}, 500);
|
||||||
stake.stakingAmountShares = await assetsToShares(stake.stakingAmount);
|
|
||||||
const stakingAmountSharesNumber = bigInt2Number(stake.stakingAmountShares, 18);
|
|
||||||
const stakeableSupplyNumber = bigInt2Number(statCollection.stakeableSupply, 18);
|
|
||||||
minStake.value = await getMinStake();
|
|
||||||
|
|
||||||
console.log(stakingAmountSharesNumber / stakeableSupplyNumber);
|
|
||||||
supplyFreeze.value = stakingAmountSharesNumber / stakeableSupplyNumber;
|
|
||||||
}, 500); // Verzögerung von 500ms
|
|
||||||
});
|
});
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
console.log("stakeSlots");
|
stakeSlots.value = (supplyFreeze.value * 1000)?.toFixed(2);
|
||||||
stakeSlots.value = (supplyFreeze.value * 1000)?.toFixed(2);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const tokenIssuance = computed(() => {
|
const _tokenIssuance = computed(() => {
|
||||||
if (statCollection.kraikenTotalSupply === 0n) {
|
if (statCollection.kraikenTotalSupply === 0n) {
|
||||||
return 0n;
|
return 0n;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (statCollection.nettoToken7d / statCollection.kraikenTotalSupply) * 100n;
|
return (statCollection.nettoToken7d / statCollection.kraikenTotalSupply) * 100n;
|
||||||
});
|
});
|
||||||
|
|
||||||
async function stakeSnatch() {
|
async function stakeSnatch() {
|
||||||
if (snatchSelection.snatchablePositions.value.length === 0) {
|
if (snatchSelection.snatchablePositions.value.length === 0) {
|
||||||
await stake.snatch(stake.stakingAmount, taxRate.value);
|
await stake.snatch(stake.stakingAmount, taxRate.value);
|
||||||
} else {
|
} else {
|
||||||
const snatchAblePositionsIds = snatchSelection.snatchablePositions.value.map((p: Position) => p.positionId);
|
const snatchAblePositionsIds = snatchSelection.snatchablePositions.value.map((p: Position) => p.positionId);
|
||||||
await stake.snatch(stake.stakingAmount, taxRate.value, snatchAblePositionsIds);
|
await stake.snatch(stake.stakingAmount, taxRate.value, snatchAblePositionsIds);
|
||||||
}
|
}
|
||||||
stakeSnatchLoading.value = true;
|
stakeSnatchLoading.value = true;
|
||||||
await new Promise((resolve) => setTimeout(resolve, 10000));
|
await new Promise(resolve => setTimeout(resolve, 10000));
|
||||||
await loadPositions();
|
await loadPositions();
|
||||||
await loadStats();
|
await loadStats();
|
||||||
stakeSnatchLoading.value = false;
|
stakeSnatchLoading.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
route,
|
route,
|
||||||
async (to) => {
|
async to => {
|
||||||
console.log("to", to.hash);
|
if (to.hash === '#stake') {
|
||||||
if (to.hash === "#stake") {
|
StakeMenuOpen.value = true;
|
||||||
console.log("StakeMenuOpen", StakeMenuOpen.value);
|
}
|
||||||
StakeMenuOpen.value = true;
|
},
|
||||||
}
|
{ flush: 'pre', immediate: true, deep: true }
|
||||||
},
|
|
||||||
{ flush: "pre", immediate: true, deep: true }
|
|
||||||
);
|
);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
try {
|
try {
|
||||||
minStake.value = await getMinStake();
|
minStake.value = await getMinStake();
|
||||||
stake.stakingAmountNumber = minStakeAmount.value;
|
stake.stakingAmountNumber = minStakeAmount.value;
|
||||||
} catch (error) {
|
} finally {
|
||||||
console.error("error", error);
|
loading.value = false;
|
||||||
} finally {
|
}
|
||||||
loading.value = false;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const minStakeAmount = computed(() => {
|
const minStakeAmount = computed(() => {
|
||||||
console.log("minStake", minStake.value);
|
return bigInt2Number(minStake.value, 18);
|
||||||
return bigInt2Number(minStake.value, 18);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const maxStakeAmount = computed(() => {
|
const maxStakeAmount = computed(() => {
|
||||||
if (wallet.balance?.value) {
|
if (wallet.balance?.value) {
|
||||||
console.log("wallet.balance.value", wallet.balance);
|
return bigInt2Number(wallet.balance.value, 18);
|
||||||
console.log("formatBigIntDivision(wallet.balance.value, 10n ** 18n)", bigInt2Number(wallet.balance.value, 18));
|
} else {
|
||||||
return bigInt2Number(wallet.balance.value, 18);
|
return 0;
|
||||||
} else {
|
}
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
minStakeAmount,
|
minStakeAmount,
|
||||||
async (newValue) => {
|
async newValue => {
|
||||||
console.log("newValue", newValue);
|
if (newValue > stake.stakingAmountNumber && stake.stakingAmountNumber === 0) {
|
||||||
if (newValue > stake.stakingAmountNumber && stake.stakingAmountNumber === 0) {
|
stake.stakingAmountNumber = minStakeAmount.value;
|
||||||
stake.stakingAmountNumber = minStakeAmount.value;
|
}
|
||||||
}
|
},
|
||||||
},
|
{ immediate: true }
|
||||||
{ immediate: true }
|
|
||||||
);
|
);
|
||||||
|
|
||||||
function setMaxAmount() {
|
function setMaxAmount() {
|
||||||
console.log("maxStakeAmount.value", maxStakeAmount.value);
|
stake.stakingAmountNumber = maxStakeAmount.value;
|
||||||
stake.stakingAmountNumber = maxStakeAmount.value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const snatchSelection = useSnatchSelection(demo);
|
const snatchSelection = useSnatchSelection(demo, taxRate);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
@ -274,4 +246,4 @@ const snatchSelection = useSnatchSelection(demo);
|
||||||
.stake-arrow
|
.stake-arrow
|
||||||
align-self: center
|
align-self: center
|
||||||
font-size: 30px
|
font-size: 30px
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,42 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="stats-output" :styles="styles">
|
<div class="stats-output" :styles="styles">
|
||||||
<f-card>
|
<FCard>
|
||||||
<h6>{{ props.headline }}</h6>
|
<h6>{{ props.headline }}</h6>
|
||||||
<f-output :name="props.name" :price="props.price">
|
<FOutput :name="props.name" :price="props.price">
|
||||||
<template #price>
|
<template #price>
|
||||||
<slot name="price"></slot>
|
<slot name="price"></slot>
|
||||||
</template>
|
</template>
|
||||||
</f-output>
|
</FOutput>
|
||||||
</f-card>
|
</FCard>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from "vue";
|
import { computed } from 'vue';
|
||||||
import FCard from "@/components/fcomponents/FCard.vue"
|
import FCard from '@/components/fcomponents/FCard.vue';
|
||||||
import FOutput from "@/components/fcomponents/FOutput.vue"
|
import FOutput from '@/components/fcomponents/FOutput.vue';
|
||||||
interface Props {
|
interface Props {
|
||||||
name?: string;
|
name?: string;
|
||||||
headline: string;
|
headline: string;
|
||||||
price: string | number;
|
price: string | number;
|
||||||
width?: number;
|
width?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Styles {
|
interface Styles {
|
||||||
width?: string;
|
width?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {});
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
name: '',
|
||||||
|
width: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
const styles = computed(() => {
|
const styles = computed(() => {
|
||||||
const returnObject: Styles = {};
|
const returnObject: Styles = {};
|
||||||
if (props.width) {
|
if (props.width) {
|
||||||
returnObject.width = `${props.width}px`;
|
returnObject.width = `${props.width}px`;
|
||||||
}
|
}
|
||||||
return returnObject;
|
return returnObject;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,121 +0,0 @@
|
||||||
<template>
|
|
||||||
<f-card :bg-color="bgcolor" :border-width="0" box-shadow="unset">
|
|
||||||
<div class="info-popup">
|
|
||||||
<div class="info-popup__header">
|
|
||||||
<h6>{{ props.header }}</h6>
|
|
||||||
</div>
|
|
||||||
<div class="info-popup__body">
|
|
||||||
<div>
|
|
||||||
{{ props.subheader }}
|
|
||||||
</div>
|
|
||||||
<div v-if="props.value">
|
|
||||||
<span class="number-big">{{ props.value }}</span
|
|
||||||
> <span>{{ props.token }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="!props.value">
|
|
||||||
<hr />
|
|
||||||
</div>
|
|
||||||
<div class="info-popup__body2" v-if="props.info" v-html="props.info"></div>
|
|
||||||
<div class="info-popup__footer">
|
|
||||||
<f-button light block small @click="closeToast">Okay</f-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- <div class="body">
|
|
||||||
<h6 class="header">test{{ props.header }}</h6>
|
|
||||||
<div class="subheader toast-header">test{{ props.subheader }}</div>
|
|
||||||
<div class="amount-number">
|
|
||||||
<div class="token-amount number-mobile">test{{ props.value }}</div>
|
|
||||||
<div class="token-name">test{{ props.token }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="info toast-body">test{{ props.info }}</div>
|
|
||||||
</div> -->
|
|
||||||
</f-card>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { getCurrentInstance, computed } from "vue";
|
|
||||||
import FButton from "@/components/fcomponents/FButton.vue";
|
|
||||||
import FCard from "@/components/fcomponents/FCard.vue";
|
|
||||||
import { useToast } from "vue-toastification";
|
|
||||||
import type { ToastID } from "vue-toastification/dist/types/types";
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
value: String,
|
|
||||||
header: String,
|
|
||||||
subheader: String,
|
|
||||||
info: String,
|
|
||||||
token: String,
|
|
||||||
type: String,
|
|
||||||
});
|
|
||||||
|
|
||||||
const bgcolor = computed(() => {
|
|
||||||
let color = "white";
|
|
||||||
console.log("props.type");
|
|
||||||
console.log(props.type);
|
|
||||||
switch (props.type) {
|
|
||||||
case "info":
|
|
||||||
color = "#5f4884";
|
|
||||||
break;
|
|
||||||
case "error":
|
|
||||||
color = "#8B0000";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return color;
|
|
||||||
});
|
|
||||||
|
|
||||||
// element.classList.add("toast-open");
|
|
||||||
const toast = useToast();
|
|
||||||
|
|
||||||
const instance = getCurrentInstance();
|
|
||||||
instance!.parent!.parent!.vnode!.el!.classList.add("toast-open");
|
|
||||||
const id = instance!.attrs["toast-id"] as ToastID;
|
|
||||||
|
|
||||||
console.log("instance", instance!.attrs["toast-id"]);
|
|
||||||
|
|
||||||
function closeToast() {
|
|
||||||
instance!.parent!.parent!.vnode!.el!.classList.remove("toast-open");
|
|
||||||
toast.dismiss(id);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style lang="sass">
|
|
||||||
.info-popup
|
|
||||||
width: 342px
|
|
||||||
display: flex
|
|
||||||
color: var(--color-white)
|
|
||||||
flex-direction: column
|
|
||||||
gap: 16px
|
|
||||||
font-size: var(--font-body1)
|
|
||||||
text-align: center
|
|
||||||
hr
|
|
||||||
border: 1px solid var(--color-grey)
|
|
||||||
.info-popup__header
|
|
||||||
h6
|
|
||||||
margin: 0
|
|
||||||
color: var(--color-white)
|
|
||||||
.info-popup__body
|
|
||||||
display: flex
|
|
||||||
flex-direction: column
|
|
||||||
gap: 8px
|
|
||||||
|
|
||||||
.Vue-Toastification__container
|
|
||||||
&.toast-open
|
|
||||||
background-color: rgb(15, 15, 15, 0.7)
|
|
||||||
height: 100vh
|
|
||||||
width: 100vw
|
|
||||||
position: fixed
|
|
||||||
left: 0
|
|
||||||
bottom: 0
|
|
||||||
z-index: 10
|
|
||||||
@media (min-width: 992px)
|
|
||||||
background-color: unset
|
|
||||||
&.top-center
|
|
||||||
@media (min-width: 600px)
|
|
||||||
left: unset
|
|
||||||
margin-left: unset
|
|
||||||
.Vue-Toastification__toast
|
|
||||||
box-shadow: unset
|
|
||||||
&.modal-overlay
|
|
||||||
background-color: unset
|
|
||||||
</style>
|
|
||||||
118
web-app/src/components/ToastNotification.vue
Normal file
118
web-app/src/components/ToastNotification.vue
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
<template>
|
||||||
|
<FCard :bg-color="bgcolor" :border-width="0" box-shadow="unset">
|
||||||
|
<div class="info-popup">
|
||||||
|
<div class="info-popup__header">
|
||||||
|
<h6>{{ props.header }}</h6>
|
||||||
|
</div>
|
||||||
|
<div class="info-popup__body">
|
||||||
|
<div>
|
||||||
|
{{ props.subheader }}
|
||||||
|
</div>
|
||||||
|
<div v-if="props.value">
|
||||||
|
<span class="number-big">{{ props.value }}</span
|
||||||
|
> <span>{{ props.token }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="!props.value">
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
<div class="info-popup__body2" v-if="props.info" v-html="props.info"></div>
|
||||||
|
<div class="info-popup__footer">
|
||||||
|
<FButton light block small @click="closeToast">Okay</FButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="body">
|
||||||
|
<h6 class="header">test{{ props.header }}</h6>
|
||||||
|
<div class="subheader toast-header">test{{ props.subheader }}</div>
|
||||||
|
<div class="amount-number">
|
||||||
|
<div class="token-amount number-mobile">test{{ props.value }}</div>
|
||||||
|
<div class="token-name">test{{ props.token }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="info toast-body">test{{ props.info }}</div>
|
||||||
|
</div> -->
|
||||||
|
</FCard>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { getCurrentInstance, computed } from 'vue';
|
||||||
|
import FButton from '@/components/fcomponents/FButton.vue';
|
||||||
|
import FCard from '@/components/fcomponents/FCard.vue';
|
||||||
|
import { useToast } from 'vue-toastification';
|
||||||
|
import type { ToastID } from 'vue-toastification/dist/types/types';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
value?: string;
|
||||||
|
header?: string;
|
||||||
|
subheader?: string;
|
||||||
|
info?: string;
|
||||||
|
token?: string;
|
||||||
|
type?: string;
|
||||||
|
}
|
||||||
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
|
const bgcolor = computed(() => {
|
||||||
|
let color = 'white';
|
||||||
|
switch (props.type) {
|
||||||
|
case 'info':
|
||||||
|
color = '#5f4884';
|
||||||
|
break;
|
||||||
|
case 'error':
|
||||||
|
color = '#8B0000';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return color;
|
||||||
|
});
|
||||||
|
|
||||||
|
// element.classList.add("toast-open");
|
||||||
|
const toast = useToast();
|
||||||
|
|
||||||
|
const instance = getCurrentInstance();
|
||||||
|
instance!.parent!.parent!.vnode!.el!.classList.add('toast-open');
|
||||||
|
const id = instance!.attrs['toast-id'] as ToastID;
|
||||||
|
|
||||||
|
function closeToast() {
|
||||||
|
instance!.parent!.parent!.vnode!.el!.classList.remove('toast-open');
|
||||||
|
toast.dismiss(id);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="sass">
|
||||||
|
.info-popup
|
||||||
|
width: 342px
|
||||||
|
display: flex
|
||||||
|
color: var(--color-white)
|
||||||
|
flex-direction: column
|
||||||
|
gap: 16px
|
||||||
|
font-size: var(--font-body1)
|
||||||
|
text-align: center
|
||||||
|
hr
|
||||||
|
border: 1px solid var(--color-grey)
|
||||||
|
.info-popup__header
|
||||||
|
h6
|
||||||
|
margin: 0
|
||||||
|
color: var(--color-white)
|
||||||
|
.info-popup__body
|
||||||
|
display: flex
|
||||||
|
flex-direction: column
|
||||||
|
gap: 8px
|
||||||
|
|
||||||
|
.Vue-Toastification__container
|
||||||
|
&.toast-open
|
||||||
|
background-color: rgb(15, 15, 15, 0.7)
|
||||||
|
height: 100vh
|
||||||
|
width: 100vw
|
||||||
|
position: fixed
|
||||||
|
left: 0
|
||||||
|
bottom: 0
|
||||||
|
z-index: 10
|
||||||
|
@media (min-width: 992px)
|
||||||
|
background-color: unset
|
||||||
|
&.top-center
|
||||||
|
@media (min-width: 600px)
|
||||||
|
left: unset
|
||||||
|
margin-left: unset
|
||||||
|
.Vue-Toastification__toast
|
||||||
|
box-shadow: unset
|
||||||
|
&.modal-overlay
|
||||||
|
background-color: unset
|
||||||
|
</style>
|
||||||
|
|
@ -1,30 +1,24 @@
|
||||||
<template>
|
<template>
|
||||||
<chart-js
|
<ChartJs :snatchedPositions="snatchPositions.map(obj => obj.id)" :positions="activePositions" :dark="darkTheme"></ChartJs>
|
||||||
:snatchedPositions="snatchPositions.map((obj) => obj.id)"
|
|
||||||
:positions="activePositions"
|
|
||||||
:dark="darkTheme"
|
|
||||||
></chart-js>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import ChartJs from "@/components/chart/ChartJs.vue";
|
import ChartJs from '@/components/chart/ChartJs.vue';
|
||||||
import {bigInt2Number, formatBigIntDivision} from "@/utils/helper";
|
import { bigInt2Number, formatBigIntDivision } from '@/utils/helper';
|
||||||
import { computed, ref } from "vue";
|
import { computed, ref } from 'vue';
|
||||||
import { useStatCollection } from "@/composables/useStatCollection";
|
import { useStatCollection } from '@/composables/useStatCollection';
|
||||||
import { useStake } from "@/composables/useStake";
|
import { useStake } from '@/composables/useStake';
|
||||||
import { usePositions, type Position } from "@/composables/usePositions";
|
import { usePositions, type Position } from '@/composables/usePositions';
|
||||||
import { useDark } from "@/composables/useDark";
|
import { useDark } from '@/composables/useDark';
|
||||||
const { darkTheme } = useDark();
|
const { darkTheme } = useDark();
|
||||||
|
|
||||||
const { activePositions, myActivePositions, tresholdValue, myClosedPositions, createRandomPosition } = usePositions();
|
const { activePositions } = usePositions();
|
||||||
const ignoreOwner = ref(false);
|
const ignoreOwner = ref(false);
|
||||||
|
|
||||||
const taxRate = ref<number>(1.0);
|
const taxRate = ref<number>(1.0);
|
||||||
|
|
||||||
const minStakeAmount = computed(() => {
|
const minStakeAmount = computed(() => {
|
||||||
console.log("minStake", minStake.value);
|
return formatBigIntDivision(minStake.value, 10n ** 18n);
|
||||||
|
|
||||||
return formatBigIntDivision(minStake.value, 10n ** 18n);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const stakeAbleHarbAmount = computed(() => statCollection.kraikenTotalSupply / 5n);
|
const stakeAbleHarbAmount = computed(() => statCollection.kraikenTotalSupply / 5n);
|
||||||
|
|
@ -34,35 +28,33 @@ const minStake = computed(() => stakeAbleHarbAmount.value / 600n);
|
||||||
const stake = useStake();
|
const stake = useStake();
|
||||||
const statCollection = useStatCollection();
|
const statCollection = useStatCollection();
|
||||||
const snatchPositions = computed(() => {
|
const snatchPositions = computed(() => {
|
||||||
if (
|
if (
|
||||||
bigInt2Number(statCollection.outstandingStake, 18) + stake.stakingAmountNumber <=
|
bigInt2Number(statCollection.outstandingStake, 18) + stake.stakingAmountNumber <=
|
||||||
bigInt2Number(statCollection.kraikenTotalSupply, 18) * 0.2
|
bigInt2Number(statCollection.kraikenTotalSupply, 18) * 0.2
|
||||||
) {
|
) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
//Differenz aus outstandingSupply und totalSupply bestimmen, wie viel HARB kann zum Snatch verwendet werden
|
//Differenz aus outstandingSupply und totalSupply bestimmen, wie viel HARB kann zum Snatch verwendet werden
|
||||||
const difference =
|
const difference =
|
||||||
bigInt2Number(statCollection.outstandingStake, 18) +
|
bigInt2Number(statCollection.outstandingStake, 18) +
|
||||||
stake.stakingAmountNumber -
|
stake.stakingAmountNumber -
|
||||||
bigInt2Number(statCollection.kraikenTotalSupply, 18) * 0.2;
|
bigInt2Number(statCollection.kraikenTotalSupply, 18) * 0.2;
|
||||||
console.log("difference", difference);
|
|
||||||
|
|
||||||
//Division ohne Rest, um zu schauen wie viele Positionen gesnatched werden könnten
|
//Division ohne Rest, um zu schauen wie viele Positionen gesnatched werden könnten
|
||||||
const snatchAblePositionsCount = Math.floor(difference / minStakeAmount.value);
|
const snatchAblePositionsCount = Math.floor(difference / minStakeAmount.value);
|
||||||
|
|
||||||
//wenn mehr als 0 Positionen gesnatched werden könnten, wird geschaut wie viele Positionen in Frage kommen
|
//wenn mehr als 0 Positionen gesnatched werden könnten, wird geschaut wie viele Positionen in Frage kommen
|
||||||
if (snatchAblePositionsCount > 0) {
|
if (snatchAblePositionsCount > 0) {
|
||||||
const snatchAblePositions = activePositions.value.filter((obj: Position) => {
|
const snatchAblePositions = activePositions.value.filter((obj: Position) => {
|
||||||
if (ignoreOwner.value) {
|
if (ignoreOwner.value) {
|
||||||
return obj.taxRatePercentage < taxRate.value;
|
return obj.taxRatePercentage < taxRate.value;
|
||||||
}
|
}
|
||||||
return obj.taxRatePercentage < taxRate.value && !obj.iAmOwner;
|
return obj.taxRatePercentage < taxRate.value && !obj.iAmOwner;
|
||||||
});
|
});
|
||||||
const slicedArray = snatchAblePositions.slice(0, snatchAblePositionsCount);
|
const slicedArray = snatchAblePositions.slice(0, snatchAblePositionsCount);
|
||||||
return slicedArray;
|
return slicedArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,79 +1,68 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="positions-graph">
|
<div class="positions-graph">
|
||||||
<div class="chart-modal" :class="{ 'chart--fullscreen': fullscreenOpen }" @click="toggleFullscreen">
|
<div class="chart-modal" :class="{ 'chart--fullscreen': fullscreenOpen }" @click="toggleFullscreen">
|
||||||
<div class="chart-inner" @click.stop>
|
<div class="chart-inner" @click.stop>
|
||||||
<div class="chart--header">
|
<div class="chart--header">
|
||||||
<div class="chart--actions" v-if="props.positions?.length > 0">
|
<div class="chart--actions" v-if="props.positions?.length > 0">
|
||||||
<reset-zoom-button @click="resetZoom"></reset-zoom-button>
|
<ResetZoomButton @click="resetZoom"></ResetZoomButton>
|
||||||
<fullscreen-button v-if="isMobile" @click="toggleFullscreen"></fullscreen-button>
|
<FullscreenButton v-if="isMobile" @click="toggleFullscreen"></FullscreenButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div class="chart--body" :class="{ 'disable-actions': props.positions?.length === 0, dark: props.dark }">
|
||||||
class="chart--body"
|
<canvas ref="Chart1"></canvas>
|
||||||
:class="{ 'disable-actions': props.positions?.length === 0, dark: props.dark }"
|
<template v-if="props.positions?.length === 0">
|
||||||
>
|
<p class="chart--no-positions">No positions</p>
|
||||||
<canvas ref="Chart1"></canvas>
|
</template>
|
||||||
<template v-if="props.positions?.length === 0">
|
</div>
|
||||||
<p class="chart--no-positions">No positions</p>
|
</div>
|
||||||
</template>
|
</div>
|
||||||
</div>
|
<div class="tooltipRef" ref="tooltipRef" :class="{ iAmOwner: activePosition?.iAmOwner }">
|
||||||
</div>
|
<template v-if="activePosition?.iAmOwner">
|
||||||
</div>
|
<p>Your staking position</p>
|
||||||
<div class="tooltipRef" ref="tooltipRef" :class="{ iAmOwner: activePosition?.iAmOwner }">
|
</template>
|
||||||
<template v-if="activePosition?.iAmOwner">
|
<template v-else>
|
||||||
<p>Your staking position</p>
|
<p>Staking position</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<b>ID {{ activePosition?.id }}</b>
|
||||||
<p>Staking position</p>
|
<b>{{ activePosition?.amount }} $KRK</b>
|
||||||
</template>
|
<b>Tax {{ activePosition?.taxRatePercentage }} %</b>
|
||||||
<b>ID {{ activePosition?.id }}</b>
|
</div>
|
||||||
<b>{{ activePosition?.amount }} $KRK</b>
|
</div>
|
||||||
<b>Tax {{ activePosition?.taxRatePercentage }} %</b>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref, watch, shallowRef, computed } from "vue";
|
import { onMounted, ref, watch, shallowRef } from 'vue';
|
||||||
import {
|
import {
|
||||||
BarController,
|
BarController,
|
||||||
BarElement,
|
BarElement,
|
||||||
CategoryScale,
|
CategoryScale,
|
||||||
Chart,
|
Chart,
|
||||||
LineController,
|
LineController,
|
||||||
LineElement,
|
LineElement,
|
||||||
LinearScale,
|
LinearScale,
|
||||||
PointElement,
|
PointElement,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
} from "chart.js";
|
type TooltipModel,
|
||||||
|
type ChartConfiguration,
|
||||||
|
} from 'chart.js';
|
||||||
// import { Chart } from "chart.js";
|
// import { Chart } from "chart.js";
|
||||||
import zoomPlugin from "chartjs-plugin-zoom";
|
import zoomPlugin from 'chartjs-plugin-zoom';
|
||||||
import { useAccount } from "@wagmi/vue";
|
import { useAccount } from '@wagmi/vue';
|
||||||
import { useMobile } from "@/composables/useMobile";
|
import { useMobile } from '@/composables/useMobile';
|
||||||
import type { Position } from "@/composables/usePositions";
|
import type { Position } from '@/composables/usePositions';
|
||||||
import FullscreenButton from "@/components/chart/FullscreenButton.vue";
|
import FullscreenButton from '@/components/chart/FullscreenButton.vue';
|
||||||
import ResetZoomButton from "@/components/chart/ResetZoomButton.vue";
|
import ResetZoomButton from '@/components/chart/ResetZoomButton.vue';
|
||||||
Chart.register(
|
Chart.register(zoomPlugin, LinearScale, CategoryScale, BarController, BarElement, LineController, LineElement, PointElement, Tooltip);
|
||||||
zoomPlugin,
|
|
||||||
LinearScale,
|
|
||||||
CategoryScale,
|
|
||||||
BarController,
|
|
||||||
BarElement,
|
|
||||||
LineController,
|
|
||||||
LineElement,
|
|
||||||
PointElement,
|
|
||||||
Tooltip
|
|
||||||
);
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
positions: Array<Position>;
|
positions: Array<Position>;
|
||||||
snatchedPositions: Array<string>;
|
snatchedPositions: Array<string>;
|
||||||
dark?: boolean;
|
dark?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
dark: false,
|
dark: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const Chart1 = ref();
|
const Chart1 = ref();
|
||||||
|
|
@ -81,298 +70,290 @@ const fullscreenOpen = ref(false);
|
||||||
const myChart = ref();
|
const myChart = ref();
|
||||||
const activePosition = ref();
|
const activePosition = ref();
|
||||||
const tooltipRef = ref();
|
const tooltipRef = ref();
|
||||||
const positionSnatched = ref();
|
const _positionSnatched = ref();
|
||||||
|
|
||||||
const account = useAccount();
|
const account = useAccount();
|
||||||
const isMobile = useMobile();
|
const isMobile = useMobile();
|
||||||
|
|
||||||
function resetZoom() {
|
function resetZoom() {
|
||||||
console.log("resetZoom", { Chart1, myChart });
|
myChart.value.value.resetZoom();
|
||||||
myChart.value.value.resetZoom();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleFullscreen() {
|
function toggleFullscreen() {
|
||||||
if (fullscreenOpen.value) {
|
if (fullscreenOpen.value) {
|
||||||
document.body.style.position = "unset";
|
document.body.style.position = 'unset';
|
||||||
document.body.style.overflow = "unset";
|
document.body.style.overflow = 'unset';
|
||||||
} else {
|
} else {
|
||||||
document.body.style.overflow = "hidden";
|
document.body.style.overflow = 'hidden';
|
||||||
document.body.style.position = "relative";
|
document.body.style.position = 'relative';
|
||||||
}
|
}
|
||||||
fullscreenOpen.value = !fullscreenOpen.value;
|
fullscreenOpen.value = !fullscreenOpen.value;
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.positions,
|
() => props.positions,
|
||||||
(newData) => {
|
() => {
|
||||||
console.log("props.positions", props.positions);
|
myChart.value.value.destroy();
|
||||||
|
renderChart(props.positions);
|
||||||
|
|
||||||
myChart.value.value.destroy();
|
// myChart.value.value.update();
|
||||||
renderChart(props.positions);
|
// myChart.value.datasets[0].bars[0].fillColor = "green"; //bar 1
|
||||||
|
},
|
||||||
// myChart.value.value.update();
|
{
|
||||||
// myChart.value.datasets[0].bars[0].fillColor = "green"; //bar 1
|
deep: true,
|
||||||
},
|
}
|
||||||
{
|
|
||||||
deep: true,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.dark,
|
() => props.dark,
|
||||||
(newData) => {
|
() => {
|
||||||
myChart.value.value.destroy();
|
myChart.value.value.destroy();
|
||||||
renderChart(props.positions);
|
renderChart(props.positions);
|
||||||
|
|
||||||
// myChart.value.value.update();
|
// myChart.value.value.update();
|
||||||
// myChart.value.datasets[0].bars[0].fillColor = "green"; //bar 1
|
// myChart.value.datasets[0].bars[0].fillColor = "green"; //bar 1
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.snatchedPositions,
|
() => props.snatchedPositions,
|
||||||
(newData) => {
|
() => {
|
||||||
// myChart.value.value.destroy();
|
// myChart.value.value.destroy();
|
||||||
// renderChart(props.positions);
|
// renderChart(props.positions);
|
||||||
let positionIndex = 0;
|
let positionIndex = 0;
|
||||||
if (myChart.value.value.data?.datasets[0]) {
|
if (myChart.value.value.data?.datasets[0]) {
|
||||||
let backgroundColorArray = myChart.value.value.data.datasets[0].backgroundColor;
|
const backgroundColorArray = myChart.value.value.data.datasets[0].backgroundColor;
|
||||||
|
|
||||||
for (let index = 0; index < backgroundColorArray.length; index++) {
|
for (let index = 0; index < backgroundColorArray.length; index++) {
|
||||||
const position: Position = myChart.value.value.data.datasets[0].data[index];
|
const position: Position = myChart.value.value.data.datasets[0].data[index];
|
||||||
if (!position) {
|
if (!position) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!position.iAmOwner) {
|
if (!position.iAmOwner) {
|
||||||
positionIndex++;
|
positionIndex++;
|
||||||
}
|
}
|
||||||
if (positionIndex <= props.snatchedPositions.length && props.snatchedPositions.includes(position.id)) {
|
if (positionIndex <= props.snatchedPositions.length && props.snatchedPositions.includes(position.id)) {
|
||||||
backgroundColorArray[index] = "##7550AE";
|
backgroundColorArray[index] = '##7550AE';
|
||||||
} else {
|
} else {
|
||||||
backgroundColorArray[index] = "##7550AE";
|
backgroundColorArray[index] = '##7550AE';
|
||||||
// const position = myChart.value.value.data.datasets[0].data[index];
|
// const position = myChart.value.value.data.datasets[0].data[index];
|
||||||
if (position.iAmOwner) {
|
if (position.iAmOwner) {
|
||||||
backgroundColorArray[index] = "#7550AE";
|
backgroundColorArray[index] = '#7550AE';
|
||||||
} else if (props.dark) {
|
} else if (props.dark) {
|
||||||
backgroundColorArray[index] = "white";
|
backgroundColorArray[index] = 'white';
|
||||||
} else {
|
} else {
|
||||||
backgroundColorArray[index] = "black";
|
backgroundColorArray[index] = 'black';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
myChart.value.value.data.datasets[0].backgroundColor = backgroundColorArray;
|
myChart.value.value.data.datasets[0].backgroundColor = backgroundColorArray;
|
||||||
|
|
||||||
myChart.value.value.ctx.save();
|
myChart.value.value.ctx.save();
|
||||||
myChart.value.value?.update();
|
myChart.value.value?.update();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
deep: true,
|
deep: true,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const externalTooltipHandler = (context: any) => {
|
const externalTooltipHandler = (context: { chart: Chart; tooltip: TooltipModel<'bar'> }) => {
|
||||||
const { chart, tooltip } = context;
|
const { chart, tooltip } = context;
|
||||||
const tooltipEl = tooltipRef.value;
|
const tooltipEl = tooltipRef.value;
|
||||||
|
|
||||||
// Tooltip ausblenden, wenn keine Daten angezeigt werden sollen
|
// Tooltip ausblenden, wenn keine Daten angezeigt werden sollen
|
||||||
if (!tooltip.opacity) {
|
if (!tooltip.opacity) {
|
||||||
tooltipEl.style.opacity = "0";
|
tooltipEl.style.opacity = '0';
|
||||||
tooltipEl.style.display = "none";
|
tooltipEl.style.display = 'none';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aktive Position setzen (Daten des angezeigten Punktes)
|
// Aktive Position setzen (Daten des angezeigten Punktes)
|
||||||
activePosition.value = tooltip.dataPoints[0].element.$context.raw;
|
activePosition.value = (tooltip.dataPoints[0] as { element?: { $context?: { raw?: Position } } }).element?.$context?.raw;
|
||||||
|
|
||||||
// Positionierung des Tooltips
|
// Positionierung des Tooltips
|
||||||
const { offsetLeft: chartX, offsetTop: chartY } = chart.canvas;
|
const { offsetLeft: chartX, offsetTop: chartY } = chart.canvas;
|
||||||
|
|
||||||
// Tooltip anpassen
|
// Tooltip anpassen
|
||||||
tooltipEl.style.opacity = "1";
|
tooltipEl.style.opacity = '1';
|
||||||
tooltipEl.style.display = "flex";
|
tooltipEl.style.display = 'flex';
|
||||||
tooltipEl.style.position = "absolute";
|
tooltipEl.style.position = 'absolute';
|
||||||
|
|
||||||
// Tooltip mittig über dem Punkt platzieren
|
// Tooltip mittig über dem Punkt platzieren
|
||||||
tooltipEl.style.left = `${chartX + tooltip.caretX}px`;
|
tooltipEl.style.left = `${chartX + tooltip.caretX}px`;
|
||||||
tooltipEl.style.top = `${chartY + tooltip.y}px`;
|
tooltipEl.style.top = `${chartY + tooltip.y}px`;
|
||||||
|
|
||||||
// Tooltip für saubere Mitte ausrichten
|
// Tooltip für saubere Mitte ausrichten
|
||||||
tooltipEl.style.transform = "translateX(-50%)";
|
tooltipEl.style.transform = 'translateX(-50%)';
|
||||||
tooltipEl.style.pointerEvents = "none";
|
tooltipEl.style.pointerEvents = 'none';
|
||||||
};
|
};
|
||||||
|
|
||||||
function renderChart(data: any) {
|
function renderChart(data: Position[]) {
|
||||||
console.log("renderChart");
|
const backgroundColors = [];
|
||||||
|
const data1 = data.map(obj => {
|
||||||
|
return {
|
||||||
|
...obj,
|
||||||
|
// taxRatePercentage: obj.taxRate * 100,
|
||||||
|
iAmOwner: obj.owner?.toLowerCase() === account.address.value?.toLowerCase(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
const backgroundColors = [];
|
for (let index = 0; index < data1.length; index++) {
|
||||||
const data1 = data.map((obj: any) => {
|
const position = data1[index];
|
||||||
return {
|
|
||||||
...obj,
|
|
||||||
// taxRatePercentage: obj.taxRate * 100,
|
|
||||||
iAmOwner: obj.owner?.toLowerCase() === account.address.value?.toLowerCase(),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
console.log("data1", data1);
|
|
||||||
|
|
||||||
for (let index = 0; index < data1.length; index++) {
|
// if(index < props.snatchedPositions){
|
||||||
const position = data1[index];
|
// backgroundColors.push("rgba(26,84,244, 0.5)");
|
||||||
|
// positionSnatched.value = index;
|
||||||
|
|
||||||
// if(index < props.snatchedPositions){
|
// }
|
||||||
// backgroundColors.push("rgba(26,84,244, 0.5)");
|
if (position.iAmOwner) {
|
||||||
// positionSnatched.value = index;
|
backgroundColors.push('rgba(117,80,174, 0.8)');
|
||||||
|
} else if (props.dark) {
|
||||||
|
backgroundColors[index] = 'white';
|
||||||
|
} else {
|
||||||
|
backgroundColors[index] = 'black';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// }
|
myChart.value = shallowRef(
|
||||||
if (position.iAmOwner) {
|
new Chart(Chart1.value, {
|
||||||
backgroundColors.push("rgba(117,80,174, 0.8)");
|
type: 'bar',
|
||||||
} else if (props.dark) {
|
data: {
|
||||||
backgroundColors[index] = "white";
|
labels: data1.map(row => row.id),
|
||||||
} else {
|
datasets: [
|
||||||
backgroundColors[index] = "black";
|
{
|
||||||
}
|
type: 'line',
|
||||||
}
|
label: 'TaxRate',
|
||||||
|
data: data1,
|
||||||
console.log("backgroundColors", backgroundColors);
|
backgroundColor: ['#7550AE'],
|
||||||
|
borderColor: ['#7550AE'],
|
||||||
myChart.value = shallowRef(
|
yAxisID: 'y',
|
||||||
new Chart(Chart1.value, {
|
parsing: {
|
||||||
type: "bar",
|
yAxisKey: 'taxRatePercentage',
|
||||||
data: {
|
xAxisKey: 'id',
|
||||||
labels: data1.map((row: any) => row.id),
|
},
|
||||||
datasets: [
|
},
|
||||||
{
|
{
|
||||||
type: "line",
|
type: 'bar',
|
||||||
label: "TaxRate",
|
label: 'Amount',
|
||||||
data: data1,
|
data: data1,
|
||||||
backgroundColor: ["#7550AE"],
|
backgroundColor: backgroundColors,
|
||||||
borderColor: ["#7550AE"],
|
yAxisID: 'y1',
|
||||||
yAxisID: "y",
|
parsing: {
|
||||||
parsing: {
|
yAxisKey: 'amount',
|
||||||
yAxisKey: "taxRatePercentage",
|
xAxisKey: 'id',
|
||||||
xAxisKey: "id",
|
},
|
||||||
},
|
},
|
||||||
},
|
],
|
||||||
{
|
},
|
||||||
type: "bar",
|
options: {
|
||||||
label: "Amount",
|
responsive: true,
|
||||||
data: data1,
|
maintainAspectRatio: false,
|
||||||
backgroundColor: backgroundColors,
|
animation: {
|
||||||
yAxisID: "y1",
|
duration: 1,
|
||||||
parsing: {
|
},
|
||||||
yAxisKey: "amount",
|
interaction: {
|
||||||
xAxisKey: "id",
|
intersect: false,
|
||||||
},
|
mode: 'index',
|
||||||
},
|
},
|
||||||
],
|
plugins: {
|
||||||
},
|
legend: {
|
||||||
options: {
|
display: false,
|
||||||
responsive: true,
|
},
|
||||||
maintainAspectRatio: false,
|
tooltip: {
|
||||||
animation: {
|
enabled: false,
|
||||||
duration: 1,
|
position: 'nearest',
|
||||||
},
|
external: externalTooltipHandler,
|
||||||
interaction: {
|
},
|
||||||
intersect: false,
|
zoom: {
|
||||||
mode: "index",
|
pan: {
|
||||||
},
|
enabled: true,
|
||||||
plugins: {
|
mode: 'xy',
|
||||||
legend: {
|
},
|
||||||
display: false,
|
limits: {
|
||||||
},
|
y: { min: 0 },
|
||||||
tooltip: {
|
},
|
||||||
enabled: false,
|
zoom: {
|
||||||
position: "nearest",
|
wheel: {
|
||||||
external: externalTooltipHandler,
|
enabled: true,
|
||||||
},
|
},
|
||||||
zoom: {
|
pinch: {
|
||||||
pan: {
|
enabled: true,
|
||||||
enabled: true,
|
},
|
||||||
mode: "xy",
|
mode: 'xy',
|
||||||
},
|
onZoomComplete(object: { chart?: Chart }) {
|
||||||
limits: {
|
// This update is needed to display up to date zoom level in the title.
|
||||||
y: { min: 0 },
|
// Without this, previous zoom level is displayed.
|
||||||
},
|
// The reason is: title uses the same beforeUpdate hook, and is evaluated before zoom.
|
||||||
zoom: {
|
object.chart?.update('none');
|
||||||
wheel: {
|
},
|
||||||
enabled: true,
|
},
|
||||||
},
|
},
|
||||||
pinch: {
|
},
|
||||||
enabled: true,
|
scales: {
|
||||||
},
|
y: {
|
||||||
mode: "xy",
|
// type: "linear",
|
||||||
onZoomComplete(object: any) {
|
display: true,
|
||||||
// This update is needed to display up to date zoom level in the title.
|
position: 'left',
|
||||||
// Without this, previous zoom level is displayed.
|
max: Math.max(...data.map(o => o.taxRate)) * 100 * 1.5,
|
||||||
// The reason is: title uses the same beforeUpdate hook, and is evaluated before zoom.
|
// min: 0,
|
||||||
object.chart?.update("none");
|
title: {
|
||||||
},
|
display: true,
|
||||||
},
|
text: 'Tax',
|
||||||
},
|
color: '#7550AE',
|
||||||
},
|
font: {
|
||||||
scales: {
|
size: 16, // Hier die Schriftgröße ändern (z. B. 16px)
|
||||||
y: {
|
weight: 'bold', // Falls du den Text fett machen möchtest
|
||||||
// type: "linear",
|
},
|
||||||
display: true,
|
},
|
||||||
position: "left",
|
},
|
||||||
max: Math.max(...data.map((o: any) => o.taxRate)) * 100 * 1.5,
|
y1: {
|
||||||
// min: 0,
|
// type: "linear",
|
||||||
title: {
|
display: true,
|
||||||
display: true,
|
position: 'right',
|
||||||
text: "Tax",
|
max: Math.max(...data.map(o => o.amount)) * 1.5,
|
||||||
color: "#7550AE",
|
title: {
|
||||||
font: {
|
display: true,
|
||||||
size: 16, // Hier die Schriftgröße ändern (z. B. 16px)
|
text: 'Slots',
|
||||||
weight: "bold", // Falls du den Text fett machen möchtest
|
color: 'white',
|
||||||
},
|
font: {
|
||||||
},
|
size: 16, // Hier die Schriftgröße ändern (z. B. 16px)
|
||||||
},
|
weight: 'bold', // Falls du den Text fett machen möchtest
|
||||||
y1: {
|
},
|
||||||
// type: "linear",
|
},
|
||||||
display: true,
|
grid: {
|
||||||
position: "right",
|
display: false,
|
||||||
max: Math.max(...data.map((o: any) => o.amount)) * 1.5,
|
},
|
||||||
title: {
|
// min: 0,
|
||||||
display: true,
|
},
|
||||||
text: "Slots",
|
x: {
|
||||||
color: "white",
|
display: true,
|
||||||
font: {
|
ticks: {
|
||||||
size: 16, // Hier die Schriftgröße ändern (z. B. 16px)
|
display: false,
|
||||||
weight: "bold", // Falls du den Text fett machen möchtest
|
},
|
||||||
},
|
grid: {
|
||||||
},
|
display: false,
|
||||||
grid: {
|
},
|
||||||
display: false,
|
title: {
|
||||||
},
|
display: true,
|
||||||
// min: 0,
|
text: 'Positions',
|
||||||
},
|
color: 'white',
|
||||||
x: {
|
font: {
|
||||||
display: true,
|
size: 16, // Hier die Schriftgröße ändern (z. B. 16px)
|
||||||
ticks: {
|
weight: 'bold', // Falls du den Text fett machen möchtest
|
||||||
display: false,
|
},
|
||||||
},
|
},
|
||||||
grid: {
|
},
|
||||||
display: false,
|
},
|
||||||
},
|
},
|
||||||
title: {
|
plugins: [],
|
||||||
display: true,
|
} as unknown as ChartConfiguration)
|
||||||
text: "Positions",
|
);
|
||||||
color: "white",
|
|
||||||
font: {
|
|
||||||
size: 16, // Hier die Schriftgröße ändern (z. B. 16px)
|
|
||||||
weight: "bold", // Falls du den Text fett machen möchtest
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: [],
|
|
||||||
} as any)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
renderChart(props.positions);
|
renderChart(props.positions);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,24 @@
|
||||||
<template>
|
<template>
|
||||||
<button class="chart-button">
|
<button class="chart-button">
|
||||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path
|
<path class="stroke" d="M10.8027 1.00195H16.002C16.3433 1.00195 16.6201 1.27869 16.6201 1.62006V6.81927" stroke-width="1.85431" />
|
||||||
class="stroke"
|
<path
|
||||||
d="M10.8027 1.00195H16.002C16.3433 1.00195 16.6201 1.27869 16.6201 1.62006V6.81927"
|
class="stroke"
|
||||||
stroke-width="1.85431"
|
d="M16.6191 10.5273L16.6191 15.7266C16.6191 16.0679 16.3424 16.3447 16.001 16.3447L10.8018 16.3447"
|
||||||
/>
|
stroke-width="1.85431"
|
||||||
<path
|
/>
|
||||||
class="stroke"
|
<path
|
||||||
d="M16.6191 10.5273L16.6191 15.7266C16.6191 16.0679 16.3424 16.3447 16.001 16.3447L10.8018 16.3447"
|
class="stroke"
|
||||||
stroke-width="1.85431"
|
d="M7.0918 16.3457L1.89258 16.3457C1.55121 16.3457 1.27448 16.069 1.27448 15.7276L1.27448 10.5284"
|
||||||
/>
|
stroke-width="1.85431"
|
||||||
<path
|
/>
|
||||||
class="stroke"
|
<path
|
||||||
d="M7.0918 16.3457L1.89258 16.3457C1.55121 16.3457 1.27448 16.069 1.27448 15.7276L1.27448 10.5284"
|
class="stroke"
|
||||||
stroke-width="1.85431"
|
d="M1.27539 6.81836L1.27539 1.61914C1.27539 1.27777 1.55213 1.00104 1.8935 1.00104L7.09271 1.00104"
|
||||||
/>
|
stroke-width="1.85431"
|
||||||
<path
|
/>
|
||||||
class="stroke"
|
</svg>
|
||||||
d="M1.27539 6.81836L1.27539 1.61914C1.27539 1.27777 1.55213 1.00104 1.8935 1.00104L7.09271 1.00104"
|
</button>
|
||||||
stroke-width="1.85431"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
<template>
|
<template>
|
||||||
<button class="chart-button">
|
<button class="chart-button">
|
||||||
<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<mask id="path-1-inside-1_3412_52485" fill="white">
|
<mask id="path-1-inside-1_3412_52485" fill="white">
|
||||||
<path
|
<path
|
||||||
d="M12.9797 15.2305C11.7286 16.2335 10.2045 16.8366 8.60589 16.9613C7.00726 17.086 5.4081 16.7265 4.01664 15.9296C2.62518 15.1327 1.50588 13.9353 0.804464 12.4934C0.103046 11.0515 -0.14799 9.43173 0.0840395 7.84511C0.316069 6.2585 1.02041 4.77849 2.10537 3.59778C3.19033 2.41708 4.60564 1.59038 6.16702 1.22532C7.72841 0.860254 9.36354 0.973741 10.8595 1.551C12.3555 2.12826 13.643 3.14256 14.5545 4.46182L13.109 5.46048C12.3981 4.4315 11.3938 3.64038 10.227 3.19013C9.0602 2.73989 7.78485 2.65137 6.56702 2.93611C5.34918 3.22084 4.24529 3.86565 3.39905 4.78656C2.55282 5.70747 2.00345 6.86183 1.82248 8.09935C1.6415 9.33686 1.8373 10.6002 2.38439 11.7249C2.93147 12.8495 3.80449 13.7835 4.88979 14.405C5.97508 15.0266 7.22237 15.307 8.46926 15.2097C9.71615 15.1124 10.9049 14.642 11.8807 13.8597L12.9797 15.2305Z"
|
d="M12.9797 15.2305C11.7286 16.2335 10.2045 16.8366 8.60589 16.9613C7.00726 17.086 5.4081 16.7265 4.01664 15.9296C2.62518 15.1327 1.50588 13.9353 0.804464 12.4934C0.103046 11.0515 -0.14799 9.43173 0.0840395 7.84511C0.316069 6.2585 1.02041 4.77849 2.10537 3.59778C3.19033 2.41708 4.60564 1.59038 6.16702 1.22532C7.72841 0.860254 9.36354 0.973741 10.8595 1.551C12.3555 2.12826 13.643 3.14256 14.5545 4.46182L13.109 5.46048C12.3981 4.4315 11.3938 3.64038 10.227 3.19013C9.0602 2.73989 7.78485 2.65137 6.56702 2.93611C5.34918 3.22084 4.24529 3.86565 3.39905 4.78656C2.55282 5.70747 2.00345 6.86183 1.82248 8.09935C1.6415 9.33686 1.8373 10.6002 2.38439 11.7249C2.93147 12.8495 3.80449 13.7835 4.88979 14.405C5.97508 15.0266 7.22237 15.307 8.46926 15.2097C9.71615 15.1124 10.9049 14.642 11.8807 13.8597L12.9797 15.2305Z"
|
||||||
/>
|
/>
|
||||||
</mask>
|
</mask>
|
||||||
<path
|
<path
|
||||||
d="M12.9797 15.2305C11.7286 16.2335 10.2045 16.8366 8.60589 16.9613C7.00726 17.086 5.4081 16.7265 4.01664 15.9296C2.62518 15.1327 1.50588 13.9353 0.804464 12.4934C0.103046 11.0515 -0.14799 9.43173 0.0840395 7.84511C0.316069 6.2585 1.02041 4.77849 2.10537 3.59778C3.19033 2.41708 4.60564 1.59038 6.16702 1.22532C7.72841 0.860254 9.36354 0.973741 10.8595 1.551C12.3555 2.12826 13.643 3.14256 14.5545 4.46182L13.109 5.46048C12.3981 4.4315 11.3938 3.64038 10.227 3.19013C9.0602 2.73989 7.78485 2.65137 6.56702 2.93611C5.34918 3.22084 4.24529 3.86565 3.39905 4.78656C2.55282 5.70747 2.00345 6.86183 1.82248 8.09935C1.6415 9.33686 1.8373 10.6002 2.38439 11.7249C2.93147 12.8495 3.80449 13.7835 4.88979 14.405C5.97508 15.0266 7.22237 15.307 8.46926 15.2097C9.71615 15.1124 10.9049 14.642 11.8807 13.8597L12.9797 15.2305Z"
|
d="M12.9797 15.2305C11.7286 16.2335 10.2045 16.8366 8.60589 16.9613C7.00726 17.086 5.4081 16.7265 4.01664 15.9296C2.62518 15.1327 1.50588 13.9353 0.804464 12.4934C0.103046 11.0515 -0.14799 9.43173 0.0840395 7.84511C0.316069 6.2585 1.02041 4.77849 2.10537 3.59778C3.19033 2.41708 4.60564 1.59038 6.16702 1.22532C7.72841 0.860254 9.36354 0.973741 10.8595 1.551C12.3555 2.12826 13.643 3.14256 14.5545 4.46182L13.109 5.46048C12.3981 4.4315 11.3938 3.64038 10.227 3.19013C9.0602 2.73989 7.78485 2.65137 6.56702 2.93611C5.34918 3.22084 4.24529 3.86565 3.39905 4.78656C2.55282 5.70747 2.00345 6.86183 1.82248 8.09935C1.6415 9.33686 1.8373 10.6002 2.38439 11.7249C2.93147 12.8495 3.80449 13.7835 4.88979 14.405C5.97508 15.0266 7.22237 15.307 8.46926 15.2097C9.71615 15.1124 10.9049 14.642 11.8807 13.8597L12.9797 15.2305Z"
|
||||||
stroke="white"
|
stroke="white"
|
||||||
stroke-width="3.54886"
|
stroke-width="3.54886"
|
||||||
mask="url(#path-1-inside-1_3412_52485)"
|
mask="url(#path-1-inside-1_3412_52485)"
|
||||||
/>
|
/>
|
||||||
<path d="M16.0005 6.89955L13.89 0.404049L9.32 5.47956L16.0005 6.89955Z" fill="white" />
|
<path d="M16.0005 6.89955L13.89 0.404049L9.32 5.47956L16.0005 6.89955Z" fill="white" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
||||||
|
|
@ -1,120 +1,101 @@
|
||||||
<template>
|
<template>
|
||||||
<f-collapse class="f-collapse-active" @collapse:opened="loadActivePositionData" :loading="loading">
|
<FCollapse class="f-collapse-active" @collapse:opened="loadActivePositionData" :loading="loading">
|
||||||
<template v-slot:header>
|
<template v-slot:header>
|
||||||
<div class="collapse-header">
|
<div class="collapse-header">
|
||||||
<div class="collapse-header-row1">
|
<div class="collapse-header-row1">
|
||||||
<div><span class="subheader2">Tax</span> {{ props.taxRate }} %</div>
|
<div><span class="subheader2">Tax</span> {{ props.taxRate }} %</div>
|
||||||
<f-button size="tiny" @click="payTax(props.id)">Pay Tax</f-button>
|
<FButton size="tiny" @click="payTax(props.id)">Pay Tax</FButton>
|
||||||
<div class="position-id">
|
<div class="position-id">
|
||||||
<span class="subheader2">ID</span> <span class="number-small">{{ props.id }}</span>
|
<span class="subheader2">ID</span> <span class="number-small">{{ props.id }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="collapse-header-row2">
|
<div class="collapse-header-row2">
|
||||||
<div>
|
<div>
|
||||||
<div class="profit-stats-item">
|
<div class="profit-stats-item">
|
||||||
<div><b>Initial Stake</b></div>
|
<div><b>Initial Stake</b></div>
|
||||||
<div>{{ compactNumber(props.amount) }} $KRK</div>
|
<div>{{ compactNumber(props.amount) }} $KRK</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tags-list">
|
<div class="tags-list">
|
||||||
<f-tag v-if="tag">{{ tag }}</f-tag>
|
<FTag v-if="tag">{{ tag }}</FTag>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="collapse-amount">
|
<!-- <div class="collapse-amount">
|
||||||
<span class="number-small">{{ compactNumber(props.amount) }}</span>
|
<span class="number-small">{{ compactNumber(props.amount) }}</span>
|
||||||
<span class="caption"> $KRK</span>
|
<span class="caption"> $KRK</span>
|
||||||
</div> -->
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="collapsed-body">
|
<div class="collapsed-body">
|
||||||
<div class="profit-stats-wrapper">
|
<div class="profit-stats-wrapper">
|
||||||
<div class="profit-stats-item">
|
<div class="profit-stats-item">
|
||||||
<div><b>Tax Paid</b></div>
|
<div><b>Tax Paid</b></div>
|
||||||
<div>{{ taxPaidGes }} $KRK</div>
|
<div>{{ taxPaidGes }} $KRK</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="profit-stats-item">
|
<div class="profit-stats-item">
|
||||||
<div><b>Issuance Earned</b></div>
|
<div><b>Issuance Earned</b></div>
|
||||||
<div>{{ profit }} $KRK</div>
|
<div>{{ profit }} $KRK</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="profit-stats-item profit-stats-total">
|
<div class="profit-stats-item profit-stats-total">
|
||||||
<div><b>Total</b></div>
|
<div><b>Total</b></div>
|
||||||
<div>{{ total.toFixed(5) }} $KRK</div>
|
<div>{{ total.toFixed(5) }} $KRK</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="collapsed-body--actions">
|
<div class="collapsed-body--actions">
|
||||||
<div :class="{ 'collapse-menu-open': showTaxMenu }">
|
<div :class="{ 'collapse-menu-open': showTaxMenu }">
|
||||||
<f-button size="small" dense block outlined v-if="adjustTaxRate.state === 'SignTransaction'"
|
<FButton size="small" dense block outlined v-if="adjustTaxRate.state === 'SignTransaction'">Sign Transaction ...</FButton>
|
||||||
>Sign Transaction ...</f-button
|
<FButton size="small" dense outlined block v-else-if="adjustTaxRate.state === 'Waiting'" @click="unstakePosition"
|
||||||
>
|
>Waiting ...</FButton
|
||||||
<f-button
|
>
|
||||||
size="small"
|
<FButton size="small" dense block v-else-if="adjustTaxRate.state === 'Action' && !showTaxMenu" @click="showTaxMenu = true"
|
||||||
dense
|
>Adjust Tax Rate</FButton
|
||||||
outlined
|
>
|
||||||
block
|
<template v-else>
|
||||||
v-else-if="adjustTaxRate.state === 'Waiting'"
|
<div class="collapse-menu-input">
|
||||||
@click="unstakePosition"
|
<FSelect :items="filteredTaxRates" v-model="newTaxRate"> </FSelect>
|
||||||
>Waiting ...</f-button
|
</div>
|
||||||
>
|
<div>
|
||||||
<f-button
|
<FButton size="small" dense @click="changeTax(props.id, newTaxRate)">Confirm</FButton>
|
||||||
size="small"
|
<FButton size="small" dense outlined @click="showTaxMenu = false">Cancel</FButton>
|
||||||
dense
|
</div>
|
||||||
block
|
</template>
|
||||||
v-else-if="adjustTaxRate.state === 'Action' && !showTaxMenu"
|
</div>
|
||||||
@click="showTaxMenu = true"
|
<div></div>
|
||||||
>Adjust Tax Rate</f-button
|
<div>
|
||||||
>
|
<FButton size="small" dense block outlined v-if="unstake.state === 'SignTransaction'">Sign Transaction ...</FButton>
|
||||||
<template v-else>
|
<FButton size="small" dense outlined block v-else-if="unstake.state === 'Waiting'">Waiting ...</FButton>
|
||||||
<div class="collapse-menu-input">
|
<FButton size="small" dense block v-else-if="unstake.state === 'Unstakeable'" @click="unstakePosition">Unstake</FButton>
|
||||||
<f-select :items="filteredTaxRates" v-model="newTaxRate"> </f-select>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
</FCollapse>
|
||||||
<f-button size="small" dense @click="changeTax(props.id, newTaxRate)">Confirm</f-button>
|
|
||||||
<f-button size="small" dense outlined @click="showTaxMenu = false">Cancel</f-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<div></div>
|
|
||||||
<div>
|
|
||||||
<f-button size="small" dense block outlined v-if="unstake.state === 'SignTransaction'"
|
|
||||||
>Sign Transaction ...</f-button
|
|
||||||
>
|
|
||||||
<f-button size="small" dense outlined block v-else-if="unstake.state === 'Waiting'"
|
|
||||||
>Waiting ...</f-button
|
|
||||||
>
|
|
||||||
<f-button size="small" dense block v-else-if="unstake.state === 'Unstakeable'" @click="unstakePosition"
|
|
||||||
>Unstake</f-button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</f-collapse>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import FButton from "@/components/fcomponents/FButton.vue";
|
import FButton from '@/components/fcomponents/FButton.vue';
|
||||||
import FTag from "@/components/fcomponents/FTag.vue";
|
import FTag from '@/components/fcomponents/FTag.vue';
|
||||||
import FSelect from "@/components/fcomponents/FSelect.vue";
|
import FSelect from '@/components/fcomponents/FSelect.vue';
|
||||||
import FCollapse from "@/components/fcomponents/FCollapse.vue";
|
import FCollapse from '@/components/fcomponents/FCollapse.vue';
|
||||||
import { compactNumber, formatBigNumber } from "@/utils/helper";
|
import { compactNumber, formatBigNumber } from '@/utils/helper';
|
||||||
import { useUnstake } from "@/composables/useUnstake";
|
import { useUnstake } from '@/composables/useUnstake';
|
||||||
import { useAdjustTaxRate } from "@/composables/useAdjustTaxRates";
|
import { useAdjustTaxRate } from '@/composables/useAdjustTaxRates';
|
||||||
import { computed, ref, onMounted } from "vue";
|
import { computed, ref, onMounted } from 'vue';
|
||||||
import { getTaxDue, payTax } from "@/contracts/stake";
|
import { getTaxDue, payTax } from '@/contracts/stake';
|
||||||
import { type Position, loadPositions } from "@/composables/usePositions";
|
import { type Position, loadPositions } from '@/composables/usePositions';
|
||||||
import { useStatCollection } from "@/composables/useStatCollection";
|
import { useStatCollection } from '@/composables/useStatCollection';
|
||||||
|
|
||||||
import { formatUnits } from "viem";
|
import { formatUnits } from 'viem';
|
||||||
|
|
||||||
const unstake = useUnstake();
|
const unstake = useUnstake();
|
||||||
const adjustTaxRate = useAdjustTaxRate();
|
const adjustTaxRate = useAdjustTaxRate();
|
||||||
const statCollection = useStatCollection();
|
const statCollection = useStatCollection();
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
taxRate: number;
|
taxRate: number;
|
||||||
treshold: number;
|
treshold: number;
|
||||||
id: bigint;
|
id: bigint;
|
||||||
amount: number;
|
amount: number;
|
||||||
position: Position;
|
position: Position;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const showTaxMenu = ref(false);
|
const showTaxMenu = ref(false);
|
||||||
|
|
@ -125,62 +106,51 @@ const profit = ref<number>();
|
||||||
const loading = ref<boolean>(false);
|
const loading = ref<boolean>(false);
|
||||||
|
|
||||||
const tag = computed(() => {
|
const tag = computed(() => {
|
||||||
if (props.taxRate < props.treshold) {
|
if (props.taxRate < props.treshold) {
|
||||||
return "Low Tax!";
|
return 'Low Tax!';
|
||||||
}
|
}
|
||||||
return "";
|
return '';
|
||||||
});
|
});
|
||||||
|
|
||||||
const total = computed(() => props.amount + profit.value! + -taxPaidGes.value!);
|
const total = computed(() => props.amount + profit.value! + -taxPaidGes.value!);
|
||||||
|
|
||||||
async function changeTax(id: bigint, newTaxRate: number) {
|
async function changeTax(id: bigint, newTaxRate: number) {
|
||||||
await adjustTaxRate.changeTax(id, newTaxRate);
|
await adjustTaxRate.changeTax(id, newTaxRate);
|
||||||
showTaxMenu.value = false;
|
showTaxMenu.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function unstakePosition() {
|
async function unstakePosition() {
|
||||||
console.log(props.id);
|
await unstake.exitPosition(props.id);
|
||||||
await unstake.exitPosition(props.id);
|
loading.value = true;
|
||||||
loading.value = true;
|
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||||
await new Promise((resolve) => setTimeout(resolve, 5000));
|
await loadPositions();
|
||||||
console.log("loadPositions begin");
|
loading.value = false;
|
||||||
await loadPositions();
|
|
||||||
console.log("loadPositions end");
|
|
||||||
loading.value = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadActivePositionData() {
|
async function loadActivePositionData() {
|
||||||
console.log("loadActivePositionData", props.position);
|
//loadTaxDue
|
||||||
|
taxDue.value = await getTaxDue(props.id);
|
||||||
|
taxPaidGes.value = formatBigNumber(taxDue.value + BigInt(props.position.taxPaid), 18);
|
||||||
|
|
||||||
//loadTaxDue
|
//loadTotalSupply
|
||||||
taxDue.value = await getTaxDue(props.id);
|
|
||||||
console.log("taxDue", taxDue.value);
|
|
||||||
taxPaidGes.value = formatBigNumber(taxDue.value + BigInt(props.position.taxPaid), 18);
|
|
||||||
console.log("loadActivePositionData", taxPaidGes.value);
|
|
||||||
|
|
||||||
//loadTotalSupply
|
const multiplier = Number(formatUnits(props.position.totalSupplyInit, 18)) / Number(formatUnits(statCollection.kraikenTotalSupply, 18));
|
||||||
|
|
||||||
const multiplier =
|
profit.value =
|
||||||
Number(formatUnits(props.position.totalSupplyInit, 18)) /
|
Number(formatUnits(statCollection.kraikenTotalSupply, 18)) * multiplier - Number(formatUnits(statCollection.kraikenTotalSupply, 18));
|
||||||
Number(formatUnits(statCollection.kraikenTotalSupply, 18));
|
|
||||||
console.log("props.position.totalSupplyInit", props.position.totalSupplyInit);
|
|
||||||
|
|
||||||
console.log("multiplier", multiplier);
|
|
||||||
|
|
||||||
profit.value =
|
|
||||||
Number(formatUnits(statCollection.kraikenTotalSupply, 18)) * multiplier -
|
|
||||||
Number(formatUnits(statCollection.kraikenTotalSupply, 18));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const taxRate = adjustTaxRate.taxRates.find((obj) => obj.index === props.position.taxRateIndex + 1);
|
if (props.position.taxRateIndex !== undefined) {
|
||||||
|
const taxRate = adjustTaxRate.taxRates.find(obj => obj.index === props.position.taxRateIndex! + 1);
|
||||||
|
|
||||||
if (taxRate) {
|
if (taxRate) {
|
||||||
newTaxRate.value = taxRate.year;
|
newTaxRate.value = taxRate.year;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const filteredTaxRates = computed(() => adjustTaxRate.taxRates.filter((obj) => obj.year > props.taxRate));
|
const filteredTaxRates = computed(() => adjustTaxRate.taxRates.filter(obj => obj.year > props.taxRate));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
||||||
|
|
@ -1,56 +1,49 @@
|
||||||
<template>
|
<template>
|
||||||
<f-collapse class="f-collapse-history">
|
<FCollapse class="f-collapse-history">
|
||||||
<template v-slot:header>
|
<template v-slot:header>
|
||||||
<div class="collapse-header">
|
<div class="collapse-header">
|
||||||
<div><span class="subheader2">Tax</span> {{ props.taxRate }} %</div>
|
<div><span class="subheader2">Tax</span> {{ props.taxRate }} %</div>
|
||||||
<div>
|
<div>
|
||||||
<span class="subheader2">ID</span> <span class="number-small">{{ props.id }}</span>
|
<span class="subheader2">ID</span> <span class="number-small">{{ props.id }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="collapse-amount">
|
<div class="collapse-amount">
|
||||||
<span class="number-small">{{ compactNumber(props.amount) }}</span>
|
<span class="number-small">{{ compactNumber(props.amount) }}</span>
|
||||||
<span class="caption"> $KRK</span>
|
<span class="caption"> $KRK</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="collapsed-body history">
|
<div class="collapsed-body history">
|
||||||
<div>
|
<div>
|
||||||
<span class="subheader2">Tax paid</span><span class="number-small">{{ props.taxPaid }}</span
|
<span class="subheader2">Tax paid</span><span class="number-small">{{ props.taxPaid }}</span
|
||||||
><span class="caption"> $KRK</span>
|
><span class="caption"> $KRK</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span class="subheader2">Profit</span><span class="number-small">{{ profit }}</span
|
<span class="subheader2">Profit</span><span class="number-small">{{ profit }}</span
|
||||||
><span class="caption"> $KRK</span>
|
><span class="caption"> $KRK</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</f-collapse>
|
</FCollapse>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Position } from "@/composables/usePositions";
|
import type { Position } from '@/composables/usePositions';
|
||||||
import FCollapse from "@/components/fcomponents/FCollapse.vue";
|
import FCollapse from '@/components/fcomponents/FCollapse.vue';
|
||||||
import { compactNumber } from "@/utils/helper";
|
import { compactNumber } from '@/utils/helper';
|
||||||
import { formatUnits } from "viem";
|
import { formatUnits } from 'viem';
|
||||||
|
|
||||||
import { computed } from "vue";
|
import { computed } from 'vue';
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
taxRate: number;
|
taxRate: number;
|
||||||
taxPaid: string;
|
taxPaid: string;
|
||||||
treshold: number;
|
treshold: number;
|
||||||
id: bigint;
|
id: bigint;
|
||||||
amount: number;
|
amount: number;
|
||||||
position: Position;
|
position: Position;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const profit = computed(() => {
|
const profit = computed(() => {
|
||||||
const multiplier =
|
const multiplier = Number(formatUnits(props.position.totalSupplyInit, 18)) / Number(formatUnits(props.position.totalSupplyEnd!, 18));
|
||||||
Number(formatUnits(props.position.totalSupplyInit, 18)) /
|
return Number(formatUnits(props.position.totalSupplyEnd!, 18)) * multiplier - Number(formatUnits(props.position.totalSupplyEnd!, 18));
|
||||||
Number(formatUnits(props.position.totalSupplyEnd!, 18));
|
|
||||||
console.log("multiplier", multiplier);
|
|
||||||
console.log("props.position.amount", props.position.amount);
|
|
||||||
return (
|
|
||||||
Number(formatUnits(props.position.totalSupplyEnd!, 18)) * multiplier -
|
|
||||||
Number(formatUnits(props.position.totalSupplyEnd!, 18))
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,48 +1,49 @@
|
||||||
<template>
|
<template>
|
||||||
<button class="f-btn" :class="classObject" :style="styleObject">
|
<button class="f-btn" :class="classObject" :style="styleObject">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
interface Props {
|
interface Props {
|
||||||
size?: string;
|
size?: string;
|
||||||
dense?: boolean;
|
dense?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
invert?: boolean;
|
invert?: boolean;
|
||||||
block?: boolean;
|
block?: boolean;
|
||||||
outlined?: boolean;
|
outlined?: boolean;
|
||||||
bgColor?: string;
|
bgColor?: string;
|
||||||
light?: boolean;
|
light?: boolean;
|
||||||
dark?: boolean;
|
dark?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
import { computed } from "vue";
|
import { computed } from 'vue';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
size: "medium",
|
size: 'medium',
|
||||||
|
bgColor: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const classObject = computed(() => ({
|
const classObject = computed(() => ({
|
||||||
"f-btn--tiny": props.size === "tiny",
|
'f-btn--tiny': props.size === 'tiny',
|
||||||
"f-btn--small": props.size === "small",
|
'f-btn--small': props.size === 'small',
|
||||||
"f-btn--medium": props.size === "medium",
|
'f-btn--medium': props.size === 'medium',
|
||||||
"f-btn--large": props.size === "large",
|
'f-btn--large': props.size === 'large',
|
||||||
"f-btn--dense": props.dense,
|
'f-btn--dense': props.dense,
|
||||||
"f-btn--disabled": props.disabled,
|
'f-btn--disabled': props.disabled,
|
||||||
"f-btn--invert": props.invert,
|
'f-btn--invert': props.invert,
|
||||||
"f-btn--block": props.block,
|
'f-btn--block': props.block,
|
||||||
"f-btn--outlined": props.outlined,
|
'f-btn--outlined': props.outlined,
|
||||||
"f-btn--light": props.light,
|
'f-btn--light': props.light,
|
||||||
"f-btn--dark": props.dark,
|
'f-btn--dark': props.dark,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const styleObject = computed(() => {
|
const styleObject = computed(() => {
|
||||||
const returnObject: any = {};
|
const returnObject: Record<string, string> = {};
|
||||||
if (props.bgColor) {
|
if (props.bgColor) {
|
||||||
returnObject["background-color"] = props.bgColor;
|
returnObject['background-color'] = props.bgColor;
|
||||||
}
|
}
|
||||||
return returnObject;
|
return returnObject;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,45 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="f-card" ref="fCard" :style="computedStyles">
|
<div class="f-card" ref="fCard" :style="computedStyles">
|
||||||
<div v-if="props.title" class="f-card__title">
|
<div v-if="props.title" class="f-card__title">
|
||||||
<h5>
|
<h5>
|
||||||
{{ props.title }}
|
{{ props.title }}
|
||||||
</h5>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="f-card__body">
|
<div class="f-card__body">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref, computed } from "vue";
|
import { onMounted, ref, computed } from 'vue';
|
||||||
|
|
||||||
const fCard = ref();
|
const fCard = ref();
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
bgColor?: string;
|
bgColor?: string;
|
||||||
borderWidth?: number;
|
borderWidth?: number;
|
||||||
boxShadow?: string;
|
boxShadow?: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {});
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
bgColor: '',
|
||||||
|
borderWidth: undefined,
|
||||||
|
boxShadow: '',
|
||||||
|
title: '',
|
||||||
|
});
|
||||||
|
|
||||||
const computedStyles = computed(() => ({
|
const computedStyles = computed(() => ({
|
||||||
"border-width": props.borderWidth,
|
'border-width': props.borderWidth,
|
||||||
"background-color": props.bgColor,
|
'background-color': props.bgColor,
|
||||||
"box-shadow": props.boxShadow,
|
'box-shadow': props.boxShadow,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// if (props.bgcolor) {
|
// if (props.bgcolor) {
|
||||||
// fCard.value.style["background-color"] = props.bgcolor;
|
// fCard.value.style["background-color"] = props.bgcolor;
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,50 +1,55 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="f-collapse">
|
<div class="f-collapse">
|
||||||
<div class="f-collapse-wrapper" :class="{loading: props.loading}">
|
<div class="f-collapse-wrapper" :class="{ loading: props.loading }">
|
||||||
<template v-if="loading">
|
<template v-if="loading">
|
||||||
<f-loader></f-loader>
|
<FLoader></FLoader>
|
||||||
</template>
|
</template>
|
||||||
<div class="f-collapse-inner">
|
<div class="f-collapse-inner">
|
||||||
<slot name="header"></slot>
|
<slot name="header"></slot>
|
||||||
<!-- <img class="toggle-collapse" src="../assets/expand-less.svg?url" alt="expand less" /> -->
|
<!-- <img class="toggle-collapse" src="../assets/expand-less.svg?url" alt="expand less" /> -->
|
||||||
<Icon v-if="isShow" class="toggle-collapse" icon="mdi:chevron-down" @click="openClose"></Icon>
|
<Icon v-if="isShow" class="toggle-collapse" icon="mdi:chevron-down" @click="openClose"></Icon>
|
||||||
<Icon v-else icon="mdi:chevron-up" class="toggle-collapse" @click="openClose"></Icon>
|
<Icon v-else icon="mdi:chevron-up" class="toggle-collapse" @click="openClose"></Icon>
|
||||||
<!-- <img
|
<!-- <img
|
||||||
class="toggle-collapse"
|
class="toggle-collapse"
|
||||||
src="../assets/expand-more.svg?url"
|
src="../assets/expand-more.svg?url"
|
||||||
alt="expand more"
|
alt="expand more"
|
||||||
v-else
|
v-else
|
||||||
|
|
||||||
/> -->
|
/> -->
|
||||||
</div>
|
</div>
|
||||||
<Transition name="collapse">
|
<Transition name="collapse">
|
||||||
<div v-if="isShow" class="collapsableDiv">
|
<div v-if="isShow" class="collapsableDiv">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
|
||||||
</Transition>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Transition>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from "vue";
|
import { ref } from 'vue';
|
||||||
import {Icon} from "@iconify/vue";
|
import { Icon } from '@iconify/vue';
|
||||||
import FLoader from "@/components/fcomponents/FLoader.vue";
|
import FLoader from '@/components/fcomponents/FLoader.vue';
|
||||||
|
|
||||||
const emit = defineEmits(["collapse:opened", "collapse:closed"]);
|
interface Emits {
|
||||||
|
(event: 'collapse:opened'): void;
|
||||||
|
(event: 'collapse:closed'): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const isShow = ref(false);
|
const isShow = ref<boolean>(false);
|
||||||
const openClose = () => {
|
const openClose = () => {
|
||||||
isShow.value = !isShow.value;
|
isShow.value = !isShow.value;
|
||||||
if(isShow.value){
|
if (isShow.value) {
|
||||||
emit("collapse:opened")
|
emit('collapse:opened');
|
||||||
} else{
|
} else {
|
||||||
emit("collapse:closed")
|
emit('collapse:closed');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
@ -92,6 +97,4 @@ const openClose = () => {
|
||||||
to
|
to
|
||||||
max-height: 0px
|
max-height: 0px
|
||||||
overflow: hidden
|
overflow: hidden
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,48 +1,47 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="f-input" :class="classObject">
|
<div class="f-input" :class="classObject">
|
||||||
<div class="f-input-label subheader2">
|
<div class="f-input-label subheader2">
|
||||||
<label v-if="props.label" :for="name">{{ props.label }}</label>
|
<label v-if="props.label" :for="name">{{ props.label }}</label>
|
||||||
<icon>
|
<Icon>
|
||||||
<template v-slot:text v-if="slots.info">
|
<template v-slot:text v-if="slots.info">
|
||||||
<slot name="info"></slot>
|
<slot name="info"></slot>
|
||||||
</template>
|
</template>
|
||||||
</icon>
|
</Icon>
|
||||||
</div>
|
</div>
|
||||||
<div class="f-input__wrapper" ref="inputWrapper" @click="setFocus">
|
<div class="f-input__wrapper" ref="inputWrapper" @click="setFocus">
|
||||||
<input
|
<input
|
||||||
:disabled="props.disabled"
|
:disabled="props.disabled"
|
||||||
:readonly="props.readonly"
|
:readonly="props.readonly"
|
||||||
:type="props.type"
|
:type="props.type"
|
||||||
:name="name"
|
:name="name"
|
||||||
:id="name"
|
:id="name"
|
||||||
@input="updateModelValue"
|
@input="updateModelValue"
|
||||||
:value="props.modelValue"
|
:value="props.modelValue"
|
||||||
/>
|
/>
|
||||||
<div class="f-input--suffix" v-if="slots.suffix">
|
<div class="f-input--suffix" v-if="slots.suffix">
|
||||||
<slot name="suffix" >test </slot>
|
<slot name="suffix">test </slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="f-input--details" v-if="slots.details">
|
||||||
<div class="f-input--details" v-if="slots.details">
|
<slot name="details"> </slot>
|
||||||
<slot name="details"> </slot>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, getCurrentInstance, useSlots, ref } from "vue";
|
import { computed, getCurrentInstance, useSlots, ref } from 'vue';
|
||||||
import useClickOutside from "@/composables/useClickOutside"
|
import useClickOutside from '@/composables/useClickOutside';
|
||||||
|
|
||||||
import Icon from "@/components/icons/IconInfo.vue";
|
import Icon from '@/components/icons/IconInfo.vue';
|
||||||
interface Props {
|
interface Props {
|
||||||
size?: string;
|
size?: string;
|
||||||
info?: string;
|
info?: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
modelValue?: any;
|
modelValue?: string | number;
|
||||||
type?: string;
|
type?: string;
|
||||||
readonly?: boolean
|
readonly?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const slots = useSlots();
|
const slots = useSlots();
|
||||||
|
|
@ -53,40 +52,47 @@ const instance = getCurrentInstance();
|
||||||
const name = `f-input-${instance!.uid}`;
|
const name = `f-input-${instance!.uid}`;
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
size: "normal",
|
size: 'normal',
|
||||||
disabled: false,
|
disabled: false,
|
||||||
readonly: false,
|
readonly: false,
|
||||||
type: "string",
|
type: 'string',
|
||||||
|
info: '',
|
||||||
|
label: '',
|
||||||
|
modelValue: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
useClickOutside(inputWrapper, () => {
|
useClickOutside(inputWrapper, () => {
|
||||||
removeFocus();
|
removeFocus();
|
||||||
})
|
});
|
||||||
|
|
||||||
const emit = defineEmits(["update:modelValue"]);
|
interface Emits {
|
||||||
|
(e: 'update:modelValue', value: string): void;
|
||||||
|
}
|
||||||
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
function updateModelValue($event: any) {
|
function updateModelValue($event: Event) {
|
||||||
emit("update:modelValue", $event.target.value);
|
const target = $event.target as HTMLInputElement;
|
||||||
|
emit('update:modelValue', target.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
const classObject = computed(() => ({
|
const classObject = computed(() => ({
|
||||||
"f-input--normal": props.size === "normal",
|
'f-input--normal': props.size === 'normal',
|
||||||
"f-input--big": props.size === "big",
|
'f-input--big': props.size === 'big',
|
||||||
"f-input--disabled": props.disabled,
|
'f-input--disabled': props.disabled,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function removeFocus(){
|
function removeFocus() {
|
||||||
const target = inputWrapper.value as HTMLElement
|
const target = inputWrapper.value as HTMLElement;
|
||||||
target.classList.remove("f-input__wrapper--focused")
|
target.classList.remove('f-input__wrapper--focused');
|
||||||
}
|
}
|
||||||
|
|
||||||
function setFocus(event:MouseEvent){
|
function setFocus(_event: MouseEvent) {
|
||||||
console.log("setFocus");
|
// console.log("setFocus");
|
||||||
if(props.disabled){
|
if (props.disabled) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
const target = inputWrapper.value as HTMLElement
|
const target = inputWrapper.value as HTMLElement;
|
||||||
target.classList.add("f-input__wrapper--focused")
|
target.classList.add('f-input__wrapper--focused');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
@ -109,7 +115,7 @@ function setFocus(event:MouseEvent){
|
||||||
background-color: #2D2D2D
|
background-color: #2D2D2D
|
||||||
&.f-input__wrapper--focused
|
&.f-input__wrapper--focused
|
||||||
outline: none
|
outline: none
|
||||||
border: 1px solid #7550AE
|
border: 1px solid #7550AE
|
||||||
box-shadow: 0 0 5px #7550AE
|
box-shadow: 0 0 5px #7550AE
|
||||||
input
|
input
|
||||||
border: 0
|
border: 0
|
||||||
|
|
@ -131,5 +137,4 @@ function setFocus(event:MouseEvent){
|
||||||
display: flex
|
display: flex
|
||||||
align-items: center
|
align-items: center
|
||||||
margin-right: 10px
|
margin-right: 10px
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="cube">
|
<div class="cube">
|
||||||
<div class="side"></div>
|
<div class="side"></div>
|
||||||
<div class="side"></div>
|
<div class="side"></div>
|
||||||
<div class="side"></div>
|
<div class="side"></div>
|
||||||
<div class="side"></div>
|
<div class="side"></div>
|
||||||
<div class="side"></div>
|
<div class="side"></div>
|
||||||
<div class="side"></div>
|
<div class="side"></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,38 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="output-wrapper"
|
class="output-wrapper"
|
||||||
:class="{
|
:class="{
|
||||||
'no-name': !props.name,
|
'no-name': !props.name,
|
||||||
'output--variant-1': props.variant === 1,
|
'output--variant-1': props.variant === 1,
|
||||||
'output--variant-2': props.variant === 2,
|
'output--variant-2': props.variant === 2,
|
||||||
'output--variant-3': props.variant === 3,
|
'output--variant-3': props.variant === 3,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div class="output-left">
|
<div class="output-left">
|
||||||
<div class="output-text">{{ props.name }}</div>
|
<div class="output-text">{{ props.name }}</div>
|
||||||
<div class="output-price">
|
<div class="output-price">
|
||||||
<slot name="price">
|
<slot name="price">
|
||||||
{{ props.price }}
|
{{ props.price }}
|
||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="output-right" v-if="slots.end">
|
<div class="output-right" v-if="slots.end">
|
||||||
<slot name="end"></slot>
|
<slot name="end"></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useSlots } from "vue";
|
import { useSlots, withDefaults } from 'vue';
|
||||||
const props = defineProps({
|
interface Props {
|
||||||
name: String,
|
name?: string;
|
||||||
price: [String, Number],
|
price?: string | number;
|
||||||
variant: {
|
variant?: number;
|
||||||
type: Number,
|
}
|
||||||
required: false,
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
default: 1,
|
variant: 1,
|
||||||
},
|
name: '',
|
||||||
|
price: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const slots = useSlots();
|
const slots = useSlots();
|
||||||
|
|
|
||||||
|
|
@ -1,163 +1,141 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="o-select" @click="clickSelect" ref="componentRef">
|
<div class="o-select" @click="clickSelect" ref="componentRef">
|
||||||
<f-input hide-details :clearable="props.clearable" :label="props.label" :selectedable="false"
|
<FInput
|
||||||
:focus="showList" :modelValue="`${year} % yearly`" readonly>
|
hide-details
|
||||||
<template #info v-if="slots.info">
|
:clearable="props.clearable"
|
||||||
<slot name="info"></slot>
|
:label="props.label ?? undefined"
|
||||||
</template>
|
:selectedable="false"
|
||||||
<template #suffix>
|
:focus="showList"
|
||||||
<Icon class="toggle-collapse" v-if="showList" icon="mdi:chevron-down"></Icon>
|
:modelValue="`${year} % yearly`"
|
||||||
<Icon class="toggle-collapse" v-else icon="mdi:chevron-up"></Icon>
|
readonly
|
||||||
</template>
|
>
|
||||||
</f-input>
|
<template #info v-if="slots.info">
|
||||||
<div class="select-list-wrapper" v-show="showList" ref="selectList" >
|
<slot name="info"></slot>
|
||||||
<div class="select-list-inner" @click.stop>
|
</template>
|
||||||
<div class="select-list-item" v-for="(item, index) in props.items" :key="item.year" :class="{'active': year === item.year, 'hovered': activeIndex === index}"
|
<template #suffix>
|
||||||
@click.stop="clickItem(item)" @mouseenter="mouseEnter($event, index)" @mouseleave="mouseLeave($event, index)">
|
<Icon class="toggle-collapse" v-if="showList" icon="mdi:chevron-down"></Icon>
|
||||||
<div class="circle">
|
<Icon class="toggle-collapse" v-else icon="mdi:chevron-up"></Icon>
|
||||||
<div class="active" v-if="year === item.year"></div>
|
</template>
|
||||||
<div class="hovered" v-else-if="activeIndex === index"></div>
|
</FInput>
|
||||||
</div>
|
<div class="select-list-wrapper" v-show="showList" ref="selectList">
|
||||||
<div class="yearly">
|
<div class="select-list-inner" @click.stop>
|
||||||
<div class="value">{{ item.year }} %</div>
|
<div
|
||||||
<div class="label">yearly</div>
|
class="select-list-item"
|
||||||
</div>
|
v-for="(item, index) in props.items"
|
||||||
<div class="daily">
|
:key="item.year"
|
||||||
<div class="value">{{ item.daily.toFixed(4) }} %</div>
|
:class="{ active: year === item.year, hovered: activeIndex === index }"
|
||||||
<div class="label">daily</div>
|
@click.stop="clickItem(item)"
|
||||||
</div>
|
@mouseenter="mouseEnter($event, index)"
|
||||||
</div>
|
@mouseleave="mouseLeave($event, index)"
|
||||||
</div>
|
>
|
||||||
|
<div class="circle">
|
||||||
|
<div class="active" v-if="year === item.year"></div>
|
||||||
|
<div class="hovered" v-else-if="activeIndex === index"></div>
|
||||||
|
</div>
|
||||||
|
<div class="yearly">
|
||||||
|
<div class="value">{{ item.year }} %</div>
|
||||||
|
<div class="label">yearly</div>
|
||||||
|
</div>
|
||||||
|
<div class="daily">
|
||||||
|
<div class="value">{{ item.daily.toFixed(4) }} %</div>
|
||||||
|
<div class="label">daily</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<slot :style="[!props.editor ? {display: 'none'} : {}]"></slot>
|
</div>
|
||||||
|
<slot :style="[!props.editor ? { display: 'none' } : {}]"></slot>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted, getCurrentInstance, type ComputedRef, useSlots } from "vue"
|
import { ref, computed, onMounted, useSlots, withDefaults } from 'vue';
|
||||||
import FInput from "@/components/fcomponents/FInput.vue"
|
import FInput from '@/components/fcomponents/FInput.vue';
|
||||||
import useClickOutside from "@/composables/useClickOutside"
|
import useClickOutside from '@/composables/useClickOutside';
|
||||||
import {Icon} from "@iconify/vue";
|
import { Icon } from '@iconify/vue';
|
||||||
|
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue'])
|
|
||||||
const slots = useSlots();
|
|
||||||
|
|
||||||
interface Item {
|
interface Item {
|
||||||
year: number;
|
year: number;
|
||||||
daily: number;
|
daily: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps({
|
interface Props {
|
||||||
items: {
|
items?: Item[];
|
||||||
type: [Array<Item>],
|
clearable?: boolean;
|
||||||
required: false,
|
itemTitle?: string;
|
||||||
default: []
|
itemValue?: string;
|
||||||
},
|
label?: string | null;
|
||||||
clearable: {
|
modelValue?: number | null;
|
||||||
type: Boolean,
|
value?: string | null;
|
||||||
required: false,
|
editor?: boolean;
|
||||||
default: false
|
}
|
||||||
},
|
|
||||||
itemTitle: {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
type: String,
|
items: () => [],
|
||||||
required: false,
|
clearable: false,
|
||||||
default: "label"
|
itemTitle: 'label',
|
||||||
},
|
itemValue: 'value',
|
||||||
itemValue: {
|
label: null,
|
||||||
type: String,
|
modelValue: null,
|
||||||
required: false,
|
value: null,
|
||||||
default: "value"
|
editor: false,
|
||||||
},
|
|
||||||
label: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
modelValue: {
|
|
||||||
type: Number,
|
|
||||||
required: false,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
editor: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const showList = ref(<boolean>false);
|
interface Emits {
|
||||||
const slotElements = ref(<any[]>[])
|
(e: 'update:modelValue', value: number): void;
|
||||||
const componentRef = ref(<any>null);
|
}
|
||||||
const selectList = ref();
|
const emit = defineEmits<Emits>();
|
||||||
const activeIndex= ref();
|
|
||||||
|
const slots = useSlots();
|
||||||
|
|
||||||
|
const showList = ref<boolean>(false);
|
||||||
|
const _slotElements = ref<unknown[]>([]);
|
||||||
|
const componentRef = ref<HTMLElement | null>(null);
|
||||||
|
const selectList = ref<HTMLElement | null>(null);
|
||||||
|
const activeIndex = ref();
|
||||||
|
|
||||||
useClickOutside(componentRef, () => {
|
useClickOutside(componentRef, () => {
|
||||||
|
showList.value = false;
|
||||||
showList.value = false
|
});
|
||||||
})
|
|
||||||
|
|
||||||
|
const year = computed({
|
||||||
const year= computed({
|
|
||||||
// getter
|
// getter
|
||||||
get() {
|
get() {
|
||||||
return props.modelValue || props.items[0].year
|
return props.modelValue || props.items[0].year;
|
||||||
},
|
},
|
||||||
// setter
|
// setter
|
||||||
set(newValue: number) {
|
set(newValue: number) {
|
||||||
|
emit('update:modelValue', newValue);
|
||||||
emit("update:modelValue", newValue)
|
},
|
||||||
|
});
|
||||||
|
|
||||||
}
|
function mouseEnter(event: MouseEvent, index: number) {
|
||||||
})
|
const target = event.target as HTMLElement;
|
||||||
|
activeIndex.value = index;
|
||||||
function mouseEnter(event:MouseEvent , index:number){
|
target.classList.add('active');
|
||||||
const target = event. target as HTMLElement;
|
|
||||||
activeIndex.value = index;
|
|
||||||
target.classList.add("active")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function mouseLeave(event:MouseEvent, index:number){
|
function mouseLeave(_event: MouseEvent, _index: number) {
|
||||||
const elements = selectList.value.querySelectorAll('.select-list-item');
|
const elements = selectList.value?.querySelectorAll('.select-list-item');
|
||||||
elements.forEach((element:HTMLElement) => {
|
elements?.forEach(element => {
|
||||||
element.classList.remove("active")
|
(element as HTMLElement).classList.remove('active');
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {});
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
|
function clickSelect(_event: unknown) {
|
||||||
|
showList.value = !showList.value;
|
||||||
|
|
||||||
|
|
||||||
function clickSelect(event: any) {
|
|
||||||
showList.value = !showList.value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clickItem(item: { year: number }) {
|
||||||
function clickItem(item: any) {
|
// console.log("item", item);
|
||||||
console.log("item", item);
|
year.value = item.year;
|
||||||
year.value = item.year
|
showList.value = false;
|
||||||
showList.value = false;
|
// console.log("showList.value", showList.value);
|
||||||
console.log("showList.value", showList.value);
|
// emit('input', item)
|
||||||
// emit('input', item)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
.o-select
|
.o-select
|
||||||
position: relative
|
position: relative
|
||||||
|
|
@ -228,7 +206,4 @@ function clickItem(item: any) {
|
||||||
&::-webkit-scrollbar-thumb
|
&::-webkit-scrollbar-thumb
|
||||||
border-radius: 10px
|
border-radius: 10px
|
||||||
background-color: lightgrey
|
background-color: lightgrey
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
|
||||||
|
|
@ -1,148 +1,158 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="slider-wrapper">
|
<div class="slider-wrapper">
|
||||||
<div class="disabled-slider" :style="{ 'flex-basis': minPercentage + '%' }">
|
<div class="disabled-slider" :style="{ 'flex-basis': minPercentage + '%' }">
|
||||||
<div class="dot"></div>
|
<div class="dot"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="range-slider">
|
<div class="range-slider">
|
||||||
<input
|
<input
|
||||||
class="range-slider__range"
|
class="range-slider__range"
|
||||||
type="range"
|
type="range"
|
||||||
ref="sliderInput"
|
ref="sliderInput"
|
||||||
:style="{
|
:style="{
|
||||||
background: `linear-gradient(90deg, ${settings.fill} ${percentageDot}%, ${settings.background} ${percentageDot + 0.1}%)`,
|
background: `linear-gradient(90deg, ${settings.fill} ${percentageDot}%, ${settings.background} ${percentageDot + 0.1}%)`,
|
||||||
}"
|
}"
|
||||||
:value="props.modelValue"
|
:value="props.modelValue"
|
||||||
:min="props.min"
|
:min="props.min"
|
||||||
:max="props.max"
|
:max="props.max"
|
||||||
@input="updateModelValue"
|
@input="updateModelValue"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="testbla"
|
class="testbla"
|
||||||
@mousemove="testMove"
|
@mousemove="testMove"
|
||||||
:style="{
|
:style="{
|
||||||
left: percentageDot + '%',
|
left: percentageDot + '%',
|
||||||
}"
|
}"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, onUnmounted, ref, computed } from "vue";
|
import { onMounted, onUnmounted, ref, computed } from 'vue';
|
||||||
|
|
||||||
const sliderInput = ref();
|
const sliderInput = ref<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
const emit = defineEmits(["update:modelValue"]);
|
interface Emits {
|
||||||
|
(event: 'update:modelValue', value: number): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: number;
|
modelValue: number;
|
||||||
min: number;
|
min: number;
|
||||||
max: number;
|
max: number;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const minPercentage = computed(() => {
|
const minPercentage = computed(() => {
|
||||||
if (!props.min || !props.max) {
|
if (!props.min || !props.max) {
|
||||||
return 0n;
|
return 0;
|
||||||
}
|
}
|
||||||
return (props.min * 100) / props.max;
|
return (props.min * 100) / props.max;
|
||||||
});
|
});
|
||||||
|
|
||||||
function updateModelValue(event: any) {
|
function updateModelValue(event: Event) {
|
||||||
emit("update:modelValue", event.target.valueAsNumber);
|
emit('update:modelValue', (event.target as HTMLInputElement).valueAsNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const sliderValue = computed({
|
const sliderValue = computed({
|
||||||
// getter
|
// getter
|
||||||
get() {
|
get() {
|
||||||
return props.modelValue || props.min;
|
return Number.isFinite(props.modelValue) ? props.modelValue : props.min;
|
||||||
},
|
},
|
||||||
// setter
|
// setter
|
||||||
set(newValue) {
|
set(newValue: number) {
|
||||||
emit("update:modelValue", newValue);
|
emit('update:modelValue', newValue);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const percentageDot = computed(() => {
|
const percentageDot = computed(() => {
|
||||||
let percentage = (100 * (sliderValue.value - props.min)) / (props.max - props.min);
|
const range = props.max - props.min;
|
||||||
|
if (range <= 0) {
|
||||||
if(percentage < 20){
|
return 0;
|
||||||
percentage = percentage + 1;
|
}
|
||||||
} else if(percentage > 50){
|
|
||||||
percentage = percentage - 2;
|
let percentage = (100 * (sliderValue.value - props.min)) / range;
|
||||||
} else if(percentage > 80){
|
|
||||||
percentage = percentage - 3;
|
if (percentage < 20) {
|
||||||
}
|
percentage = percentage + 1;
|
||||||
|
} else if (percentage > 50) {
|
||||||
return percentage
|
percentage = percentage - 2;
|
||||||
|
} else if (percentage > 80) {
|
||||||
|
percentage = percentage - 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
return percentage;
|
||||||
});
|
});
|
||||||
|
|
||||||
const settings = {
|
const settings = {
|
||||||
fill: "#7550AE",
|
fill: '#7550AE',
|
||||||
background: "#000000",
|
background: '#000000',
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
sliderInput.value.addEventListener("input", setSliderValue);
|
if (sliderInput.value) {
|
||||||
|
sliderInput.value.addEventListener('input', setSliderValue);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function setSliderValue(event: any){
|
function setSliderValue(event: Event) {
|
||||||
sliderValue.value = event.target.value;
|
const target = event.target as HTMLInputElement | null;
|
||||||
|
if (target) {
|
||||||
|
sliderValue.value = target.valueAsNumber;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function testMove(event: any) {
|
function testMove(_event: unknown) {}
|
||||||
}
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
console.log("sliderInput.value", sliderInput.value);
|
if (sliderInput.value) {
|
||||||
if(sliderInput.value){
|
sliderInput.value.removeEventListener('input', setSliderValue);
|
||||||
sliderInput.value.removeEventListener("input", setSliderValue);
|
}
|
||||||
}
|
});
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.slider-wrapper {
|
.slider-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
.testbla {
|
.testbla {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 25px;
|
height: 25px;
|
||||||
width: 25px;
|
width: 25px;
|
||||||
background-color: var(--color-based-blue);
|
background-color: var(--color-based-blue);
|
||||||
user-select: none;
|
user-select: none;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
right: 50%;
|
right: 50%;
|
||||||
border-radius: 25px;
|
border-radius: 25px;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
.disabled-slider {
|
.disabled-slider {
|
||||||
width: 60px;
|
width: 60px;
|
||||||
height: 7px;
|
height: 7px;
|
||||||
background-color: var(--color-border-main);
|
background-color: var(--color-border-main);
|
||||||
/* margin-top: auto; */
|
/* margin-top: auto; */
|
||||||
align-self: center;
|
align-self: center;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
.dot {
|
.dot {
|
||||||
border-radius: 50px;
|
border-radius: 50px;
|
||||||
height: 15px;
|
height: 15px;
|
||||||
width: 15px;
|
width: 15px;
|
||||||
background-color: var(--color-border-main);
|
background-color: var(--color-border-main);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: -14px;
|
right: -14px;
|
||||||
/* bottom: 50%; */
|
/* bottom: 50%; */
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
// Base Colors
|
// Base Colors
|
||||||
$shade-10: #4682b4 !default;
|
$shade-10: #4682b4 !default;
|
||||||
|
|
@ -164,96 +174,96 @@ $range-label-color: $shade-10 !default;
|
||||||
$range-label-width: 60px !default;
|
$range-label-width: 60px !default;
|
||||||
|
|
||||||
.range-slider {
|
.range-slider {
|
||||||
width: $range-width;
|
width: $range-width;
|
||||||
position: relative;
|
position: relative;
|
||||||
accent-color: #7550AE;
|
accent-color: #7550ae;
|
||||||
}
|
}
|
||||||
|
|
||||||
.range-slider__range {
|
.range-slider__range {
|
||||||
// width: calc(100% - (#{$range-label-width + 13px}));
|
// width: calc(100% - (#{$range-label-width + 13px}));
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: $range-track-height;
|
height: $range-track-height;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
background: $range-track-color;
|
background: $range-track-color;
|
||||||
outline: none;
|
outline: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
// Range Handle
|
// Range Handle
|
||||||
&::-webkit-slider-thumb {
|
&::-webkit-slider-thumb {
|
||||||
appearance: none;
|
appearance: none;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:active::-webkit-slider-thumb {
|
&:active::-webkit-slider-thumb {
|
||||||
background: $range-handle-color-hover;
|
background: $range-handle-color-hover;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::-moz-range-thumb {
|
&::-moz-range-thumb {
|
||||||
width: $range-handle-size;
|
width: $range-handle-size;
|
||||||
height: $range-handle-size;
|
height: $range-handle-size;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: $range-handle-color;
|
background: $range-handle-color;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background 0.15s ease-in-out;
|
transition: background 0.15s ease-in-out;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: $range-handle-color-hover;
|
background: $range-handle-color-hover;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:active::-moz-range-thumb {
|
&:active::-moz-range-thumb {
|
||||||
background: $range-handle-color-hover;
|
background: $range-handle-color-hover;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Focus state
|
// Focus state
|
||||||
&:focus {
|
&:focus {
|
||||||
&::-webkit-slider-thumb {
|
&::-webkit-slider-thumb {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Range Label
|
// Range Label
|
||||||
.range-slider__value {
|
.range-slider__value {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
width: $range-label-width;
|
width: $range-label-width;
|
||||||
color: $shade-0;
|
color: $shade-0;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
background: $range-label-color;
|
background: $range-label-color;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 8px;
|
top: 8px;
|
||||||
left: -7px;
|
left: -7px;
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
border-top: 7px solid transparent;
|
border-top: 7px solid transparent;
|
||||||
border-right: 7px solid $range-label-color;
|
border-right: 7px solid $range-label-color;
|
||||||
border-bottom: 7px solid transparent;
|
border-bottom: 7px solid transparent;
|
||||||
content: "";
|
content: '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Firefox Overrides
|
// Firefox Overrides
|
||||||
::-moz-range-track {
|
::-moz-range-track {
|
||||||
background: $range-track-color;
|
background: $range-track-color;
|
||||||
border: 0;
|
border: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
input::-moz-focus-inner,
|
input::-moz-focus-inner,
|
||||||
input::-moz-focus-outer {
|
input::-moz-focus-outer {
|
||||||
border: 0;
|
border: 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="f-tag">
|
<div class="f-tag">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,41 @@
|
||||||
<template>
|
<template>
|
||||||
<section v-show="value === props.name" ref="tab" role="tabpanel" tabindex="-1">
|
<section v-show="value === props.name" ref="tab" role="tabpanel" tabindex="-1">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, onBeforeMount, getCurrentInstance, ref, computed, watch, inject } from "vue";
|
import { onMounted, onBeforeMount, getCurrentInstance, ref, watch, inject, type ComponentInternalInstance } from 'vue';
|
||||||
const instance = ref<any>();
|
|
||||||
|
|
||||||
const isActive = ref<any>(null);
|
interface Tab {
|
||||||
|
uid: number;
|
||||||
|
name: string;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
const value = inject("value");
|
const instance = ref<ComponentInternalInstance | null>();
|
||||||
var updateTab: any = inject("updateTab");
|
|
||||||
|
const isActive = ref<unknown>(null);
|
||||||
|
|
||||||
|
const value = inject('value');
|
||||||
|
const updateTab: (tab: Tab) => void = inject('updateTab') as (tab: Tab) => void;
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
isActive,
|
isActive,
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeMount(() => {});
|
onBeforeMount(() => {});
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
instance.value = getCurrentInstance();
|
instance.value = getCurrentInstance();
|
||||||
|
|
||||||
var addTab: any = inject("addTab");
|
const addTab = inject('addTab') as (tab: Tab) => void;
|
||||||
addTab({
|
addTab({
|
||||||
uid: instance.value!.uid,
|
uid: instance.value?.uid ?? 0,
|
||||||
name: props.name,
|
name: props.name,
|
||||||
label: props.label,
|
label: props.label,
|
||||||
});
|
});
|
||||||
|
|
||||||
// resolved = {"test123": 123, "blub": "abc"}
|
// resolved = {"test123": 123, "blub": "abc"}
|
||||||
});
|
});
|
||||||
|
|
||||||
// const test = computed(() => {
|
// const test = computed(() => {
|
||||||
|
|
@ -38,27 +45,20 @@ onMounted(() => {
|
||||||
// })
|
// })
|
||||||
|
|
||||||
//Props
|
//Props
|
||||||
const props = defineProps({
|
interface Props {
|
||||||
label: {
|
label: string;
|
||||||
type: String,
|
name: string;
|
||||||
required: true,
|
}
|
||||||
default: null,
|
const props = defineProps<Props>();
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: [String],
|
|
||||||
required: true,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(props, (newValue, oldValue) => {
|
watch(props, (_newValue, _oldValue) => {
|
||||||
console.log("newValue", newValue);
|
// console.log("newValue", newValue);
|
||||||
console.log("instance", instance.value);
|
// console.log("instance", instance.value);
|
||||||
|
|
||||||
updateTab({
|
updateTab({
|
||||||
uid: instance.value!.uid,
|
uid: instance.value?.uid ?? 0,
|
||||||
name: props.name,
|
name: props.name,
|
||||||
label: props.label,
|
label: props.label,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,89 +1,96 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="f-tabs" ref="tabsRef">
|
<div class="f-tabs" ref="tabsRef">
|
||||||
<div class="f-tabs__header" ref="headerRef">
|
<div class="f-tabs__header" ref="headerRef">
|
||||||
<div
|
<div class="f-tab" ref="tabsRef1" :id="`f-tab-${tab.uid}`" v-for="tab in tabs" :key="tab.uid" @click="setActive(tab)">
|
||||||
class="f-tab"
|
<h5>{{ tab.label }}</h5>
|
||||||
ref="tabsRef1"
|
</div>
|
||||||
:id="`f-tab-${tab.uid}`"
|
</div>
|
||||||
v-for="tab in tabs"
|
<div class="f-tabs__content">
|
||||||
:key="tab.uid"
|
<slot ref="tabsTest"></slot>
|
||||||
@click="setActive(tab)"
|
</div>
|
||||||
>
|
</div>
|
||||||
<h5>{{ tab.label }}</h5>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="f-tabs__content">
|
|
||||||
<slot ref="tabsTest"></slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, nextTick, h, computed, useSlots, provide, getCurrentInstance } from "vue";
|
import { ref, onMounted, nextTick, useSlots, provide, withDefaults } from 'vue';
|
||||||
|
|
||||||
|
interface Tab {
|
||||||
|
uid: number;
|
||||||
|
name: string;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
});
|
});
|
||||||
const props = defineProps({
|
interface Props {
|
||||||
modelValue: {
|
modelValue?: string | null;
|
||||||
type: String,
|
}
|
||||||
required: false,
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
default: null,
|
modelValue: null,
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(["update:modelValue"]);
|
interface Emits {
|
||||||
|
(e: 'update:modelValue', value: string): void;
|
||||||
|
}
|
||||||
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
const slots1 = useSlots();
|
const _slots1 = useSlots();
|
||||||
|
|
||||||
const tabsRef = ref<HTMLElement>();
|
const tabsRef = ref<HTMLElement>();
|
||||||
const tabsRef1 = ref<HTMLElement>();
|
const tabsRef1 = ref<HTMLElement>();
|
||||||
const headerRef = ref<HTMLElement>();
|
const headerRef = ref<HTMLElement>();
|
||||||
const tabsArray = ref<Array<HTMLElement>>();
|
const _tabsArray = ref<Array<HTMLElement>>();
|
||||||
const tabsComponents = ref<Array<any>>();
|
const _tabsComponents = ref<Array<unknown>>();
|
||||||
const content = ref<any>();
|
const _content = ref<unknown>();
|
||||||
const target = ref<any>();
|
const _target = ref<unknown>();
|
||||||
const slots = ref<any>();
|
const _slots = ref<unknown>();
|
||||||
const value = ref<any>();
|
const value = ref<unknown>();
|
||||||
const tabsTest = ref<any>();
|
const tabsTest = ref<unknown>();
|
||||||
const tabs = ref<Array<any>>([]);
|
const tabs = ref<Array<Tab>>([]);
|
||||||
provide("value", value);
|
provide('value', value);
|
||||||
provide("addTab", (tab: any) => {
|
provide('addTab', (tab: Tab) => {
|
||||||
tabs.value.push(tab);
|
tabs.value.push(tab);
|
||||||
});
|
});
|
||||||
|
|
||||||
provide("updateTab", (tab: any) => {
|
provide('updateTab', (tab: Tab) => {
|
||||||
let changedTabIndex = tabs.value.findIndex((obj) => obj.uid === tab.uid);
|
const changedTabIndex = tabs.value.findIndex(obj => obj.uid === tab.uid);
|
||||||
if (changedTabIndex > -1) {
|
if (changedTabIndex > -1) {
|
||||||
tabs.value[changedTabIndex] = tab;
|
tabs.value[changedTabIndex] = tab;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// provide('addTab', tabs.value)
|
// provide('addTab', tabs.value)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if(props.modelValue){
|
if (props.modelValue) {
|
||||||
const tab = tabs.value.find((obj) => obj.name === props.modelValue)
|
const tab = tabs.value.find(obj => obj.name === props.modelValue);
|
||||||
setActive(tab);
|
if (tab) {
|
||||||
|
setActive(tab);
|
||||||
} else {
|
}
|
||||||
setActive(tabs.value[0]);
|
} else {
|
||||||
}
|
const firstTab = tabs.value[0];
|
||||||
});
|
if (firstTab) {
|
||||||
|
setActive(firstTab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function setActive(tab: any) {
|
function setActive(tab: Tab) {
|
||||||
|
nextTick(() => {
|
||||||
|
const tabElement = headerRef.value?.querySelector(`#f-tab-${tab.uid}`) as HTMLElement | null;
|
||||||
|
const array = Array.prototype.slice.call(tabsRef.value?.children[0].children);
|
||||||
|
|
||||||
nextTick(() => {
|
array.forEach((element: HTMLElement) => {
|
||||||
const tabElement: any = headerRef.value?.querySelector(`#f-tab-${tab.uid}`);
|
element.classList.remove('f-tab--active');
|
||||||
var array = Array.prototype.slice.call(tabsRef.value?.children[0].children);
|
});
|
||||||
|
if (tabElement) {
|
||||||
array.forEach((element: HTMLElement) => {
|
tabElement.classList.add('f-tab--active');
|
||||||
element.classList.remove("f-tab--active");
|
}
|
||||||
});
|
value.value = tab.name;
|
||||||
tabElement.classList.add("f-tab--active");
|
emit('update:modelValue', tab.name);
|
||||||
value.value = tab.name;
|
});
|
||||||
emit("update:modelValue", tab.name);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,10 +41,9 @@ describe('FTabs.vue', () => {
|
||||||
await wrapper.vm.$nextTick();
|
await wrapper.vm.$nextTick();
|
||||||
|
|
||||||
const sections = wrapper.findAll('section');
|
const sections = wrapper.findAll('section');
|
||||||
|
|
||||||
expect(sections[0].attributes('style')).not.toContain('display: none');
|
expect(sections[0].attributes('style')).not.toContain('display: none');
|
||||||
expect(sections[1].attributes('style')).toContain('display: none');
|
expect(sections[1].attributes('style')).toContain('display: none');
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('switches to the correct tab on click', async () => {
|
it('switches to the correct tab on click', async () => {
|
||||||
|
|
@ -66,7 +65,7 @@ describe('FTabs.vue', () => {
|
||||||
await tabs[1].trigger('click');
|
await tabs[1].trigger('click');
|
||||||
|
|
||||||
const sections = wrapper.findAll('section');
|
const sections = wrapper.findAll('section');
|
||||||
expect(sections[0].attributes('style')).toContain('display: none');
|
expect(sections[0].attributes('style')).toContain('display: none');
|
||||||
expect(sections[1].attributes('style')).not.toContain('display: none');
|
expect(sections[1].attributes('style')).not.toContain('display: none');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -1,29 +1,30 @@
|
||||||
<template>
|
<template>
|
||||||
<svg width="23" height="18" viewBox="0 0 23 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="23" height="18" viewBox="0 0 23 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<g clip-path="url(#clip0_2590_494)">
|
<g clip-path="url(#clip0_2590_494)">
|
||||||
<path
|
<path
|
||||||
ref="svgPath"
|
ref="svgPath"
|
||||||
d="M19.419 3.40166C16.7691 1.45664 14.2483 1.51053 14.2483 1.51053L13.9906 1.79865C17.1188 2.73512 18.5723 4.08593 18.5723 4.08593C16.6585 3.05941 14.7817 2.55501 13.0336 2.35694C11.7088 2.21276 10.4391 2.24892 9.3167 2.39287C9.20634 2.39287 9.11433 2.41083 9.00397 2.42879C8.35994 2.48292 6.79584 2.71692 4.82702 3.56357C4.14628 3.86966 3.74131 4.08593 3.74131 4.08593C3.74131 4.08593 5.2687 2.66303 8.58065 1.72656L8.39664 1.51053C8.39664 1.51053 5.87579 1.4564 3.22598 3.40166C3.22598 3.40166 0.576172 8.10242 0.576172 13.9018C0.576172 13.9018 2.12191 16.5134 6.18851 16.6393C6.18851 16.6393 6.86925 15.8289 7.42129 15.1446C5.08444 14.4601 4.20109 13.0195 4.20109 13.0195C4.20109 13.0195 4.3851 13.1454 4.71642 13.3256C4.73477 13.3435 4.75313 13.3615 4.79007 13.3797C4.84537 13.4156 4.90043 13.4338 4.95573 13.4697C5.41576 13.7219 5.87579 13.92 6.29911 14.0821C7.05351 14.3703 7.95521 14.6584 9.00397 14.8565C10.3841 15.1087 12.0032 15.1987 13.7697 14.8744C14.6344 14.7303 15.5178 14.4783 16.4378 14.0999C17.0819 13.8656 17.7996 13.5236 18.554 13.0372C18.554 13.0372 17.6339 14.514 15.2234 15.1805C15.7754 15.865 16.4378 16.6393 16.4378 16.6393C20.5047 16.5131 22.0688 13.9018 22.0688 13.9018C22.0688 8.10242 19.419 3.40166 19.419 3.40166ZM7.8818 12.2267C6.85138 12.2267 6.00498 11.3262 6.00498 10.2276C6.00498 9.12894 6.83303 8.22841 7.8818 8.22841C8.93056 8.22841 9.77697 9.12894 9.75861 10.2276C9.75861 11.3262 8.93056 12.2267 7.8818 12.2267ZM14.598 12.2267C13.5675 12.2267 12.7211 11.3262 12.7211 10.2276C12.7211 9.12894 13.5492 8.22841 14.598 8.22841C15.6467 8.22841 16.4748 9.12894 16.4748 10.2276C16.4748 11.3262 15.647 12.2267 14.598 12.2267Z"
|
d="M19.419 3.40166C16.7691 1.45664 14.2483 1.51053 14.2483 1.51053L13.9906 1.79865C17.1188 2.73512 18.5723 4.08593 18.5723 4.08593C16.6585 3.05941 14.7817 2.55501 13.0336 2.35694C11.7088 2.21276 10.4391 2.24892 9.3167 2.39287C9.20634 2.39287 9.11433 2.41083 9.00397 2.42879C8.35994 2.48292 6.79584 2.71692 4.82702 3.56357C4.14628 3.86966 3.74131 4.08593 3.74131 4.08593C3.74131 4.08593 5.2687 2.66303 8.58065 1.72656L8.39664 1.51053C8.39664 1.51053 5.87579 1.4564 3.22598 3.40166C3.22598 3.40166 0.576172 8.10242 0.576172 13.9018C0.576172 13.9018 2.12191 16.5134 6.18851 16.6393C6.18851 16.6393 6.86925 15.8289 7.42129 15.1446C5.08444 14.4601 4.20109 13.0195 4.20109 13.0195C4.20109 13.0195 4.3851 13.1454 4.71642 13.3256C4.73477 13.3435 4.75313 13.3615 4.79007 13.3797C4.84537 13.4156 4.90043 13.4338 4.95573 13.4697C5.41576 13.7219 5.87579 13.92 6.29911 14.0821C7.05351 14.3703 7.95521 14.6584 9.00397 14.8565C10.3841 15.1087 12.0032 15.1987 13.7697 14.8744C14.6344 14.7303 15.5178 14.4783 16.4378 14.0999C17.0819 13.8656 17.7996 13.5236 18.554 13.0372C18.554 13.0372 17.6339 14.514 15.2234 15.1805C15.7754 15.865 16.4378 16.6393 16.4378 16.6393C20.5047 16.5131 22.0688 13.9018 22.0688 13.9018C22.0688 8.10242 19.419 3.40166 19.419 3.40166ZM7.8818 12.2267C6.85138 12.2267 6.00498 11.3262 6.00498 10.2276C6.00498 9.12894 6.83303 8.22841 7.8818 8.22841C8.93056 8.22841 9.77697 9.12894 9.75861 10.2276C9.75861 11.3262 8.93056 12.2267 7.8818 12.2267ZM14.598 12.2267C13.5675 12.2267 12.7211 11.3262 12.7211 10.2276C12.7211 9.12894 13.5492 8.22841 14.598 8.22841C15.6467 8.22841 16.4748 9.12894 16.4748 10.2276C16.4748 11.3262 15.647 12.2267 14.598 12.2267Z"
|
||||||
:fill="props.color"
|
:fill="props.color"
|
||||||
/>
|
/>
|
||||||
</g>
|
</g>
|
||||||
<defs>
|
<defs>
|
||||||
<clipPath id="clip0_2590_494">
|
<clipPath id="clip0_2590_494">
|
||||||
<rect width="22" height="17" fill="white" transform="translate(0.333984 0.58667)" />
|
<rect width="22" height="17" fill="white" transform="translate(0.333984 0.58667)" />
|
||||||
</clipPath>
|
</clipPath>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref } from "vue";
|
import { ref } from 'vue';
|
||||||
|
|
||||||
const svgPath = ref();
|
const svgPath = ref();
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
color?: string;
|
color?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {});
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
color: 'currentColor',
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
|
||||||
<!-- Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc. -->
|
<!-- Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc. -->
|
||||||
<path
|
<path
|
||||||
d="M96 0C43 0 0 43 0 96L0 416c0 53 43 96 96 96l288 0 32 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l0-64c17.7 0 32-14.3 32-32l0-320c0-17.7-14.3-32-32-32L384 0 96 0zm0 384l256 0 0 64L96 448c-17.7 0-32-14.3-32-32s14.3-32 32-32zm32-240c0-8.8 7.2-16 16-16l192 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-192 0c-8.8 0-16-7.2-16-16zm16 48l192 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-192 0c-8.8 0-16-7.2-16-16s7.2-16 16-16z"
|
d="M96 0C43 0 0 43 0 96L0 416c0 53 43 96 96 96l288 0 32 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l0-64c17.7 0 32-14.3 32-32l0-320c0-17.7-14.3-32-32-32L384 0 96 0zm0 384l256 0 0 64L96 448c-17.7 0-32-14.3-32-32s14.3-32 32-32zm32-240c0-8.8 7.2-16 16-16l192 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-192 0c-8.8 0-16-7.2-16-16zm16 48l192 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-192 0c-8.8 0-16-7.2-16-16s7.2-16 16-16z"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
|
||||||
<path
|
<path
|
||||||
d="M575.8 255.5c0 18-15 32.1-32 32.1h-32l.7 160.2c0 2.7-.2 5.4-.5 8.1V472c0 22.1-17.9 40-40 40H456c-1.1 0-2.2 0-3.3-.1c-1.4 .1-2.8 .1-4.2 .1H416 392c-22.1 0-40-17.9-40-40V448 384c0-17.7-14.3-32-32-32H256c-17.7 0-32 14.3-32 32v64 24c0 22.1-17.9 40-40 40H160 128.1c-1.5 0-3-.1-4.5-.2c-1.2 .1-2.4 .2-3.6 .2H104c-22.1 0-40-17.9-40-40V360c0-.9 0-1.9 .1-2.8V287.6H32c-18 0-32-14-32-32.1c0-9 3-17 10-24L266.4 8c7-7 15-8 22-8s15 2 21 7L564.8 231.5c8 7 12 15 11 24z"
|
d="M575.8 255.5c0 18-15 32.1-32 32.1h-32l.7 160.2c0 2.7-.2 5.4-.5 8.1V472c0 22.1-17.9 40-40 40H456c-1.1 0-2.2 0-3.3-.1c-1.4 .1-2.8 .1-4.2 .1H416 392c-22.1 0-40-17.9-40-40V448 384c0-17.7-14.3-32-32-32H256c-17.7 0-32 14.3-32 32v64 24c0 22.1-17.9 40-40 40H160 128.1c-1.5 0-3-.1-4.5-.2c-1.2 .1-2.4 .2-3.6 .2H104c-22.1 0-40-17.9-40-40V360c0-.9 0-1.9 .1-2.8V287.6H32c-18 0-32-14-32-32.1c0-9 3-17 10-24L266.4 8c7-7 15-8 22-8s15 2 21 7L564.8 231.5c8 7 12 15 11 24z"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,39 @@
|
||||||
<template>
|
<template>
|
||||||
<tippy v-if="slots.text" theme="my-theme" trigger="click">
|
<Tippy v-if="slots.text" theme="my-theme" trigger="click">
|
||||||
<div class="info-icon">
|
<div class="info-icon">
|
||||||
<svg :width="props.size" :height="props.size" viewBox="0 0 14 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg :width="props.size" :height="props.size" viewBox="0 0 14 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path
|
<path
|
||||||
d="M6.2 6.26416C6.2 5.84995 6.53579 5.51416 6.95 5.51416C7.36421 5.51416 7.7 5.84995 7.7 6.26416V9.76416C7.7 10.1784 7.36421 10.5142 6.95 10.5142C6.53579 10.5142 6.2 10.1784 6.2 9.76416V6.26416Z"
|
d="M6.2 6.26416C6.2 5.84995 6.53579 5.51416 6.95 5.51416C7.36421 5.51416 7.7 5.84995 7.7 6.26416V9.76416C7.7 10.1784 7.36421 10.5142 6.95 10.5142C6.53579 10.5142 6.2 10.1784 6.2 9.76416V6.26416Z"
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
d="M7 3.01416C7.55228 3.01416 8 3.46188 8 4.01416C8 4.56644 7.55228 5.01416 7 5.01416C6.44772 5.01416 6 4.56644 6 4.01416C6 3.46188 6.44772 3.01416 7 3.01416Z"
|
d="M7 3.01416C7.55228 3.01416 8 3.46188 8 4.01416C8 4.56644 7.55228 5.01416 7 5.01416C6.44772 5.01416 6 4.56644 6 4.01416C6 3.46188 6.44772 3.01416 7 3.01416Z"
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
fill-rule="evenodd"
|
fill-rule="evenodd"
|
||||||
clip-rule="evenodd"
|
clip-rule="evenodd"
|
||||||
d="M14 7.01416C14 10.8802 10.866 14.0142 7 14.0142C3.13401 14.0142 0 10.8802 0 7.01416C0 3.14817 3.13401 0.0141602 7 0.0141602C10.866 0.0141602 14 3.14817 14 7.01416ZM1.5 7.01416C1.5 10.0517 3.96243 12.5142 7 12.5142C10.0376 12.5142 12.5 10.0517 12.5 7.01416C12.5 3.97659 10.0376 1.51416 7 1.51416C3.96243 1.51416 1.5 3.97659 1.5 7.01416Z"
|
d="M14 7.01416C14 10.8802 10.866 14.0142 7 14.0142C3.13401 14.0142 0 10.8802 0 7.01416C0 3.14817 3.13401 0.0141602 7 0.0141602C10.866 0.0141602 14 3.14817 14 7.01416ZM1.5 7.01416C1.5 10.0517 3.96243 12.5142 7 12.5142C10.0376 12.5142 12.5 10.0517 12.5 7.01416C12.5 3.97659 10.0376 1.51416 7 1.51416C3.96243 1.51416 1.5 3.97659 1.5 7.01416Z"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<template #content>
|
<template #content>
|
||||||
<slot name="text"></slot>
|
<slot name="text"></slot>
|
||||||
|
</template>
|
||||||
</template>
|
</Tippy>
|
||||||
</tippy>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useSlots, ref, onMounted } from "vue";
|
import { useSlots } from 'vue';
|
||||||
import { Tippy } from "vue-tippy";
|
import { Tippy } from 'vue-tippy';
|
||||||
import "tippy.js/dist/tippy.css"; // optional for styling
|
import 'tippy.js/dist/tippy.css'; // optional for styling
|
||||||
const slots = useSlots();
|
const slots = useSlots();
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
size?: string;
|
size?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
size: "15px",
|
size: '15px',
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
@ -49,8 +46,8 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
fill: var(--color-font)
|
fill: var(--color-font)
|
||||||
.tippy-arrow
|
.tippy-arrow
|
||||||
color: rgba(15, 15, 15, 0.9) !important
|
color: rgba(15, 15, 15, 0.9) !important
|
||||||
.tippy-box[data-theme~='my-theme']
|
.tippy-box[data-theme~='my-theme']
|
||||||
background-color: rgba(15, 15, 15, 0.9)
|
background-color: rgba(15, 15, 15, 0.9)
|
||||||
height: fit-content
|
height: fit-content
|
||||||
color: white
|
color: white
|
||||||
font-size: 12px
|
font-size: 12px
|
||||||
|
|
@ -58,5 +55,4 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
border-radius: var(--border-radius)
|
border-radius: var(--border-radius)
|
||||||
// @media (min-width: 768px)
|
// @media (min-width: 768px)
|
||||||
// max-width: 400px
|
// max-width: 400px
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="login-icon" viewBox="0 0 512 512">
|
<svg xmlns="http://www.w3.org/2000/svg" class="login-icon" viewBox="0 0 512 512">
|
||||||
<path
|
<path
|
||||||
d="M352 96l64 0c17.7 0 32 14.3 32 32l0 256c0 17.7-14.3 32-32 32l-64 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l64 0c53 0 96-43 96-96l0-256c0-53-43-96-96-96l-64 0c-17.7 0-32 14.3-32 32s14.3 32 32 32zm-9.4 182.6c12.5-12.5 12.5-32.8 0-45.3l-128-128c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L242.7 224 32 224c-17.7 0-32 14.3-32 32s14.3 32 32 32l210.7 0-73.4 73.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l128-128z"
|
d="M352 96l64 0c17.7 0 32 14.3 32 32l0 256c0 17.7-14.3 32-32 32l-64 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l64 0c53 0 96-43 96-96l0-256c0-53-43-96-96-96l-64 0c-17.7 0-32 14.3-32 32s14.3 32 32 32zm-9.4 182.6c12.5-12.5 12.5-32.8 0-45.3l-128-128c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L242.7 224 32 224c-17.7 0-32 14.3-32 32s14.3 32 32 32l210.7 0-73.4 73.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l128-128z"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,34 @@
|
||||||
<template>
|
<template>
|
||||||
<svg width="20" height="16" viewBox="0 0 20 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="20" height="16" viewBox="0 0 20 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<g clip-path="url(#clip0_2590_499)">
|
<g clip-path="url(#clip0_2590_499)">
|
||||||
<path
|
<path
|
||||||
ref="svgPath"
|
ref="svgPath"
|
||||||
d="M17.058 0.932576L1.74462 6.83727C0.699293 7.25659 0.705631 7.83977 1.55407 8.09947L5.48317 9.32593L6.83449 13.7733C7.01217 14.2637 6.92458 14.4583 7.43975 14.4583C7.83718 14.4583 8.01274 14.2765 8.2346 14.0608L10.1441 12.2041L14.1166 15.1392C14.8477 15.5426 15.3754 15.3336 15.5575 14.4606L18.1654 2.17133C18.4324 1.10065 17.7574 0.615059 17.058 0.932576Z"
|
d="M17.058 0.932576L1.74462 6.83727C0.699293 7.25659 0.705631 7.83977 1.55407 8.09947L5.48317 9.32593L6.83449 13.7733C7.01217 14.2637 6.92458 14.4583 7.43975 14.4583C7.83718 14.4583 8.01274 14.2765 8.2346 14.0608L10.1441 12.2041L14.1166 15.1392C14.8477 15.5426 15.3754 15.3336 15.5575 14.4606L18.1654 2.17133C18.4324 1.10065 17.7574 0.615059 17.058 0.932576Z"
|
||||||
:fill="props.color"
|
:fill="props.color"
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
d="M7.45616 13.2757L6.16016 9.01067L16.1361 3.09253L8.76406 10.3509L7.45616 13.2757Z"
|
d="M7.45616 13.2757L6.16016 9.01067L16.1361 3.09253L8.76406 10.3509L7.45616 13.2757Z"
|
||||||
:fill="(props.color === 'black' ? 'white' : 'black')"
|
:fill="props.color === 'black' ? 'white' : 'black'"
|
||||||
/>
|
/>
|
||||||
</g>
|
</g>
|
||||||
<defs>
|
<defs>
|
||||||
<clipPath id="clip0_2590_499">
|
<clipPath id="clip0_2590_499">
|
||||||
<rect width="18.125" height="14.5" fill="white" transform="translate(0.9375 0.83667)" />
|
<rect width="18.125" height="14.5" fill="white" transform="translate(0.9375 0.83667)" />
|
||||||
</clipPath>
|
</clipPath>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref } from "vue";
|
import { ref } from 'vue';
|
||||||
|
|
||||||
const svgPath = ref();
|
const svgPath = ref();
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
color?: string;
|
color?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {});
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
color: 'currentColor',
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,31 @@
|
||||||
<template>
|
<template>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
width="20px"
|
width="20px"
|
||||||
height="18px"
|
height="18px"
|
||||||
viewBox="0 0 19 18"
|
viewBox="0 0 19 18"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
>
|
>
|
||||||
<g id="surface1">
|
<g id="surface1">
|
||||||
<path
|
<path
|
||||||
ref="svgPath"
|
ref="svgPath"
|
||||||
d="M 14.945312 0 L 17.859375 0 L 11.464844 7.636719 L 18.9375 18 L 13.070312 18 L 8.480469 11.703125 L 3.222656 18 L 0.308594 18 L 7.085938 9.832031 L -0.0703125 0 L 5.941406 0 L 10.089844 5.753906 Z M 13.925781 16.207031 L 15.542969 16.207031 L 5.09375 1.726562 L 3.355469 1.726562 Z M 13.925781 16.207031 "
|
d="M 14.945312 0 L 17.859375 0 L 11.464844 7.636719 L 18.9375 18 L 13.070312 18 L 8.480469 11.703125 L 3.222656 18 L 0.308594 18 L 7.085938 9.832031 L -0.0703125 0 L 5.941406 0 L 10.089844 5.753906 Z M 13.925781 16.207031 L 15.542969 16.207031 L 5.09375 1.726562 L 3.355469 1.726562 Z M 13.925781 16.207031 "
|
||||||
:fill="props.color"
|
:fill="props.color"
|
||||||
/>
|
/>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref } from "vue";
|
import { ref } from 'vue';
|
||||||
|
|
||||||
const svgPath = ref();
|
const svgPath = ref();
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
color?: string;
|
color?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {});
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
color: 'currentColor',
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,23 @@
|
||||||
<template>
|
<template>
|
||||||
<template v-if="status === 'disconnected'">
|
<template v-if="status === 'disconnected'">
|
||||||
<f-button class="connect-button--disconnected">Connect</f-button>
|
<FButton class="connect-button--disconnected">Connect</FButton>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="status === 'connected'">
|
<template v-else-if="status === 'connected'">
|
||||||
<f-button class="connect-button connect-button--connected">
|
<FButton class="connect-button connect-button--connected">
|
||||||
<img :src="getBlocky(address!)" alt="avatar" />
|
<img :src="getBlocky(address!)" alt="avatar" />
|
||||||
<div>{{ getAddressShortName(address!) }}</div>
|
<div>{{ getAddressShortName(address!) }}</div>
|
||||||
</f-button>
|
</FButton>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<f-button class="connect-button--loading">loading</f-button>
|
<FButton class="connect-button--loading">loading</FButton>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import FButton from "@/components/fcomponents/FButton.vue";
|
import FButton from '@/components/fcomponents/FButton.vue';
|
||||||
import { getAddressShortName } from "@/utils/helper";
|
import { getAddressShortName } from '@/utils/helper';
|
||||||
import { useAccount } from "@wagmi/vue";
|
import { useAccount } from '@wagmi/vue';
|
||||||
import { getBlocky } from "@/utils/blockies";
|
import { getBlocky } from '@/utils/blockies';
|
||||||
|
|
||||||
const { address, status } = useAccount();
|
const { address, status } = useAccount();
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,113 +1,103 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="connect-wallet-wrapper">
|
<div class="connect-wallet-wrapper">
|
||||||
<template v-if="status === 'connected'">
|
<template v-if="status === 'connected'">
|
||||||
<div class="connected-header">
|
<div class="connected-header">
|
||||||
<div class="connected-header-avatar">
|
<div class="connected-header-avatar">
|
||||||
<img :src="getBlocky(address!)" alt="avatar" />
|
<img :src="getBlocky(address!)" alt="avatar" />
|
||||||
</div>
|
</div>
|
||||||
<div class="connected-header-address" :title="address">
|
<div class="connected-header-address" :title="address">
|
||||||
{{ getAddressShortName(address!) }}
|
{{ getAddressShortName(address!) }}
|
||||||
</div>
|
</div>
|
||||||
<div class="connected-header-logout" @click="() => disconnect()">
|
<div class="connected-header-logout" @click="() => disconnect()">
|
||||||
<img src="@/assets/logout.svg" alt="Logout" />
|
<img src="@/assets/logout.svg" alt="Logout" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h6 class="connected-tokens-headline">Your Tokens</h6>
|
<h6 class="connected-tokens-headline">Your Tokens</h6>
|
||||||
<div class="connected-tokens">
|
<div class="connected-tokens">
|
||||||
<f-output name="$KRK" :price="harbAmount" :variant="3">
|
<FOutput name="$KRK" :price="harbAmount" :variant="3">
|
||||||
<template #end>
|
<template #end>
|
||||||
<f-button size="small" dense
|
<FButton size="small" dense><a :href="chain.chainData?.uniswap" target="_blank">Buy</a></FButton>
|
||||||
><a :href="chain.chainData?.uniswap" target="_blank">Buy</a></f-button
|
</template>
|
||||||
>
|
</FOutput>
|
||||||
</template>
|
<FOutput name="Staked $KRK" :price="compactNumber(stakedAmount)" :variant="3">
|
||||||
</f-output>
|
<template #end>
|
||||||
<f-output name="Staked $KRK" :price="compactNumber(stakedAmount)" :variant="3">
|
<FButton size="small" dense @click="stakeLink">Stake</FButton>
|
||||||
<template #end>
|
</template>
|
||||||
<f-button size="small" dense @click="stakeLink">Stake</f-button>
|
</FOutput>
|
||||||
</template>
|
</div>
|
||||||
</f-output>
|
</template>
|
||||||
</div>
|
<template v-else-if="status === 'disconnected'">
|
||||||
</template>
|
<h6 class="connect-wallet-headline low-mid-text">Connect your wallet</h6>
|
||||||
<template v-else-if="status === 'disconnected'">
|
<div class="connectors-wrapper">
|
||||||
<h6 class="connect-wallet-headline low-mid-text">Connect your wallet</h6>
|
<div
|
||||||
<div class="connectors-wrapper">
|
class="connectors-element card"
|
||||||
<div
|
:class="[[connector.id]]"
|
||||||
class="connectors-element card"
|
v-for="connector in connectors"
|
||||||
:class="[[connector.id]]"
|
:key="connector.id"
|
||||||
v-for="connector in connectors"
|
@click="connectWallet(connector, chainId)"
|
||||||
:key="connector.id"
|
>
|
||||||
@click="connectWallet(connector, chainId)"
|
<div class="connector-icon">
|
||||||
>
|
<img :src="loadConnectorImage(connector)" :alt="`${connector.name} Logo`" />
|
||||||
<div class="connector-icon">
|
</div>
|
||||||
<img :src="loadConnectorImage(connector)" :alt="`${connector.name} Logo`" />
|
<div class="connector-name">
|
||||||
</div>
|
{{ connector.name }}
|
||||||
<div class="connector-name">
|
</div>
|
||||||
{{ connector.name }}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div>Are you coming from Binance, Bybit, Kucoin or any other crypto exchange?</div>
|
||||||
</div>
|
<div><u>Follow these instructions first.</u></div>
|
||||||
<div>Are you coming from Binance, Bybit, Kucoin or any other crypto exchange?</div>
|
</template>
|
||||||
<div><u>Follow these instructions first.</u></div>
|
<template v-else>
|
||||||
</template>
|
<div>loading</div>
|
||||||
<template v-else>
|
</template>
|
||||||
<div>loading</div>
|
</div>
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { getCurrentInstance, computed } from "vue";
|
import { getCurrentInstance, computed } from 'vue';
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from 'vue-router';
|
||||||
import { getAddressShortName, compactNumber, formatBigIntDivision } from "@/utils/helper";
|
import { getAddressShortName, compactNumber, formatBigIntDivision } from '@/utils/helper';
|
||||||
import { getBlocky } from "@/utils/blockies";
|
import { getBlocky } from '@/utils/blockies';
|
||||||
import FButton from "@/components/fcomponents/FButton.vue";
|
import FButton from '@/components/fcomponents/FButton.vue';
|
||||||
import FOutput from "@/components/fcomponents/FOutput.vue";
|
import FOutput from '@/components/fcomponents/FOutput.vue';
|
||||||
|
|
||||||
// import { usePositions } from "@/composables/usePositions";
|
// import { usePositions } from "@/composables/usePositions";
|
||||||
// import { useWallet } from "@/composables/useWallet";
|
// import { useWallet } from "@/composables/useWallet";
|
||||||
import { usePositions } from "@/composables/usePositions";
|
import { usePositions } from '@/composables/usePositions';
|
||||||
import { useWallet } from "@/composables/useWallet";
|
import { useWallet } from '@/composables/useWallet';
|
||||||
import { useChain } from "@/composables/useChain";
|
import { useChain } from '@/composables/useChain';
|
||||||
|
|
||||||
import {
|
import { useAccount, useDisconnect, useConnect, useChainId, type CreateConnectorFn, type Connector } from '@wagmi/vue';
|
||||||
useAccount,
|
|
||||||
useDisconnect,
|
|
||||||
useConnect,
|
|
||||||
useChainId,
|
|
||||||
useBalance,
|
|
||||||
type CreateConnectorFn,
|
|
||||||
type Connector,
|
|
||||||
} from "@wagmi/vue";
|
|
||||||
|
|
||||||
const { address, status } = useAccount();
|
const { address, status } = useAccount();
|
||||||
const { disconnect } = useDisconnect();
|
const { disconnect } = useDisconnect();
|
||||||
const { connectors, connect, error } = useConnect();
|
const { connectors, connect } = useConnect();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const chainId = useChainId();
|
const chainId = useChainId();
|
||||||
const { myActivePositions } = usePositions();
|
const { myActivePositions } = usePositions();
|
||||||
const wallet = useWallet();
|
const wallet = useWallet();
|
||||||
const chain = useChain();
|
const chain = useChain();
|
||||||
function loadConnectorImage(connector: Connector) {
|
function loadConnectorImage(connector: Connector) {
|
||||||
if (connector.icon) {
|
if (connector.icon) {
|
||||||
return connector.icon;
|
return connector.icon;
|
||||||
} else {
|
} else {
|
||||||
return `./img/connectors/${connector.name}.svg`;
|
return `./img/connectors/${connector.name}.svg`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// const goerliKey = import.meta.env.VITE_GOERLI_KEY;
|
// const goerliKey = import.meta.env.VITE_GOERLI_KEY;
|
||||||
|
|
||||||
const harbAmount = computed(() => {
|
const harbAmount = computed(() => {
|
||||||
if (wallet.balance?.value) {
|
if (wallet.balance?.value) {
|
||||||
return compactNumber(formatBigIntDivision(wallet.balance?.value, 10n ** BigInt(wallet.balance.decimals)));
|
return compactNumber(formatBigIntDivision(wallet.balance?.value, 10n ** BigInt(wallet.balance.decimals)));
|
||||||
} else {
|
} else {
|
||||||
return "0";
|
return '0';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const stakedAmount = computed(() =>
|
const stakedAmount = computed(() =>
|
||||||
myActivePositions.value.reduce(function (a, b) {
|
myActivePositions.value.reduce(function (a, b) {
|
||||||
return a + b.amount;
|
return a + b.amount;
|
||||||
}, 0)
|
}, 0)
|
||||||
);
|
);
|
||||||
|
|
||||||
const instance = getCurrentInstance();
|
const instance = getCurrentInstance();
|
||||||
|
|
@ -142,20 +132,20 @@ const instance = getCurrentInstance();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
function closeModal() {
|
function closeModal() {
|
||||||
instance!.parent!.emit("update:modelValue", false);
|
instance!.parent!.emit('update:modelValue', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// //special case for metaMask, but I think that is the most used wallet
|
// //special case for metaMask, but I think that is the most used wallet
|
||||||
async function connectWallet(connector: CreateConnectorFn | Connector, chainId: any) {
|
async function connectWallet(connector: CreateConnectorFn | Connector, chainId: number) {
|
||||||
console.log("connector", connector);
|
// console.log("connector", connector);
|
||||||
console.log("connector", connector.name);
|
// console.log("connector", connector.name);
|
||||||
connect({ connector, chainId });
|
connect({ connector, chainId });
|
||||||
closeModal();
|
closeModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
function stakeLink() {
|
function stakeLink() {
|
||||||
router.push("/dashboard#stake");
|
router.push('/dashboard#stake');
|
||||||
closeModal();
|
closeModal();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,24 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="footer" :style="{ color: (props.dark) ? 'white': 'black' }">
|
<div class="footer" :style="{ color: props.dark ? 'white' : 'black' }">
|
||||||
<div class="footer-inner">
|
<div class="footer-inner">
|
||||||
<div class="footer-headline subheader2">Follow HARBERG Protocol</div>
|
<div class="footer-headline subheader2">Follow HARBERG Protocol</div>
|
||||||
<div class="social-links">
|
<div class="social-links">
|
||||||
<social-button :dark="props.dark" type="discord" :href="discord"></social-button>
|
<SocialButton :dark="props.dark" type="discord" :href="discord"></SocialButton>
|
||||||
<social-button :dark="props.dark" type="telegram" :href="telegram"></social-button>
|
<SocialButton :dark="props.dark" type="telegram" :href="telegram"></SocialButton>
|
||||||
<social-button :dark="props.dark" type="twitter" :href="twitter"></social-button>
|
<SocialButton :dark="props.dark" type="twitter" :href="twitter"></SocialButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import socialButton from '@/components/socialButton.vue';
|
import SocialButton from '@/components/SocialButton.vue';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
dark?: boolean;
|
dark?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const discord = import.meta.env.VITE_DISCORD
|
const discord = import.meta.env.VITE_DISCORD;
|
||||||
const telegram = import.meta.env.VITE_TELEGRAM;
|
const telegram = import.meta.env.VITE_TELEGRAM;
|
||||||
const twitter = import.meta.env.VITE_TWITTER;
|
const twitter = import.meta.env.VITE_TWITTER;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,69 +1,63 @@
|
||||||
<template>
|
<template>
|
||||||
<header>
|
<header>
|
||||||
<div class="navbar">
|
<div class="navbar">
|
||||||
<div class="navbar-inner navigation-font">
|
<div class="navbar-inner navigation-font">
|
||||||
<div class="navbar-left" @click="router.push('/')">
|
<div class="navbar-left" @click="router.push('/')">
|
||||||
<div class="navbar-title">
|
<div class="navbar-title">
|
||||||
<span>K</span>
|
<span>K</span>
|
||||||
<span class="big-spacing">r</span>
|
<span class="big-spacing">r</span>
|
||||||
<span class="small-spacing">A</span>
|
<span class="small-spacing">A</span>
|
||||||
<span class="big-spacing">I</span>
|
<span class="big-spacing">I</span>
|
||||||
<span>ken</span>
|
<span>ken</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="navbar-center"></div>
|
<div class="navbar-center"></div>
|
||||||
<div class="navbar-end">
|
<div class="navbar-end">
|
||||||
<nav v-if="!isMobile">
|
<nav v-if="!isMobile">
|
||||||
<RouterLink v-for="navbarRoute in navbarRoutes" :key="navbarRoute.name" :to="navbarRoute.path">
|
<RouterLink v-for="navbarRoute in navbarRoutes" :key="navbarRoute.name" :to="navbarRoute.path">
|
||||||
{{ navbarRoute.title }}
|
{{ navbarRoute.title }}
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
<a href="/#/docs">Docs</a>
|
<a href="/#/docs">Docs</a>
|
||||||
</nav>
|
</nav>
|
||||||
<template v-if="!isMobile">
|
<template v-if="!isMobile">
|
||||||
<div class="vertical-line"></div>
|
<div class="vertical-line"></div>
|
||||||
<network-changer></network-changer>
|
<NetworkChanger></NetworkChanger>
|
||||||
<div class="vertical-line"></div>
|
<div class="vertical-line"></div>
|
||||||
</template>
|
</template>
|
||||||
<connect-button @click="showPanel = true" v-if="!isMobile"></connect-button>
|
<ConnectButton @click="showPanel = true" v-if="!isMobile"></ConnectButton>
|
||||||
<icon-login @click="showPanel = true" v-else-if="isMobile && !address"></icon-login>
|
<IconLogin @click="showPanel = true" v-else-if="isMobile && !address"></IconLogin>
|
||||||
<img
|
<img @click="showPanel = true" v-else-if="isMobile && address" :src="getBlocky(address)" alt="avatar" />
|
||||||
@click="showPanel = true"
|
</div>
|
||||||
v-else-if="isMobile && address"
|
</div>
|
||||||
:src="getBlocky(address)"
|
</div>
|
||||||
alt="avatar"
|
</header>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { getBlocky } from "@/utils/blockies";
|
import { getBlocky } from '@/utils/blockies';
|
||||||
import { RouterLink, useRouter } from "vue-router";
|
import { RouterLink, useRouter } from 'vue-router';
|
||||||
import IconLogin from "@/components/icons/IconLogin.vue";
|
import IconLogin from '@/components/icons/IconLogin.vue';
|
||||||
import ThemeToggle from "@/components/layouts/ThemeToggle.vue";
|
// import ThemeToggle from "@/components/layouts/ThemeToggle.vue";
|
||||||
import NetworkChanger from "@/components/layouts/NetworkChanger.vue";
|
import NetworkChanger from '@/components/layouts/NetworkChanger.vue';
|
||||||
import ConnectButton from "@/components/layouts/ConnectButton.vue";
|
import ConnectButton from '@/components/layouts/ConnectButton.vue';
|
||||||
import { computed, inject, ref } from "vue";
|
import { inject, ref } from 'vue';
|
||||||
import { useAccount } from "@wagmi/vue";
|
import { useAccount } from '@wagmi/vue';
|
||||||
import { useDark } from "@/composables/useDark";
|
import { useDark } from '@/composables/useDark';
|
||||||
const {darkTheme} = useDark();
|
const { darkTheme: _darkTheme } = useDark();
|
||||||
const {address} = useAccount();
|
const { address } = useAccount();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
const showPanel = inject<boolean>('showPanel', false);
|
||||||
|
const isMobile = inject<boolean>('isMobile', false);
|
||||||
|
|
||||||
const showPanel = inject<boolean>("showPanel", false);
|
const _dark1 = ref(false);
|
||||||
const isMobile = inject<boolean>("isMobile", false);
|
|
||||||
|
|
||||||
const dark1 = ref(false)
|
|
||||||
const routes = router.getRoutes();
|
const routes = router.getRoutes();
|
||||||
const navbarRoutes = routes.flatMap((obj) => {
|
const navbarRoutes = routes.flatMap(obj => {
|
||||||
if (obj.meta.group === "navbar") {
|
if (obj.meta.group === 'navbar') {
|
||||||
return { title: obj.meta.title, name: obj.name, path: obj.path };
|
return { title: obj.meta.title, name: obj.name, path: obj.path };
|
||||||
} else {
|
} else {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -1,38 +1,38 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="network-changer" v-click-outside="closeMenu">
|
<div class="network-changer" v-click-outside="closeMenu">
|
||||||
<div class="network-changer-inner" @click="showMenu = !showMenu">
|
<div class="network-changer-inner" @click="showMenu = !showMenu">
|
||||||
<icon-base></icon-base>
|
<IconBase></IconBase>
|
||||||
<Icon v-if="showMenu" class="toggle-icon" icon="mdi:chevron-down"></Icon>
|
<Icon v-if="showMenu" class="toggle-icon" icon="mdi:chevron-down"></Icon>
|
||||||
<Icon v-else class="toggle-icon" icon="mdi:chevron-up"></Icon>
|
<Icon v-else class="toggle-icon" icon="mdi:chevron-up"></Icon>
|
||||||
</div>
|
</div>
|
||||||
<Transition name="collapse-network">
|
<Transition name="collapse-network">
|
||||||
<div v-show="showMenu" class="network-changer--list">
|
<div v-show="showMenu" class="network-changer--list">
|
||||||
<div class="list-inner" ref="listInner">
|
<div class="list-inner" ref="listInner">
|
||||||
<div class="list--element" v-for="chain in chains" :key="chain.id">
|
<div class="list--element" v-for="chain in chains" :key="chain.id">
|
||||||
<template v-if="chain.id === wallet.account.chainId">
|
<template v-if="chain.id === wallet.account.chainId">
|
||||||
<icon-base></icon-base>
|
<IconBase></IconBase>
|
||||||
<div>{{ chain.name }}</div>
|
<div>{{ chain.name }}</div>
|
||||||
<div>Connected</div>
|
<div>Connected</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<icon-base></icon-base>
|
<IconBase></IconBase>
|
||||||
<div>{{ chain.name }}</div>
|
<div>{{ chain.name }}</div>
|
||||||
<f-button outlined dense @click="switchNetwork(chain)">Switch Network</f-button>
|
<FButton outlined dense @click="switchNetwork(chain)">Switch Network</FButton>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import IconBase from "@/components/icons/IconBase.vue";
|
import IconBase from '@/components/icons/IconBase.vue';
|
||||||
import { ref } from "vue";
|
import { ref } from 'vue';
|
||||||
import { useChains, useChainId, useSwitchChain } from "@wagmi/vue";
|
import { useChains, useSwitchChain } from '@wagmi/vue';
|
||||||
import {Icon} from "@iconify/vue";
|
import { Icon } from '@iconify/vue';
|
||||||
import FButton from "@/components/fcomponents/FButton.vue";
|
import FButton from '@/components/fcomponents/FButton.vue';
|
||||||
import { useWallet } from "@/composables/useWallet";
|
import { useWallet } from '@/composables/useWallet';
|
||||||
const chains = useChains();
|
const chains = useChains();
|
||||||
const wallet = useWallet();
|
const wallet = useWallet();
|
||||||
const { switchChain } = useSwitchChain();
|
const { switchChain } = useSwitchChain();
|
||||||
|
|
@ -41,15 +41,14 @@ const showMenu = ref<boolean>(false);
|
||||||
const listInner = ref();
|
const listInner = ref();
|
||||||
|
|
||||||
function closeMenu() {
|
function closeMenu() {
|
||||||
if (showMenu.value) {
|
if (showMenu.value) {
|
||||||
console.log("tesat");
|
// console.log("tesat");
|
||||||
showMenu.value = false;
|
showMenu.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function switchNetwork(chain: any) {
|
async function switchNetwork(chain: { id: number }) {
|
||||||
console.log("chain", chain);
|
switchChain({ chainId: chain.id });
|
||||||
switchChain({ chainId: chain.id });
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,26 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="slideout-overlay" :class="{ open: props.modelValue }" @click="showPanel = false"></div>
|
<div class="slideout-overlay" :class="{ open: props.modelValue }" @click="showPanel = false"></div>
|
||||||
<div :class="{ 'slideout-wrapper': true, open: props.modelValue }">
|
<div :class="{ 'slideout-wrapper': true, open: props.modelValue }">
|
||||||
<div class="slideout-panel">
|
<div class="slideout-panel">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { inject, watchEffect } from "vue";
|
import { inject } from 'vue';
|
||||||
|
|
||||||
const props = defineProps({
|
interface Props {
|
||||||
modelValue: Boolean,
|
modelValue?: boolean;
|
||||||
});
|
}
|
||||||
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
defineEmits(["update:modelValue"]);
|
interface Emits {
|
||||||
|
(e: 'update:modelValue', value: boolean): void;
|
||||||
|
}
|
||||||
|
defineEmits<Emits>();
|
||||||
|
|
||||||
const showPanel = inject("showPanel");
|
const showPanel = inject('showPanel');
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
||||||
|
|
@ -1,150 +1,85 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="toggle-dark-light">
|
<div class="toggle-dark-light">
|
||||||
<input type="checkbox" id="darkmode-toggle" @click="toggleTheme" :checked="props.modelValue" />
|
<input type="checkbox" id="darkmode-toggle" @click="toggleTheme" :checked="props.modelValue" />
|
||||||
<label for="darkmode-toggle">
|
<label for="darkmode-toggle">
|
||||||
<svg
|
<svg
|
||||||
version="1.1"
|
version="1.1"
|
||||||
class="sun"
|
class="sun"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
x="0px"
|
x="0px"
|
||||||
y="0px"
|
y="0px"
|
||||||
viewBox="0 0 496 496"
|
viewBox="0 0 496 496"
|
||||||
style="enable-background: new 0 0 496 496"
|
style="enable-background: new 0 0 496 496"
|
||||||
xml:space="preserve"
|
xml:space="preserve"
|
||||||
>
|
>
|
||||||
<rect
|
<rect x="152.994" y="58.921" transform="matrix(0.3827 0.9239 -0.9239 0.3827 168.6176 -118.5145)" width="40.001" height="16" />
|
||||||
x="152.994"
|
<rect x="46.9" y="164.979" transform="matrix(0.9239 0.3827 -0.3827 0.9239 71.29 -12.4346)" width="40.001" height="16" />
|
||||||
y="58.921"
|
<rect x="46.947" y="315.048" transform="matrix(0.9239 -0.3827 0.3827 0.9239 -118.531 50.2116)" width="40.001" height="16" />
|
||||||
transform="matrix(0.3827 0.9239 -0.9239 0.3827 168.6176 -118.5145)"
|
|
||||||
width="40.001"
|
|
||||||
height="16"
|
|
||||||
/>
|
|
||||||
<rect
|
|
||||||
x="46.9"
|
|
||||||
y="164.979"
|
|
||||||
transform="matrix(0.9239 0.3827 -0.3827 0.9239 71.29 -12.4346)"
|
|
||||||
width="40.001"
|
|
||||||
height="16"
|
|
||||||
/>
|
|
||||||
<rect
|
|
||||||
x="46.947"
|
|
||||||
y="315.048"
|
|
||||||
transform="matrix(0.9239 -0.3827 0.3827 0.9239 -118.531 50.2116)"
|
|
||||||
width="40.001"
|
|
||||||
height="16"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<rect
|
<rect x="164.966" y="409.112" transform="matrix(-0.9238 -0.3828 0.3828 -0.9238 168.4872 891.7491)" width="16" height="39.999" />
|
||||||
x="164.966"
|
|
||||||
y="409.112"
|
|
||||||
transform="matrix(-0.9238 -0.3828 0.3828 -0.9238 168.4872 891.7491)"
|
|
||||||
width="16"
|
|
||||||
height="39.999"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<rect
|
<rect x="303.031" y="421.036" transform="matrix(-0.3827 -0.9239 0.9239 -0.3827 50.2758 891.6655)" width="40.001" height="16" />
|
||||||
x="303.031"
|
|
||||||
y="421.036"
|
|
||||||
transform="matrix(-0.3827 -0.9239 0.9239 -0.3827 50.2758 891.6655)"
|
|
||||||
width="40.001"
|
|
||||||
height="16"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<rect
|
<rect x="409.088" y="315.018" transform="matrix(-0.9239 -0.3827 0.3827 -0.9239 701.898 785.6559)" width="40.001" height="16" />
|
||||||
x="409.088"
|
|
||||||
y="315.018"
|
|
||||||
transform="matrix(-0.9239 -0.3827 0.3827 -0.9239 701.898 785.6559)"
|
|
||||||
width="40.001"
|
|
||||||
height="16"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<rect
|
<rect x="409.054" y="165.011" transform="matrix(-0.9239 0.3827 -0.3827 -0.9239 891.6585 168.6574)" width="40.001" height="16" />
|
||||||
x="409.054"
|
<rect x="315.001" y="46.895" transform="matrix(0.9238 0.3828 -0.3828 0.9238 50.212 -118.5529)" width="16" height="39.999" />
|
||||||
y="165.011"
|
<path
|
||||||
transform="matrix(-0.9239 0.3827 -0.3827 -0.9239 891.6585 168.6574)"
|
d="M248,88c-88.224,0-160,71.776-160,160s71.776,160,160,160s160-71.776,160-160S336.224,88,248,88z M248,392
|
||||||
width="40.001"
|
|
||||||
height="16"
|
|
||||||
/>
|
|
||||||
<rect
|
|
||||||
x="315.001"
|
|
||||||
y="46.895"
|
|
||||||
transform="matrix(0.9238 0.3828 -0.3828 0.9238 50.212 -118.5529)"
|
|
||||||
width="16"
|
|
||||||
height="39.999"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M248,88c-88.224,0-160,71.776-160,160s71.776,160,160,160s160-71.776,160-160S336.224,88,248,88z M248,392
|
|
||||||
c-79.4,0-144-64.6-144-144s64.6-144,144-144s144,64.6,144,144S327.4,392,248,392z"
|
c-79.4,0-144-64.6-144-144s64.6-144,144-144s144,64.6,144,144S327.4,392,248,392z"
|
||||||
/>
|
/>
|
||||||
<rect x="240" width="16" height="72" />
|
<rect x="240" width="16" height="72" />
|
||||||
<rect
|
<rect x="62.097" y="90.096" transform="matrix(0.7071 0.7071 -0.7071 0.7071 98.0963 -40.6334)" width="71.999" height="16" />
|
||||||
x="62.097"
|
<rect y="240" width="72" height="16" />
|
||||||
y="90.096"
|
|
||||||
transform="matrix(0.7071 0.7071 -0.7071 0.7071 98.0963 -40.6334)"
|
|
||||||
width="71.999"
|
|
||||||
height="16"
|
|
||||||
/>
|
|
||||||
<rect y="240" width="72" height="16" />
|
|
||||||
|
|
||||||
<rect
|
<rect x="90.091" y="361.915" transform="matrix(-0.7071 -0.7071 0.7071 -0.7071 -113.9157 748.643)" width="16" height="71.999" />
|
||||||
x="90.091"
|
<rect x="240" y="424" width="16" height="72" />
|
||||||
y="361.915"
|
|
||||||
transform="matrix(-0.7071 -0.7071 0.7071 -0.7071 -113.9157 748.643)"
|
|
||||||
width="16"
|
|
||||||
height="71.999"
|
|
||||||
/>
|
|
||||||
<rect x="240" y="424" width="16" height="72" />
|
|
||||||
|
|
||||||
<rect
|
<rect x="361.881" y="389.915" transform="matrix(-0.7071 -0.7071 0.7071 -0.7071 397.8562 960.6281)" width="71.999" height="16" />
|
||||||
x="361.881"
|
<rect x="424" y="240" width="72" height="16" />
|
||||||
y="389.915"
|
<rect x="389.911" y="62.091" transform="matrix(0.7071 0.7071 -0.7071 0.7071 185.9067 -252.6357)" width="16" height="71.999" />
|
||||||
transform="matrix(-0.7071 -0.7071 0.7071 -0.7071 397.8562 960.6281)"
|
</svg>
|
||||||
width="71.999"
|
<svg
|
||||||
height="16"
|
version="1.1"
|
||||||
/>
|
class="moon"
|
||||||
<rect x="424" y="240" width="72" height="16" />
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<rect
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
x="389.911"
|
x="0px"
|
||||||
y="62.091"
|
y="0px"
|
||||||
transform="matrix(0.7071 0.7071 -0.7071 0.7071 185.9067 -252.6357)"
|
viewBox="0 0 49.739 49.739"
|
||||||
width="16"
|
style="enable-background: new 0 0 49.739 49.739"
|
||||||
height="71.999"
|
xml:space="preserve"
|
||||||
/>
|
>
|
||||||
</svg>
|
<path
|
||||||
<svg
|
d="M25.068,48.889c-9.173,0-18.017-5.06-22.396-13.804C-3.373,23.008,1.164,8.467,13.003,1.979l2.061-1.129l-0.615,2.268
|
||||||
version="1.1"
|
|
||||||
class="moon"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
||||||
x="0px"
|
|
||||||
y="0px"
|
|
||||||
viewBox="0 0 49.739 49.739"
|
|
||||||
style="enable-background: new 0 0 49.739 49.739"
|
|
||||||
xml:space="preserve"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M25.068,48.889c-9.173,0-18.017-5.06-22.396-13.804C-3.373,23.008,1.164,8.467,13.003,1.979l2.061-1.129l-0.615,2.268
|
|
||||||
c-1.479,5.459-0.899,11.25,1.633,16.306c2.75,5.493,7.476,9.587,13.305,11.526c5.831,1.939,12.065,1.492,17.559-1.258v0
|
c-1.479,5.459-0.899,11.25,1.633,16.306c2.75,5.493,7.476,9.587,13.305,11.526c5.831,1.939,12.065,1.492,17.559-1.258v0
|
||||||
c0.25-0.125,0.492-0.258,0.734-0.391l2.061-1.13l-0.585,2.252c-1.863,6.873-6.577,12.639-12.933,15.822
|
c0.25-0.125,0.492-0.258,0.734-0.391l2.061-1.13l-0.585,2.252c-1.863,6.873-6.577,12.639-12.933,15.822
|
||||||
C32.639,48.039,28.825,48.888,25.068,48.889z M12.002,4.936c-9.413,6.428-12.756,18.837-7.54,29.253
|
C32.639,48.039,28.825,48.888,25.068,48.889z M12.002,4.936c-9.413,6.428-12.756,18.837-7.54,29.253
|
||||||
c5.678,11.34,19.522,15.945,30.864,10.268c5.154-2.582,9.136-7.012,11.181-12.357c-5.632,2.427-11.882,2.702-17.752,0.748
|
c5.678,11.34,19.522,15.945,30.864,10.268c5.154-2.582,9.136-7.012,11.181-12.357c-5.632,2.427-11.882,2.702-17.752,0.748
|
||||||
c-6.337-2.108-11.473-6.557-14.463-12.528C11.899,15.541,11.11,10.16,12.002,4.936z"
|
c-6.337-2.108-11.473-6.557-14.463-12.528C11.899,15.541,11.11,10.16,12.002,4.936z"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const emit = defineEmits(["update:modelValue"]);
|
interface Emits {
|
||||||
|
(event: 'update:modelValue', value: boolean): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: boolean;
|
modelValue: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
function toggleTheme(event: any) {
|
function toggleTheme(event: Event) {
|
||||||
emit("update:modelValue", event.target.checked);
|
const target = event.target as HTMLInputElement | null;
|
||||||
|
if (target) {
|
||||||
|
emit('update:modelValue', target.checked);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,97 +1,97 @@
|
||||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||||
import { defineAsyncComponent } from "vue";
|
import { mount } from '@vue/test-utils';
|
||||||
import { mount, flushPromises } from "@vue/test-utils";
|
import { defineComponent } from 'vue';
|
||||||
import SocialBadge from "./socialButton.vue";
|
import SocialBadge from './SocialButton.vue';
|
||||||
|
|
||||||
|
function mockIconComponent(name: string) {
|
||||||
const mockIconComponent = (name:string) => ({
|
return {
|
||||||
default: {
|
__esModule: true,
|
||||||
|
default: defineComponent({
|
||||||
name,
|
name,
|
||||||
template: `<svg class='${name.toLowerCase()}'></svg>`,
|
template: `<svg class='${name.toLowerCase()}'></svg>`,
|
||||||
},
|
}),
|
||||||
});
|
};
|
||||||
|
}
|
||||||
vi.mock("../components/icons/IconDiscord.vue", () => mockIconComponent("IconDiscord"));
|
|
||||||
vi.mock("../components/icons/IconTwitter.vue", () => mockIconComponent("IconTwitter"));
|
|
||||||
vi.mock("../components/icons/IconTelegram.vue", () => mockIconComponent("IconTelegram"));
|
|
||||||
|
|
||||||
|
vi.mock('@/components/icons/IconDiscord.vue', () => mockIconComponent('IconDiscord'));
|
||||||
|
vi.mock('@/components/icons/IconTwitter.vue', () => mockIconComponent('IconTwitter'));
|
||||||
|
vi.mock('@/components/icons/IconTelegram.vue', () => mockIconComponent('IconTelegram'));
|
||||||
|
|
||||||
describe("SocialBadge.vue", () => {
|
describe('SocialBadge.vue', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
wrapper = null;
|
wrapper = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders the correct link", () => {
|
it('renders the correct link', () => {
|
||||||
wrapper = mount(SocialBadge, {
|
wrapper = mount(SocialBadge, {
|
||||||
props: {
|
props: {
|
||||||
href: "https://example.com",
|
href: 'https://example.com',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const link = wrapper.find("a");
|
const link = wrapper.find('a');
|
||||||
expect(link.exists()).toBe(true);
|
expect(link.exists()).toBe(true);
|
||||||
expect(link.attributes("href")).toBe("https://example.com");
|
expect(link.attributes('href')).toBe('https://example.com');
|
||||||
expect(link.attributes("target")).toBe("_blank");
|
expect(link.attributes('target')).toBe('_blank');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("applies the correct color for light mode", () => {
|
it('applies the correct color for light mode', () => {
|
||||||
wrapper = mount(SocialBadge, {
|
wrapper = mount(SocialBadge, {
|
||||||
props: {
|
props: {
|
||||||
dark: false,
|
dark: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const badge = wrapper.find(".social-badge");
|
const badge = wrapper.find('.social-badge');
|
||||||
expect(badge.attributes("style")).toContain("color: black");
|
expect(badge.attributes('style')).toContain('color: black');
|
||||||
expect(badge.attributes("style")).toContain("border-color: black");
|
expect(badge.attributes('style')).toContain('border-color: black');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("applies the correct color for dark mode", () => {
|
it('applies the correct color for dark mode', () => {
|
||||||
wrapper = mount(SocialBadge, {
|
wrapper = mount(SocialBadge, {
|
||||||
props: {
|
props: {
|
||||||
dark: true,
|
dark: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const badge = wrapper.find(".social-badge");
|
const badge = wrapper.find('.social-badge');
|
||||||
expect(badge.attributes("style")).toContain("color: white");
|
expect(badge.attributes('style')).toContain('color: white');
|
||||||
expect(badge.attributes("style")).toContain("border-color: white");
|
expect(badge.attributes('style')).toContain('border-color: white');
|
||||||
});
|
});
|
||||||
|
|
||||||
// it("renders the correct icon based on the type prop", async () => {
|
// it("renders the correct icon based on the type prop", async () => {
|
||||||
// wrapper = mount(SocialBadge, {
|
// wrapper = mount(SocialBadge, {
|
||||||
// props: {
|
// props: {
|
||||||
// type: "discord",
|
// type: "discord",
|
||||||
// },
|
// },
|
||||||
// });
|
// });
|
||||||
// await flushPromises();
|
// await flushPromises();
|
||||||
// const current = wrapper.getCurrentComponent()?.setupState?.img.__asyncResolved
|
// const current = wrapper.getCurrentComponent()?.setupState?.img.__asyncResolved
|
||||||
// console.log("current", current.default.name);
|
// console.log("current", current.default.name);
|
||||||
|
|
||||||
// expect(current.default.name).toBe("IconDiscord");
|
|
||||||
|
|
||||||
// // expect(icon.exists()).toBe(true);
|
// expect(current.default.name).toBe("IconDiscord");
|
||||||
// });
|
|
||||||
|
|
||||||
it("does not render an icon if the type is unsupported", async () => {
|
// // expect(icon.exists()).toBe(true);
|
||||||
|
// });
|
||||||
|
|
||||||
|
it('does not render an icon if the type is unsupported', async () => {
|
||||||
wrapper = mount(SocialBadge, {
|
wrapper = mount(SocialBadge, {
|
||||||
props: {
|
props: {
|
||||||
type: "unsupported",
|
type: 'unsupported',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
await wrapper.vm.$nextTick();
|
await wrapper.vm.$nextTick();
|
||||||
|
|
||||||
const icon = wrapper.find(".social-badge-icon").find("component");
|
const icon = wrapper.find('.social-badge-icon').find('component');
|
||||||
expect(icon.exists()).toBe(false);
|
expect(icon.exists()).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders without crashing when no props are provided", () => {
|
it('renders without crashing when no props are provided', () => {
|
||||||
wrapper = mount(SocialBadge);
|
wrapper = mount(SocialBadge);
|
||||||
|
|
||||||
expect(wrapper.exists()).toBe(true);
|
expect(wrapper.exists()).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
<template>
|
|
||||||
<a :href="props.href" target="_blank">
|
|
||||||
<div
|
|
||||||
class="social-badge"
|
|
||||||
:style="{ color: color, 'border-color': color }"
|
|
||||||
:class="{ 'social-badge--dark': props.dark }"
|
|
||||||
>
|
|
||||||
<div class="social-badge-icon">
|
|
||||||
<component :color="color" :is="img" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { defineAsyncComponent, computed } from "vue";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
type?: string;
|
|
||||||
dark?: boolean;
|
|
||||||
href?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {});
|
|
||||||
|
|
||||||
const color = computed(() => (props.dark ? "white" : "black"));
|
|
||||||
|
|
||||||
const img = computed(() => {
|
|
||||||
let img;
|
|
||||||
switch (props.type) {
|
|
||||||
case "discord":
|
|
||||||
img = defineAsyncComponent(() => import(`../components/icons/IconDiscord.vue`));
|
|
||||||
break;
|
|
||||||
case "twitter":
|
|
||||||
img = defineAsyncComponent(() => import(`../components/icons/IconTwitter.vue`));
|
|
||||||
break;
|
|
||||||
case "telegram":
|
|
||||||
img = defineAsyncComponent(() => import(`../components/icons/IconTelegram.vue`));
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return img;
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<style lang="sass">
|
|
||||||
.social-badge
|
|
||||||
border-radius: 14px
|
|
||||||
display: flex
|
|
||||||
border: 1px solid var(--color-social-border)
|
|
||||||
padding: 6px 20px
|
|
||||||
align-items: center
|
|
||||||
flex: 0 1 0
|
|
||||||
color: black
|
|
||||||
&:hover,&:active,&:focus
|
|
||||||
background-color: var(--color-white-hovered)
|
|
||||||
cursor: pointer
|
|
||||||
&.social-badge--dark
|
|
||||||
&:hover, &:active, &:focus
|
|
||||||
background-color: var(--color-black-hovered)
|
|
||||||
// font-size: 0
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,71 +1,94 @@
|
||||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||||
import { ref, nextTick } from 'vue'
|
import { ref, nextTick, type Ref } from 'vue';
|
||||||
import { useSnatchSelection } from '../useSnatchSelection'
|
import { useSnatchSelection } from '../useSnatchSelection';
|
||||||
import { usePositions } from '../usePositions'
|
import { usePositions, type Position } from '../usePositions';
|
||||||
import { useStake } from '../useStake'
|
import { useStake } from '../useStake';
|
||||||
import { useWallet } from '../useWallet'
|
import { useWallet } from '../useWallet';
|
||||||
import { useStatCollection } from '../useStatCollection'
|
import { useStatCollection } from '../useStatCollection';
|
||||||
import { useAdjustTaxRate } from '../useAdjustTaxRates'
|
import { useAdjustTaxRate } from '../useAdjustTaxRates';
|
||||||
|
|
||||||
|
interface MockPositionsReturn {
|
||||||
|
activePositions: Ref<Partial<Position>[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MockStakeReturn {
|
||||||
|
stakingAmountShares: bigint;
|
||||||
|
taxRate: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MockWalletReturn {
|
||||||
|
account: { address: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MockStatCollectionReturn {
|
||||||
|
stakeTotalSupply?: bigint;
|
||||||
|
kraikenTotalSupply?: bigint;
|
||||||
|
outstandingStake?: bigint;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MockAdjustTaxRateReturn {
|
||||||
|
taxRates: Array<{ year: number }>;
|
||||||
|
}
|
||||||
|
|
||||||
// Mock all composables
|
// Mock all composables
|
||||||
vi.mock('../usePositions', () => ({
|
vi.mock('../usePositions', () => ({
|
||||||
usePositions: vi.fn()
|
usePositions: vi.fn(),
|
||||||
}))
|
}));
|
||||||
|
|
||||||
vi.mock('../useStake', () => ({
|
vi.mock('../useStake', () => ({
|
||||||
useStake: vi.fn()
|
useStake: vi.fn(),
|
||||||
}))
|
}));
|
||||||
|
|
||||||
vi.mock('../useWallet', () => ({
|
vi.mock('../useWallet', () => ({
|
||||||
useWallet: vi.fn()
|
useWallet: vi.fn(),
|
||||||
}))
|
}));
|
||||||
|
|
||||||
vi.mock('../useStatCollection', () => ({
|
vi.mock('../useStatCollection', () => ({
|
||||||
useStatCollection: vi.fn()
|
useStatCollection: vi.fn(),
|
||||||
}))
|
}));
|
||||||
|
|
||||||
vi.mock('../useAdjustTaxRates', () => ({
|
vi.mock('../useAdjustTaxRates', () => ({
|
||||||
useAdjustTaxRate: vi.fn()
|
useAdjustTaxRate: vi.fn(),
|
||||||
}))
|
}));
|
||||||
|
|
||||||
vi.mock('kraiken-lib/staking', () => ({
|
vi.mock('kraiken-lib/staking', () => ({
|
||||||
calculateSnatchShortfall: vi.fn((outstandingStake, stakingShares, stakeTotalSupply) => {
|
calculateSnatchShortfall: vi.fn((outstandingStake, stakingShares, _stakeTotalSupply) => {
|
||||||
return stakingShares > outstandingStake ? 0n : outstandingStake - stakingShares
|
return stakingShares > outstandingStake ? 0n : outstandingStake - stakingShares;
|
||||||
})
|
}),
|
||||||
}))
|
}));
|
||||||
|
|
||||||
vi.mock('kraiken-lib/snatch', () => ({
|
vi.mock('kraiken-lib/snatch', () => ({
|
||||||
selectSnatchPositions: vi.fn((candidates, options) => {
|
selectSnatchPositions: vi.fn((candidates, options) => {
|
||||||
if (candidates.length === 0) {
|
if (candidates.length === 0) {
|
||||||
return { selected: [], remainingShortfall: options.shortfallShares }
|
return { selected: [], remainingShortfall: options.shortfallShares };
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
selected: candidates,
|
selected: candidates,
|
||||||
remainingShortfall: 0n,
|
remainingShortfall: 0n,
|
||||||
maxSelectedTaxRateIndex: candidates[candidates.length - 1].taxRateIndex
|
maxSelectedTaxRateIndex: candidates[candidates.length - 1].taxRateIndex,
|
||||||
}
|
};
|
||||||
}),
|
}),
|
||||||
minimumTaxRate: vi.fn(() => 0.01)
|
minimumTaxRate: vi.fn(() => 0.01),
|
||||||
}))
|
}));
|
||||||
|
|
||||||
describe('useSnatchSelection', () => {
|
describe('useSnatchSelection', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// Reset all mocks
|
// Reset all mocks
|
||||||
vi.clearAllMocks()
|
vi.clearAllMocks();
|
||||||
|
|
||||||
// Setup default mock values with proper refs
|
// Setup default mock values with proper refs
|
||||||
vi.mocked(usePositions).mockReturnValue({
|
vi.mocked(usePositions).mockReturnValue({
|
||||||
activePositions: ref([])
|
activePositions: ref([]),
|
||||||
} as any)
|
} as MockPositionsReturn);
|
||||||
|
|
||||||
vi.mocked(useStake).mockReturnValue({
|
vi.mocked(useStake).mockReturnValue({
|
||||||
stakingAmountShares: 0n,
|
stakingAmountShares: 0n,
|
||||||
taxRate: 1.0
|
taxRate: 1.0,
|
||||||
} as any)
|
} as MockStakeReturn);
|
||||||
|
|
||||||
vi.mocked(useWallet).mockReturnValue({
|
vi.mocked(useWallet).mockReturnValue({
|
||||||
account: { address: '0x123' }
|
account: { address: '0x123' },
|
||||||
} as any)
|
} as MockWalletReturn);
|
||||||
|
|
||||||
// Mock with realistic values for local computation
|
// Mock with realistic values for local computation
|
||||||
// stakeTotalSupply is typically 10^(18+7) = 10^25 from contract
|
// stakeTotalSupply is typically 10^(18+7) = 10^25 from contract
|
||||||
|
|
@ -73,40 +96,40 @@ describe('useSnatchSelection', () => {
|
||||||
vi.mocked(useStatCollection).mockReturnValue({
|
vi.mocked(useStatCollection).mockReturnValue({
|
||||||
stakeTotalSupply: 10000000000000000000000000n, // 10^25
|
stakeTotalSupply: 10000000000000000000000000n, // 10^25
|
||||||
kraikenTotalSupply: 10000000000000000000000000n, // 10^25
|
kraikenTotalSupply: 10000000000000000000000000n, // 10^25
|
||||||
outstandingStake: 500n
|
outstandingStake: 500n,
|
||||||
} as any)
|
} as MockStatCollectionReturn);
|
||||||
|
|
||||||
vi.mocked(useAdjustTaxRate).mockReturnValue({
|
vi.mocked(useAdjustTaxRate).mockReturnValue({
|
||||||
taxRates: [{ year: 1 }]
|
taxRates: [{ year: 1 }],
|
||||||
} as any)
|
} as MockAdjustTaxRateReturn);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should initialize with empty snatchable positions', () => {
|
it('should initialize with empty snatchable positions', () => {
|
||||||
const { snatchablePositions } = useSnatchSelection()
|
const { snatchablePositions } = useSnatchSelection();
|
||||||
expect(snatchablePositions.value).toEqual([])
|
expect(snatchablePositions.value).toEqual([]);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should handle no active positions', () => {
|
it('should handle no active positions', () => {
|
||||||
const { snatchablePositions, floorTax } = useSnatchSelection()
|
const { snatchablePositions, floorTax } = useSnatchSelection();
|
||||||
expect(snatchablePositions.value).toEqual([])
|
expect(snatchablePositions.value).toEqual([]);
|
||||||
expect(floorTax.value).toBe(1)
|
expect(floorTax.value).toBe(1);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should handle no shortfall', () => {
|
it('should handle no shortfall', () => {
|
||||||
vi.mocked(useStatCollection).mockReturnValue({
|
vi.mocked(useStatCollection).mockReturnValue({
|
||||||
stakeTotalSupply: 1000n,
|
stakeTotalSupply: 1000n,
|
||||||
outstandingStake: 100n
|
outstandingStake: 100n,
|
||||||
})
|
});
|
||||||
|
|
||||||
vi.mocked(useStake).mockReturnValue({
|
vi.mocked(useStake).mockReturnValue({
|
||||||
stakingAmountShares: 900n,
|
stakingAmountShares: 900n,
|
||||||
taxRate: 1.0
|
taxRate: 1.0,
|
||||||
})
|
});
|
||||||
|
|
||||||
const { snatchablePositions, openPositionsAvailable } = useSnatchSelection()
|
const { snatchablePositions, openPositionsAvailable } = useSnatchSelection();
|
||||||
expect(snatchablePositions.value).toEqual([])
|
expect(snatchablePositions.value).toEqual([]);
|
||||||
expect(openPositionsAvailable.value).toBe(true)
|
expect(openPositionsAvailable.value).toBe(true);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should filter out positions with higher tax rate', async () => {
|
it('should filter out positions with higher tax rate', async () => {
|
||||||
vi.mocked(usePositions).mockReturnValue({
|
vi.mocked(usePositions).mockReturnValue({
|
||||||
|
|
@ -117,23 +140,23 @@ describe('useSnatchSelection', () => {
|
||||||
harbDeposit: 100n,
|
harbDeposit: 100n,
|
||||||
taxRate: 2.0,
|
taxRate: 2.0,
|
||||||
taxRateIndex: 1,
|
taxRateIndex: 1,
|
||||||
iAmOwner: false
|
iAmOwner: false,
|
||||||
}
|
},
|
||||||
])
|
]) as Ref<Partial<Position>[]>,
|
||||||
} as any)
|
} as MockPositionsReturn);
|
||||||
|
|
||||||
vi.mocked(useStake).mockReturnValue({
|
vi.mocked(useStake).mockReturnValue({
|
||||||
stakingAmountShares: 100n,
|
stakingAmountShares: 100n,
|
||||||
taxRate: 1.0
|
taxRate: 1.0,
|
||||||
} as any)
|
} as MockStakeReturn);
|
||||||
|
|
||||||
const { snatchablePositions } = useSnatchSelection()
|
const { snatchablePositions } = useSnatchSelection();
|
||||||
|
|
||||||
// Wait for watchEffect to run
|
// Wait for watchEffect to run
|
||||||
await new Promise(resolve => setTimeout(resolve, 0))
|
await new Promise(resolve => setTimeout(resolve, 0));
|
||||||
|
|
||||||
expect(snatchablePositions.value).toEqual([])
|
expect(snatchablePositions.value).toEqual([]);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should filter out owned positions by default', async () => {
|
it('should filter out owned positions by default', async () => {
|
||||||
vi.mocked(usePositions).mockReturnValue({
|
vi.mocked(usePositions).mockReturnValue({
|
||||||
|
|
@ -144,15 +167,15 @@ describe('useSnatchSelection', () => {
|
||||||
harbDeposit: 100n,
|
harbDeposit: 100n,
|
||||||
taxRate: 0.5,
|
taxRate: 0.5,
|
||||||
taxRateIndex: 1,
|
taxRateIndex: 1,
|
||||||
iAmOwner: true
|
iAmOwner: true,
|
||||||
}
|
},
|
||||||
])
|
]) as Ref<Partial<Position>[]>,
|
||||||
} as any)
|
} as MockPositionsReturn);
|
||||||
|
|
||||||
const { snatchablePositions } = useSnatchSelection()
|
const { snatchablePositions } = useSnatchSelection();
|
||||||
await new Promise(resolve => setTimeout(resolve, 0))
|
await new Promise(resolve => setTimeout(resolve, 0));
|
||||||
expect(snatchablePositions.value).toEqual([])
|
expect(snatchablePositions.value).toEqual([]);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should include owned positions when demo mode is enabled', async () => {
|
it('should include owned positions when demo mode is enabled', async () => {
|
||||||
const position = {
|
const position = {
|
||||||
|
|
@ -161,32 +184,32 @@ describe('useSnatchSelection', () => {
|
||||||
harbDeposit: 100n,
|
harbDeposit: 100n,
|
||||||
taxRate: 0.005, // 0.5% tax rate (less than maxTaxRate)
|
taxRate: 0.005, // 0.5% tax rate (less than maxTaxRate)
|
||||||
taxRateIndex: 1,
|
taxRateIndex: 1,
|
||||||
iAmOwner: true
|
iAmOwner: true,
|
||||||
}
|
};
|
||||||
|
|
||||||
vi.mocked(usePositions).mockReturnValue({
|
vi.mocked(usePositions).mockReturnValue({
|
||||||
activePositions: ref([position])
|
activePositions: ref([position]),
|
||||||
} as any)
|
} as MockPositionsReturn);
|
||||||
|
|
||||||
vi.mocked(useStake).mockReturnValue({
|
vi.mocked(useStake).mockReturnValue({
|
||||||
stakingAmountShares: 100n,
|
stakingAmountShares: 100n,
|
||||||
taxRate: 1.0 // Will be converted to 0.01 (1%) decimal
|
taxRate: 1.0, // Will be converted to 0.01 (1%) decimal
|
||||||
} as any)
|
} as MockStakeReturn);
|
||||||
|
|
||||||
// Need outstandingStake > stakingAmountShares to create shortfall
|
// Need outstandingStake > stakingAmountShares to create shortfall
|
||||||
vi.mocked(useStatCollection).mockReturnValue({
|
vi.mocked(useStatCollection).mockReturnValue({
|
||||||
stakeTotalSupply: 10000000000000000000000000n,
|
stakeTotalSupply: 10000000000000000000000000n,
|
||||||
kraikenTotalSupply: 10000000000000000000000000n,
|
kraikenTotalSupply: 10000000000000000000000000n,
|
||||||
outstandingStake: 500n
|
outstandingStake: 500n,
|
||||||
} as any)
|
} as MockStatCollectionReturn);
|
||||||
|
|
||||||
const { snatchablePositions } = useSnatchSelection(true)
|
const { snatchablePositions } = useSnatchSelection(true);
|
||||||
|
|
||||||
// Wait for watchEffect to run (no longer async)
|
// Wait for watchEffect to run (no longer async)
|
||||||
await nextTick()
|
await nextTick();
|
||||||
|
|
||||||
expect(snatchablePositions.value).toContainEqual(position)
|
expect(snatchablePositions.value).toContainEqual(position);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should handle partial fills', async () => {
|
it('should handle partial fills', async () => {
|
||||||
const position1 = {
|
const position1 = {
|
||||||
|
|
@ -195,8 +218,8 @@ describe('useSnatchSelection', () => {
|
||||||
harbDeposit: 100n,
|
harbDeposit: 100n,
|
||||||
taxRate: 0.005, // 0.5% tax rate
|
taxRate: 0.005, // 0.5% tax rate
|
||||||
taxRateIndex: 1,
|
taxRateIndex: 1,
|
||||||
iAmOwner: false
|
iAmOwner: false,
|
||||||
}
|
};
|
||||||
|
|
||||||
const position2 = {
|
const position2 = {
|
||||||
positionId: 2n,
|
positionId: 2n,
|
||||||
|
|
@ -204,32 +227,32 @@ describe('useSnatchSelection', () => {
|
||||||
harbDeposit: 200n,
|
harbDeposit: 200n,
|
||||||
taxRate: 0.006, // 0.6% tax rate
|
taxRate: 0.006, // 0.6% tax rate
|
||||||
taxRateIndex: 2,
|
taxRateIndex: 2,
|
||||||
iAmOwner: false
|
iAmOwner: false,
|
||||||
}
|
};
|
||||||
|
|
||||||
vi.mocked(usePositions).mockReturnValue({
|
vi.mocked(usePositions).mockReturnValue({
|
||||||
activePositions: ref([position1, position2])
|
activePositions: ref([position1, position2]) as Ref<Position[]>,
|
||||||
} as any)
|
} as MockPositionsReturn);
|
||||||
|
|
||||||
vi.mocked(useStake).mockReturnValue({
|
vi.mocked(useStake).mockReturnValue({
|
||||||
stakingAmountShares: 150n,
|
stakingAmountShares: 150n,
|
||||||
taxRate: 1.0 // Will be converted to 0.01 (1%) decimal
|
taxRate: 1.0, // Will be converted to 0.01 (1%) decimal
|
||||||
} as any)
|
} as MockStakeReturn);
|
||||||
|
|
||||||
// Need outstandingStake > stakingAmountShares to create shortfall
|
// Need outstandingStake > stakingAmountShares to create shortfall
|
||||||
vi.mocked(useStatCollection).mockReturnValue({
|
vi.mocked(useStatCollection).mockReturnValue({
|
||||||
stakeTotalSupply: 10000000000000000000000000n,
|
stakeTotalSupply: 10000000000000000000000000n,
|
||||||
kraikenTotalSupply: 10000000000000000000000000n,
|
kraikenTotalSupply: 10000000000000000000000000n,
|
||||||
outstandingStake: 500n
|
outstandingStake: 500n,
|
||||||
} as any)
|
} as MockStatCollectionReturn);
|
||||||
|
|
||||||
const { snatchablePositions } = useSnatchSelection()
|
const { snatchablePositions } = useSnatchSelection();
|
||||||
|
|
||||||
// Wait for watchEffect to run
|
// Wait for watchEffect to run
|
||||||
await nextTick()
|
await nextTick();
|
||||||
|
|
||||||
expect(snatchablePositions.value).toEqual([position1, position2])
|
expect(snatchablePositions.value).toEqual([position1, position2]);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should update floor tax based on selected positions', async () => {
|
it('should update floor tax based on selected positions', async () => {
|
||||||
const position = {
|
const position = {
|
||||||
|
|
@ -238,40 +261,36 @@ describe('useSnatchSelection', () => {
|
||||||
harbDeposit: 100n,
|
harbDeposit: 100n,
|
||||||
taxRate: 0.005, // 0.5% tax rate
|
taxRate: 0.005, // 0.5% tax rate
|
||||||
taxRateIndex: 1,
|
taxRateIndex: 1,
|
||||||
iAmOwner: false
|
iAmOwner: false,
|
||||||
}
|
};
|
||||||
|
|
||||||
vi.mocked(usePositions).mockReturnValue({
|
vi.mocked(usePositions).mockReturnValue({
|
||||||
activePositions: ref([position])
|
activePositions: ref([position]),
|
||||||
} as any)
|
} as MockPositionsReturn);
|
||||||
|
|
||||||
vi.mocked(useStake).mockReturnValue({
|
vi.mocked(useStake).mockReturnValue({
|
||||||
stakingAmountShares: 100n,
|
stakingAmountShares: 100n,
|
||||||
taxRate: 1.0 // Will be converted to 0.01 (1%) decimal
|
taxRate: 1.0, // Will be converted to 0.01 (1%) decimal
|
||||||
} as any)
|
} as MockStakeReturn);
|
||||||
|
|
||||||
// Need outstandingStake > stakingAmountShares to create shortfall
|
// Need outstandingStake > stakingAmountShares to create shortfall
|
||||||
vi.mocked(useStatCollection).mockReturnValue({
|
vi.mocked(useStatCollection).mockReturnValue({
|
||||||
stakeTotalSupply: 10000000000000000000000000n,
|
stakeTotalSupply: 10000000000000000000000000n,
|
||||||
kraikenTotalSupply: 10000000000000000000000000n,
|
kraikenTotalSupply: 10000000000000000000000000n,
|
||||||
outstandingStake: 500n
|
outstandingStake: 500n,
|
||||||
} as any)
|
} as MockStatCollectionReturn);
|
||||||
|
|
||||||
vi.mocked(useAdjustTaxRate).mockReturnValue({
|
vi.mocked(useAdjustTaxRate).mockReturnValue({
|
||||||
taxRates: [
|
taxRates: [{ year: 1 }, { year: 2 }, { year: 3 }],
|
||||||
{ year: 1 },
|
} as MockAdjustTaxRateReturn);
|
||||||
{ year: 2 },
|
|
||||||
{ year: 3 }
|
|
||||||
]
|
|
||||||
} as any)
|
|
||||||
|
|
||||||
const { floorTax } = useSnatchSelection()
|
const { floorTax } = useSnatchSelection();
|
||||||
|
|
||||||
// Wait for watchEffect to run
|
// Wait for watchEffect to run
|
||||||
await nextTick()
|
await nextTick();
|
||||||
|
|
||||||
// Floor tax should be taxRates[maxSelectedTaxRateIndex + 1]
|
// Floor tax should be taxRates[maxSelectedTaxRateIndex + 1]
|
||||||
// Position has taxRateIndex: 1, so nextIndex = 2, taxRates[2] = { year: 3 }
|
// Position has taxRateIndex: 1, so nextIndex = 2, taxRates[2] = { year: 3 }
|
||||||
expect(floorTax.value).toBe(3)
|
expect(floorTax.value).toBe(3);
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,72 +1,65 @@
|
||||||
import { ref, onMounted, onUnmounted, reactive, computed, type ComputedRef } from "vue";
|
import { ref, reactive, computed, type ComputedRef } from 'vue';
|
||||||
import * as StakeContract from "@/contracts/stake";
|
import * as StakeContract from '@/contracts/stake';
|
||||||
import { waitForTransactionReceipt } from "@wagmi/core";
|
import { waitForTransactionReceipt, type Config } from '@wagmi/core';
|
||||||
import { config } from "@/wagmi";
|
import { config } from '@/wagmi';
|
||||||
import { compactNumber, formatBigIntDivision } from "@/utils/helper";
|
// import { compactNumber, formatBigIntDivision } from "@/utils/helper";
|
||||||
import { useContractToast } from "./useContractToast";
|
import { useContractToast } from './useContractToast';
|
||||||
import { TAX_RATE_OPTIONS } from "kraiken-lib/taxRates";
|
import { TAX_RATE_OPTIONS } from 'kraiken-lib/taxRates';
|
||||||
|
|
||||||
const contractToast = useContractToast();
|
const contractToast = useContractToast();
|
||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
SignTransaction = "SignTransaction",
|
SignTransaction = 'SignTransaction',
|
||||||
Waiting = "Waiting",
|
Waiting = 'Waiting',
|
||||||
Action = "Action",
|
Action = 'Action',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const taxRates = TAX_RATE_OPTIONS.map(({ index, year, daily }) => ({
|
export const taxRates = TAX_RATE_OPTIONS.map(({ index, year, daily }) => ({
|
||||||
index,
|
index,
|
||||||
year,
|
year,
|
||||||
daily,
|
daily,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export function useAdjustTaxRate() {
|
export function useAdjustTaxRate() {
|
||||||
const loading = ref();
|
const loading = ref();
|
||||||
const waiting = ref();
|
const waiting = ref();
|
||||||
|
|
||||||
|
|
||||||
const state: ComputedRef<State> = computed(() => {
|
const state: ComputedRef<State> = computed(() => {
|
||||||
if (loading.value) {
|
if (loading.value) {
|
||||||
return State.SignTransaction;
|
return State.SignTransaction;
|
||||||
} else if (waiting.value) {
|
} else if (waiting.value) {
|
||||||
return State.Waiting;
|
return State.Waiting;
|
||||||
} else {
|
} else {
|
||||||
return State.Action;
|
return State.Action;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
async function changeTax(positionId: bigint, taxRate: number) {
|
async function changeTax(positionId: bigint, taxRate: number) {
|
||||||
try {
|
try {
|
||||||
console.log("changeTax", { positionId, taxRate });
|
// console.log("changeTax", { positionId, taxRate });
|
||||||
|
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const index = taxRates.findIndex((obj) => obj.year === taxRate)
|
const index = taxRates.findIndex(obj => obj.year === taxRate);
|
||||||
const hash = await StakeContract.changeTax(positionId, index);
|
const hash = await StakeContract.changeTax(positionId, index);
|
||||||
console.log("hash", hash);
|
// console.log("hash", hash);
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
waiting.value = true;
|
waiting.value = true;
|
||||||
const data = await waitForTransactionReceipt(config as any, {
|
await waitForTransactionReceipt(config as Config, {
|
||||||
hash: hash,
|
hash: hash,
|
||||||
});
|
});
|
||||||
|
|
||||||
contractToast.showSuccessToast(
|
contractToast.showSuccessToast(taxRate.toString(), 'Success!', 'You adjusted your position tax to', '', '%');
|
||||||
taxRate.toString(),
|
waiting.value = false;
|
||||||
"Success!",
|
} catch (error: unknown) {
|
||||||
"You adjusted your position tax to",
|
// console.error("error", error);
|
||||||
"",
|
// console.log(JSON.stringify(error, (_, v) => (typeof v === "bigint" ? v.toString() : v)));
|
||||||
"%"
|
|
||||||
);
|
|
||||||
waiting.value = false;
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error("error", error);
|
|
||||||
console.log(JSON.stringify(error, (_, v) => (typeof v === "bigint" ? v.toString() : v)));
|
|
||||||
|
|
||||||
contractToast.showFailToast(error.shortMessage);
|
contractToast.showFailToast((error as { shortMessage?: string })?.shortMessage ?? 'Transaction failed');
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
waiting.value = false;
|
waiting.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return reactive({ state, taxRates, changeTax });
|
return reactive({ state, taxRates, changeTax });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,51 +1,45 @@
|
||||||
import { ref, reactive, computed } from "vue";
|
import { ref, reactive, computed } from 'vue';
|
||||||
import { getChainId, watchChainId, getAccount, watchAccount } from "@wagmi/core";
|
// import { getChainId, watchChainId, getAccount, watchAccount } from "@wagmi/core";
|
||||||
import { config } from "@/wagmi";
|
// import { config } from "@/wagmi";
|
||||||
import { setHarbContract } from "@/contracts/harb";
|
// import { setHarbContract } from "@/contracts/harb";
|
||||||
import { setStakeContract } from "@/contracts/stake";
|
// import { setStakeContract } from "@/contracts/stake";
|
||||||
import {chainsData} from "@/config"
|
import { chainsData } from '@/config';
|
||||||
import logger from "@/utils/logger";
|
// import logger from "@/utils/logger";
|
||||||
const activeChain = ref()
|
const activeChain = ref();
|
||||||
let unwatch: any = null;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const chainData = computed(() => {
|
export const chainData = computed(() => {
|
||||||
return chainsData.find((obj) => obj.id === activeChain.value)
|
return chainsData.find(obj => obj.id === activeChain.value);
|
||||||
})
|
});
|
||||||
|
|
||||||
export function useChain() {
|
export function useChain() {
|
||||||
|
// if (!unwatch) {
|
||||||
|
// console.log("useChain function");
|
||||||
|
// const chain = getChainId(config as Config)
|
||||||
|
// activeChain.value = chain
|
||||||
|
// unwatch = watchChainId(config as Config, {
|
||||||
|
// async onChange(chainId) {
|
||||||
|
// console.log("Chain changed", chainId);
|
||||||
|
// activeChain.value = chainId
|
||||||
|
// setHarbContract()
|
||||||
|
// setStakeContract()
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!unwatch) {
|
||||||
// if (!unwatch) {
|
// console.log("useWallet function");
|
||||||
// console.log("useChain function");
|
|
||||||
// const chain = getChainId(config as any)
|
|
||||||
// activeChain.value = chain
|
|
||||||
// unwatch = watchChainId(config as any, {
|
|
||||||
// async onChange(chainId) {
|
|
||||||
// console.log("Chain changed", chainId);
|
|
||||||
// activeChain.value = chainId
|
|
||||||
// setHarbContract()
|
|
||||||
// setStakeContract()
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (!unwatch) {
|
// unwatch = watchAccount(config as Config, {
|
||||||
// console.log("useWallet function");
|
// async onChange(data) {
|
||||||
|
// console.log("watchaccount-useChain", data);
|
||||||
// unwatch = watchAccount(config as any, {
|
|
||||||
// async onChange(data) {
|
|
||||||
// console.log("watchaccount-useChain", data);
|
|
||||||
|
|
||||||
// if(!data.address) {
|
|
||||||
// } else if (activeChain.value !== data.chainId) {
|
|
||||||
// logger.info(`Chain changed!:`, data.chainId);
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
return reactive({ chainData });
|
// if(!data.address) {
|
||||||
|
// } else if (activeChain.value !== data.chainId) {
|
||||||
|
// logger.info(`Chain changed!:`, data.chainId);
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
return reactive({ chainData });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,13 @@
|
||||||
import { ref, onMounted, onUnmounted, reactive, computed } from "vue";
|
import { ref, reactive, computed } from 'vue';
|
||||||
import { type ComputedRef } from "vue";
|
import { type ComputedRef } from 'vue';
|
||||||
import { config } from "@/wagmi";
|
import { config } from '@/wagmi';
|
||||||
import { AbiEncodingArrayLengthMismatchError, type WatchEventReturnType } from "viem";
|
// import axios from "axios";
|
||||||
import axios from "axios";
|
import { waitForTransactionReceipt, watchAccount, type Config } from '@wagmi/core';
|
||||||
import { getAccount, watchContractEvent, readContract, waitForTransactionReceipt, watchAccount } from "@wagmi/core";
|
import { type WatchAccountReturnType } from '@wagmi/core';
|
||||||
import { type WatchAccountReturnType } from "@wagmi/core";
|
import * as HarbContract from '@/contracts/harb';
|
||||||
import * as HarbContract from "@/contracts/harb";
|
|
||||||
// import HarbJson from "@/assets/contracts/harb.json";
|
// import HarbJson from "@/assets/contracts/harb.json";
|
||||||
import { type Abi, type Address } from "viem";
|
import { useWallet } from './useWallet';
|
||||||
import { useWallet } from "./useWallet";
|
// import { compactNumber, formatBigIntDivision } from "@/utils/helper";
|
||||||
import { compactNumber, formatBigIntDivision } from "@/utils/helper";
|
|
||||||
|
|
||||||
const wallet = useWallet();
|
const wallet = useWallet();
|
||||||
|
|
||||||
|
|
@ -19,64 +17,58 @@ const ubiDue = HarbContract.ubiDue;
|
||||||
let unwatch: WatchAccountReturnType;
|
let unwatch: WatchAccountReturnType;
|
||||||
|
|
||||||
enum ClaimState {
|
enum ClaimState {
|
||||||
NothingToClaim = "NothingToClaim",
|
NothingToClaim = 'NothingToClaim',
|
||||||
StakeAble = "StakeAble",
|
StakeAble = 'StakeAble',
|
||||||
SignTransaction = "SignTransaction",
|
SignTransaction = 'SignTransaction',
|
||||||
Waiting = "Waiting",
|
Waiting = 'Waiting',
|
||||||
NotEnoughApproval = "NotEnoughApproval",
|
NotEnoughApproval = 'NotEnoughApproval',
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useClaim() {
|
export function useClaim() {
|
||||||
|
const state: ComputedRef<ClaimState> = computed(() => {
|
||||||
|
if (loading.value) {
|
||||||
|
return ClaimState.SignTransaction;
|
||||||
|
} else if (ubiDue.value === 0n) {
|
||||||
|
return ClaimState.NothingToClaim;
|
||||||
|
} else if (waiting.value) {
|
||||||
|
return ClaimState.Waiting;
|
||||||
|
} else {
|
||||||
|
return ClaimState.StakeAble;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const state: ComputedRef<ClaimState> = computed(() => {
|
async function claimUbi() {
|
||||||
if (loading.value) {
|
try {
|
||||||
return ClaimState.SignTransaction;
|
const address = wallet.account.address!;
|
||||||
} else if (ubiDue.value === 0n) {
|
|
||||||
return ClaimState.NothingToClaim;
|
|
||||||
} else if (waiting.value) {
|
|
||||||
return ClaimState.Waiting;
|
|
||||||
} else {
|
|
||||||
return ClaimState.StakeAble;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
async function claimUbi() {
|
loading.value = true;
|
||||||
try {
|
const hash = await HarbContract.claimUbi(address);
|
||||||
const address = wallet.account.address!;
|
// console.log("hash", hash);
|
||||||
|
loading.value = false;
|
||||||
|
waiting.value = true;
|
||||||
|
await waitForTransactionReceipt(config as Config, {
|
||||||
|
hash: hash,
|
||||||
|
});
|
||||||
|
} catch (_error) {
|
||||||
|
// console.error("error", error);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
waiting.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
loading.value = true;
|
if (!unwatch) {
|
||||||
const hash = await HarbContract.claimUbi(address);
|
// console.log("useClaim function");
|
||||||
console.log("hash", hash);
|
|
||||||
loading.value = false;
|
|
||||||
waiting.value = true;
|
|
||||||
const data = await waitForTransactionReceipt(config as any, {
|
|
||||||
hash: hash,
|
|
||||||
});
|
|
||||||
console.log("data.logs", data.logs);
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error("error", error);
|
|
||||||
} finally {
|
|
||||||
loading.value = false;
|
|
||||||
waiting.value = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(async () => {});
|
unwatch = watchAccount(config as Config, {
|
||||||
|
async onChange(data) {
|
||||||
|
// console.log("watchAccount", data);
|
||||||
|
|
||||||
if (!unwatch) {
|
if (data.address) {
|
||||||
console.log("useClaim function");
|
await HarbContract.setHarbContract();
|
||||||
|
}
|
||||||
unwatch = watchAccount(config as any, {
|
},
|
||||||
async onChange(data) {
|
});
|
||||||
console.log("watchAccount", data);
|
}
|
||||||
|
return reactive({ claimUbi, ubiDue, state });
|
||||||
if (data.address) {
|
|
||||||
await HarbContract.setHarbContract();
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return reactive({ claimUbi, ubiDue, state });
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,21 @@
|
||||||
import {onBeforeUnmount, onMounted} from 'vue'
|
import { onBeforeUnmount, onMounted, type Ref } from 'vue';
|
||||||
|
|
||||||
export default function useClickOutside(component: any, callback: any) {
|
export default function useClickOutside(component: Ref<HTMLElement | null>, callback: () => void) {
|
||||||
|
if (!component) return;
|
||||||
if (!component) return
|
const listener = (event: MouseEvent) => {
|
||||||
const listener = (event: any) => {
|
if (event.target !== component.value && event.composedPath().includes(component.value as EventTarget)) {
|
||||||
|
return;
|
||||||
if (event.target !== component.value && event.composedPath().includes(component.value)) {
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (typeof callback === 'function') {
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
onMounted(() => { window.addEventListener('click', listener) })
|
if (typeof callback === 'function') {
|
||||||
onBeforeUnmount(() => {window.removeEventListener('click', listener)})
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('click', listener);
|
||||||
|
});
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.removeEventListener('click', listener);
|
||||||
|
});
|
||||||
|
|
||||||
return {listener}
|
return { listener };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,62 +1,55 @@
|
||||||
import Toast from "@/components/Toast.vue";
|
import ToastNotification from '@/components/ToastNotification.vue';
|
||||||
import { POSITION, useToast } from "vue-toastification";
|
import { POSITION, useToast } from 'vue-toastification';
|
||||||
import { reactive } from "vue";
|
import { reactive } from 'vue';
|
||||||
|
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
||||||
export function useContractToast() {
|
export function useContractToast() {
|
||||||
function showFailToast(name?: string) {
|
function showFailToast(name?: string) {
|
||||||
console.log("name", name);
|
// console.log("name", name);
|
||||||
if (name === "UserRejectedRequestError") {
|
if (name === 'UserRejectedRequestError') {
|
||||||
//
|
//
|
||||||
} else {
|
} else {
|
||||||
showSuccessToast(
|
showSuccessToast(
|
||||||
"",
|
'',
|
||||||
"Failed!",
|
'Failed!',
|
||||||
"Your transaction didn’t go through. Please try again.",
|
'Your transaction didn’t go through. Please try again.',
|
||||||
"If the issue persists, please send a message in #helpdesk on our Discord.",
|
'If the issue persists, please send a message in #helpdesk on our Discord.',
|
||||||
"",
|
'',
|
||||||
"error"
|
'error'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showSuccessToast(
|
function showSuccessToast(value: string, header: string, subheader: string, info: string, unit: string, type: string = 'info') {
|
||||||
value: string,
|
// Define the content object with the component, props and listeners
|
||||||
header: string,
|
const content = {
|
||||||
subheader: string,
|
component: ToastNotification,
|
||||||
info: string,
|
// Any prop can be passed, but don't expect them to be reactive
|
||||||
unit: string,
|
props: {
|
||||||
type: string = "info"
|
value: value,
|
||||||
) {
|
header: header,
|
||||||
// Define the content object with the component, props and listeners
|
subheader: subheader,
|
||||||
const content = {
|
info: info,
|
||||||
component: Toast,
|
token: unit,
|
||||||
// Any prop can be passed, but don't expect them to be reactive
|
type: type,
|
||||||
props: {
|
},
|
||||||
value: value,
|
// Listen and react to events using callbacks. In this case we listen for
|
||||||
header: header,
|
// the "click" event emitted when clicking the toast button
|
||||||
subheader: subheader,
|
listeners: {},
|
||||||
info: info,
|
};
|
||||||
token: unit,
|
|
||||||
type: type,
|
|
||||||
},
|
|
||||||
// Listen and react to events using callbacks. In this case we listen for
|
|
||||||
// the "click" event emitted when clicking the toast button
|
|
||||||
listeners: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Render the toast and its contents
|
// Render the toast and its contents
|
||||||
toast(content, {
|
toast(content, {
|
||||||
position: POSITION.TOP_RIGHT,
|
position: POSITION.TOP_RIGHT,
|
||||||
icon: false,
|
icon: false,
|
||||||
closeOnClick: false,
|
closeOnClick: false,
|
||||||
toastClassName: "modal-overlay",
|
toastClassName: 'modal-overlay',
|
||||||
closeButton: false,
|
closeButton: false,
|
||||||
hideProgressBar: true,
|
hideProgressBar: true,
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return reactive({ showSuccessToast, showFailToast });
|
return reactive({ showSuccessToast, showFailToast });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,35 @@
|
||||||
|
import { ref, onMounted, watch } from 'vue';
|
||||||
|
|
||||||
import { ref, onMounted, watch } from 'vue'
|
|
||||||
// by convention, composable function names start with "use"
|
// by convention, composable function names start with "use"
|
||||||
const darkTheme = ref(false)
|
const darkTheme = ref(false);
|
||||||
export function useDark() {
|
export function useDark() {
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if(localStorage.getItem("theme") === "dark") {
|
if (localStorage.getItem('theme') === 'dark') {
|
||||||
document.documentElement.classList.add("dark")
|
document.documentElement.classList.add('dark');
|
||||||
darkTheme.value = true
|
darkTheme.value = true;
|
||||||
} else {
|
} else {
|
||||||
darkTheme.value = false
|
darkTheme.value = false;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
})
|
watch(
|
||||||
|
() => darkTheme.value,
|
||||||
|
newData => {
|
||||||
|
document.documentElement.removeAttribute('data-theme');
|
||||||
|
localStorage.removeItem('theme');
|
||||||
|
if (newData) {
|
||||||
|
// import('@/assets/sass/elementplus-dark.scss');
|
||||||
|
document.documentElement.classList.add('dark');
|
||||||
|
document.documentElement.setAttribute('data-theme', 'dark');
|
||||||
|
localStorage.setItem('theme', 'dark');
|
||||||
|
} else {
|
||||||
|
// import('@/assets/sass/elementplus-light.scss');
|
||||||
|
|
||||||
watch(
|
document.documentElement.classList.remove('dark');
|
||||||
() => darkTheme.value,
|
document.documentElement.setAttribute('data-theme', 'default');
|
||||||
(newData) => {
|
localStorage.setItem('theme', 'default');
|
||||||
document.documentElement.removeAttribute("data-theme");
|
}
|
||||||
localStorage.removeItem("theme")
|
}
|
||||||
if (newData) {
|
);
|
||||||
// import('@/assets/sass/elementplus-dark.scss');
|
|
||||||
document.documentElement.classList.add("dark")
|
|
||||||
document.documentElement.setAttribute("data-theme", "dark");
|
|
||||||
localStorage.setItem("theme", "dark");
|
|
||||||
} else {
|
|
||||||
// import('@/assets/sass/elementplus-light.scss');
|
|
||||||
|
|
||||||
document.documentElement.classList.remove("dark")
|
return { darkTheme };
|
||||||
document.documentElement.setAttribute("data-theme", "default");
|
|
||||||
localStorage.setItem("theme", "default");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return { darkTheme }
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,30 @@
|
||||||
import { ref, onMounted, onUnmounted } from "vue";
|
import { ref, onMounted, onUnmounted } from 'vue';
|
||||||
|
|
||||||
// by convention, composable function names start with "use"
|
// by convention, composable function names start with "use"
|
||||||
export function useMobile() {
|
export function useMobile() {
|
||||||
const isMobile = ref<boolean>(false);
|
const isMobile = ref<boolean>(false);
|
||||||
|
|
||||||
const handleWindowSizeChange = () => {
|
const handleWindowSizeChange = () => {
|
||||||
isMobile.value = isMobileFunc();
|
isMobile.value = isMobileFunc();
|
||||||
};
|
};
|
||||||
|
|
||||||
isMobile.value = isMobileFunc();
|
isMobile.value = isMobileFunc();
|
||||||
function isMobileFunc() {
|
function isMobileFunc() {
|
||||||
if (screen.width <= 768) {
|
if (screen.width <= 768) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
window.addEventListener("resize", handleWindowSizeChange);
|
window.addEventListener('resize', handleWindowSizeChange);
|
||||||
handleWindowSizeChange();
|
handleWindowSizeChange();
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
window.removeEventListener("resize", handleWindowSizeChange);
|
window.removeEventListener('resize', handleWindowSizeChange);
|
||||||
});
|
});
|
||||||
|
|
||||||
return isMobile;
|
return isMobile;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,16 @@
|
||||||
import { ref, onMounted, onUnmounted, reactive, computed, type ComputedRef } from "vue";
|
import { ref, computed, type ComputedRef, onMounted, onUnmounted } from 'vue';
|
||||||
import { config } from "@/wagmi";
|
import { config } from '@/wagmi';
|
||||||
import { type WatchEventReturnType, toBytes, type Hex } from "viem";
|
import { type WatchEventReturnType, type Hex, toBytes } from 'viem';
|
||||||
import axios from "axios";
|
import axios from 'axios';
|
||||||
import { getAccount, watchContractEvent, watchChainId, watchAccount } from "@wagmi/core";
|
import { getAccount, watchContractEvent, watchChainId, watchAccount, type Config } from '@wagmi/core';
|
||||||
import type { WatchChainIdReturnType, WatchAccountReturnType, GetAccountReturnType} from "@wagmi/core";
|
import type { WatchChainIdReturnType, WatchAccountReturnType, GetAccountReturnType } from '@wagmi/core';
|
||||||
|
|
||||||
import { HarbContract } from "@/contracts/harb";
|
import { HarbContract } from '@/contracts/harb';
|
||||||
import { bytesToUint256, uint256ToBytes } from "kraiken-lib";
|
import { bytesToUint256 } from 'kraiken-lib';
|
||||||
import { bigInt2Number } from "@/utils/helper";
|
import { bigInt2Number } from '@/utils/helper';
|
||||||
import { useChain } from "@/composables/useChain";
|
import { taxRates } from '@/composables/useAdjustTaxRates';
|
||||||
import { taxRates } from "@/composables/useAdjustTaxRates";
|
import { chainData } from '@/composables/useWallet';
|
||||||
import { chainData } from "@/composables/useWallet";
|
import logger from '@/utils/logger';
|
||||||
import logger from "@/utils/logger";
|
|
||||||
const rawActivePositions = ref<Array<Position>>([]);
|
const rawActivePositions = ref<Array<Position>>([]);
|
||||||
const rawClosedPositoins = ref<Array<Position>>([]);
|
const rawClosedPositoins = ref<Array<Position>>([]);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|
@ -21,108 +20,108 @@ const POSITIONS_RETRY_MAX_DELAY = 60_000;
|
||||||
const positionsRetryDelayMs = ref(POSITIONS_RETRY_BASE_DELAY);
|
const positionsRetryDelayMs = ref(POSITIONS_RETRY_BASE_DELAY);
|
||||||
const GRAPHQL_TIMEOUT_MS = 15_000;
|
const GRAPHQL_TIMEOUT_MS = 15_000;
|
||||||
let positionsRetryTimer: number | null = null;
|
let positionsRetryTimer: number | null = null;
|
||||||
const chain = useChain();
|
|
||||||
const activePositions = computed(() => {
|
const activePositions = computed(() => {
|
||||||
const account = getAccount(config as any);
|
const account = getAccount(config as Config);
|
||||||
|
|
||||||
return rawActivePositions.value
|
return rawActivePositions.value
|
||||||
.map((obj: any) => {
|
.map(obj => {
|
||||||
return {
|
return {
|
||||||
...obj,
|
...obj,
|
||||||
positionId: formatId(obj.id),
|
positionId: formatId(obj.id as Hex),
|
||||||
amount: bigInt2Number(obj.harbDeposit, 18),
|
amount: bigInt2Number(obj.harbDeposit, 18),
|
||||||
taxRatePercentage: Number(obj.taxRate) * 100,
|
taxRatePercentage: Number(obj.taxRate) * 100,
|
||||||
taxRate: Number(obj.taxRate),
|
taxRate: Number(obj.taxRate),
|
||||||
taxRateIndex: taxRates.find((taxRate) => taxRate.year === Number(obj.taxRate) * 100)?.index,
|
taxRateIndex: taxRates.find(taxRate => taxRate.year === Number(obj.taxRate) * 100)?.index,
|
||||||
iAmOwner: obj.owner?.toLowerCase() === account.address?.toLowerCase(),
|
iAmOwner: obj.owner?.toLowerCase() === account.address?.toLowerCase(),
|
||||||
totalSupplyEnd: obj.totalSupplyEnd ? BigInt(obj.totalSupplyEnd) : undefined,
|
totalSupplyEnd: obj.totalSupplyEnd ? BigInt(obj.totalSupplyEnd) : undefined,
|
||||||
totalSupplyInit: BigInt(obj.totalSupplyInit),
|
totalSupplyInit: BigInt(obj.totalSupplyInit),
|
||||||
taxPaid: BigInt(obj.taxPaid),
|
taxPaid: BigInt(obj.taxPaid),
|
||||||
share: Number(obj.share),
|
share: Number(obj.share),
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
if (a.taxRate > b.taxRate) {
|
if (a.taxRate > b.taxRate) {
|
||||||
return 1;
|
return 1;
|
||||||
} else if (a.taxRate < b.taxRate) {
|
} else if (a.taxRate < b.taxRate) {
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
export interface Position {
|
export interface Position {
|
||||||
creationTime: Date;
|
creationTime: Date;
|
||||||
id: string;
|
id: string;
|
||||||
positionId: bigint;
|
positionId: bigint;
|
||||||
owner: string;
|
owner: string;
|
||||||
lastTaxTime: Date;
|
lastTaxTime: Date;
|
||||||
taxPaid: bigint;
|
taxPaid: bigint;
|
||||||
taxRate: number;
|
taxRate: number;
|
||||||
taxRateIndex: number;
|
taxRateIndex?: number;
|
||||||
taxRatePercentage: number;
|
taxRatePercentage: number;
|
||||||
share: number;
|
share: number;
|
||||||
status: string;
|
status: string;
|
||||||
totalSupplyEnd?: bigint;
|
totalSupplyEnd?: bigint;
|
||||||
totalSupplyInit: bigint;
|
totalSupplyInit: bigint;
|
||||||
amount: number;
|
amount: number;
|
||||||
harbDeposit: bigint;
|
harbDeposit: bigint;
|
||||||
iAmOwner: boolean;
|
iAmOwner: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const myClosedPositions: ComputedRef<Position[]> = computed(() => {
|
const myClosedPositions: ComputedRef<Position[]> = computed(() => {
|
||||||
const account = getAccount(config as any);
|
const account = getAccount(config as Config);
|
||||||
|
|
||||||
return rawClosedPositoins.value.map((obj: any) => {
|
return rawClosedPositoins.value.map(obj => {
|
||||||
const taxRatePosition = obj.taxRate * 100;
|
// console.log("taxRatePosition", taxRatePosition);
|
||||||
console.log("taxRatePosition", taxRatePosition);
|
|
||||||
|
|
||||||
console.log("taxRates[taxRatePosition]", taxRates[taxRatePosition]);
|
// console.log("taxRates[taxRatePosition]", taxRates[taxRatePosition]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...obj,
|
...obj,
|
||||||
positionId: formatId(obj.id),
|
positionId: formatId(obj.id as Hex),
|
||||||
amount: obj.share * 1000000,
|
amount: obj.share * 1000000,
|
||||||
// amount: bigInt2Number(obj.harbDeposit, 18),
|
// amount: bigInt2Number(obj.harbDeposit, 18),
|
||||||
taxRatePercentage: Number(obj.taxRate) * 100,
|
taxRatePercentage: Number(obj.taxRate) * 100,
|
||||||
taxRate: Number(obj.taxRate),
|
taxRate: Number(obj.taxRate),
|
||||||
taxRateIndex: taxRates.find((taxRate) => taxRate.year === Number(obj.taxRate) * 100)?.index,
|
taxRateIndex: taxRates.find(taxRate => taxRate.year === Number(obj.taxRate) * 100)?.index,
|
||||||
iAmOwner: obj.owner?.toLowerCase() === account.address?.toLowerCase(),
|
iAmOwner: obj.owner?.toLowerCase() === account.address?.toLowerCase(),
|
||||||
totalSupplyEnd: BigInt(obj.totalSupplyEnd),
|
totalSupplyEnd: obj.totalSupplyEnd !== undefined ? BigInt(obj.totalSupplyEnd) : undefined,
|
||||||
totalSupplyInit: BigInt(obj.totalSupplyInit),
|
totalSupplyInit: BigInt(obj.totalSupplyInit),
|
||||||
taxPaid: BigInt(obj.taxPaid),
|
taxPaid: BigInt(obj.taxPaid),
|
||||||
share: Number(obj.share),
|
share: Number(obj.share),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const myActivePositions: ComputedRef<Position[]> = computed(() =>
|
const myActivePositions: ComputedRef<Position[]> = computed(() =>
|
||||||
activePositions.value.filter((obj: Position) => {
|
activePositions.value.filter((obj: Position) => {
|
||||||
return obj.iAmOwner;
|
return obj.iAmOwner;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const tresholdValue = computed(() => {
|
const tresholdValue = computed(() => {
|
||||||
const arrayTaxRatePositions = activePositions.value.map((obj) => obj.taxRatePercentage);
|
const arrayTaxRatePositions = activePositions.value.map(obj => obj.taxRatePercentage);
|
||||||
const sortedPositions = arrayTaxRatePositions.sort((a: any, b: any) => (a > b ? 1 : -1));
|
const sortedPositions = arrayTaxRatePositions.sort((a, b) => (a > b ? 1 : -1));
|
||||||
const sumq = sortedPositions.reduce((partialSum, a) => partialSum + a, 0);
|
const sumq = sortedPositions.reduce((partialSum, a) => partialSum + a, 0);
|
||||||
const avg = sumq / sortedPositions.length;
|
const avg = sumq / sortedPositions.length;
|
||||||
|
|
||||||
return avg / 2;
|
return avg / 2;
|
||||||
});
|
});
|
||||||
|
|
||||||
export async function loadActivePositions(endpoint?: string) {
|
export async function loadActivePositions(endpoint?: string) {
|
||||||
logger.info(`loadActivePositions for chain: ${chainData.value?.path}`);
|
logger.info(`loadActivePositions for chain: ${chainData.value?.path}`);
|
||||||
const targetEndpoint = endpoint ?? chainData.value?.graphql?.trim();
|
const targetEndpoint = endpoint ?? chainData.value?.graphql?.trim();
|
||||||
if (!targetEndpoint) {
|
if (!targetEndpoint) {
|
||||||
throw new Error("GraphQL endpoint not configured for this chain.");
|
throw new Error('GraphQL endpoint not configured for this chain.');
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("chainData.value?.graphql", targetEndpoint);
|
// console.log("chainData.value?.graphql", targetEndpoint);
|
||||||
|
|
||||||
const res = await axios.post(targetEndpoint, {
|
const res = await axios.post(
|
||||||
query: `query ActivePositions {
|
targetEndpoint,
|
||||||
|
{
|
||||||
|
query: `query ActivePositions {
|
||||||
positionss(where: { status: "Active" }, orderBy: "taxRate", orderDirection: "asc", limit: 1000) {
|
positionss(where: { status: "Active" }, orderBy: "taxRate", orderDirection: "asc", limit: 1000) {
|
||||||
items {
|
items {
|
||||||
id
|
id
|
||||||
|
|
@ -140,34 +139,38 @@ export async function loadActivePositions(endpoint?: string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}`,
|
}`,
|
||||||
}, { timeout: GRAPHQL_TIMEOUT_MS });
|
},
|
||||||
|
{ timeout: GRAPHQL_TIMEOUT_MS }
|
||||||
|
);
|
||||||
|
|
||||||
const errors = res.data?.errors;
|
const errors = res.data?.errors;
|
||||||
if (Array.isArray(errors) && errors.length > 0) {
|
if (Array.isArray(errors) && errors.length > 0) {
|
||||||
throw new Error(errors.map((err: any) => err?.message ?? "GraphQL error").join(", "));
|
throw new Error(errors.map((err: unknown) => (err as { message?: string })?.message ?? 'GraphQL error').join(', '));
|
||||||
}
|
}
|
||||||
|
|
||||||
const items = res.data?.data?.positionss?.items ?? [];
|
const items = res.data?.data?.positionss?.items ?? [];
|
||||||
return items.map((item: any) => ({
|
return items.map((item: Record<string, unknown>) => ({
|
||||||
...item,
|
...item,
|
||||||
harbDeposit: item.kraikenDeposit ?? "0",
|
harbDeposit: item.kraikenDeposit ?? '0',
|
||||||
})) as Position[];
|
})) as Position[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatId(id: Hex) {
|
function formatId(id: Hex) {
|
||||||
const bytes = toBytes(id);
|
const bytes = toBytes(id);
|
||||||
const bigIntId = bytesToUint256(bytes);
|
const bigIntId = bytesToUint256(bytes);
|
||||||
return bigIntId;
|
return bigIntId;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadMyClosedPositions(endpoint: string | undefined, account: GetAccountReturnType) {
|
export async function loadMyClosedPositions(endpoint: string | undefined, account: GetAccountReturnType) {
|
||||||
logger.info(`loadMyClosedPositions for chain: ${chainData.value?.path}`);
|
logger.info(`loadMyClosedPositions for chain: ${chainData.value?.path}`);
|
||||||
const targetEndpoint = endpoint ?? chainData.value?.graphql?.trim();
|
const targetEndpoint = endpoint ?? chainData.value?.graphql?.trim();
|
||||||
if (!targetEndpoint) {
|
if (!targetEndpoint) {
|
||||||
throw new Error("GraphQL endpoint not configured for this chain.");
|
throw new Error('GraphQL endpoint not configured for this chain.');
|
||||||
}
|
}
|
||||||
const res = await axios.post(targetEndpoint, {
|
const res = await axios.post(
|
||||||
query: `query ClosedPositions {
|
targetEndpoint,
|
||||||
|
{
|
||||||
|
query: `query ClosedPositions {
|
||||||
positionss(where: { status: "Closed", owner: "${account.address?.toLowerCase()}" }, limit: 1000) {
|
positionss(where: { status: "Closed", owner: "${account.address?.toLowerCase()}" }, limit: 1000) {
|
||||||
items {
|
items {
|
||||||
id
|
id
|
||||||
|
|
@ -185,52 +188,53 @@ export async function loadMyClosedPositions(endpoint: string | undefined, accoun
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}`,
|
}`,
|
||||||
}, { timeout: GRAPHQL_TIMEOUT_MS });
|
},
|
||||||
const errors = res.data?.errors;
|
{ timeout: GRAPHQL_TIMEOUT_MS }
|
||||||
if (Array.isArray(errors) && errors.length > 0) {
|
);
|
||||||
throw new Error(errors.map((err: any) => err?.message ?? "GraphQL error").join(", "));
|
const errors = res.data?.errors;
|
||||||
}
|
if (Array.isArray(errors) && errors.length > 0) {
|
||||||
const items = res.data?.data?.positionss?.items ?? [];
|
throw new Error(errors.map((err: unknown) => (err as { message?: string })?.message ?? 'GraphQL error').join(', '));
|
||||||
return items.map((item: any) => ({
|
}
|
||||||
...item,
|
const items = res.data?.data?.positionss?.items ?? [];
|
||||||
harbDeposit: item.kraikenDeposit ?? "0",
|
return items.map((item: Record<string, unknown>) => ({
|
||||||
})) as Position[];
|
...item,
|
||||||
|
harbDeposit: item.kraikenDeposit ?? '0',
|
||||||
|
})) as Position[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadPositions() {
|
export async function loadPositions() {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
|
||||||
const endpoint = chainData.value?.graphql?.trim();
|
const endpoint = chainData.value?.graphql?.trim();
|
||||||
if (!endpoint) {
|
if (!endpoint) {
|
||||||
rawActivePositions.value = [];
|
rawActivePositions.value = [];
|
||||||
rawClosedPositoins.value = [];
|
rawClosedPositoins.value = [];
|
||||||
positionsError.value = "GraphQL endpoint not configured for this chain.";
|
positionsError.value = 'GraphQL endpoint not configured for this chain.';
|
||||||
clearPositionsRetryTimer();
|
clearPositionsRetryTimer();
|
||||||
positionsRetryDelayMs.value = POSITIONS_RETRY_BASE_DELAY;
|
positionsRetryDelayMs.value = POSITIONS_RETRY_BASE_DELAY;
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
rawActivePositions.value = await loadActivePositions(endpoint);
|
rawActivePositions.value = await loadActivePositions(endpoint);
|
||||||
const account = getAccount(config as any);
|
const account = getAccount(config as Config);
|
||||||
if (account.address) {
|
if (account.address) {
|
||||||
rawClosedPositoins.value = await loadMyClosedPositions(endpoint, account);
|
rawClosedPositoins.value = await loadMyClosedPositions(endpoint, account);
|
||||||
} else {
|
} else {
|
||||||
rawClosedPositoins.value = [];
|
rawClosedPositoins.value = [];
|
||||||
}
|
}
|
||||||
positionsError.value = null;
|
positionsError.value = null;
|
||||||
positionsRetryDelayMs.value = POSITIONS_RETRY_BASE_DELAY;
|
positionsRetryDelayMs.value = POSITIONS_RETRY_BASE_DELAY;
|
||||||
clearPositionsRetryTimer();
|
clearPositionsRetryTimer();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn("[positions] loadPositions() failed", error);
|
rawActivePositions.value = [];
|
||||||
rawActivePositions.value = [];
|
rawClosedPositoins.value = [];
|
||||||
rawClosedPositoins.value = [];
|
positionsError.value = formatGraphqlError(error);
|
||||||
positionsError.value = formatGraphqlError(error);
|
schedulePositionsRetry();
|
||||||
schedulePositionsRetry();
|
} finally {
|
||||||
} finally {
|
loading.value = false;
|
||||||
loading.value = false;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let unwatch: WatchEventReturnType | null;
|
let unwatch: WatchEventReturnType | null;
|
||||||
|
|
@ -239,174 +243,174 @@ let unwatchChainSwitch: WatchChainIdReturnType | null;
|
||||||
let unwatchAccountChanged: WatchAccountReturnType | null;
|
let unwatchAccountChanged: WatchAccountReturnType | null;
|
||||||
|
|
||||||
function formatGraphqlError(error: unknown): string {
|
function formatGraphqlError(error: unknown): string {
|
||||||
if (axios.isAxiosError(error)) {
|
if (axios.isAxiosError(error)) {
|
||||||
const responseErrors = (error.response?.data as any)?.errors;
|
const responseErrors = (error.response?.data as { errors?: unknown[] })?.errors;
|
||||||
if (Array.isArray(responseErrors) && responseErrors.length > 0) {
|
if (Array.isArray(responseErrors) && responseErrors.length > 0) {
|
||||||
return responseErrors.map((err: any) => err?.message ?? "GraphQL error").join(", ");
|
return responseErrors.map((err: unknown) => (err as { message?: string })?.message ?? 'GraphQL error').join(', ');
|
||||||
}
|
}
|
||||||
if (error.response?.status) {
|
if (error.response?.status) {
|
||||||
return `GraphQL request failed with status ${error.response.status}`;
|
return `GraphQL request failed with status ${error.response.status}`;
|
||||||
}
|
}
|
||||||
if (error.message) {
|
if (error.message) {
|
||||||
return error.message;
|
return error.message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (error instanceof Error && error.message) {
|
if (error instanceof Error && error.message) {
|
||||||
return error.message;
|
return error.message;
|
||||||
}
|
}
|
||||||
return "Unknown GraphQL error";
|
return 'Unknown GraphQL error';
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearPositionsRetryTimer() {
|
function clearPositionsRetryTimer() {
|
||||||
if (positionsRetryTimer !== null) {
|
if (positionsRetryTimer !== null) {
|
||||||
clearTimeout(positionsRetryTimer);
|
clearTimeout(positionsRetryTimer);
|
||||||
positionsRetryTimer = null;
|
positionsRetryTimer = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function schedulePositionsRetry() {
|
function schedulePositionsRetry() {
|
||||||
if (typeof window === "undefined") {
|
if (typeof window === 'undefined') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (positionsRetryTimer !== null) {
|
if (positionsRetryTimer !== null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const delay = positionsRetryDelayMs.value;
|
const delay = positionsRetryDelayMs.value;
|
||||||
positionsRetryTimer = window.setTimeout(async () => {
|
positionsRetryTimer = window.setTimeout(async () => {
|
||||||
positionsRetryTimer = null;
|
positionsRetryTimer = null;
|
||||||
await loadPositions();
|
await loadPositions();
|
||||||
}, delay);
|
}, delay);
|
||||||
positionsRetryDelayMs.value = Math.min(positionsRetryDelayMs.value * 2, POSITIONS_RETRY_MAX_DELAY);
|
positionsRetryDelayMs.value = Math.min(positionsRetryDelayMs.value * 2, POSITIONS_RETRY_MAX_DELAY);
|
||||||
}
|
}
|
||||||
export function usePositions() {
|
export function usePositions() {
|
||||||
function watchEvent() {
|
function watchEvent() {
|
||||||
unwatch = watchContractEvent(config as any, {
|
unwatch = watchContractEvent(config as Config, {
|
||||||
address: HarbContract.contractAddress,
|
address: HarbContract.contractAddress,
|
||||||
abi: HarbContract.abi,
|
abi: HarbContract.abi,
|
||||||
eventName: "PositionCreated",
|
eventName: 'PositionCreated',
|
||||||
async onLogs(logs) {
|
async onLogs(_logs) {
|
||||||
console.log("new Position", logs);
|
// console.log("new Position", logs);
|
||||||
await loadPositions();
|
await loadPositions();
|
||||||
// await getMinStake();
|
// await getMinStake();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function watchPositionRemoved() {
|
function watchPositionRemoved() {
|
||||||
unwatchPositionRemovedEvent = watchContractEvent(config as any, {
|
unwatchPositionRemovedEvent = watchContractEvent(config as Config, {
|
||||||
address: HarbContract.contractAddress,
|
address: HarbContract.contractAddress,
|
||||||
abi: HarbContract.abi,
|
abi: HarbContract.abi,
|
||||||
eventName: "PositionRemoved",
|
eventName: 'PositionRemoved',
|
||||||
async onLogs(logs) {
|
async onLogs(_logs) {
|
||||||
console.log("Position removed", logs);
|
// console.log("Position removed", logs);
|
||||||
await loadPositions();
|
await loadPositions();
|
||||||
// await getMinStake();
|
// await getMinStake();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
//initial loading positions
|
//initial loading positions
|
||||||
|
|
||||||
if (activePositions.value.length < 1 && loading.value === false) {
|
if (activePositions.value.length < 1 && loading.value === false) {
|
||||||
await loadPositions();
|
await loadPositions();
|
||||||
// await getMinStake();
|
// await getMinStake();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!unwatch) {
|
if (!unwatch) {
|
||||||
watchEvent();
|
watchEvent();
|
||||||
}
|
}
|
||||||
if (!unwatchPositionRemovedEvent) {
|
if (!unwatchPositionRemovedEvent) {
|
||||||
watchPositionRemoved();
|
watchPositionRemoved();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!unwatchChainSwitch) {
|
if (!unwatchChainSwitch) {
|
||||||
unwatchChainSwitch = watchChainId(config as any, {
|
unwatchChainSwitch = watchChainId(config as Config, {
|
||||||
async onChange(chainId) {
|
async onChange(_chainId) {
|
||||||
await loadPositions();
|
await loadPositions();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!unwatchAccountChanged) {
|
if (!unwatchAccountChanged) {
|
||||||
unwatchAccountChanged = watchAccount(config as any, {
|
unwatchAccountChanged = watchAccount(config as Config, {
|
||||||
async onChange() {
|
async onChange() {
|
||||||
await loadPositions();
|
await loadPositions();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
if (unwatch) {
|
if (unwatch) {
|
||||||
unwatch();
|
unwatch();
|
||||||
unwatch = null;
|
unwatch = null;
|
||||||
}
|
}
|
||||||
if (unwatchPositionRemovedEvent) {
|
if (unwatchPositionRemovedEvent) {
|
||||||
unwatchPositionRemovedEvent();
|
unwatchPositionRemovedEvent();
|
||||||
unwatchPositionRemovedEvent = null;
|
unwatchPositionRemovedEvent = null;
|
||||||
}
|
}
|
||||||
if (unwatchChainSwitch) {
|
if (unwatchChainSwitch) {
|
||||||
unwatchChainSwitch();
|
unwatchChainSwitch();
|
||||||
unwatchChainSwitch = null;
|
unwatchChainSwitch = null;
|
||||||
}
|
}
|
||||||
if (unwatchAccountChanged) {
|
if (unwatchAccountChanged) {
|
||||||
unwatchAccountChanged();
|
unwatchAccountChanged();
|
||||||
unwatchAccountChanged = null;
|
unwatchAccountChanged = null;
|
||||||
}
|
}
|
||||||
clearPositionsRetryTimer();
|
clearPositionsRetryTimer();
|
||||||
});
|
});
|
||||||
|
|
||||||
function createRandomPosition(amount: number = 1) {
|
function createRandomPosition(amount: number = 1) {
|
||||||
for (let index = 0; index < amount; index++) {
|
for (let index = 0; index < amount; index++) {
|
||||||
const newPosition: Position = {
|
const newPosition: Position = {
|
||||||
creationTime: new Date(),
|
creationTime: new Date(),
|
||||||
id: "123",
|
id: '123',
|
||||||
positionId: 123n,
|
positionId: 123n,
|
||||||
owner: "bla",
|
owner: 'bla',
|
||||||
lastTaxTime: new Date(),
|
lastTaxTime: new Date(),
|
||||||
taxPaid: 100n,
|
taxPaid: 100n,
|
||||||
taxRate: randomInRange(0.01, 1),
|
taxRate: randomInRange(0.01, 1),
|
||||||
taxRateIndex: randomInRange(1, 30),
|
taxRateIndex: Math.floor(randomInRange(1, 30)),
|
||||||
taxRatePercentage: getRandomInt(1, 100),
|
taxRatePercentage: getRandomInt(1, 100),
|
||||||
share: getRandomInt(0.001, 0.09),
|
share: getRandomInt(0.001, 0.09),
|
||||||
status: "active",
|
status: 'active',
|
||||||
totalSupplyEnd: undefined,
|
totalSupplyEnd: undefined,
|
||||||
totalSupplyInit: 1000000000000n,
|
totalSupplyInit: 1000000000000n,
|
||||||
amount: 150,
|
amount: 150,
|
||||||
harbDeposit: getRandomBigInt(1000, 5000),
|
harbDeposit: getRandomBigInt(1000, 5000),
|
||||||
iAmOwner: false,
|
iAmOwner: false,
|
||||||
};
|
};
|
||||||
rawActivePositions.value.push(newPosition);
|
rawActivePositions.value.push(newPosition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function randomInRange(min: number, max: number) {
|
function randomInRange(min: number, max: number) {
|
||||||
return Math.random() < 0.5 ? (1 - Math.random()) * (max - min) + min : Math.random() * (max - min) + min;
|
return Math.random() < 0.5 ? (1 - Math.random()) * (max - min) + min : Math.random() * (max - min) + min;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRandomInt(min: number, max: number) {
|
function getRandomInt(min: number, max: number) {
|
||||||
const minCeiled = Math.ceil(min);
|
const minCeiled = Math.ceil(min);
|
||||||
const maxFloored = Math.floor(max);
|
const maxFloored = Math.floor(max);
|
||||||
const randomNumber = Math.floor(Math.random() * (maxFloored - minCeiled) + minCeiled); // The maximum is exclusive and the minimum is inclusive
|
const randomNumber = Math.floor(Math.random() * (maxFloored - minCeiled) + minCeiled); // The maximum is exclusive and the minimum is inclusive
|
||||||
|
|
||||||
return randomNumber;
|
return randomNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRandomBigInt(min: number, max: number) {
|
function getRandomBigInt(min: number, max: number) {
|
||||||
const randomNumber = getRandomInt(min, max);
|
const randomNumber = getRandomInt(min, max);
|
||||||
return BigInt(randomNumber) * 10n ** 18n;
|
return BigInt(randomNumber) * 10n ** 18n;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
activePositions,
|
activePositions,
|
||||||
myActivePositions,
|
myActivePositions,
|
||||||
myClosedPositions,
|
myClosedPositions,
|
||||||
tresholdValue,
|
tresholdValue,
|
||||||
watchEvent,
|
watchEvent,
|
||||||
watchPositionRemoved,
|
watchPositionRemoved,
|
||||||
createRandomPosition,
|
createRandomPosition,
|
||||||
positionsError,
|
positionsError,
|
||||||
loading,
|
loading,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,11 @@
|
||||||
import { ref, watchEffect, computed } from 'vue'
|
import { ref, watchEffect, computed, type Ref } from 'vue';
|
||||||
import { usePositions, type Position } from './usePositions'
|
import { usePositions, type Position } from './usePositions';
|
||||||
import { useStake } from './useStake'
|
import { useStake } from './useStake';
|
||||||
import { useWallet } from './useWallet'
|
import { useWallet } from './useWallet';
|
||||||
import { useStatCollection } from './useStatCollection'
|
import { useStatCollection } from './useStatCollection';
|
||||||
import { useAdjustTaxRate } from './useAdjustTaxRates'
|
import { useAdjustTaxRate } from './useAdjustTaxRates';
|
||||||
import { calculateSnatchShortfall } from 'kraiken-lib/staking'
|
import { calculateSnatchShortfall } from 'kraiken-lib/staking';
|
||||||
import {
|
import { selectSnatchPositions, minimumTaxRate, type SnatchablePosition } from 'kraiken-lib/snatch';
|
||||||
selectSnatchPositions,
|
|
||||||
minimumTaxRate,
|
|
||||||
type SnatchablePosition,
|
|
||||||
} from 'kraiken-lib/snatch'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts Kraiken token assets to shares using the same formula as Stake.sol:
|
* Converts Kraiken token assets to shares using the same formula as Stake.sol:
|
||||||
|
|
@ -20,113 +16,100 @@ import {
|
||||||
* @param stakeTotalSupply - Total supply of stake shares (constant from contract)
|
* @param stakeTotalSupply - Total supply of stake shares (constant from contract)
|
||||||
* @returns Number of shares corresponding to the assets
|
* @returns Number of shares corresponding to the assets
|
||||||
*/
|
*/
|
||||||
function assetsToSharesLocal(
|
function assetsToSharesLocal(assets: bigint, kraikenTotalSupply: bigint, stakeTotalSupply: bigint): bigint {
|
||||||
assets: bigint,
|
|
||||||
kraikenTotalSupply: bigint,
|
|
||||||
stakeTotalSupply: bigint
|
|
||||||
): bigint {
|
|
||||||
if (kraikenTotalSupply === 0n) {
|
if (kraikenTotalSupply === 0n) {
|
||||||
return 0n
|
return 0n;
|
||||||
}
|
}
|
||||||
// Equivalent to: assets.mulDiv(stakeTotalSupply, kraikenTotalSupply, Math.Rounding.Down)
|
// Equivalent to: assets.mulDiv(stakeTotalSupply, kraikenTotalSupply, Math.Rounding.Down)
|
||||||
return (assets * stakeTotalSupply) / kraikenTotalSupply
|
return (assets * stakeTotalSupply) / kraikenTotalSupply;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useSnatchSelection(demo = false, taxRate?: Ref<number>) {
|
||||||
|
const { activePositions } = usePositions();
|
||||||
|
const stake = useStake();
|
||||||
|
const wallet = useWallet();
|
||||||
|
const statCollection = useStatCollection();
|
||||||
|
const adjustTaxRate = useAdjustTaxRate();
|
||||||
|
|
||||||
export function useSnatchSelection(demo = false) {
|
const snatchablePositions = ref<Position[]>([]);
|
||||||
const { activePositions } = usePositions()
|
const shortfallShares = ref<bigint>(0n);
|
||||||
const stake = useStake()
|
const floorTax = ref(1);
|
||||||
const wallet = useWallet()
|
let selectionRun = 0;
|
||||||
const statCollection = useStatCollection()
|
|
||||||
const adjustTaxRate = useAdjustTaxRate()
|
|
||||||
|
|
||||||
const snatchablePositions = ref<Position[]>([])
|
const openPositionsAvailable = computed(() => shortfallShares.value <= 0n);
|
||||||
const shortfallShares = ref<bigint>(0n)
|
|
||||||
const floorTax = ref(1)
|
|
||||||
let selectionRun = 0
|
|
||||||
|
|
||||||
const openPositionsAvailable = computed(() => shortfallShares.value <= 0n)
|
|
||||||
|
|
||||||
function getMinFloorTax() {
|
function getMinFloorTax() {
|
||||||
const minRate = minimumTaxRate(activePositions.value, 0)
|
const minRate = minimumTaxRate(activePositions.value, 0);
|
||||||
return Math.round(minRate * 100)
|
return Math.round(minRate * 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
watchEffect((onCleanup) => {
|
watchEffect(onCleanup => {
|
||||||
const runId = ++selectionRun
|
const runId = ++selectionRun;
|
||||||
let cancelled = false
|
let cancelled = false;
|
||||||
onCleanup(() => {
|
onCleanup(() => {
|
||||||
cancelled = true
|
cancelled = true;
|
||||||
})
|
});
|
||||||
|
|
||||||
// No longer async since we compute shares locally
|
// No longer async since we compute shares locally
|
||||||
const compute = () => {
|
const compute = () => {
|
||||||
if (statCollection.stakeTotalSupply === 0n) {
|
if (statCollection.stakeTotalSupply === 0n) {
|
||||||
shortfallShares.value = 0n
|
shortfallShares.value = 0n;
|
||||||
if (!cancelled && runId === selectionRun) {
|
if (!cancelled && runId === selectionRun) {
|
||||||
snatchablePositions.value = []
|
snatchablePositions.value = [];
|
||||||
floorTax.value = getMinFloorTax()
|
floorTax.value = getMinFloorTax();
|
||||||
}
|
}
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const stakingShares = stake.stakingAmountShares ?? 0n
|
const stakingShares = stake.stakingAmountShares ?? 0n;
|
||||||
const shortfall = calculateSnatchShortfall(
|
const shortfall = calculateSnatchShortfall(statCollection.outstandingStake, stakingShares, statCollection.stakeTotalSupply, 2n, 10n);
|
||||||
statCollection.outstandingStake,
|
|
||||||
stakingShares,
|
|
||||||
statCollection.stakeTotalSupply,
|
|
||||||
2n,
|
|
||||||
10n
|
|
||||||
)
|
|
||||||
|
|
||||||
shortfallShares.value = shortfall
|
shortfallShares.value = shortfall;
|
||||||
|
|
||||||
if (shortfall <= 0n) {
|
if (shortfall <= 0n) {
|
||||||
if (!cancelled && runId === selectionRun) {
|
if (!cancelled && runId === selectionRun) {
|
||||||
snatchablePositions.value = []
|
snatchablePositions.value = [];
|
||||||
floorTax.value = getMinFloorTax()
|
floorTax.value = getMinFloorTax();
|
||||||
}
|
}
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxTaxRateDecimal = (stake.taxRate ?? 0) / 100
|
const stakeTaxRate = (stake as { taxRate?: number }).taxRate;
|
||||||
const includeOwned = demo
|
const taxRatePercent = taxRate?.value ?? stakeTaxRate ?? Number.POSITIVE_INFINITY;
|
||||||
const recipient = wallet.account.address ?? null
|
const maxTaxRateDecimal = Number.isFinite(taxRatePercent) ? taxRatePercent / 100 : Number.POSITIVE_INFINITY;
|
||||||
|
const includeOwned = demo;
|
||||||
|
const recipient = wallet.account.address ?? null;
|
||||||
|
|
||||||
const eligiblePositions = activePositions.value.filter((position: Position) => {
|
const eligiblePositions = activePositions.value.filter((position: Position) => {
|
||||||
if (position.taxRate >= maxTaxRateDecimal) {
|
if (position.taxRate >= maxTaxRateDecimal) {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
if (!includeOwned && position.iAmOwner) {
|
if (!includeOwned && position.iAmOwner) {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
return true
|
return true;
|
||||||
})
|
});
|
||||||
|
|
||||||
if (eligiblePositions.length === 0) {
|
if (eligiblePositions.length === 0) {
|
||||||
if (!cancelled && runId === selectionRun) {
|
if (!cancelled && runId === selectionRun) {
|
||||||
snatchablePositions.value = []
|
snatchablePositions.value = [];
|
||||||
floorTax.value = getMinFloorTax()
|
floorTax.value = getMinFloorTax();
|
||||||
}
|
}
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const candidates: SnatchablePosition[] = []
|
const candidates: SnatchablePosition[] = [];
|
||||||
|
|
||||||
// Compute shares locally using the same formula as Stake.sol
|
// Compute shares locally using the same formula as Stake.sol
|
||||||
for (const position of eligiblePositions) {
|
for (const position of eligiblePositions) {
|
||||||
const shares = assetsToSharesLocal(
|
const shares = assetsToSharesLocal(position.harbDeposit, statCollection.kraikenTotalSupply, statCollection.stakeTotalSupply);
|
||||||
position.harbDeposit,
|
|
||||||
statCollection.kraikenTotalSupply,
|
|
||||||
statCollection.stakeTotalSupply
|
|
||||||
)
|
|
||||||
candidates.push({
|
candidates.push({
|
||||||
id: position.positionId,
|
id: position.positionId,
|
||||||
owner: position.owner,
|
owner: position.owner,
|
||||||
stakeShares: shares,
|
stakeShares: shares,
|
||||||
taxRate: position.taxRate,
|
taxRate: position.taxRate,
|
||||||
taxRateIndex: position.taxRateIndex,
|
taxRateIndex: position.taxRateIndex,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const selection = selectSnatchPositions(candidates, {
|
const selection = selectSnatchPositions(candidates, {
|
||||||
|
|
@ -134,47 +117,45 @@ export function useSnatchSelection(demo = false) {
|
||||||
maxTaxRate: maxTaxRateDecimal,
|
maxTaxRate: maxTaxRateDecimal,
|
||||||
includeOwned,
|
includeOwned,
|
||||||
recipientAddress: recipient,
|
recipientAddress: recipient,
|
||||||
})
|
});
|
||||||
|
|
||||||
if (cancelled || runId !== selectionRun) {
|
if (cancelled || runId !== selectionRun) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selection.remainingShortfall > 0n) {
|
if (selection.remainingShortfall > 0n) {
|
||||||
snatchablePositions.value = []
|
snatchablePositions.value = [];
|
||||||
floorTax.value = getMinFloorTax()
|
floorTax.value = getMinFloorTax();
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const positionById = new Map<bigint, Position>()
|
const positionById = new Map<bigint, Position>();
|
||||||
for (const position of activePositions.value) {
|
for (const position of activePositions.value) {
|
||||||
positionById.set(position.positionId, position)
|
positionById.set(position.positionId, position);
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedPositions = selection.selected
|
const selectedPositions = selection.selected
|
||||||
.map((candidate) => positionById.get(candidate.id))
|
.map(candidate => positionById.get(candidate.id))
|
||||||
.filter((value): value is Position => Boolean(value))
|
.filter((value): value is Position => Boolean(value));
|
||||||
|
|
||||||
snatchablePositions.value = selectedPositions
|
snatchablePositions.value = selectedPositions;
|
||||||
|
|
||||||
if (selection.maxSelectedTaxRateIndex !== undefined) {
|
if (selection.maxSelectedTaxRateIndex !== undefined) {
|
||||||
const nextIndex = selection.maxSelectedTaxRateIndex + 1
|
const nextIndex = selection.maxSelectedTaxRateIndex + 1;
|
||||||
const option =
|
const option = adjustTaxRate.taxRates[nextIndex] ?? adjustTaxRate.taxRates[selection.maxSelectedTaxRateIndex];
|
||||||
adjustTaxRate.taxRates[nextIndex] ??
|
floorTax.value = option ? option.year : getMinFloorTax();
|
||||||
adjustTaxRate.taxRates[selection.maxSelectedTaxRateIndex]
|
|
||||||
floorTax.value = option ? option.year : getMinFloorTax()
|
|
||||||
} else {
|
} else {
|
||||||
floorTax.value = getMinFloorTax()
|
floorTax.value = getMinFloorTax();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
compute()
|
compute();
|
||||||
})
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
snatchablePositions,
|
snatchablePositions,
|
||||||
shortfallShares,
|
shortfallShares,
|
||||||
floorTax,
|
floorTax,
|
||||||
openPositionsAvailable,
|
openPositionsAvailable,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,188 +1,166 @@
|
||||||
import { ref, onMounted, onUnmounted, reactive, computed } from "vue";
|
import { ref, onMounted, reactive, computed } from 'vue';
|
||||||
import { type ComputedRef } from "vue";
|
import { type ComputedRef } from 'vue';
|
||||||
import { config } from "@/wagmi";
|
import { config } from '@/wagmi';
|
||||||
import { AbiEncodingArrayLengthMismatchError, type WatchEventReturnType, decodeEventLog, type Hex } from "viem";
|
import { decodeEventLog, type Hex, type DecodeEventLogReturnType, type TypedDataDefinition } from 'viem';
|
||||||
import axios from "axios";
|
import { getAccount, signTypedData, waitForTransactionReceipt, type Config } from '@wagmi/core';
|
||||||
import {
|
import { HarbContract } from '@/contracts/harb';
|
||||||
getAccount,
|
import { StakeContract, permitAndSnatch, minStake } from '@/contracts/stake';
|
||||||
watchContractEvent,
|
import { getNonce, nonce, getName } from '@/contracts/harb';
|
||||||
readContract,
|
import { useWallet } from '@/composables/useWallet';
|
||||||
signTypedData,
|
import { createPermitObject, getSignatureRSV } from '@/utils/blockchain';
|
||||||
waitForTransactionReceipt,
|
import { formatBigIntDivision, compactNumber } from '@/utils/helper';
|
||||||
watchAccount,
|
import { taxRates } from '@/composables/useAdjustTaxRates';
|
||||||
verifyTypedData,
|
import { useContractToast } from './useContractToast';
|
||||||
} from "@wagmi/core";
|
|
||||||
import { HarbContract } from "@/contracts/harb";
|
|
||||||
import { type Abi, type Address } from "viem";
|
|
||||||
import { StakeContract, minStake, snatchService, permitAndSnatch, assetsToShares } from "@/contracts/stake";
|
|
||||||
import { getNonce, nonce, getName } from "@/contracts/harb";
|
|
||||||
import { useWallet } from "@/composables/useWallet";
|
|
||||||
import { createPermitObject, getSignatureRSV } from "@/utils/blockchain";
|
|
||||||
import { formatBigIntDivision, compactNumber } from "@/utils/helper";
|
|
||||||
import { useToast } from "vue-toastification";
|
|
||||||
import { taxRates } from "@/composables/useAdjustTaxRates";
|
|
||||||
import { useContractToast } from "./useContractToast";
|
|
||||||
const wallet = useWallet();
|
const wallet = useWallet();
|
||||||
const toast = useToast();
|
|
||||||
const contractToast = useContractToast();
|
const contractToast = useContractToast();
|
||||||
|
const wagmiConfig: Config = config;
|
||||||
|
|
||||||
enum StakeState {
|
enum StakeState {
|
||||||
NoBalance = "NoBalance",
|
NoBalance = 'NoBalance',
|
||||||
StakeAble = "StakeAble",
|
StakeAble = 'StakeAble',
|
||||||
SignTransaction = "SignTransaction",
|
SignTransaction = 'SignTransaction',
|
||||||
Waiting = "Waiting",
|
Waiting = 'Waiting',
|
||||||
NotEnoughApproval = "NotEnoughApproval",
|
NotEnoughApproval = 'NotEnoughApproval',
|
||||||
}
|
|
||||||
|
|
||||||
interface PositionCreatedEvent {
|
|
||||||
eventName: undefined;
|
|
||||||
args: PositionCreatedArgs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PositionCreatedArgs {
|
interface PositionCreatedArgs {
|
||||||
creationTime: number;
|
creationTime: number;
|
||||||
owner: Hex;
|
owner: Hex;
|
||||||
harbergDeposit: bigint;
|
harbergDeposit: bigint;
|
||||||
positionId: bigint;
|
positionId: bigint;
|
||||||
share: bigint;
|
share: bigint;
|
||||||
taxRate: number;
|
taxRate: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// const state = ref<StakeState>(StakeState.NoBalance);
|
// const state = ref<StakeState>(StakeState.NoBalance);
|
||||||
|
|
||||||
export function useStake() {
|
export function useStake() {
|
||||||
const stakingAmountRaw = ref();
|
const stakingAmountRaw = ref();
|
||||||
const stakingAmountShares = ref();
|
const stakingAmountShares = ref();
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const waiting = ref(false);
|
const waiting = ref(false);
|
||||||
|
|
||||||
onMounted(async () => {});
|
onMounted(async () => {});
|
||||||
|
|
||||||
const state: ComputedRef<StakeState> = computed(() => {
|
const state: ComputedRef<StakeState> = computed(() => {
|
||||||
const balance = wallet.balance.value;
|
const balance = wallet.balance.value;
|
||||||
console.log("balance123", balance);
|
// console.log("balance123", balance);
|
||||||
console.log("wallet", wallet);
|
// console.log("wallet", wallet);
|
||||||
|
|
||||||
|
|
||||||
if (loading.value) {
|
|
||||||
return StakeState.SignTransaction;
|
|
||||||
} else if (minStake.value > balance || stakingAmount.value > balance) {
|
|
||||||
return StakeState.NoBalance;
|
|
||||||
} else if (waiting.value) {
|
|
||||||
return StakeState.Waiting;
|
|
||||||
} else {
|
|
||||||
return StakeState.StakeAble;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const stakingAmount = computed({
|
if (loading.value) {
|
||||||
// getter
|
return StakeState.SignTransaction;
|
||||||
get() {
|
} else if (minStake.value > balance || stakingAmount.value > balance) {
|
||||||
return stakingAmountRaw.value || minStake.value;
|
return StakeState.NoBalance;
|
||||||
},
|
} else if (waiting.value) {
|
||||||
// setter
|
return StakeState.Waiting;
|
||||||
set(newValue) {
|
} else {
|
||||||
stakingAmountRaw.value = newValue;
|
return StakeState.StakeAble;
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const stakingAmountNumber = computed({
|
const stakingAmount = computed({
|
||||||
// getter
|
// getter
|
||||||
get() {
|
get() {
|
||||||
return formatBigIntDivision(stakingAmount.value, 10n ** 18n);
|
return stakingAmountRaw.value || minStake.value;
|
||||||
},
|
},
|
||||||
// setter
|
// setter
|
||||||
set(newValue) {
|
set(newValue) {
|
||||||
stakingAmount.value = BigInt(newValue * 10 ** 18);
|
stakingAmountRaw.value = newValue;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const stakingAmountNumber = computed({
|
||||||
|
// getter
|
||||||
|
get() {
|
||||||
|
return formatBigIntDivision(stakingAmount.value, 10n ** 18n);
|
||||||
|
},
|
||||||
|
// setter
|
||||||
|
set(newValue) {
|
||||||
|
stakingAmount.value = BigInt(newValue * 10 ** 18);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// const stakingAmountNumber = computed(() => return staking)
|
// const stakingAmountNumber = computed(() => return staking)
|
||||||
|
|
||||||
async function snatch(stakingAmount: BigInt, taxRate: number, positions:Array<any> = []) {
|
async function snatch(stakingAmount: bigint, taxRate: number, positions: Array<bigint> = []) {
|
||||||
console.log("snatch", { stakingAmount, taxRate, positions });
|
// console.log("snatch", { stakingAmount, taxRate, positions });
|
||||||
const account = getAccount(config as any);
|
const account = getAccount(wagmiConfig);
|
||||||
const taxRateObj = taxRates.find((obj) => obj.year === taxRate);
|
const taxRateObj = taxRates.find(obj => obj.year === taxRate);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const assets: BigInt = stakingAmount;
|
const assets: bigint = stakingAmount;
|
||||||
const receiver = wallet.account.address!;
|
// console.log("receiver", receiver);
|
||||||
console.log("receiver", receiver);
|
|
||||||
|
|
||||||
// await snatchService(assets, receiver, taxRate, []);
|
// await snatchService(assets, receiver, taxRate, []);
|
||||||
// assets: BigInt, receiver: Address, taxRate: Number, positionsToSnatch: Array<BigInt>
|
// assets: BigInt, receiver: Address, taxRate: Number, positionsToSnatch: Array<BigInt>
|
||||||
const deadline = BigInt(Date.now()) / 1000n + 1200n;
|
const deadline = BigInt(Date.now()) / 1000n + 1200n;
|
||||||
|
|
||||||
const name = await getName();
|
const name = await getName();
|
||||||
|
|
||||||
const { types, message, domain, primaryType } = createPermitObject(
|
const { types, message, domain, primaryType } = createPermitObject(
|
||||||
HarbContract.contractAddress,
|
HarbContract.contractAddress,
|
||||||
account.address!,
|
account.address!,
|
||||||
StakeContract.contractAddress,
|
StakeContract.contractAddress,
|
||||||
nonce.value,
|
nonce.value,
|
||||||
deadline,
|
deadline,
|
||||||
assets,
|
assets,
|
||||||
account.chainId!,
|
account.chainId!,
|
||||||
name
|
name
|
||||||
);
|
);
|
||||||
console.log("resultPermitObject", { types, message, domain, primaryType });
|
// console.log("resultPermitObject", { types, message, domain, primaryType });
|
||||||
|
|
||||||
const signature = await signTypedData(config as any, {
|
const typedData: TypedDataDefinition = {
|
||||||
domain: domain as any,
|
domain,
|
||||||
message: message,
|
message,
|
||||||
primaryType: primaryType,
|
primaryType,
|
||||||
types: types,
|
types,
|
||||||
});
|
};
|
||||||
|
|
||||||
console.log("signature", {
|
const signature = await signTypedData(wagmiConfig, typedData);
|
||||||
domain: domain as any,
|
|
||||||
message: message,
|
|
||||||
primaryType: primaryType,
|
|
||||||
types: types,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { r, s, v } = getSignatureRSV(signature);
|
// console.log("signature", {
|
||||||
loading.value = true;
|
// domain,
|
||||||
console.log("permitAndSnatch", assets, account.address!, taxRateObj?.index!, positions, deadline, v, r, s);
|
// message,
|
||||||
|
// primaryType,
|
||||||
const hash = await permitAndSnatch(assets, account.address!, taxRateObj?.index!, positions, deadline, v, r, s);
|
// types,
|
||||||
console.log("hash", hash);
|
// });
|
||||||
loading.value = false;
|
|
||||||
waiting.value = true;
|
|
||||||
const data = await waitForTransactionReceipt(config as any, {
|
|
||||||
hash: hash,
|
|
||||||
});
|
|
||||||
|
|
||||||
const topics: any = decodeEventLog({
|
const { r, s, v } = getSignatureRSV(signature);
|
||||||
abi: StakeContract.abi,
|
loading.value = true;
|
||||||
data: data.logs[3].data,
|
// console.log("permitAndSnatch", assets, account.address!, taxRateObj?.index!, positions, deadline, v, r, s);
|
||||||
topics: data.logs[3].topics,
|
|
||||||
});
|
|
||||||
const eventArgs: PositionCreatedArgs = topics.args;
|
|
||||||
|
|
||||||
const amount = compactNumber(
|
const taxRateIndex = taxRateObj?.index ?? 0;
|
||||||
formatBigIntDivision(eventArgs.harbergDeposit, 10n ** BigInt(wallet.balance.decimals))
|
const hash = await permitAndSnatch(assets, account.address!, taxRateIndex, positions, deadline, v, r, s);
|
||||||
);
|
// console.log("hash", hash);
|
||||||
contractToast.showSuccessToast(
|
loading.value = false;
|
||||||
amount,
|
waiting.value = true;
|
||||||
"Success!",
|
const data = await waitForTransactionReceipt(wagmiConfig, {
|
||||||
"You Staked",
|
hash: hash,
|
||||||
"Check your positions on the<br /> Staker Dashboard",
|
});
|
||||||
"$KRK"
|
|
||||||
);
|
|
||||||
|
|
||||||
waiting.value = false;
|
const topics = decodeEventLog({
|
||||||
await getNonce();
|
abi: StakeContract.abi,
|
||||||
} catch (error: any) {
|
data: data.logs[3].data,
|
||||||
console.error("error", error);
|
topics: data.logs[3].topics,
|
||||||
console.log(JSON.parse(JSON.stringify(error)));
|
}) as DecodeEventLogReturnType & { args: PositionCreatedArgs };
|
||||||
contractToast.showFailToast(error.name);
|
const eventArgs: PositionCreatedArgs = topics.args;
|
||||||
} finally {
|
|
||||||
loading.value = false;
|
|
||||||
waiting.value = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return reactive({ stakingAmount, stakingAmountShares, stakingAmountNumber, state, snatch });
|
const decimalsAsBigInt = typeof wallet.balance.decimals === 'bigint' ? wallet.balance.decimals : BigInt(wallet.balance.decimals);
|
||||||
|
const amount = compactNumber(formatBigIntDivision(eventArgs.harbergDeposit, 10n ** decimalsAsBigInt));
|
||||||
|
contractToast.showSuccessToast(amount, 'Success!', 'You Staked', 'Check your positions on the<br /> Staker Dashboard', '$KRK');
|
||||||
|
|
||||||
|
waiting.value = false;
|
||||||
|
await getNonce();
|
||||||
|
} catch (error) {
|
||||||
|
// console.error("error", error);
|
||||||
|
// console.log(JSON.parse(JSON.stringify(error)));
|
||||||
|
const message = error instanceof Error ? error.name : 'Transaction failed';
|
||||||
|
contractToast.showFailToast(message);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
waiting.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return reactive({ stakingAmount, stakingAmountShares, stakingAmountNumber, state, snatch });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,32 @@
|
||||||
import { ref, onMounted, onUnmounted, reactive, computed } from "vue";
|
import { ref, reactive, computed } from 'vue';
|
||||||
import axios from "axios";
|
import axios from 'axios';
|
||||||
import { chainData } from "./useWallet";
|
import { chainData } from './useWallet';
|
||||||
import { watchBlocks, watchChainId } from "@wagmi/core";
|
import { watchChainId } from '@wagmi/core';
|
||||||
import { config } from "@/wagmi";
|
import type { Config } from '@wagmi/core';
|
||||||
import logger from "@/utils/logger";
|
import { config } from '@/wagmi';
|
||||||
import type { WatchBlocksReturnType } from "viem";
|
import logger from '@/utils/logger';
|
||||||
import { bigInt2Number } from "@/utils/helper";
|
import type { WatchBlocksReturnType } from 'viem';
|
||||||
const demo = sessionStorage.getItem("demo") === "true";
|
import { bigInt2Number } from '@/utils/helper';
|
||||||
|
const demo = sessionStorage.getItem('demo') === 'true';
|
||||||
|
|
||||||
const GRAPHQL_TIMEOUT_MS = 15_000;
|
const GRAPHQL_TIMEOUT_MS = 15_000;
|
||||||
const RETRY_BASE_DELAY_MS = 1_500;
|
const RETRY_BASE_DELAY_MS = 1_500;
|
||||||
const RETRY_MAX_DELAY_MS = 60_000;
|
const RETRY_MAX_DELAY_MS = 60_000;
|
||||||
|
|
||||||
interface StatsRecord {
|
interface StatsRecord {
|
||||||
burnNextHourProjected: string;
|
burnNextHourProjected: string;
|
||||||
burnedLastDay: string;
|
burnedLastDay: string;
|
||||||
burnedLastWeek: string;
|
burnedLastWeek: string;
|
||||||
id: string;
|
id: string;
|
||||||
mintNextHourProjected: string;
|
mintNextHourProjected: string;
|
||||||
mintedLastDay: string;
|
mintedLastDay: string;
|
||||||
mintedLastWeek: string;
|
mintedLastWeek: string;
|
||||||
outstandingStake: string;
|
outstandingStake: string;
|
||||||
kraikenTotalSupply: string;
|
kraikenTotalSupply: string;
|
||||||
stakeTotalSupply: string;
|
stakeTotalSupply: string;
|
||||||
ringBufferPointer: number;
|
ringBufferPointer: number;
|
||||||
totalBurned: string;
|
totalBurned: string;
|
||||||
totalMinted: string;
|
totalMinted: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rawStatsCollections = ref<Array<StatsRecord>>([]);
|
const rawStatsCollections = ref<Array<StatsRecord>>([]);
|
||||||
|
|
@ -36,50 +37,52 @@ const statsRetryDelayMs = ref(RETRY_BASE_DELAY_MS);
|
||||||
let statsRetryTimer: number | null = null;
|
let statsRetryTimer: number | null = null;
|
||||||
|
|
||||||
function formatGraphqlError(error: unknown): string {
|
function formatGraphqlError(error: unknown): string {
|
||||||
if (axios.isAxiosError(error)) {
|
if (axios.isAxiosError(error)) {
|
||||||
const responseErrors = (error.response?.data as any)?.errors;
|
const responseErrors = (error.response?.data as { errors?: unknown[] })?.errors;
|
||||||
if (Array.isArray(responseErrors) && responseErrors.length > 0) {
|
if (Array.isArray(responseErrors) && responseErrors.length > 0) {
|
||||||
return responseErrors.map((err: any) => err?.message ?? "GraphQL error").join(", ");
|
return responseErrors.map((err: unknown) => (err as { message?: string })?.message ?? 'GraphQL error').join(', ');
|
||||||
}
|
}
|
||||||
if (error.response?.status) {
|
if (error.response?.status) {
|
||||||
return `GraphQL request failed with status ${error.response.status}`;
|
return `GraphQL request failed with status ${error.response.status}`;
|
||||||
}
|
}
|
||||||
if (error.message) {
|
if (error.message) {
|
||||||
return error.message;
|
return error.message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (error instanceof Error && error.message) {
|
if (error instanceof Error && error.message) {
|
||||||
return error.message;
|
return error.message;
|
||||||
}
|
}
|
||||||
return "Unknown GraphQL error";
|
return 'Unknown GraphQL error';
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearStatsRetryTimer() {
|
function clearStatsRetryTimer() {
|
||||||
if (statsRetryTimer !== null) {
|
if (statsRetryTimer !== null) {
|
||||||
clearTimeout(statsRetryTimer);
|
clearTimeout(statsRetryTimer);
|
||||||
statsRetryTimer = null;
|
statsRetryTimer = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function scheduleStatsRetry() {
|
function scheduleStatsRetry() {
|
||||||
if (typeof window === "undefined") {
|
if (typeof window === 'undefined') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (statsRetryTimer !== null) {
|
if (statsRetryTimer !== null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const delay = statsRetryDelayMs.value;
|
const delay = statsRetryDelayMs.value;
|
||||||
statsRetryTimer = window.setTimeout(async () => {
|
statsRetryTimer = window.setTimeout(async () => {
|
||||||
statsRetryTimer = null;
|
statsRetryTimer = null;
|
||||||
await loadStats();
|
await loadStats();
|
||||||
}, delay);
|
}, delay);
|
||||||
statsRetryDelayMs.value = Math.min(statsRetryDelayMs.value * 2, RETRY_MAX_DELAY_MS);
|
statsRetryDelayMs.value = Math.min(statsRetryDelayMs.value * 2, RETRY_MAX_DELAY_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadStatsCollection(endpoint: string) {
|
export async function loadStatsCollection(endpoint: string) {
|
||||||
logger.info(`loadStatsCollection for chain: ${chainData.value?.path}`);
|
logger.info(`loadStatsCollection for chain: ${chainData.value?.path}`);
|
||||||
const res = await axios.post(endpoint, {
|
const res = await axios.post(
|
||||||
query: `query StatsQuery {
|
endpoint,
|
||||||
|
{
|
||||||
|
query: `query StatsQuery {
|
||||||
stats(id: "0x01") {
|
stats(id: "0x01") {
|
||||||
burnNextHourProjected
|
burnNextHourProjected
|
||||||
burnedLastDay
|
burnedLastDay
|
||||||
|
|
@ -96,209 +99,207 @@ export async function loadStatsCollection(endpoint: string) {
|
||||||
totalMinted
|
totalMinted
|
||||||
}
|
}
|
||||||
}`,
|
}`,
|
||||||
}, { timeout: GRAPHQL_TIMEOUT_MS });
|
},
|
||||||
|
{ timeout: GRAPHQL_TIMEOUT_MS }
|
||||||
|
);
|
||||||
|
|
||||||
const errors = res.data?.errors;
|
const errors = res.data?.errors;
|
||||||
if (Array.isArray(errors) && errors.length > 0) {
|
if (Array.isArray(errors) && errors.length > 0) {
|
||||||
throw new Error(errors.map((err: any) => err?.message ?? "GraphQL error").join(", "));
|
throw new Error(errors.map((err: unknown) => (err as { message?: string })?.message ?? 'GraphQL error').join(', '));
|
||||||
}
|
}
|
||||||
|
|
||||||
const stats = res.data?.data?.stats as StatsRecord | undefined;
|
const stats = res.data?.data?.stats as StatsRecord | undefined;
|
||||||
if (!stats) {
|
if (!stats) {
|
||||||
throw new Error("Stats entity not found in GraphQL response");
|
throw new Error('Stats entity not found in GraphQL response');
|
||||||
}
|
}
|
||||||
|
|
||||||
return [{ ...stats, kraikenTotalSupply: stats.kraikenTotalSupply }];
|
return [{ ...stats, kraikenTotalSupply: stats.kraikenTotalSupply }];
|
||||||
}
|
}
|
||||||
|
|
||||||
const profit7d = computed(() => {
|
const profit7d = computed(() => {
|
||||||
if (!statsCollection.value) {
|
if (!statsCollection.value) {
|
||||||
return 0n;
|
return 0n;
|
||||||
}
|
}
|
||||||
const mintedLastWeek = BigInt(statsCollection.value.mintedLastWeek);
|
const mintedLastWeek = BigInt(statsCollection.value.mintedLastWeek);
|
||||||
const burnedLastWeek = BigInt(statsCollection.value.burnedLastWeek);
|
const burnedLastWeek = BigInt(statsCollection.value.burnedLastWeek);
|
||||||
const totalMinted = BigInt(statsCollection.value.totalMinted);
|
const totalMinted = BigInt(statsCollection.value.totalMinted);
|
||||||
const totalBurned = BigInt(statsCollection.value.totalBurned);
|
const totalBurned = BigInt(statsCollection.value.totalBurned);
|
||||||
const denominator = totalMinted - totalBurned;
|
const denominator = totalMinted - totalBurned;
|
||||||
if (denominator === 0n) {
|
if (denominator === 0n) {
|
||||||
return 0n;
|
return 0n;
|
||||||
}
|
}
|
||||||
return (mintedLastWeek - burnedLastWeek) / denominator;
|
return (mintedLastWeek - burnedLastWeek) / denominator;
|
||||||
});
|
});
|
||||||
const nettoToken7d = computed(() => {
|
const nettoToken7d = computed(() => {
|
||||||
if (!statsCollection.value) {
|
if (!statsCollection.value) {
|
||||||
return 0n;
|
return 0n;
|
||||||
}
|
}
|
||||||
|
|
||||||
return BigInt(statsCollection.value.mintedLastWeek) - BigInt(statsCollection.value.burnedLastWeek);
|
return BigInt(statsCollection.value.mintedLastWeek) - BigInt(statsCollection.value.burnedLastWeek);
|
||||||
});
|
});
|
||||||
|
|
||||||
const statsCollection = computed(() => {
|
const statsCollection = computed(() => {
|
||||||
if (rawStatsCollections.value?.length > 0) {
|
if (rawStatsCollections.value?.length > 0) {
|
||||||
return rawStatsCollections.value[0];
|
return rawStatsCollections.value[0];
|
||||||
} else {
|
} else {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const outstandingStake = computed(() => {
|
const outstandingStake = computed(() => {
|
||||||
if (demo) {
|
if (demo) {
|
||||||
// outStandingStake = 1990000000000000000000000n;
|
// outStandingStake = 1990000000000000000000000n;
|
||||||
return 2000000000000000000000000n;
|
return 2000000000000000000000000n;
|
||||||
}
|
}
|
||||||
if (rawStatsCollections.value?.length > 0) {
|
if (rawStatsCollections.value?.length > 0) {
|
||||||
return BigInt(rawStatsCollections.value[0].outstandingStake);
|
return BigInt(rawStatsCollections.value[0].outstandingStake);
|
||||||
} else {
|
} else {
|
||||||
return 0n;
|
return 0n;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const kraikenTotalSupply = computed(() => {
|
const kraikenTotalSupply = computed(() => {
|
||||||
if (rawStatsCollections.value?.length > 0) {
|
if (rawStatsCollections.value?.length > 0) {
|
||||||
return BigInt(rawStatsCollections.value[0].kraikenTotalSupply);
|
return BigInt(rawStatsCollections.value[0].kraikenTotalSupply);
|
||||||
} else {
|
} else {
|
||||||
return 0n;
|
return 0n;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const stakeTotalSupply = computed(() => {
|
const stakeTotalSupply = computed(() => {
|
||||||
if (rawStatsCollections.value?.length > 0) {
|
if (rawStatsCollections.value?.length > 0) {
|
||||||
return BigInt(rawStatsCollections.value[0].stakeTotalSupply);
|
return BigInt(rawStatsCollections.value[0].stakeTotalSupply);
|
||||||
} else {
|
} else {
|
||||||
return 0n;
|
return 0n;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//Total Supply Change / 7d=mintedLastWeek−burnedLastWeek
|
//Total Supply Change / 7d=mintedLastWeek−burnedLastWeek
|
||||||
const totalSupplyChange7d = computed(() => {
|
const totalSupplyChange7d = computed(() => {
|
||||||
if (rawStatsCollections.value?.length > 0) {
|
if (rawStatsCollections.value?.length > 0) {
|
||||||
return (
|
return BigInt(rawStatsCollections.value[0].mintedLastWeek) - BigInt(rawStatsCollections.value[0].burnedLastWeek);
|
||||||
BigInt(rawStatsCollections.value[0].mintedLastWeek) -
|
} else {
|
||||||
BigInt(rawStatsCollections.value[0].burnedLastWeek)
|
return 0n;
|
||||||
);
|
}
|
||||||
} else {
|
|
||||||
return 0n;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//totalsupply Change7d / harbtotalsupply
|
//totalsupply Change7d / harbtotalsupply
|
||||||
const inflation7d = computed(() => {
|
const inflation7d = computed(() => {
|
||||||
if (rawStatsCollections.value?.length > 0 && BigInt(rawStatsCollections.value[0].kraikenTotalSupply) > 0n) {
|
if (rawStatsCollections.value?.length > 0 && BigInt(rawStatsCollections.value[0].kraikenTotalSupply) > 0n) {
|
||||||
return (
|
return BigInt(rawStatsCollections.value[0].mintedLastWeek) - BigInt(rawStatsCollections.value[0].burnedLastWeek);
|
||||||
BigInt(rawStatsCollections.value[0].mintedLastWeek) -
|
} else {
|
||||||
BigInt(rawStatsCollections.value[0].burnedLastWeek)
|
return 0n;
|
||||||
);
|
}
|
||||||
} else {
|
|
||||||
return 0n;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const stakeableSupply = computed(() => {
|
const stakeableSupply = computed(() => {
|
||||||
if (rawStatsCollections.value?.length > 0 && BigInt(rawStatsCollections.value[0].kraikenTotalSupply) > 0n) {
|
if (rawStatsCollections.value?.length > 0 && BigInt(rawStatsCollections.value[0].kraikenTotalSupply) > 0n) {
|
||||||
console.log("rawStatsCollections.value[0]", rawStatsCollections.value[0]);
|
// console.log("rawStatsCollections.value[0]", rawStatsCollections.value[0]);
|
||||||
|
|
||||||
return stakeTotalSupply.value / 5n;
|
return stakeTotalSupply.value / 5n;
|
||||||
} else {
|
} else {
|
||||||
return 0n;
|
return 0n;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//maxSlots
|
//maxSlots
|
||||||
const maxSlots = computed(() => {
|
const maxSlots = computed(() => {
|
||||||
if (rawStatsCollections.value?.length > 0 && BigInt(rawStatsCollections.value[0].kraikenTotalSupply) > 0n) {
|
if (rawStatsCollections.value?.length > 0 && BigInt(rawStatsCollections.value[0].kraikenTotalSupply) > 0n) {
|
||||||
console.log("rawStatsCollections.value[0]", rawStatsCollections.value[0]);
|
// console.log("rawStatsCollections.value[0]", rawStatsCollections.value[0]);
|
||||||
|
|
||||||
return (bigInt2Number(stakeTotalSupply.value, 18) * 0.2) / 100;
|
return (bigInt2Number(stakeTotalSupply.value, 18) * 0.2) / 100;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const claimedSlots = computed(() => {
|
const claimedSlots = computed(() => {
|
||||||
if (stakeTotalSupply.value > 0n) {
|
if (stakeTotalSupply.value > 0n) {
|
||||||
const stakeableSupplyNumber = bigInt2Number(stakeableSupply.value, 18);
|
const stakeableSupplyNumber = bigInt2Number(stakeableSupply.value, 18);
|
||||||
const outstandingStakeNumber = bigInt2Number(outstandingStake.value, 18);
|
const outstandingStakeNumber = bigInt2Number(outstandingStake.value, 18);
|
||||||
return (outstandingStakeNumber / stakeableSupplyNumber) * maxSlots.value;
|
return (outstandingStakeNumber / stakeableSupplyNumber) * maxSlots.value;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export async function loadStats() {
|
export async function loadStats() {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
|
||||||
const endpoint = chainData.value?.graphql?.trim();
|
const endpoint = chainData.value?.graphql?.trim();
|
||||||
if (!endpoint) {
|
if (!endpoint) {
|
||||||
rawStatsCollections.value = [];
|
rawStatsCollections.value = [];
|
||||||
statsError.value = "GraphQL endpoint not configured for this chain.";
|
statsError.value = 'GraphQL endpoint not configured for this chain.';
|
||||||
clearStatsRetryTimer();
|
clearStatsRetryTimer();
|
||||||
statsRetryDelayMs.value = RETRY_BASE_DELAY_MS;
|
statsRetryDelayMs.value = RETRY_BASE_DELAY_MS;
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
initialized.value = true;
|
initialized.value = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
rawStatsCollections.value = await loadStatsCollection(endpoint);
|
rawStatsCollections.value = await loadStatsCollection(endpoint);
|
||||||
statsError.value = null;
|
statsError.value = null;
|
||||||
statsRetryDelayMs.value = RETRY_BASE_DELAY_MS;
|
statsRetryDelayMs.value = RETRY_BASE_DELAY_MS;
|
||||||
clearStatsRetryTimer();
|
clearStatsRetryTimer();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn("[stats] loadStats() failed", error);
|
// console.warn('[stats] loadStats() failed', error);
|
||||||
rawStatsCollections.value = [];
|
rawStatsCollections.value = [];
|
||||||
statsError.value = formatGraphqlError(error);
|
statsError.value = formatGraphqlError(error);
|
||||||
scheduleStatsRetry();
|
scheduleStatsRetry();
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
initialized.value = true;
|
initialized.value = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let unwatch: any = null;
|
import { onMounted, onUnmounted } from 'vue';
|
||||||
let unwatchBlock: WatchBlocksReturnType;
|
|
||||||
const loadingWatchBlock = ref(false);
|
let unwatch: WatchBlocksReturnType | null = null;
|
||||||
export function useStatCollection() {
|
export function useStatCollection() {
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
//initial loading stats
|
//initial loading stats
|
||||||
if (rawStatsCollections.value?.length === 0 && !loading.value) {
|
if (rawStatsCollections.value?.length === 0 && !loading.value) {
|
||||||
await loadStats();
|
await loadStats();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!unwatch) {
|
if (!unwatch) {
|
||||||
console.log("watchChain");
|
// console.log("watchChain");
|
||||||
|
|
||||||
//chain Switch reload stats for other chain
|
//chain Switch reload stats for other chain
|
||||||
unwatch = watchChainId(config as any, {
|
unwatch = watchChainId(config as Config, {
|
||||||
async onChange(chainId) {
|
async onChange(_chainId) {
|
||||||
await loadStats();
|
await loadStats();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// const unwatchBlock = watchBlocks(config as any, {
|
// const unwatchBlock = watchBlocks(config as Config, {
|
||||||
// async onBlock(block) {
|
// async onBlock(block) {
|
||||||
// console.log('Block changed!', block)
|
// console.log('Block changed!', block)
|
||||||
// await loadStats();
|
// await loadStats();
|
||||||
// },
|
// },
|
||||||
// })
|
// })
|
||||||
}
|
}
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
clearStatsRetryTimer();
|
clearStatsRetryTimer();
|
||||||
unwatch();
|
if (unwatch) {
|
||||||
});
|
unwatch();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return reactive({
|
return reactive({
|
||||||
profit7d,
|
profit7d,
|
||||||
nettoToken7d,
|
nettoToken7d,
|
||||||
inflation7d,
|
inflation7d,
|
||||||
outstandingStake,
|
outstandingStake,
|
||||||
kraikenTotalSupply,
|
kraikenTotalSupply,
|
||||||
stakeTotalSupply,
|
stakeTotalSupply,
|
||||||
totalSupplyChange7d,
|
totalSupplyChange7d,
|
||||||
initialized,
|
initialized,
|
||||||
maxSlots,
|
maxSlots,
|
||||||
stakeableSupply,
|
stakeableSupply,
|
||||||
claimedSlots,
|
claimedSlots,
|
||||||
statsError,
|
statsError,
|
||||||
loading,
|
loading,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,77 +1,76 @@
|
||||||
import { ref, onMounted, onUnmounted, reactive, computed, type ComputedRef } from "vue";
|
import { ref, reactive, computed, type ComputedRef } from 'vue';
|
||||||
import * as StakeContract from "@/contracts/stake";
|
import * as StakeContract from '@/contracts/stake';
|
||||||
import { waitForTransactionReceipt } from "@wagmi/core";
|
import { waitForTransactionReceipt } from '@wagmi/core';
|
||||||
import { config } from "@/wagmi";
|
import type { Config } from '@wagmi/core';
|
||||||
import { useContractToast } from "./useContractToast";
|
import { config } from '@/wagmi';
|
||||||
import { compactNumber, formatBigIntDivision } from "@/utils/helper";
|
import { useContractToast } from './useContractToast';
|
||||||
import { decodeEventLog } from "viem";
|
import { compactNumber, formatBigIntDivision } from '@/utils/helper';
|
||||||
import { useWallet } from "./useWallet";
|
import { decodeEventLog, type DecodeEventLogReturnType } from 'viem';
|
||||||
|
import { useWallet } from './useWallet';
|
||||||
|
|
||||||
const contractToast = useContractToast();
|
const contractToast = useContractToast();
|
||||||
const wallet = useWallet();
|
const wallet = useWallet();
|
||||||
|
|
||||||
enum StakeState {
|
enum StakeState {
|
||||||
SignTransaction = "SignTransaction",
|
SignTransaction = 'SignTransaction',
|
||||||
Waiting = "Waiting",
|
Waiting = 'Waiting',
|
||||||
Unstakeable = "Unstakeable",
|
Unstakeable = 'Unstakeable',
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PositionRemovedArgs {
|
interface PositionRemovedArgs {
|
||||||
lastTaxTime: number;
|
lastTaxTime: number;
|
||||||
positionId: bigint;
|
positionId: bigint;
|
||||||
harbergPayout: bigint;
|
harbergPayout: bigint;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useUnstake() {
|
export function useUnstake() {
|
||||||
const loading = ref();
|
const loading = ref();
|
||||||
const waiting = ref();
|
const waiting = ref();
|
||||||
|
|
||||||
const state: ComputedRef<StakeState> = computed(() => {
|
const state: ComputedRef<StakeState> = computed(() => {
|
||||||
if (loading.value) {
|
if (loading.value) {
|
||||||
return StakeState.SignTransaction;
|
return StakeState.SignTransaction;
|
||||||
} else if (waiting.value) {
|
} else if (waiting.value) {
|
||||||
return StakeState.Waiting;
|
return StakeState.Waiting;
|
||||||
} else {
|
} else {
|
||||||
return StakeState.Unstakeable;
|
return StakeState.Unstakeable;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
async function exitPosition(positionId: bigint) {
|
async function exitPosition(positionId: bigint) {
|
||||||
try {
|
try {
|
||||||
console.log("positionId", positionId);
|
// console.log("positionId", positionId);
|
||||||
|
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const hash = await StakeContract.exitPosition(positionId);
|
const hash = await StakeContract.exitPosition(positionId);
|
||||||
console.log("hash", hash);
|
// console.log("hash", hash);
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
waiting.value = true;
|
waiting.value = true;
|
||||||
const data = await waitForTransactionReceipt(config as any, {
|
const data = await waitForTransactionReceipt(config as Config, {
|
||||||
hash: hash,
|
hash: hash,
|
||||||
});
|
});
|
||||||
|
|
||||||
const topics: any = decodeEventLog({
|
const topics = decodeEventLog({
|
||||||
abi: StakeContract.StakeContract.abi,
|
abi: StakeContract.StakeContract.abi,
|
||||||
data: data.logs[2].data,
|
data: data.logs[2].data,
|
||||||
topics: data.logs[2].topics,
|
topics: data.logs[2].topics,
|
||||||
});
|
}) as DecodeEventLogReturnType & { args: PositionRemovedArgs };
|
||||||
|
|
||||||
const eventArgs: PositionRemovedArgs = topics.args;
|
const eventArgs: PositionRemovedArgs = topics.args;
|
||||||
|
|
||||||
const amount = compactNumber(
|
const amount = compactNumber(formatBigIntDivision(eventArgs.harbergPayout, 10n ** BigInt(wallet.balance.decimals)));
|
||||||
formatBigIntDivision(eventArgs.harbergPayout, 10n ** BigInt(wallet.balance.decimals))
|
contractToast.showSuccessToast(amount, 'Success!', 'You unstaked', '', '$KRK');
|
||||||
);
|
|
||||||
contractToast.showSuccessToast(amount, "Success!", "You unstaked", "", "$KRK");
|
|
||||||
|
|
||||||
waiting.value = false;
|
waiting.value = false;
|
||||||
wallet.loadBalance();
|
wallet.loadBalance();
|
||||||
} catch (error: any) {
|
} catch (error) {
|
||||||
console.error("error", error);
|
// console.error("error", error);
|
||||||
contractToast.showFailToast(error.name);
|
contractToast.showFailToast((error as Error).name);
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
waiting.value = false;
|
waiting.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return reactive({ state, exitPosition });
|
return reactive({ state, exitPosition });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,96 +1,91 @@
|
||||||
import { ref, onMounted, onUnmounted, reactive, computed } from "vue";
|
import { ref, reactive, computed } from 'vue';
|
||||||
import { type Ref } from "vue";
|
import { getAccount, getBalance, watchAccount, watchChainId, type Config } from '@wagmi/core';
|
||||||
import { getAccount, getBalance, watchAccount, watchChainId } from "@wagmi/core";
|
import { type WatchAccountReturnType, type GetAccountReturnType, type GetBalanceReturnType } from '@wagmi/core';
|
||||||
import { type WatchAccountReturnType, type GetAccountReturnType, type GetBalanceReturnType } from "@wagmi/core";
|
import { config } from '@/wagmi';
|
||||||
import { config } from "@/wagmi";
|
import { getAllowance, HarbContract, getNonce } from '@/contracts/harb';
|
||||||
import { getAllowance, HarbContract, getNonce } from "@/contracts/harb";
|
import logger from '@/utils/logger';
|
||||||
import logger from "@/utils/logger";
|
import { setHarbContract } from '@/contracts/harb';
|
||||||
import { setHarbContract } from "@/contracts/harb";
|
import { setStakeContract } from '@/contracts/stake';
|
||||||
import { setStakeContract } from "@/contracts/stake";
|
import { chainsData, DEFAULT_CHAIN_ID } from '@/config';
|
||||||
import {chainsData, DEFAULT_CHAIN_ID} from "@/config"
|
|
||||||
|
|
||||||
const balance = ref<GetBalanceReturnType>({
|
const balance = ref<GetBalanceReturnType>({
|
||||||
value: 0n,
|
value: 0n,
|
||||||
decimals: 0,
|
decimals: 0,
|
||||||
symbol: "",
|
symbol: '',
|
||||||
formatted: ""
|
formatted: '',
|
||||||
});
|
});
|
||||||
const account = ref<GetAccountReturnType>(getAccount(config as any));
|
const account = ref<GetAccountReturnType>(getAccount(config as Config));
|
||||||
if (!account.value.chainId) {
|
if (!account.value.chainId) {
|
||||||
(account.value as any).chainId = DEFAULT_CHAIN_ID;
|
account.value.chainId = DEFAULT_CHAIN_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedChainId = computed(() => account.value.chainId ?? DEFAULT_CHAIN_ID);
|
const selectedChainId = computed(() => account.value.chainId ?? DEFAULT_CHAIN_ID);
|
||||||
|
|
||||||
export const chainData = computed(() => {
|
export const chainData = computed(() => {
|
||||||
return chainsData.find((obj) => obj.id === selectedChainId.value)
|
return chainsData.find(obj => obj.id === selectedChainId.value);
|
||||||
})
|
});
|
||||||
|
|
||||||
let unwatch: any = null;
|
let unwatch: WatchAccountReturnType | null = null;
|
||||||
let unwatchChain: any = null;
|
let unwatchChain: WatchAccountReturnType | null = null;
|
||||||
export function useWallet() {
|
export function useWallet() {
|
||||||
|
async function loadBalance() {
|
||||||
|
logger.contract('loadBalance');
|
||||||
|
if (account.value.address) {
|
||||||
|
// console.log("HarbContract",HarbContract );
|
||||||
|
|
||||||
async function loadBalance() {
|
balance.value = await getBalance(config as Config, {
|
||||||
logger.contract("loadBalance")
|
address: account.value.address,
|
||||||
if (account.value.address) {
|
token: HarbContract.contractAddress,
|
||||||
console.log("HarbContract",HarbContract );
|
});
|
||||||
|
// console.log("balance.value", balance.value);
|
||||||
balance.value = await getBalance(config as any, {
|
|
||||||
address: account.value.address,
|
|
||||||
token: HarbContract.contractAddress,
|
|
||||||
});
|
|
||||||
console.log("balance.value", balance.value);
|
|
||||||
|
|
||||||
return balance.value;
|
|
||||||
} else {
|
|
||||||
return 0n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!unwatch) {
|
return balance.value;
|
||||||
console.log("useWallet function");
|
} else {
|
||||||
|
return 0n;
|
||||||
unwatch = watchAccount(config as any, {
|
|
||||||
async onChange(data) {
|
|
||||||
console.log("watchaccount-useWallet", data);
|
|
||||||
|
|
||||||
if(!data.address) {
|
|
||||||
logger.info(`disconnected`);
|
|
||||||
balance.value = {
|
|
||||||
value: 0n,
|
|
||||||
decimals: 0,
|
|
||||||
symbol: "",
|
|
||||||
formatted: ""
|
|
||||||
}
|
|
||||||
} else if (account.value.address !== data.address || account.value.chainId !== data.chainId) {
|
|
||||||
logger.info(`Account changed!:`, data.address);
|
|
||||||
account.value = data;
|
|
||||||
await loadBalance();
|
|
||||||
await getAllowance();
|
|
||||||
// await loadPositions();
|
|
||||||
await getNonce();
|
|
||||||
setHarbContract()
|
|
||||||
setStakeContract()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!unwatch) {
|
||||||
|
// console.log("useWallet function");
|
||||||
|
|
||||||
//funzt nicht mehr-> library Änderung?
|
unwatch = watchAccount(config as Config, {
|
||||||
if(!unwatchChain){
|
async onChange(data) {
|
||||||
console.log("unwatchChain");
|
// console.log("watchaccount-useWallet", data);
|
||||||
|
|
||||||
unwatchChain = watchChainId(config as any, {
|
if (!data.address) {
|
||||||
async onChange(chainId) {
|
logger.info(`disconnected`);
|
||||||
console.log("chainId123", chainId);
|
balance.value = {
|
||||||
|
value: 0n,
|
||||||
await loadBalance();
|
decimals: 0,
|
||||||
await getAllowance();
|
symbol: '',
|
||||||
await getNonce();
|
formatted: '',
|
||||||
}
|
};
|
||||||
});
|
} else if (account.value.address !== data.address || account.value.chainId !== data.chainId) {
|
||||||
}
|
logger.info(`Account changed!:`, data.address);
|
||||||
|
account.value = data;
|
||||||
|
await loadBalance();
|
||||||
|
await getAllowance();
|
||||||
|
// await loadPositions();
|
||||||
|
await getNonce();
|
||||||
|
setHarbContract();
|
||||||
|
setStakeContract();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return reactive({ balance, account, loadBalance });
|
//funzt nicht mehr-> library Änderung?
|
||||||
|
if (!unwatchChain) {
|
||||||
|
// console.log("unwatchChain");
|
||||||
|
|
||||||
|
unwatchChain = watchChainId(config as Config, {
|
||||||
|
async onChange(_chainId) {
|
||||||
|
await loadBalance();
|
||||||
|
await getAllowance();
|
||||||
|
await getNonce();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return reactive({ balance, account, loadBalance });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,96 +1,107 @@
|
||||||
import deploymentsLocal from "../../onchain/deployments-local.json";
|
import deploymentsLocal from '../../onchain/deployments-local.json';
|
||||||
|
|
||||||
const env = import.meta.env;
|
const env = import.meta.env;
|
||||||
|
|
||||||
const LOCAL_PONDER_URL = env.VITE_PONDER_BASE_SEPOLIA_LOCAL_FORK ?? "http://127.0.0.1:42069/graphql";
|
const LOCAL_PONDER_URL = env.VITE_PONDER_BASE_SEPOLIA_LOCAL_FORK ?? 'http://127.0.0.1:42069/graphql';
|
||||||
const LOCAL_TXNBOT_URL = env.VITE_TXNBOT_BASE_SEPOLIA_LOCAL_FORK ?? "http://127.0.0.1:43069";
|
const LOCAL_TXNBOT_URL = env.VITE_TXNBOT_BASE_SEPOLIA_LOCAL_FORK ?? 'http://127.0.0.1:43069';
|
||||||
const LOCAL_RPC_URL = env.VITE_LOCAL_RPC_URL ?? "/rpc/anvil";
|
const LOCAL_RPC_URL = env.VITE_LOCAL_RPC_URL ?? '/rpc/anvil';
|
||||||
|
|
||||||
const localContracts = (deploymentsLocal as any)?.contracts ?? {};
|
interface DeploymentContracts {
|
||||||
const localInfra = (deploymentsLocal as any)?.infrastructure ?? {};
|
Kraiken?: string;
|
||||||
const LOCAL_KRAIKEN = (env.VITE_KRAIKEN_ADDRESS ?? localContracts.Kraiken ?? "").trim();
|
Stake?: string;
|
||||||
const LOCAL_STAKE = (env.VITE_STAKE_ADDRESS ?? localContracts.Stake ?? "").trim();
|
LiquidityManager?: string;
|
||||||
const LOCAL_LM = (env.VITE_LIQUIDITY_MANAGER ?? localContracts.LiquidityManager ?? "").trim();
|
}
|
||||||
const LOCAL_WETH = (env.VITE_LOCAL_WETH ?? localInfra.weth ?? "0x4200000000000000000000000000000000000006").trim();
|
|
||||||
const LOCAL_ROUTER = (env.VITE_SWAP_ROUTER ?? "0x94cC0AaC535CCDB3C01d6787D6413C739ae12bc4").trim();
|
interface DeploymentInfrastructure {
|
||||||
|
weth?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const localContracts = (deploymentsLocal as { contracts?: DeploymentContracts })?.contracts ?? {};
|
||||||
|
const localInfra = (deploymentsLocal as { infrastructure?: DeploymentInfrastructure })?.infrastructure ?? {};
|
||||||
|
const LOCAL_KRAIKEN = (env.VITE_KRAIKEN_ADDRESS ?? localContracts.Kraiken ?? '').trim();
|
||||||
|
const LOCAL_STAKE = (env.VITE_STAKE_ADDRESS ?? localContracts.Stake ?? '').trim();
|
||||||
|
const LOCAL_LM = (env.VITE_LIQUIDITY_MANAGER ?? localContracts.LiquidityManager ?? '').trim();
|
||||||
|
const LOCAL_WETH = (env.VITE_LOCAL_WETH ?? localInfra.weth ?? '0x4200000000000000000000000000000000000006').trim();
|
||||||
|
const LOCAL_ROUTER = (env.VITE_SWAP_ROUTER ?? '0x94cC0AaC535CCDB3C01d6787D6413C739ae12bc4').trim();
|
||||||
|
|
||||||
function detectDefaultChainId(): number {
|
function detectDefaultChainId(): number {
|
||||||
const envValue = import.meta.env.VITE_DEFAULT_CHAIN_ID;
|
const envValue = import.meta.env.VITE_DEFAULT_CHAIN_ID;
|
||||||
if (envValue) {
|
if (envValue) {
|
||||||
const parsed = Number(envValue);
|
const parsed = Number(envValue);
|
||||||
if (Number.isFinite(parsed)) {
|
if (Number.isFinite(parsed)) {
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== 'undefined') {
|
||||||
const host = window.location.hostname.toLowerCase();
|
const host = window.location.hostname.toLowerCase();
|
||||||
if (host === "localhost" || host === "127.0.0.1" || host === "::1") {
|
if (host === 'localhost' || host === '127.0.0.1' || host === '::1') {
|
||||||
return 31337;
|
return 31337;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 84532;
|
return 84532;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DEFAULT_CHAIN_ID = detectDefaultChainId();
|
export const DEFAULT_CHAIN_ID = detectDefaultChainId();
|
||||||
|
|
||||||
export const chainsData = [
|
export const chainsData = [
|
||||||
{
|
{
|
||||||
// local base sepolia fork
|
// local base sepolia fork
|
||||||
id: 31337,
|
id: 31337,
|
||||||
graphql: LOCAL_PONDER_URL,
|
graphql: LOCAL_PONDER_URL,
|
||||||
path: "local",
|
path: 'local',
|
||||||
stake: LOCAL_STAKE,
|
stake: LOCAL_STAKE,
|
||||||
harb: LOCAL_KRAIKEN,
|
harb: LOCAL_KRAIKEN,
|
||||||
uniswap: "",
|
uniswap: '',
|
||||||
cheats: {
|
cheats: {
|
||||||
weth: LOCAL_WETH,
|
weth: LOCAL_WETH,
|
||||||
swapRouter: LOCAL_ROUTER,
|
swapRouter: LOCAL_ROUTER,
|
||||||
liquidityManager: LOCAL_LM,
|
liquidityManager: LOCAL_LM,
|
||||||
txnBot: LOCAL_TXNBOT_URL,
|
txnBot: LOCAL_TXNBOT_URL,
|
||||||
rpc: LOCAL_RPC_URL,
|
rpc: LOCAL_RPC_URL,
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
// sepolia
|
{
|
||||||
id: 11155111,
|
// sepolia
|
||||||
graphql: import.meta.env.VITE_PONDER_SEPOLIA ?? "",
|
id: 11155111,
|
||||||
path: "sepolia",
|
graphql: import.meta.env.VITE_PONDER_SEPOLIA ?? '',
|
||||||
stake: "0xCd21a41a137BCAf8743E47D048F57D92398f7Da9",
|
path: 'sepolia',
|
||||||
harb: "0x087F256D11fe533b0c7d372e44Ee0F9e47C89dF9",
|
stake: '0xCd21a41a137BCAf8743E47D048F57D92398f7Da9',
|
||||||
uniswap: "https://app.uniswap.org/swap?chain=mainnet&inputCurrency=NATIVE",
|
harb: '0x087F256D11fe533b0c7d372e44Ee0F9e47C89dF9',
|
||||||
cheats: null
|
uniswap: 'https://app.uniswap.org/swap?chain=mainnet&inputCurrency=NATIVE',
|
||||||
}, {
|
cheats: null,
|
||||||
// base-sepolia (local dev default)
|
},
|
||||||
id: 84532,
|
{
|
||||||
graphql: import.meta.env.VITE_PONDER_BASE_SEPOLIA_LOCAL_FORK ?? LOCAL_PONDER_URL,
|
// base-sepolia (local dev default)
|
||||||
path: "sepoliabase",
|
id: 84532,
|
||||||
stake: "0xe28020BCdEeAf2779dd47c670A8eFC2973316EE2",
|
graphql: import.meta.env.VITE_PONDER_BASE_SEPOLIA_LOCAL_FORK ?? LOCAL_PONDER_URL,
|
||||||
harb: "0x22c264Ecf8D4E49D1E3CabD8DD39b7C4Ab51C1B8",
|
path: 'sepoliabase',
|
||||||
uniswap: "https://app.uniswap.org/swap?chain=mainnet&inputCurrency=NATIVE",
|
stake: '0xe28020BCdEeAf2779dd47c670A8eFC2973316EE2',
|
||||||
cheats: {
|
harb: '0x22c264Ecf8D4E49D1E3CabD8DD39b7C4Ab51C1B8',
|
||||||
weth: "0x4200000000000000000000000000000000000006",
|
uniswap: 'https://app.uniswap.org/swap?chain=mainnet&inputCurrency=NATIVE',
|
||||||
swapRouter: "0x94cC0AaC535CCDB3C01d6787D6413C739ae12bc4",
|
cheats: {
|
||||||
txnBot: import.meta.env.VITE_TXNBOT_BASE_SEPOLIA_LOCAL_FORK ?? LOCAL_TXNBOT_URL
|
weth: '0x4200000000000000000000000000000000000006',
|
||||||
}
|
swapRouter: '0x94cC0AaC535CCDB3C01d6787D6413C739ae12bc4',
|
||||||
|
txnBot: import.meta.env.VITE_TXNBOT_BASE_SEPOLIA_LOCAL_FORK ?? LOCAL_TXNBOT_URL,
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
// base mainnet
|
{
|
||||||
id: 8453,
|
// base mainnet
|
||||||
graphql: import.meta.env.VITE_PONDER_BASE ?? "",
|
id: 8453,
|
||||||
path: "base",
|
graphql: import.meta.env.VITE_PONDER_BASE ?? '',
|
||||||
stake: "0xed70707fab05d973ad41eae8d17e2bcd36192cfc",
|
path: 'base',
|
||||||
harb: "0x45caa5929f6ee038039984205bdecf968b954820",
|
stake: '0xed70707fab05d973ad41eae8d17e2bcd36192cfc',
|
||||||
uniswap: "https://app.uniswap.org/swap?chain=mainnet&inputCurrency=NATIVE",
|
harb: '0x45caa5929f6ee038039984205bdecf968b954820',
|
||||||
cheats: {
|
uniswap: 'https://app.uniswap.org/swap?chain=mainnet&inputCurrency=NATIVE',
|
||||||
weth: "0x4200000000000000000000000000000000000006",
|
cheats: {
|
||||||
swapRouter: "",
|
weth: '0x4200000000000000000000000000000000000006',
|
||||||
txnBot: import.meta.env.VITE_TXNBOT_BASE ?? ""
|
swapRouter: '',
|
||||||
}
|
txnBot: import.meta.env.VITE_TXNBOT_BASE ?? '',
|
||||||
},
|
},
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export function getChain(id:number){
|
export function getChain(id: number) {
|
||||||
return chainsData.find((obj) => obj.id === id)
|
return chainsData.find(obj => obj.id === id);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,18 @@
|
||||||
import { ref, onMounted, onUnmounted, reactive, computed, watch } from "vue";
|
import { ref } from 'vue';
|
||||||
import { config } from "@/wagmi";
|
import { config } from '@/wagmi';
|
||||||
import { type WatchEventReturnType } from "viem";
|
import { getAccount, readContract, writeContract, waitForTransactionReceipt, getChainId } from '@wagmi/core';
|
||||||
import {
|
import type { Config } from '@wagmi/core';
|
||||||
getAccount,
|
import { KraikenAbi } from 'kraiken-lib';
|
||||||
watchContractEvent,
|
import { type Abi, type Address, type Hash } from 'viem';
|
||||||
readContract,
|
import { StakeContract } from '@/contracts/stake';
|
||||||
writeContract,
|
import { getChain } from '@/config';
|
||||||
waitForTransactionReceipt,
|
import logger from '@/utils/logger';
|
||||||
type WaitForTransactionReceiptParameters,
|
|
||||||
getChainId
|
|
||||||
} from "@wagmi/core";
|
|
||||||
import { KraikenAbi } from "kraiken-lib";
|
|
||||||
import { type Abi, type Address } from "viem";
|
|
||||||
import { StakeContract } from "@/contracts/stake";
|
|
||||||
import {getChain} from "@/config"
|
|
||||||
import logger from "@/utils/logger";
|
|
||||||
// const chain1 = useChain();
|
// const chain1 = useChain();
|
||||||
// console.log("chain1", chain1);
|
// console.log("chain1", chain1);
|
||||||
|
|
||||||
|
|
||||||
interface Contract {
|
interface Contract {
|
||||||
abi: Abi;
|
abi: Abi;
|
||||||
contractAddress: Address;
|
contractAddress: Address;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const allowance = ref();
|
export const allowance = ref();
|
||||||
|
|
@ -33,138 +24,133 @@ export const totalSupply = ref(0n);
|
||||||
// export const HarbContract = await getHarbJson()
|
// export const HarbContract = await getHarbJson()
|
||||||
export let HarbContract = getHarbJson();
|
export let HarbContract = getHarbJson();
|
||||||
|
|
||||||
function getHarbJson(){
|
function getHarbJson() {
|
||||||
console.log("getHarbJson");
|
// console.log("getHarbJson");
|
||||||
|
|
||||||
const chainId = getChainId(config as any);
|
const chainId = getChainId(config as Config);
|
||||||
console.log("chainId", chainId);
|
// console.log("chainId", chainId);
|
||||||
|
|
||||||
const chain = getChain(chainId)
|
const chain = getChain(chainId);
|
||||||
|
|
||||||
return {abi: KraikenAbi as Abi, contractAddress: chain?.harb} as Contract;
|
return { abi: KraikenAbi as Abi, contractAddress: chain?.harb } as Contract;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setHarbContract(){
|
export function setHarbContract() {
|
||||||
console.log("setHarbContract");
|
// console.log("setHarbContract");
|
||||||
|
|
||||||
HarbContract = getHarbJson();
|
HarbContract = getHarbJson();
|
||||||
}
|
}
|
||||||
|
|
||||||
// watch(chainData, async (newQuestion, oldQuestion) => {
|
// watch(chainData, async (newQuestion, oldQuestion) => {
|
||||||
// console.log("log harb update");
|
// console.log("log harb update");
|
||||||
|
|
||||||
// });
|
// });
|
||||||
|
|
||||||
|
|
||||||
export async function getAllowance() {
|
export async function getAllowance() {
|
||||||
logger.contract("getAllowance");
|
logger.contract('getAllowance');
|
||||||
|
|
||||||
const account = getAccount(config as any);
|
const account = getAccount(config as Config);
|
||||||
if (!account.address) {
|
if (!account.address) {
|
||||||
return 0n;
|
return 0n;
|
||||||
}
|
}
|
||||||
const result = await readContract(config as any, {
|
const result = await readContract(config as Config, {
|
||||||
abi: HarbContract.abi,
|
abi: HarbContract.abi,
|
||||||
address: HarbContract.contractAddress,
|
address: HarbContract.contractAddress,
|
||||||
functionName: "allowance",
|
functionName: 'allowance',
|
||||||
args: [account.address, StakeContract.contractAddress],
|
args: [account.address, StakeContract.contractAddress],
|
||||||
});
|
});
|
||||||
allowance.value = result;
|
allowance.value = result;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getMinStake() {
|
export async function getMinStake() {
|
||||||
logger.contract("getMinStake");
|
logger.contract('getMinStake');
|
||||||
|
|
||||||
const result: bigint = await readContract(config as any, {
|
const result: bigint = (await readContract(config as Config, {
|
||||||
abi: HarbContract.abi,
|
abi: HarbContract.abi,
|
||||||
address: HarbContract.contractAddress,
|
address: HarbContract.contractAddress,
|
||||||
functionName: "minStake",
|
functionName: 'minStake',
|
||||||
args: [],
|
args: [],
|
||||||
}) as bigint;
|
})) as bigint;
|
||||||
allowance.value = result;
|
allowance.value = result;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getNonce() {
|
export async function getNonce() {
|
||||||
logger.contract("getNonce");
|
logger.contract('getNonce');
|
||||||
|
|
||||||
const account = getAccount(config as any);
|
const account = getAccount(config as Config);
|
||||||
if (!account.address) {
|
if (!account.address) {
|
||||||
return 0n;
|
return 0n;
|
||||||
}
|
}
|
||||||
console.log("HarbContract.contractAddress", HarbContract.contractAddress);
|
// console.log("HarbContract.contractAddress", HarbContract.contractAddress);
|
||||||
|
|
||||||
const result = await readContract(config as any, {
|
const result = await readContract(config as Config, {
|
||||||
abi: HarbContract.abi,
|
abi: HarbContract.abi,
|
||||||
address: HarbContract.contractAddress,
|
address: HarbContract.contractAddress,
|
||||||
functionName: "nonces",
|
functionName: 'nonces',
|
||||||
args: [account.address],
|
args: [account.address],
|
||||||
});
|
});
|
||||||
nonce.value = result;
|
nonce.value = result;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getName() {
|
export async function getName() {
|
||||||
logger.contract("getName");
|
logger.contract('getName');
|
||||||
|
|
||||||
|
const result = await readContract(config as Config, {
|
||||||
const result = await readContract(config as any, {
|
abi: HarbContract.abi,
|
||||||
abi: HarbContract.abi,
|
address: HarbContract.contractAddress,
|
||||||
address: HarbContract.contractAddress,
|
functionName: 'name',
|
||||||
functionName: "name",
|
args: [],
|
||||||
args: [],
|
});
|
||||||
});
|
name.value = result;
|
||||||
name.value = result;
|
|
||||||
|
return result as string;
|
||||||
return result as string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function approve(amount: bigint): Promise<Hash> {
|
||||||
|
const account = getAccount(config as Config);
|
||||||
|
if (!account.address) {
|
||||||
|
throw new Error('no address found');
|
||||||
|
}
|
||||||
|
const result = await writeContract(config as Config, {
|
||||||
|
abi: HarbContract.abi,
|
||||||
|
address: HarbContract.contractAddress,
|
||||||
|
functionName: 'approve',
|
||||||
|
args: [StakeContract.contractAddress, amount],
|
||||||
|
});
|
||||||
|
// console.log("result", result);
|
||||||
|
await waitForTransactionReceipt(config as Config, {
|
||||||
|
hash: result,
|
||||||
|
});
|
||||||
|
// console.log("transactionReceipt", transactionReceipt);
|
||||||
|
|
||||||
export async function approve(amount: bigint): Promise<any> {
|
return result;
|
||||||
const account = getAccount(config as any);
|
|
||||||
if (!account.address) {
|
|
||||||
throw new Error("no address found");
|
|
||||||
}
|
|
||||||
const result = await writeContract(config as any, {
|
|
||||||
abi: HarbContract.abi,
|
|
||||||
address: HarbContract.contractAddress,
|
|
||||||
functionName: "approve",
|
|
||||||
args: [StakeContract.contractAddress, amount],
|
|
||||||
});
|
|
||||||
console.log("result", result);
|
|
||||||
const transactionReceipt = waitForTransactionReceipt(config as any, {
|
|
||||||
hash: result,
|
|
||||||
});
|
|
||||||
console.log("transactionReceipt", transactionReceipt);
|
|
||||||
|
|
||||||
return transactionReceipt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//claim
|
//claim
|
||||||
export async function claimUbi(address: Address): Promise<any> {
|
export async function claimUbi(address: Address): Promise<Hash> {
|
||||||
const result = await writeContract(config as any, {
|
const result = await writeContract(config as Config, {
|
||||||
abi: HarbContract.abi,
|
abi: HarbContract.abi,
|
||||||
address: HarbContract.contractAddress,
|
address: HarbContract.contractAddress,
|
||||||
functionName: "claimUbi",
|
functionName: 'claimUbi',
|
||||||
args: [address],
|
args: [address],
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export async function getTotalSupply() {
|
export async function getTotalSupply() {
|
||||||
logger.contract("getTotalSupply");
|
logger.contract('getTotalSupply');
|
||||||
|
|
||||||
|
const result = await readContract(config as Config, {
|
||||||
const result = await readContract(config as any, {
|
abi: HarbContract.abi,
|
||||||
abi: HarbContract.abi,
|
address: HarbContract.contractAddress,
|
||||||
address: HarbContract.contractAddress,
|
functionName: 'totalSupply',
|
||||||
functionName: "totalSupply",
|
args: [],
|
||||||
args: [],
|
});
|
||||||
});
|
totalSupply.value = result as bigint;
|
||||||
totalSupply.value = result as bigint;
|
return result;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,164 +1,157 @@
|
||||||
import { ref } from "vue";
|
import { ref } from 'vue';
|
||||||
import { config } from "@/wagmi";
|
import { config } from '@/wagmi';
|
||||||
import { readContract, writeContract, getChainId } from "@wagmi/core";
|
import { readContract, writeContract, getChainId } from '@wagmi/core';
|
||||||
import { StakeAbi } from "kraiken-lib";
|
import type { Config } from '@wagmi/core';
|
||||||
import { type Abi, type Address } from "viem";
|
import { StakeAbi } from 'kraiken-lib';
|
||||||
import {getChain} from "@/config"
|
import { type Abi, type Address, type Hash, type Hex } from 'viem';
|
||||||
import logger from "@/utils/logger";
|
import { getChain } from '@/config';
|
||||||
|
import logger from '@/utils/logger';
|
||||||
|
|
||||||
const TAX_FLOOR_DURATION = 60 * 60 * 24 * 3;
|
const TAX_FLOOR_DURATION = 60 * 60 * 24 * 3;
|
||||||
interface Contract {
|
interface Contract {
|
||||||
abi: Abi;
|
abi: Abi;
|
||||||
contractAddress: Address;
|
contractAddress: Address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const minStake = ref();
|
export const minStake = ref();
|
||||||
export const totalSupply = ref(0n);
|
export const totalSupply = ref(0n);
|
||||||
export const outstandingSupply = ref(0n)
|
export const outstandingSupply = ref(0n);
|
||||||
|
|
||||||
|
export let StakeContract = getStakeJson();
|
||||||
|
|
||||||
export let StakeContract = getStakeJson()
|
function getStakeJson() {
|
||||||
|
const chainId = getChainId(config as Config);
|
||||||
|
// console.log("chainId", chainId);
|
||||||
|
|
||||||
function getStakeJson(){
|
const chain = getChain(chainId);
|
||||||
const chainId = getChainId(config as any);
|
|
||||||
console.log("chainId", chainId);
|
|
||||||
|
|
||||||
const chain = getChain(chainId)
|
return { abi: StakeAbi as Abi, contractAddress: chain?.stake } as Contract;
|
||||||
|
|
||||||
return {abi: StakeAbi as Abi, contractAddress: chain?.stake} as Contract;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setStakeContract(){
|
export function setStakeContract() {
|
||||||
logger.contract("setStakeContract")
|
logger.contract('setStakeContract');
|
||||||
StakeContract = getStakeJson();
|
StakeContract = getStakeJson();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function snatchService(assets: bigint, receiver: Address, taxRate: number, positionsToSnatch: Array<bigint>) {
|
||||||
|
// console.log("StakeContract", StakeContract);
|
||||||
|
|
||||||
export async function snatchService(
|
const result = await writeContract(config as Config, {
|
||||||
assets: BigInt,
|
abi: StakeContract.abi,
|
||||||
receiver: Address,
|
address: StakeContract.contractAddress,
|
||||||
taxRate: Number,
|
functionName: 'snatch',
|
||||||
positionsToSnatch: Array<BigInt>
|
args: [assets, receiver, taxRate, positionsToSnatch],
|
||||||
) {
|
});
|
||||||
console.log("StakeContract", StakeContract);
|
return result;
|
||||||
|
|
||||||
const result = await writeContract(config as any, {
|
|
||||||
abi: StakeContract.abi,
|
|
||||||
address: StakeContract.contractAddress,
|
|
||||||
functionName: "snatch",
|
|
||||||
args: [assets, receiver, taxRate, positionsToSnatch],
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function exitPosition(positionId: bigint): Promise<any> {
|
export async function exitPosition(positionId: bigint): Promise<Hash> {
|
||||||
const result = await writeContract(config as any, {
|
const result = await writeContract(config as Config, {
|
||||||
abi: StakeContract.abi,
|
abi: StakeContract.abi,
|
||||||
address: StakeContract.contractAddress,
|
address: StakeContract.contractAddress,
|
||||||
functionName: "exitPosition",
|
functionName: 'exitPosition',
|
||||||
args: [positionId],
|
args: [positionId],
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
//changeTax
|
//changeTax
|
||||||
export async function changeTax(positionId: bigint, taxRate: number): Promise<any> {
|
export async function changeTax(positionId: bigint, taxRate: number): Promise<Hash> {
|
||||||
const result = await writeContract(config as any, {
|
const result = await writeContract(config as Config, {
|
||||||
abi: StakeContract.abi,
|
abi: StakeContract.abi,
|
||||||
address: StakeContract.contractAddress,
|
address: StakeContract.contractAddress,
|
||||||
functionName: "changeTax",
|
functionName: 'changeTax',
|
||||||
args: [positionId, taxRate],
|
args: [positionId, taxRate],
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* snatch/stake with permit
|
* snatch/stake with permit
|
||||||
*/
|
*/
|
||||||
export async function permitAndSnatch(
|
export async function permitAndSnatch(
|
||||||
assets: BigInt,
|
assets: bigint,
|
||||||
receiver: Address,
|
receiver: Address,
|
||||||
taxRate: number,
|
taxRate: number,
|
||||||
positionsToSnatch: Array<BigInt>,
|
positionsToSnatch: Array<bigint>,
|
||||||
deadline: BigInt,
|
deadline: bigint,
|
||||||
v: any,
|
v: number,
|
||||||
r: any,
|
r: Hex,
|
||||||
s: any
|
s: Hex
|
||||||
) {
|
) {
|
||||||
console.log("permitAndSnatch", assets, receiver, taxRate, positionsToSnatch, deadline, v, r, s);
|
// console.log("permitAndSnatch", assets, receiver, taxRate, positionsToSnatch, deadline, v, r, s);
|
||||||
|
|
||||||
const result = await writeContract(config as any, {
|
const result = await writeContract(config as Config, {
|
||||||
abi: StakeContract.abi,
|
abi: StakeContract.abi,
|
||||||
address: StakeContract.contractAddress,
|
address: StakeContract.contractAddress,
|
||||||
functionName: "permitAndSnatch",
|
functionName: 'permitAndSnatch',
|
||||||
args: [assets, receiver, taxRate, positionsToSnatch, deadline, v, r, s],
|
args: [assets, receiver, taxRate, positionsToSnatch, deadline, v, r, s],
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getTotalSupply() {
|
export async function getTotalSupply() {
|
||||||
logger.contract("getTotalSupply")
|
logger.contract('getTotalSupply');
|
||||||
await setStakeContract();
|
await setStakeContract();
|
||||||
const result = await readContract(config as any, {
|
const result = await readContract(config as Config, {
|
||||||
abi: StakeContract.abi,
|
abi: StakeContract.abi,
|
||||||
address: StakeContract.contractAddress,
|
address: StakeContract.contractAddress,
|
||||||
functionName: "totalSupply",
|
functionName: 'totalSupply',
|
||||||
args: [],
|
args: [],
|
||||||
});
|
});
|
||||||
console.log("result", result);
|
// console.log("result", result);
|
||||||
|
|
||||||
totalSupply.value = result as bigint;
|
totalSupply.value = result as bigint;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getOutstandingSupply() {
|
export async function getOutstandingSupply() {
|
||||||
logger.contract("getOutstandingSupply")
|
logger.contract('getOutstandingSupply');
|
||||||
|
|
||||||
const result = await readContract(config as any, {
|
const result = await readContract(config as Config, {
|
||||||
abi: StakeContract.abi,
|
abi: StakeContract.abi,
|
||||||
address: StakeContract.contractAddress,
|
address: StakeContract.contractAddress,
|
||||||
functionName: "outstandingStake",
|
functionName: 'outstandingStake',
|
||||||
args: [],
|
args: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
outstandingSupply.value = result as bigint;
|
outstandingSupply.value = result as bigint;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getTaxDue(positionID: bigint) {
|
export async function getTaxDue(positionID: bigint) {
|
||||||
logger.contract("getTaxDue")
|
logger.contract('getTaxDue');
|
||||||
const result = await readContract(config as any, {
|
const result = await readContract(config as Config, {
|
||||||
abi: StakeContract.abi,
|
abi: StakeContract.abi,
|
||||||
address: StakeContract.contractAddress,
|
address: StakeContract.contractAddress,
|
||||||
functionName: "taxDue",
|
functionName: 'taxDue',
|
||||||
args: [positionID, TAX_FLOOR_DURATION],
|
args: [positionID, TAX_FLOOR_DURATION],
|
||||||
});
|
});
|
||||||
return result as bigint;
|
return result as bigint;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function payTax(positionID: bigint) {
|
export async function payTax(positionID: bigint) {
|
||||||
console.log("payTax", positionID);
|
// console.log("payTax", positionID);
|
||||||
|
|
||||||
const result = await writeContract(config as any, {
|
const result = await writeContract(config as Config, {
|
||||||
abi: StakeContract.abi,
|
abi: StakeContract.abi,
|
||||||
address: StakeContract.contractAddress,
|
address: StakeContract.contractAddress,
|
||||||
functionName: "payTax",
|
functionName: 'payTax',
|
||||||
args: [positionID],
|
args: [positionID],
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function assetsToShares(asset: bigint) {
|
export async function assetsToShares(asset: bigint) {
|
||||||
console.log("assetsToShares", asset);
|
// console.log("assetsToShares", asset);
|
||||||
|
|
||||||
const result = await readContract(config as any, {
|
const result = await readContract(config as Config, {
|
||||||
abi: StakeContract.abi,
|
abi: StakeContract.abi,
|
||||||
address: StakeContract.contractAddress,
|
address: StakeContract.contractAddress,
|
||||||
functionName: "assetsToShares",
|
functionName: 'assetsToShares',
|
||||||
args: [asset],
|
args: [asset],
|
||||||
});
|
});
|
||||||
return result as bigint;
|
return result as bigint;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,25 @@
|
||||||
|
import type { DirectiveBinding } from 'vue';
|
||||||
|
|
||||||
|
interface ClickOutsideElement extends HTMLElement {
|
||||||
|
clickOutsideEvent?: (event: MouseEvent) => void;
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
beforeMount(el: any, binding: any) {
|
beforeMount(el: ClickOutsideElement, binding: DirectiveBinding<(event: MouseEvent) => void>) {
|
||||||
el.clickOutsideEvent = function (event: any) {
|
el.clickOutsideEvent = function (event: MouseEvent) {
|
||||||
// Check if the clicked element is neither the element
|
// Check if the clicked element is neither the element
|
||||||
// to which the directive is applied nor its child
|
// to which the directive is applied nor its child
|
||||||
if (!(el === event.target || el.contains(event.target))) {
|
if (!(el === event.target || el.contains(event.target as Node))) {
|
||||||
// Invoke the provided method
|
// Invoke the provided method
|
||||||
binding.value(event);
|
binding.value(event);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
document.addEventListener("click", el.clickOutsideEvent);
|
document.addEventListener('click', el.clickOutsideEvent);
|
||||||
},
|
},
|
||||||
unmounted(el: any) {
|
unmounted(el: ClickOutsideElement) {
|
||||||
// Remove the event listener when the bound element is unmounted
|
// Remove the event listener when the bound element is unmounted
|
||||||
document.removeEventListener("click", el.clickOutsideEvent);
|
if (el.clickOutsideEvent) {
|
||||||
},
|
document.removeEventListener('click', el.clickOutsideEvent);
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<main>
|
<main>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,57 +1,53 @@
|
||||||
<!-- src/layouts/NavbarLayout.vue -->
|
<!-- src/layouts/NavbarLayout.vue -->
|
||||||
<template>
|
<template>
|
||||||
<navbar></navbar>
|
<NavbarHeader></NavbarHeader>
|
||||||
<slideout-panel v-model="showPanel">
|
<SlideoutPanel v-model="showPanel">
|
||||||
<connect-wallet></connect-wallet>
|
<ConnectWallet></ConnectWallet>
|
||||||
<!-- <f-footer :dark="true"></f-footer> -->
|
<!-- <f-footer :dark="true"></f-footer> -->
|
||||||
</slideout-panel>
|
</SlideoutPanel>
|
||||||
<div class="navbar-layout">
|
<div class="navbar-layout">
|
||||||
<main>
|
<main>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
<!-- <footer>
|
<!-- <footer>
|
||||||
<f-footer :dark="darkTheme"></f-footer>
|
<f-footer :dark="darkTheme"></f-footer>
|
||||||
</footer> -->
|
</footer> -->
|
||||||
<div class="mobile-navigation-bar" v-if="isMobile">
|
<div class="mobile-navigation-bar" v-if="isMobile">
|
||||||
<div class="mobile-navigation-tab" @click="router.push('/')">
|
<div class="mobile-navigation-tab" @click="router.push('/')">
|
||||||
<div class="mobile-navigation-tab__icon">
|
<div class="mobile-navigation-tab__icon">
|
||||||
<icon-home />
|
<IconHome />
|
||||||
</div>
|
</div>
|
||||||
<div class="mobile-navigation-tab__title">Stake</div>
|
<div class="mobile-navigation-tab__title">Stake</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mobile-navigation-tab" @click="openDocs">
|
<div class="mobile-navigation-tab" @click="openDocs">
|
||||||
<div class="mobile-navigation-tab__icon">
|
<div class="mobile-navigation-tab__icon">
|
||||||
<icon-docs />
|
<IconDocs />
|
||||||
</div>
|
</div>
|
||||||
<div class="mobile-navigation-tab__title">Docs</div>
|
<div class="mobile-navigation-tab__title">Docs</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Navbar from "@/components/layouts/Navbar.vue";
|
import NavbarHeader from '@/components/layouts/NavbarHeader.vue';
|
||||||
import SlideoutPanel from "@/components/layouts/SlideoutPanel.vue";
|
import SlideoutPanel from '@/components/layouts/SlideoutPanel.vue';
|
||||||
import ConnectWallet from "@/components/layouts/ConnectWallet.vue";
|
import ConnectWallet from '@/components/layouts/ConnectWallet.vue';
|
||||||
import ThemeToggle from "@/components/layouts/ThemeToggle.vue";
|
import { useMobile } from '@/composables/useMobile';
|
||||||
import FFooter from "@/components/layouts/FFooter.vue";
|
import IconHome from '@/components/icons/IconHome.vue';
|
||||||
import { useMobile } from "@/composables/useMobile";
|
import IconDocs from '@/components/icons/IconDocs.vue';
|
||||||
import { useDark } from "@/composables/useDark";
|
import { ref, provide } from 'vue';
|
||||||
import IconHome from "@/components/icons/IconHome.vue";
|
import { useRouter } from 'vue-router';
|
||||||
import IconDocs from "@/components/icons/IconDocs.vue";
|
|
||||||
import { ref, provide } from "vue";
|
|
||||||
import {useRouter } from "vue-router"
|
|
||||||
const showPanel = ref(false);
|
const showPanel = ref(false);
|
||||||
const { darkTheme } = useDark();
|
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter();
|
||||||
|
|
||||||
const isMobile = useMobile();
|
const isMobile = useMobile();
|
||||||
provide("isMobile", isMobile);
|
provide('isMobile', isMobile);
|
||||||
provide("showPanel", showPanel);
|
provide('showPanel', showPanel);
|
||||||
|
|
||||||
function openDocs(){
|
function openDocs() {
|
||||||
window.open("https://emberspirit007.github.io/KraikenLanding/#/docs/Introduction")
|
window.open('https://emberspirit007.github.io/KraikenLanding/#/docs/Introduction');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -85,4 +81,4 @@ function openDocs(){
|
||||||
fill: white
|
fill: white
|
||||||
.mobile-navigation-tab__title
|
.mobile-navigation-tab__title
|
||||||
font-size: 12px
|
font-size: 12px
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,25 @@
|
||||||
import { WagmiPlugin } from "@wagmi/vue";
|
import { WagmiPlugin } from '@wagmi/vue';
|
||||||
import { QueryClient, VueQueryPlugin } from "@tanstack/vue-query";
|
import { QueryClient, VueQueryPlugin } from '@tanstack/vue-query';
|
||||||
import { createApp } from "vue";
|
import { createApp } from 'vue';
|
||||||
import { config } from "./wagmi";
|
import { config } from './wagmi';
|
||||||
import ClickOutSide from "@/directives/ClickOutsideDirective";
|
import ClickOutSide from '@/directives/ClickOutsideDirective';
|
||||||
import router from "./router";
|
import router from './router';
|
||||||
|
|
||||||
import App from "./App.vue";
|
import App from './App.vue';
|
||||||
import "./assets/styles/main.sass";
|
import './assets/styles/main.sass';
|
||||||
import Toast from "vue-toastification";
|
import Toast from 'vue-toastification';
|
||||||
import "vue-toastification/dist/index.css";
|
import 'vue-toastification/dist/index.css';
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
|
|
||||||
app.directive("click-outside", ClickOutSide);
|
app.directive('click-outside', ClickOutSide);
|
||||||
|
|
||||||
app.use(WagmiPlugin, { config });
|
app.use(WagmiPlugin, { config });
|
||||||
app.use(VueQueryPlugin, { queryClient });
|
app.use(VueQueryPlugin, { queryClient });
|
||||||
app.use(router);
|
app.use(router);
|
||||||
app.use(Toast, {
|
app.use(Toast, {
|
||||||
transition: "Vue-Toastification__fade",
|
transition: 'Vue-Toastification__fade',
|
||||||
containerClassName: "harb-toast-container",
|
containerClassName: 'harb-toast-container',
|
||||||
});
|
});
|
||||||
app.mount("#app");
|
app.mount('#app');
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,43 @@
|
||||||
import { createRouter, createWebHashHistory } from "vue-router";
|
import { createRouter, createWebHashHistory } from 'vue-router';
|
||||||
import HomeView from "../views/HomeView.vue";
|
|
||||||
import { authGuard } from './authGuard';
|
import { authGuard } from './authGuard';
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHashHistory(),
|
history: createWebHashHistory(),
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: "/",
|
path: '/',
|
||||||
name: "home",
|
name: 'home',
|
||||||
redirect: "/stake",
|
redirect: '/stake',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/stake",
|
path: '/stake',
|
||||||
name: "stake",
|
name: 'stake',
|
||||||
meta: {
|
meta: {
|
||||||
title: "Stake",
|
title: 'Stake',
|
||||||
group: "navbar",
|
group: 'navbar',
|
||||||
layout: 'NavbarLayout'
|
layout: 'NavbarLayout',
|
||||||
},
|
},
|
||||||
beforeEnter: authGuard,
|
beforeEnter: authGuard,
|
||||||
component: () => import("../views/StakeView.vue"),
|
component: () => import('../views/StakeView.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/login",
|
path: '/login',
|
||||||
name: "login",
|
name: 'login',
|
||||||
meta: {
|
meta: {
|
||||||
layout: 'DefaultLayout'
|
layout: 'DefaultLayout',
|
||||||
, },
|
},
|
||||||
component: () => import("../views/LoginView.vue"),
|
component: () => import('../views/LoginView.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/cheats",
|
path: '/cheats',
|
||||||
name: "cheats",
|
name: 'cheats',
|
||||||
meta: {
|
meta: {
|
||||||
title: "Cheats",
|
title: 'Cheats',
|
||||||
layout: 'NavbarLayout'
|
layout: 'NavbarLayout',
|
||||||
},
|
},
|
||||||
component: () => import("../views/CheatsView.vue"),
|
component: () => import('../views/CheatsView.vue'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|
|
||||||
|
|
@ -1,85 +1,83 @@
|
||||||
import { type Address, type TypedDataDomain, type Hex, slice, hexToNumber, hexToBigInt, recoverAddress } from "viem";
|
import { type Address, type TypedDataDomain, slice, hexToBigInt } from 'viem';
|
||||||
|
|
||||||
|
|
||||||
export function createPermitObject(
|
export function createPermitObject(
|
||||||
verifyingContract: Address,
|
verifyingContract: Address,
|
||||||
fromAddress: Address,
|
fromAddress: Address,
|
||||||
spender: Address,
|
spender: Address,
|
||||||
nonce: BigInt,
|
nonce: bigint,
|
||||||
deadline: BigInt,
|
deadline: bigint,
|
||||||
value: BigInt,
|
value: bigint,
|
||||||
chain: number,
|
chain: number,
|
||||||
domainName: string
|
domainName: string
|
||||||
) {
|
) {
|
||||||
const message = {
|
const message = {
|
||||||
owner: fromAddress,
|
owner: fromAddress,
|
||||||
spender: spender,
|
spender: spender,
|
||||||
nonce: nonce,
|
nonce: nonce,
|
||||||
deadline: deadline,
|
deadline: deadline,
|
||||||
value: value,
|
value: value,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const domainType = [
|
const domainType = [
|
||||||
{ name: "name", type: "string" },
|
{ name: 'name', type: 'string' },
|
||||||
{ name: "version", type: "string" },
|
{ name: 'version', type: 'string' },
|
||||||
{ name: "chainId", type: "uint256" },
|
{ name: 'chainId', type: 'uint256' },
|
||||||
{ name: "verifyingContract", type: "address" },
|
{ name: 'verifyingContract', type: 'address' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const primaryType: "EIP712Domain" | "Permit" = "Permit";
|
const primaryType: 'EIP712Domain' | 'Permit' = 'Permit';
|
||||||
|
|
||||||
const types = {
|
const types = {
|
||||||
EIP712Domain: domainType,
|
EIP712Domain: domainType,
|
||||||
Permit: [
|
Permit: [
|
||||||
{
|
{
|
||||||
name: "owner",
|
name: 'owner',
|
||||||
type: "address",
|
type: 'address',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "spender",
|
name: 'spender',
|
||||||
type: "address",
|
type: 'address',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "value",
|
name: 'value',
|
||||||
type: "uint256",
|
type: 'uint256',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nonce",
|
name: 'nonce',
|
||||||
type: "uint256",
|
type: 'uint256',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "deadline",
|
name: 'deadline',
|
||||||
type: "uint256",
|
type: 'uint256',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const domain: TypedDataDomain | undefined = {
|
const domain: TypedDataDomain | undefined = {
|
||||||
name: domainName,
|
name: domainName,
|
||||||
version: "1",
|
version: '1',
|
||||||
chainId: chain,
|
chainId: chain,
|
||||||
verifyingContract: verifyingContract,
|
verifyingContract: verifyingContract,
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
types,
|
types,
|
||||||
message,
|
message,
|
||||||
domain,
|
domain,
|
||||||
primaryType,
|
primaryType,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSignatureRSV2(sig: `0x${string}`) {
|
export function getSignatureRSV2(sig: `0x${string}`) {
|
||||||
// splits the signature to r, s, and v values.
|
// splits the signature to r, s, and v values.
|
||||||
// const pureSig = sig.replace("0x", "");
|
// const pureSig = sig.replace("0x", "");
|
||||||
const [r, s, v] = [slice(sig, 0, 32), slice(sig, 32, 64), slice(sig, 64, 65)];
|
const [r, s, v] = [slice(sig, 0, 32), slice(sig, 32, 64), slice(sig, 64, 65)];
|
||||||
return { r, s, v: Number(v) };
|
return { r, s, v: Number(v) };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSignatureRSV(signature: `0x${string}`) {
|
export function getSignatureRSV(signature: `0x${string}`) {
|
||||||
const r = signature.slice(0, 66) as `0x${string}`;
|
const r = signature.slice(0, 66) as `0x${string}`;
|
||||||
const s = `0x${signature.slice(66, 130)}` as `0x${string}`;
|
const s = `0x${signature.slice(66, 130)}` as `0x${string}`;
|
||||||
const v = hexToBigInt(`0x${signature.slice(130, 132)}`);
|
const v = Number(hexToBigInt(`0x${signature.slice(130, 132)}`));
|
||||||
return { r, s, v };
|
return { r, s, v };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,135 +1,133 @@
|
||||||
var randseed = new Array(4); // Xorshift: [x, y, z, w] 32 bit values
|
const randseed = new Array(4); // Xorshift: [x, y, z, w] 32 bit values
|
||||||
|
|
||||||
interface BlockiesOpt {
|
interface BlockiesOpt {
|
||||||
seed: string;
|
seed: string;
|
||||||
size: number;
|
size: number;
|
||||||
scale: number;
|
scale: number;
|
||||||
color?: string;
|
color?: string;
|
||||||
bgcolor?: string;
|
bgcolor?: string;
|
||||||
spotcolor?: string;
|
spotcolor?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getBlocky(address: string) {
|
export function getBlocky(address: string) {
|
||||||
if (!address || typeof address !== "string" ) {
|
if (!address || typeof address !== 'string') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log("address", address);
|
|
||||||
|
|
||||||
var blockiesData = createIcon({
|
|
||||||
seed: address?.toLowerCase(),
|
|
||||||
size: 8,
|
|
||||||
scale: 4,
|
|
||||||
}).toDataURL();
|
|
||||||
|
|
||||||
return blockiesData;
|
const blockiesData = createIcon({
|
||||||
|
seed: address?.toLowerCase(),
|
||||||
|
size: 8,
|
||||||
|
scale: 4,
|
||||||
|
}).toDataURL();
|
||||||
|
|
||||||
|
return blockiesData;
|
||||||
}
|
}
|
||||||
|
|
||||||
function seedrand(seed: string) {
|
function seedrand(seed: string) {
|
||||||
for (var i = 0; i < randseed.length; i++) {
|
for (let i = 0; i < randseed.length; i++) {
|
||||||
randseed[i] = 0;
|
randseed[i] = 0;
|
||||||
}
|
}
|
||||||
for (var i = 0; i < seed.length; i++) {
|
for (let i = 0; i < seed.length; i++) {
|
||||||
randseed[i % 4] = (randseed[i % 4] << 5) - randseed[i % 4] + seed.charCodeAt(i);
|
randseed[i % 4] = (randseed[i % 4] << 5) - randseed[i % 4] + seed.charCodeAt(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function rand() {
|
function rand() {
|
||||||
// based on Java's String.hashCode(), expanded to 4 32bit values
|
// based on Java's String.hashCode(), expanded to 4 32bit values
|
||||||
var t = randseed[0] ^ (randseed[0] << 11);
|
const t = randseed[0] ^ (randseed[0] << 11);
|
||||||
|
|
||||||
randseed[0] = randseed[1];
|
randseed[0] = randseed[1];
|
||||||
randseed[1] = randseed[2];
|
randseed[1] = randseed[2];
|
||||||
randseed[2] = randseed[3];
|
randseed[2] = randseed[3];
|
||||||
randseed[3] = randseed[3] ^ (randseed[3] >> 19) ^ t ^ (t >> 8);
|
randseed[3] = randseed[3] ^ (randseed[3] >> 19) ^ t ^ (t >> 8);
|
||||||
|
|
||||||
return (randseed[3] >>> 0) / ((1 << 31) >>> 0);
|
return (randseed[3] >>> 0) / ((1 << 31) >>> 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createColor() {
|
function createColor() {
|
||||||
//saturation is the whole color spectrum
|
//saturation is the whole color spectrum
|
||||||
var h = Math.floor(rand() * 360);
|
const h = Math.floor(rand() * 360);
|
||||||
//saturation goes from 40 to 100, it avoids greyish colors
|
//saturation goes from 40 to 100, it avoids greyish colors
|
||||||
var s = rand() * 60 + 40 + "%";
|
const s = rand() * 60 + 40 + '%';
|
||||||
//lightness can be anything from 0 to 100, but probabilities are a bell curve around 50%
|
//lightness can be anything from 0 to 100, but probabilities are a bell curve around 50%
|
||||||
var l = (rand() + rand() + rand() + rand()) * 25 + "%";
|
const l = (rand() + rand() + rand() + rand()) * 25 + '%';
|
||||||
|
|
||||||
var color = "hsl(" + h + "," + s + "," + l + ")";
|
const color = 'hsl(' + h + ',' + s + ',' + l + ')';
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createImageData(size: number) {
|
function createImageData(size: number) {
|
||||||
var width = size; // Only support square icons for now
|
const width = size; // Only support square icons for now
|
||||||
var height = size;
|
const height = size;
|
||||||
|
|
||||||
var dataWidth = Math.ceil(width / 2);
|
const dataWidth = Math.ceil(width / 2);
|
||||||
var mirrorWidth = width - dataWidth;
|
const mirrorWidth = width - dataWidth;
|
||||||
|
|
||||||
var data = [];
|
const data = [];
|
||||||
for (var y = 0; y < height; y++) {
|
for (let y = 0; y < height; y++) {
|
||||||
var row = [];
|
let row = [];
|
||||||
for (var x = 0; x < dataWidth; x++) {
|
for (let x = 0; x < dataWidth; x++) {
|
||||||
// this makes foreground and background color to have a 43% (1/2.3) probability
|
// this makes foreground and background color to have a 43% (1/2.3) probability
|
||||||
// spot color has 13% chance
|
// spot color has 13% chance
|
||||||
row[x] = Math.floor(rand() * 2.3);
|
row[x] = Math.floor(rand() * 2.3);
|
||||||
}
|
}
|
||||||
var r = row.slice(0, mirrorWidth);
|
const r = row.slice(0, mirrorWidth);
|
||||||
r.reverse();
|
r.reverse();
|
||||||
row = row.concat(r);
|
row = row.concat(r);
|
||||||
|
|
||||||
for (var i = 0; i < row.length; i++) {
|
for (let i = 0; i < row.length; i++) {
|
||||||
data.push(row[i]);
|
data.push(row[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildOpts(opts: any) {
|
function buildOpts(opts: Partial<BlockiesOpt>): BlockiesOpt {
|
||||||
var newOpts: any = {};
|
const seed = opts.seed || Math.floor(Math.random() * Math.pow(10, 16)).toString(16);
|
||||||
newOpts.seed = opts.seed || Math.floor(Math.random() * Math.pow(10, 16)).toString(16);
|
seedrand(seed);
|
||||||
|
|
||||||
seedrand(newOpts.seed);
|
return {
|
||||||
|
seed,
|
||||||
newOpts.size = opts.size || 8;
|
size: opts.size || 8,
|
||||||
newOpts.scale = opts.scale || 4;
|
scale: opts.scale || 4,
|
||||||
newOpts.color = opts.color || createColor();
|
color: opts.color || createColor(),
|
||||||
newOpts.bgcolor = opts.bgcolor || createColor();
|
bgcolor: opts.bgcolor || createColor(),
|
||||||
newOpts.spotcolor = opts.spotcolor || createColor();
|
spotcolor: opts.spotcolor || createColor(),
|
||||||
|
};
|
||||||
return newOpts;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderIcon(opts: BlockiesOpt, canvas: HTMLCanvasElement) {
|
function renderIcon(opts: Partial<BlockiesOpt>, canvas: HTMLCanvasElement) {
|
||||||
opts = buildOpts(opts || {});
|
const fullOpts = buildOpts(opts);
|
||||||
var imageData = createImageData(opts.size);
|
const imageData = createImageData(fullOpts.size);
|
||||||
var width = Math.sqrt(imageData.length);
|
const width = Math.sqrt(imageData.length);
|
||||||
|
|
||||||
canvas.width = canvas.height = opts.size * opts.scale;
|
canvas.width = canvas.height = fullOpts.size * fullOpts.scale;
|
||||||
|
|
||||||
var cc = canvas.getContext("2d")!;
|
const cc = canvas.getContext('2d')!;
|
||||||
cc.fillStyle = opts.bgcolor!;
|
cc.fillStyle = fullOpts.bgcolor!;
|
||||||
cc.fillRect(0, 0, canvas.width, canvas.height);
|
cc.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
cc.fillStyle = opts.color!;
|
cc.fillStyle = fullOpts.color!;
|
||||||
|
|
||||||
for (var i = 0; i < imageData.length; i++) {
|
for (let i = 0; i < imageData.length; i++) {
|
||||||
// if data is 0, leave the background
|
// if data is 0, leave the background
|
||||||
if (imageData[i]) {
|
if (imageData[i]) {
|
||||||
var row = Math.floor(i / width);
|
const row = Math.floor(i / width);
|
||||||
var col = i % width;
|
const col = i % width;
|
||||||
|
|
||||||
// if data is 2, choose spot color, if 1 choose foreground
|
// if data is 2, choose spot color, if 1 choose foreground
|
||||||
cc.fillStyle = imageData[i] == 1 ? opts.color! : opts.spotcolor!;
|
cc.fillStyle = imageData[i] == 1 ? fullOpts.color! : fullOpts.spotcolor!;
|
||||||
|
|
||||||
cc.fillRect(col * opts.scale, row * opts.scale, opts.scale, opts.scale);
|
cc.fillRect(col * fullOpts.scale, row * fullOpts.scale, fullOpts.scale, fullOpts.scale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return canvas;
|
return canvas;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createIcon(opts: BlockiesOpt) {
|
export function createIcon(opts: BlockiesOpt) {
|
||||||
var canvas = document.createElement("canvas");
|
const canvas = document.createElement('canvas');
|
||||||
|
|
||||||
renderIcon(opts, canvas);
|
renderIcon(opts, canvas);
|
||||||
|
|
||||||
return canvas;
|
return canvas;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
* @returns Number of shares corresponding to the input assets.
|
* @returns Number of shares corresponding to the input assets.
|
||||||
*/
|
*/
|
||||||
export function assetsToShares(assets: bigint, totalSupply: bigint, harbergTotalSupply: bigint): bigint {
|
export function assetsToShares(assets: bigint, totalSupply: bigint, harbergTotalSupply: bigint): bigint {
|
||||||
return (assets * totalSupply) / harbergTotalSupply;
|
return (assets * totalSupply) / harbergTotalSupply;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -17,5 +17,5 @@ export function assetsToShares(assets: bigint, totalSupply: bigint, harbergTotal
|
||||||
* @returns The equivalent number of Harberg tokens for the given shares.
|
* @returns The equivalent number of Harberg tokens for the given shares.
|
||||||
*/
|
*/
|
||||||
export function sharesToAssets(shares: bigint, totalSupply: bigint, harbergTotalSupply: bigint): bigint {
|
export function sharesToAssets(shares: bigint, totalSupply: bigint, harbergTotalSupply: bigint): bigint {
|
||||||
return (shares * harbergTotalSupply) / totalSupply;
|
return (shares * harbergTotalSupply) / totalSupply;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,64 +1,60 @@
|
||||||
import { formatUnits } from 'viem'
|
import { formatUnits } from 'viem';
|
||||||
|
|
||||||
|
|
||||||
export function getAddressShortName(address: string) {
|
export function getAddressShortName(address: string) {
|
||||||
if (!address) {
|
if (!address) {
|
||||||
return "";
|
return '';
|
||||||
}
|
}
|
||||||
const addressBegin = address.substring(0, 6);
|
const addressBegin = address.substring(0, 6);
|
||||||
const addressEnd = address.substring(address.length - 4, address.length);
|
const addressEnd = address.substring(address.length - 4, address.length);
|
||||||
return addressBegin + "..." + addressEnd;
|
return addressBegin + '...' + addressEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function compactNumber(number: number) {
|
export function compactNumber(number: number) {
|
||||||
return Intl.NumberFormat("en-US", {
|
return Intl.NumberFormat('en-US', {
|
||||||
notation: "compact",
|
notation: 'compact',
|
||||||
// minimumFractionDigits: 2,
|
// minimumFractionDigits: 2,
|
||||||
maximumFractionDigits: 2,
|
maximumFractionDigits: 2,
|
||||||
}).format(number);
|
}).format(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatBigNumber(number: bigint, decimals: number, digits: number = 5) {
|
export function formatBigNumber(number: bigint, decimals: number, digits: number = 5) {
|
||||||
let bigIntNumber = number;
|
let bigIntNumber = number;
|
||||||
if(!bigIntNumber){
|
if (!bigIntNumber) {
|
||||||
bigIntNumber = BigInt(0)
|
bigIntNumber = BigInt(0);
|
||||||
}
|
}
|
||||||
const formattedNumber = Number(formatUnits(bigIntNumber, decimals))
|
const formattedNumber = Number(formatUnits(bigIntNumber, decimals));
|
||||||
if(formattedNumber === 0){
|
if (formattedNumber === 0) {
|
||||||
return "0"
|
return '0';
|
||||||
}
|
}
|
||||||
return formattedNumber.toFixed(digits)
|
return formattedNumber.toFixed(digits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function bigInt2Number(number: bigint, decimals: number) {
|
export function bigInt2Number(number: bigint, decimals: number) {
|
||||||
let bigIntNumber = number;
|
let bigIntNumber = number;
|
||||||
if(!bigIntNumber){
|
if (!bigIntNumber) {
|
||||||
bigIntNumber = BigInt(0)
|
bigIntNumber = BigInt(0);
|
||||||
}
|
}
|
||||||
const formattedNumber = Number(formatUnits(bigIntNumber, decimals))
|
const formattedNumber = Number(formatUnits(bigIntNumber, decimals));
|
||||||
return formattedNumber
|
return formattedNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function InsertCommaNumber(number: any) {
|
export function InsertCommaNumber(number: number) {
|
||||||
if (!number) {
|
if (!number) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
const formattedWithOptions = number.toLocaleString("en-US");
|
const formattedWithOptions = number.toLocaleString('en-US');
|
||||||
return formattedWithOptions;
|
return formattedWithOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatBigIntDivision(nominator: bigint, denominator: bigint, digits: number = 2) {
|
export function formatBigIntDivision(nominator: bigint, denominator: bigint, _digits: number = 2) {
|
||||||
if (!nominator) {
|
if (!nominator) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
let display = nominator.toString();
|
const display = nominator.toString();
|
||||||
const decimal = (Number(denominator) / 10).toString().length;
|
const decimal = (Number(denominator) / 10).toString().length;
|
||||||
|
|
||||||
let [integer, fraction] = [display.slice(0, display.length - decimal), display.slice(display.length - decimal)];
|
const [integer, fraction] = [display.slice(0, display.length - decimal), display.slice(display.length - decimal)];
|
||||||
|
|
||||||
// output type number
|
// output type number
|
||||||
return Number(integer + "." + fraction);
|
return Number(integer + '.' + fraction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,20 @@
|
||||||
export function info(text: string, data: any = null ){
|
export function info(text: string, data: unknown = null) {
|
||||||
if(data){
|
if (data) {
|
||||||
console.log(`%c ${text}`, 'color: #17a2b8', data);
|
// console.log(`%c ${text}`, 'color: #17a2b8', data);
|
||||||
|
} else {
|
||||||
} else{
|
// console.log(`%c ${text}`, 'color: #17a2b8');
|
||||||
console.log(`%c ${text}`, 'color: #17a2b8');
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function contract(text: string, data: any = null ){
|
export function contract(text: string, data: unknown = null) {
|
||||||
if(data){
|
if (data) {
|
||||||
console.log(`%c ${text}`, 'color: #8732a8', data);
|
// console.log(`%c ${text}`, 'color: #8732a8', data);
|
||||||
|
} else {
|
||||||
} else{
|
// console.log(`%c ${text}`, 'color: #8732a8');
|
||||||
console.log(`%c ${text}`, 'color: #8732a8');
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
info,
|
info,
|
||||||
contract
|
contract,
|
||||||
}
|
};
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,30 +1,26 @@
|
||||||
<template>
|
<template>
|
||||||
<chart-js
|
<ChartJs :snatchedPositions="snatchPositions.map(obj => obj.id)" :positions="activePositions" :dark="darkTheme"></ChartJs>
|
||||||
:snatchedPositions="snatchPositions.map((obj) => obj.id)"
|
|
||||||
:positions="activePositions"
|
|
||||||
:dark="darkTheme"
|
|
||||||
></chart-js>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import ChartJs from "@/components/chart/ChartJs.vue";
|
import ChartJs from '@/components/chart/ChartJs.vue';
|
||||||
import {bigInt2Number, formatBigIntDivision} from "@/utils/helper";
|
import { bigInt2Number, formatBigIntDivision } from '@/utils/helper';
|
||||||
import { computed, ref } from "vue";
|
import { computed, ref } from 'vue';
|
||||||
import { useStatCollection } from "@/composables/useStatCollection";
|
import { useStatCollection } from '@/composables/useStatCollection';
|
||||||
import { useStake } from "@/composables/useStake";
|
import { useStake } from '@/composables/useStake';
|
||||||
import { usePositions, type Position } from "@/composables/usePositions";
|
import { usePositions, type Position } from '@/composables/usePositions';
|
||||||
import { useDark } from "@/composables/useDark";
|
import { useDark } from '@/composables/useDark';
|
||||||
const { darkTheme } = useDark();
|
const { darkTheme } = useDark();
|
||||||
|
|
||||||
const { activePositions, myActivePositions, tresholdValue, myClosedPositions, createRandomPosition } = usePositions();
|
const { activePositions } = usePositions();
|
||||||
const ignoreOwner = ref(false);
|
const ignoreOwner = ref(false);
|
||||||
|
|
||||||
const taxRate = ref<number>(1.0);
|
const taxRate = ref<number>(1.0);
|
||||||
|
|
||||||
const minStakeAmount = computed(() => {
|
const minStakeAmount = computed(() => {
|
||||||
console.log("minStake", minStake.value);
|
// console.log("minStake", minStake.value);
|
||||||
|
|
||||||
return formatBigIntDivision(minStake.value, 10n ** 18n);
|
return formatBigIntDivision(minStake.value, 10n ** 18n);
|
||||||
});
|
});
|
||||||
|
|
||||||
const stakeAbleHarbAmount = computed(() => statCollection.kraikenTotalSupply / 5n);
|
const stakeAbleHarbAmount = computed(() => statCollection.kraikenTotalSupply / 5n);
|
||||||
|
|
@ -34,35 +30,34 @@ const minStake = computed(() => stakeAbleHarbAmount.value / 600n);
|
||||||
const stake = useStake();
|
const stake = useStake();
|
||||||
const statCollection = useStatCollection();
|
const statCollection = useStatCollection();
|
||||||
const snatchPositions = computed(() => {
|
const snatchPositions = computed(() => {
|
||||||
if (
|
if (
|
||||||
bigInt2Number(statCollection.outstandingStake, 18) + stake.stakingAmountNumber <=
|
bigInt2Number(statCollection.outstandingStake, 18) + stake.stakingAmountNumber <=
|
||||||
bigInt2Number(statCollection.kraikenTotalSupply, 18) * 0.2
|
bigInt2Number(statCollection.kraikenTotalSupply, 18) * 0.2
|
||||||
) {
|
) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
//Differenz aus outstandingSupply und totalSupply bestimmen, wie viel HARB kann zum Snatch verwendet werden
|
//Differenz aus outstandingSupply und totalSupply bestimmen, wie viel HARB kann zum Snatch verwendet werden
|
||||||
const difference =
|
const difference =
|
||||||
bigInt2Number(statCollection.outstandingStake, 18) +
|
bigInt2Number(statCollection.outstandingStake, 18) +
|
||||||
stake.stakingAmountNumber -
|
stake.stakingAmountNumber -
|
||||||
bigInt2Number(statCollection.kraikenTotalSupply, 18) * 0.2;
|
bigInt2Number(statCollection.kraikenTotalSupply, 18) * 0.2;
|
||||||
console.log("difference", difference);
|
// console.log("difference", difference);
|
||||||
|
|
||||||
//Division ohne Rest, um zu schauen wie viele Positionen gesnatched werden könnten
|
//Division ohne Rest, um zu schauen wie viele Positionen gesnatched werden könnten
|
||||||
const snatchAblePositionsCount = Math.floor(difference / minStakeAmount.value);
|
const snatchAblePositionsCount = Math.floor(difference / minStakeAmount.value);
|
||||||
|
|
||||||
//wenn mehr als 0 Positionen gesnatched werden könnten, wird geschaut wie viele Positionen in Frage kommen
|
//wenn mehr als 0 Positionen gesnatched werden könnten, wird geschaut wie viele Positionen in Frage kommen
|
||||||
if (snatchAblePositionsCount > 0) {
|
if (snatchAblePositionsCount > 0) {
|
||||||
const snatchAblePositions = activePositions.value.filter((obj: Position) => {
|
const snatchAblePositions = activePositions.value.filter((obj: Position) => {
|
||||||
if (ignoreOwner.value) {
|
if (ignoreOwner.value) {
|
||||||
return obj.taxRatePercentage < taxRate.value;
|
return obj.taxRatePercentage < taxRate.value;
|
||||||
}
|
}
|
||||||
return obj.taxRatePercentage < taxRate.value && !obj.iAmOwner;
|
return obj.taxRatePercentage < taxRate.value && !obj.iAmOwner;
|
||||||
});
|
});
|
||||||
const slicedArray = snatchAblePositions.slice(0, snatchAblePositionsCount);
|
const slicedArray = snatchAblePositions.slice(0, snatchAblePositionsCount);
|
||||||
return slicedArray;
|
return slicedArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts"></script>
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main>
|
<main></main>
|
||||||
</main>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,48 +1,48 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="login-wrapper">
|
<div class="login-wrapper">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<div class="left-overlay">
|
<div class="left-overlay">
|
||||||
<div class="login-dialog">
|
<div class="login-dialog">
|
||||||
<h1>Enter Staking</h1>
|
<h1>Enter Staking</h1>
|
||||||
<div class="error-box" v-if="error">
|
<div class="error-box" v-if="error">
|
||||||
{{ error }}
|
{{ error }}
|
||||||
</div>
|
</div>
|
||||||
<f-input type="password" @input="error = ''" v-model="inputPassword" label="Password" @keyup.enter="login"></f-input>
|
<FInput type="password" @input="error = ''" v-model="inputPassword" label="Password" @keyup.enter="login"></FInput>
|
||||||
<f-button size="large" @click="login">Login</f-button>
|
<FButton size="large" @click="login">Login</FButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import FButton from "@/components/fcomponents/FButton.vue";
|
import FButton from '@/components/fcomponents/FButton.vue';
|
||||||
import FInput from "@/components/fcomponents/FInput.vue";
|
import FInput from '@/components/fcomponents/FInput.vue';
|
||||||
import { onMounted, ref } from "vue";
|
import { onMounted, ref } from 'vue';
|
||||||
import {useRouter} from "vue-router"
|
import { useRouter } from 'vue-router';
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const error = ref("")
|
const error = ref('');
|
||||||
const passwords = ref(["lobsterDao", "test123", "lobster-x010syqe?412!"]);
|
const passwords = ref(['lobsterDao', 'test123', 'lobster-x010syqe?412!']);
|
||||||
const inputPassword = ref("")
|
const inputPassword = ref('');
|
||||||
function login(){
|
function login() {
|
||||||
if(passwords.value.includes(inputPassword.value)){
|
if (passwords.value.includes(inputPassword.value)) {
|
||||||
localStorage.setItem('authentificated', "true");
|
localStorage.setItem('authentificated', 'true');
|
||||||
router.push("/")
|
router.push('/');
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
error.value = "wrong password"
|
error.value = 'wrong password';
|
||||||
// Token nach erfolgreichem Login speichern
|
// Token nach erfolgreichem Login speichern
|
||||||
// Beim Logout Token entfernen
|
// Beim Logout Token entfernen
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if(localStorage.getItem('authentificated') === "true"){
|
if (localStorage.getItem('authentificated') === 'true') {
|
||||||
router.push("/")
|
router.push('/');
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
||||||
|
|
@ -1,112 +1,104 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="stake-view">
|
<div class="stake-view">
|
||||||
<div class="stake-view-wrapper">
|
<div class="stake-view-wrapper">
|
||||||
<h3>
|
<h3>
|
||||||
Staking Dashboard
|
Staking Dashboard
|
||||||
<IconInfo size="20px">
|
<IconInfo size="20px">
|
||||||
<template #text>
|
<template #text>
|
||||||
Stake your $KRK and claim owner slots. Owner slots give a return on every token buy that
|
Stake your $KRK and claim owner slots. Owner slots give a return on every token buy that increases the liquidity of KrAIken.
|
||||||
increases the liquidity of KrAIken. Owner slots are limited to 20,000 slots in total. To enable
|
Owner slots are limited to 20,000 slots in total. To enable everyone and anyone to join staking is protected by a “Harberger
|
||||||
everyone and anyone to join staking is protected by a “Harberger Tax” mechanism.
|
Tax” mechanism.
|
||||||
</template>
|
</template>
|
||||||
</IconInfo>
|
</IconInfo>
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<div class="stake-view-body">
|
<div class="stake-view-body">
|
||||||
<chart-complete></chart-complete>
|
<ChartComplete></ChartComplete>
|
||||||
<div class="hold-stake-wrapper">
|
<div class="hold-stake-wrapper">
|
||||||
<f-card class="inner-border">
|
<FCard class="inner-border">
|
||||||
<template v-if="!isChainSupported">
|
<template v-if="!isChainSupported"> Chain not supported </template>
|
||||||
Chain not supported
|
<template v-else-if="status !== 'connected'">
|
||||||
</template>
|
<FButton @click="showPanel = true" size="large" block>Connect Wallet</FButton>
|
||||||
<template v-else-if="status !== 'connected'">
|
</template>
|
||||||
<f-button @click="showPanel = true" size="large" block
|
<template v-else>
|
||||||
>Connect Wallet</f-button
|
<StakeHolder></StakeHolder>
|
||||||
>
|
</template>
|
||||||
</template>
|
</FCard>
|
||||||
<template v-else >
|
</div>
|
||||||
|
</div>
|
||||||
<stake-holder ></stake-holder>
|
</div>
|
||||||
</template>
|
<div class="statistics-wrapper">
|
||||||
</f-card>
|
<h3>Statistics</h3>
|
||||||
</div>
|
<div class="statistics-outputs-wrapper">
|
||||||
</div>
|
<StatsOutput headline="Average Slot Tax" :price="`${averageTaxRate.toFixed(2)} %`"></StatsOutput>
|
||||||
</div>
|
<StatsOutput
|
||||||
<div class="statistics-wrapper">
|
headline="Claimed Owner Slots"
|
||||||
<h3>Statistics</h3>
|
:price="`${InsertCommaNumber(stats.claimedSlots)} / ${InsertCommaNumber(stats.maxSlots)}`"
|
||||||
<div class="statistics-outputs-wrapper">
|
></StatsOutput>
|
||||||
<stats-output headline="Average Slot Tax" :price="`${averageTaxRate.toFixed(2)} %`"></stats-output>
|
<StatsOutput headline="Total Supply Change / 7d" :price="`+ ${stats.totalSupplyChange7d} $KRK`"></StatsOutput>
|
||||||
<stats-output
|
<StatsOutput headline="Inflation / 7d" :price="`+${stats.inflation7d}%`"></StatsOutput>
|
||||||
headline="Claimed Owner Slots"
|
</div>
|
||||||
:price="`${InsertCommaNumber(stats.claimedSlots)} / ${InsertCommaNumber(stats.maxSlots)}`"
|
</div>
|
||||||
></stats-output>
|
<div class="active-positions-wrapper">
|
||||||
<stats-output
|
<h3>Active Positions</h3>
|
||||||
headline="Total Supply Change / 7d"
|
<div class="active-positions-list">
|
||||||
:price="`+ ${stats.totalSupplyChange7d} $KRK`"
|
<CollapseActive
|
||||||
></stats-output>
|
v-for="position in myActivePositions"
|
||||||
<stats-output headline="Inflation / 7d" :price="`+${stats.inflation7d}%`"></stats-output>
|
:taxRate="position.taxRatePercentage"
|
||||||
</div>
|
:amount="position.amount"
|
||||||
</div>
|
:treshold="tresholdValue"
|
||||||
<div class="active-positions-wrapper">
|
:id="position.positionId"
|
||||||
<h3>Active Positions</h3>
|
:position="position"
|
||||||
<div class="active-positions-list">
|
:key="position.id"
|
||||||
<collapse-active
|
></CollapseActive>
|
||||||
v-for="position in myActivePositions"
|
</div>
|
||||||
:taxRate="position.taxRatePercentage"
|
</div>
|
||||||
:amount="position.amount"
|
<!-- <f-button @click="getGraphData">graphql test</f-button>
|
||||||
:treshold="tresholdValue"
|
|
||||||
:id="position.positionId"
|
|
||||||
:position="position"
|
|
||||||
:key="position.id"
|
|
||||||
></collapse-active>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- <f-button @click="getGraphData">graphql test</f-button>
|
|
||||||
<div v-for="position in positions" :key="position.id">
|
<div v-for="position in positions" :key="position.id">
|
||||||
{{ position }}
|
{{ position }}
|
||||||
</div> -->
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import StakeHolder from "@/components/StakeHolder.vue";
|
import StakeHolder from '@/components/StakeHolder.vue';
|
||||||
import ChartComplete from "@/components/chart/ChartComplete.vue";
|
import ChartComplete from '@/components/chart/ChartComplete.vue';
|
||||||
import StatsOutput from "@/components/StatsOutput.vue";
|
import StatsOutput from '@/components/StatsOutput.vue';
|
||||||
import CollapseActive from "@/components/collapse/CollapseActive.vue";
|
import CollapseActive from '@/components/collapse/CollapseActive.vue';
|
||||||
import { onMounted, computed, inject } from "vue";
|
import { onMounted, computed, inject } from 'vue';
|
||||||
import { useStatCollection } from "@/composables/useStatCollection";
|
import { useStatCollection } from '@/composables/useStatCollection';
|
||||||
import { useChains, useAccount } from "@wagmi/vue";
|
import { useChains, useAccount } from '@wagmi/vue';
|
||||||
import { useWallet } from "@/composables/useWallet";
|
import { useWallet } from '@/composables/useWallet';
|
||||||
import FCard from "@/components/fcomponents/FCard.vue";
|
import FCard from '@/components/fcomponents/FCard.vue';
|
||||||
import IconInfo from "@/components/icons/IconInfo.vue";
|
import IconInfo from '@/components/icons/IconInfo.vue';
|
||||||
import FButton from "@/components/fcomponents/FButton.vue";
|
import FButton from '@/components/fcomponents/FButton.vue';
|
||||||
import { DEFAULT_CHAIN_ID } from "@/config";
|
import { DEFAULT_CHAIN_ID } from '@/config';
|
||||||
|
|
||||||
// todo interface positions
|
// todo interface positions
|
||||||
import { usePositions } from "@/composables/usePositions";
|
import { usePositions } from '@/composables/usePositions';
|
||||||
const { status } = useAccount();
|
const { status } = useAccount();
|
||||||
const showPanel = inject("showPanel");
|
const showPanel = inject('showPanel');
|
||||||
|
|
||||||
import { compactNumber, InsertCommaNumber } from "@/utils/helper";
|
import { InsertCommaNumber } from '@/utils/helper';
|
||||||
const { myActivePositions, tresholdValue, activePositions } = usePositions();
|
const { myActivePositions, tresholdValue, activePositions } = usePositions();
|
||||||
|
|
||||||
const stats = useStatCollection();
|
const stats = useStatCollection();
|
||||||
const wallet = useWallet();
|
const wallet = useWallet();
|
||||||
const chains = useChains();
|
const chains = useChains();
|
||||||
|
|
||||||
function calculateAverageTaxRate(data: any): number {
|
function calculateAverageTaxRate(data: Array<{ taxRate: number | string }>): number {
|
||||||
console.log("data", data);
|
// console.log("data", data);
|
||||||
|
|
||||||
if (data.length === 0) {
|
if (data.length === 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
const totalTaxRate = data.reduce((sum: any, entry: any) => sum + parseFloat(entry.taxRate), 0);
|
const totalTaxRate = data.reduce((sum: number, entry: { taxRate: number | string }) => sum + parseFloat(String(entry.taxRate)), 0);
|
||||||
const averageTaxRate = totalTaxRate / data.length;
|
const averageTaxRate = totalTaxRate / data.length;
|
||||||
return averageTaxRate * 100;
|
return averageTaxRate * 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
const averageTaxRate = computed(() => calculateAverageTaxRate(activePositions.value));
|
const averageTaxRate = computed(() => calculateAverageTaxRate(activePositions.value));
|
||||||
const supportedChainIds = computed(() => chains.value.map((chain) => chain.id));
|
const supportedChainIds = computed(() => chains.value.map(chain => chain.id));
|
||||||
const currentChainId = computed(() => wallet.account.chainId ?? DEFAULT_CHAIN_ID);
|
const currentChainId = computed(() => wallet.account.chainId ?? DEFAULT_CHAIN_ID);
|
||||||
const isChainSupported = computed(() => supportedChainIds.value.includes(currentChainId.value));
|
const isChainSupported = computed(() => supportedChainIds.value.includes(currentChainId.value));
|
||||||
onMounted(async () => {});
|
onMounted(async () => {});
|
||||||
|
|
|
||||||
|
|
@ -1,54 +1,54 @@
|
||||||
import { http, createConfig, createStorage } from "@wagmi/vue";
|
import { http, createConfig, createStorage } from '@wagmi/vue';
|
||||||
import { baseSepolia } from "@wagmi/vue/chains";
|
import { baseSepolia } from '@wagmi/vue/chains';
|
||||||
import { coinbaseWallet, walletConnect } from "@wagmi/vue/connectors";
|
import { coinbaseWallet, walletConnect } from '@wagmi/vue/connectors';
|
||||||
import { defineChain } from "viem";
|
import { defineChain } from 'viem';
|
||||||
|
|
||||||
const LOCAL_RPC_URL = import.meta.env.VITE_LOCAL_RPC_URL ?? "/rpc/anvil";
|
const LOCAL_RPC_URL = import.meta.env.VITE_LOCAL_RPC_URL ?? '/rpc/anvil';
|
||||||
|
|
||||||
const kraikenLocalFork = defineChain({
|
const kraikenLocalFork = defineChain({
|
||||||
id: 31337,
|
id: 31337,
|
||||||
name: "Kraiken Local Fork",
|
name: 'Kraiken Local Fork',
|
||||||
network: "kraiken-local",
|
network: 'kraiken-local',
|
||||||
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
|
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
||||||
rpcUrls: {
|
rpcUrls: {
|
||||||
default: { http: [LOCAL_RPC_URL] },
|
default: { http: [LOCAL_RPC_URL] },
|
||||||
public: { http: [LOCAL_RPC_URL] },
|
public: { http: [LOCAL_RPC_URL] },
|
||||||
},
|
},
|
||||||
blockExplorers: {
|
blockExplorers: {
|
||||||
default: { name: "Local Explorer", url: "" },
|
default: { name: 'Local Explorer', url: '' },
|
||||||
},
|
},
|
||||||
testnet: true,
|
testnet: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const config = createConfig({
|
export const config = createConfig({
|
||||||
chains: [kraikenLocalFork, baseSepolia],
|
chains: [kraikenLocalFork, baseSepolia],
|
||||||
storage: createStorage({ storage: window.localStorage }),
|
storage: createStorage({ storage: window.localStorage }),
|
||||||
|
|
||||||
connectors: [
|
connectors: [
|
||||||
walletConnect({
|
walletConnect({
|
||||||
projectId: "d8e5ecb0353c02e21d4c0867d4473ac5",
|
projectId: 'd8e5ecb0353c02e21d4c0867d4473ac5',
|
||||||
metadata: {
|
metadata: {
|
||||||
name: "Kraiken",
|
name: 'Kraiken',
|
||||||
description: "Connect your wallet with Kraiken",
|
description: 'Connect your wallet with Kraiken',
|
||||||
url: "https://kraiken.eth.limo",
|
url: 'https://kraiken.eth.limo',
|
||||||
icons: [""],
|
icons: [''],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
coinbaseWallet({
|
coinbaseWallet({
|
||||||
appName: "Kraiken",
|
appName: 'Kraiken',
|
||||||
darkMode: true,
|
darkMode: true,
|
||||||
preference: {
|
preference: {
|
||||||
options: "all",
|
options: 'all',
|
||||||
telemetry: false,
|
telemetry: false,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
transports: {
|
transports: {
|
||||||
[kraikenLocalFork.id]: http(LOCAL_RPC_URL),
|
[kraikenLocalFork.id]: http(LOCAL_RPC_URL),
|
||||||
[baseSepolia.id]: http(),
|
[baseSepolia.id]: http(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (typeof window !== "undefined" && config.state.chainId !== kraikenLocalFork.id) {
|
if (typeof window !== 'undefined' && config.state.chainId !== kraikenLocalFork.id) {
|
||||||
config.setState((state) => ({ ...state, chainId: kraikenLocalFork.id }));
|
config.setState(state => ({ ...state, chainId: kraikenLocalFork.id }));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue