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
|
||||
cd kraiken-lib || exit 1
|
||||
npx lint-staged
|
||||
set -e
|
||||
|
||||
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": {
|
||||
"@playwright/test": "^1.55.1",
|
||||
"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" />
|
||||
|
||||
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",
|
||||
"type-check": "vue-tsc --build",
|
||||
"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": {
|
||||
"@tanstack/vue-query": "^5.64.2",
|
||||
|
|
@ -31,13 +36,23 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@iconify/vue": "^4.3.0",
|
||||
"@stylistic/eslint-plugin": "^2.8.0",
|
||||
"@tsconfig/node22": "^22.0.0",
|
||||
"@types/node": "^22.10.7",
|
||||
"@typescript-eslint/eslint-plugin": "^8.45.0",
|
||||
"@typescript-eslint/parser": "^8.45.0",
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"@vue/eslint-config-typescript": "^14.6.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",
|
||||
"husky": "^9.1.7",
|
||||
"jsdom": "^27.0.0",
|
||||
"lint-staged": "^16.2.3",
|
||||
"npm-run-all2": "^7.0.2",
|
||||
"prettier": "^3.6.2",
|
||||
"typescript": "~5.7.3",
|
||||
"vite": "^6.0.11",
|
||||
"vite-plugin-vue-devtools": "^7.7.0",
|
||||
|
|
|
|||
|
|
@ -1,37 +1,31 @@
|
|||
<template>
|
||||
|
||||
<component :is="layoutComponent">
|
||||
<router-view />
|
||||
<RouterView />
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { RouterView, useRoute, useRouter } from "vue-router";
|
||||
import { RouterView, useRoute } from 'vue-router';
|
||||
import type { LayoutName } from '@/types/router';
|
||||
|
||||
|
||||
import DefaultLayout from '@/layouts/DefaultLayout.vue';
|
||||
import NavbarLayout from '@/layouts/NavbarLayout.vue';
|
||||
|
||||
const layouts = {
|
||||
DefaultLayout,
|
||||
NavbarLayout
|
||||
NavbarLayout,
|
||||
};
|
||||
|
||||
import { computed, defineAsyncComponent, provide, ref } from "vue";
|
||||
import { computed } from 'vue';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
|
||||
const layoutComponent = computed(() => {
|
||||
const layoutName: LayoutName = route.meta.layout ?? 'DefaultLayout';
|
||||
return layouts[layoutName]; // Jetzt kennt TypeScript den Typ!
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="sass">
|
||||
footer
|
||||
margin-top: auto
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
msg: string
|
||||
}>()
|
||||
msg: string;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<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>
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
<div class="stake-inner">
|
||||
<template v-if="!statCollection.initialized">
|
||||
<div>
|
||||
<f-loader></f-loader>
|
||||
<FLoader></FLoader>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -12,131 +12,114 @@
|
|||
<FSlider :min="minStakeAmount" :max="maxStakeAmount" v-model="stake.stakingAmountNumber"></FSlider>
|
||||
<div class="formular">
|
||||
<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>
|
||||
<div class="balance">Balance: {{ maxStakeAmount.toFixed(2) }} $KRK</div>
|
||||
<div @click="setMaxAmount" class="staking-amount-max">
|
||||
<b>Max</b>
|
||||
</div>
|
||||
</template>
|
||||
</f-input>
|
||||
</FInput>
|
||||
<Icon class="stake-arrow" icon="mdi:chevron-triple-right"></Icon>
|
||||
<f-input
|
||||
label="Owner Slots"
|
||||
class="staking-amount"
|
||||
disabled
|
||||
:modelValue="`${stakeSlots}(${supplyFreeze?.toFixed(4)})`"
|
||||
>
|
||||
<FInput label="Owner Slots" class="staking-amount" disabled :modelValue="`${stakeSlots}(${supplyFreeze?.toFixed(4)})`">
|
||||
<template #info>
|
||||
Slots correspond to a percentage of ownership in the protocol.<br /><br />1,000 Slots =
|
||||
1% Ownership<br /><br />When you unstake you get the exact percentage of the current
|
||||
$KRK total supply. When the total supply increased since you staked you get more tokens
|
||||
back than before.
|
||||
Slots correspond to a percentage of ownership in the protocol.<br /><br />1,000 Slots = 1% Ownership<br /><br />When you
|
||||
unstake you get the exact percentage of the current $KRK total supply. When the total supply increased since you staked you
|
||||
get more tokens back than before.
|
||||
</template>
|
||||
</f-input>
|
||||
</FInput>
|
||||
</div>
|
||||
<div class="row row-2">
|
||||
<f-select :items="adjustTaxRate.taxRates" label="Tax" v-model="taxRate">
|
||||
<FSelect :items="adjustTaxRate.taxRates" label="Tax" v-model="taxRate">
|
||||
<template v-slot:info>
|
||||
The yearly tax you have to pay to keep your slots open. The tax is paid when unstaking
|
||||
or manually in the dashboard. If someone pays a higher tax they can buy you out.
|
||||
The yearly tax you have to pay to keep your slots open. The tax is paid when unstaking or manually in the dashboard. If
|
||||
someone pays a higher tax they can buy you out.
|
||||
</template>
|
||||
</f-select>
|
||||
<f-input label="Floor Tax" disabled :modelValue="snatchSelection.floorTax">
|
||||
</FSelect>
|
||||
<FInput label="Floor Tax" disabled :modelValue="String(snatchSelection.floorTax)">
|
||||
<template v-slot:info> This is the current minimum tax you have to pay to claim owner slots from other owners. </template>
|
||||
</FInput>
|
||||
<FInput label="Positions Buyout" disabled :modelValue="String(snatchSelection.snatchablePositions.value.length)">
|
||||
<template v-slot:info>
|
||||
This is the current minimum tax you have to pay to claim owner slots from other owners.
|
||||
This shows you the numbers of staking positions you buy out from current owners by paying a higher tax. If you get bought
|
||||
out yourself by new owners you get paid out the current market value of your position incl. your profits.
|
||||
</template>
|
||||
</f-input>
|
||||
<f-input label="Positions Buyout" disabled :modelValue="snatchSelection.snatchablePositions.length">
|
||||
<template v-slot:info>
|
||||
This shows you the numbers of staking positions you buy out from current owners by
|
||||
paying a higher tax. If you get bought out yourself by new owners you get paid out the
|
||||
current market value of your position incl. your profits.
|
||||
</template>
|
||||
</f-input>
|
||||
</FInput>
|
||||
</div>
|
||||
</div>
|
||||
<f-button size="large" disabled block v-if="stake.state === 'NoBalance'">Insufficient Balance</f-button>
|
||||
<f-button size="large" disabled block v-else-if="stake.stakingAmountNumber < minStakeAmount"
|
||||
>Stake amount too low</f-button
|
||||
>
|
||||
<f-button
|
||||
<FButton size="large" disabled block v-if="stake.state === 'NoBalance'">Insufficient Balance</FButton>
|
||||
<FButton size="large" disabled block v-else-if="stake.stakingAmountNumber < minStakeAmount">Stake amount too low</FButton>
|
||||
<FButton
|
||||
size="large"
|
||||
disabled
|
||||
block
|
||||
v-else-if="
|
||||
!snatchSelection.openPositionsAvailable && stake.state === 'StakeAble' && snatchSelection.snatchablePositions.length === 0
|
||||
!snatchSelection.openPositionsAvailable && stake.state === 'StakeAble' && snatchSelection.snatchablePositions.value.length === 0
|
||||
"
|
||||
>taxRate too low to snatch</f-button
|
||||
>taxRate too low to snatch</FButton
|
||||
>
|
||||
<f-button
|
||||
<FButton
|
||||
size="large"
|
||||
block
|
||||
v-else-if="stake.state === 'StakeAble' && snatchSelection.snatchablePositions.length === 0"
|
||||
v-else-if="stake.state === 'StakeAble' && snatchSelection.snatchablePositions.value.length === 0"
|
||||
@click="stakeSnatch"
|
||||
>Stake</f-button
|
||||
>Stake</FButton
|
||||
>
|
||||
<f-button
|
||||
<FButton
|
||||
size="large"
|
||||
block
|
||||
v-else-if="stake.state === 'StakeAble' && snatchSelection.snatchablePositions.length > 0"
|
||||
v-else-if="stake.state === 'StakeAble' && snatchSelection.snatchablePositions.value.length > 0"
|
||||
@click="stakeSnatch"
|
||||
>Snatch and Stake</f-button
|
||||
>Snatch and Stake</FButton
|
||||
>
|
||||
<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>
|
||||
<FButton size="large" outlined block v-else-if="stake.state === 'SignTransaction'">Sign Transaction ...</FButton>
|
||||
<FButton size="large" outlined block v-else-if="stake.state === 'Waiting'">Waiting ...</FButton>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import FButton from "@/components/fcomponents/FButton.vue";
|
||||
import FInput from "@/components/fcomponents/FInput.vue";
|
||||
import FSelect from "@/components/fcomponents/FSelect.vue";
|
||||
import FLoader from "@/components/fcomponents/FLoader.vue";
|
||||
import FSlider from "@/components/fcomponents/FSlider.vue";
|
||||
import FOutput from "@/components/fcomponents/FOutput.vue";
|
||||
import { Icon } from "@iconify/vue";
|
||||
import { formatBigIntDivision, InsertCommaNumber, formatBigNumber, bigInt2Number } from "@/utils/helper";
|
||||
import { formatUnits } from "viem";
|
||||
import { loadPositions, usePositions } from "@/composables/usePositions";
|
||||
import { useStake } from "@/composables/useStake";
|
||||
import { useClaim } from "@/composables/useClaim";
|
||||
import { useAdjustTaxRate } from "@/composables/useAdjustTaxRates";
|
||||
import { useSnatchSelection } from "@/composables/useSnatchSelection";
|
||||
import { getMinStake } from "@/contracts/harb";
|
||||
import { useWallet } from "@/composables/useWallet";
|
||||
import { ref, onMounted, watch, computed, inject, watchEffect } from "vue";
|
||||
import { useStatCollection, loadStats } from "@/composables/useStatCollection";
|
||||
import { useRoute } from "vue-router";
|
||||
import FButton from '@/components/fcomponents/FButton.vue';
|
||||
import FInput from '@/components/fcomponents/FInput.vue';
|
||||
import FSelect from '@/components/fcomponents/FSelect.vue';
|
||||
import FLoader from '@/components/fcomponents/FLoader.vue';
|
||||
import FSlider from '@/components/fcomponents/FSlider.vue';
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { bigInt2Number } from '@/utils/helper';
|
||||
import { loadPositions, usePositions, type Position } from '@/composables/usePositions';
|
||||
import { useStake } from '@/composables/useStake';
|
||||
import { useClaim } from '@/composables/useClaim';
|
||||
import { useAdjustTaxRate } from '@/composables/useAdjustTaxRates';
|
||||
import { useSnatchSelection } from '@/composables/useSnatchSelection';
|
||||
import { assetsToShares } from '@/contracts/stake';
|
||||
import { getMinStake } from '@/contracts/harb';
|
||||
import { useWallet } from '@/composables/useWallet';
|
||||
import { ref, onMounted, watch, computed, watchEffect } from 'vue';
|
||||
import { useStatCollection, loadStats } from '@/composables/useStatCollection';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
const demo = sessionStorage.getItem("demo") === "true";
|
||||
const demo = sessionStorage.getItem('demo') === 'true';
|
||||
const route = useRoute();
|
||||
|
||||
const adjustTaxRate = useAdjustTaxRate();
|
||||
|
||||
const activeTab = ref("stake");
|
||||
const StakeMenuOpen = ref(false);
|
||||
const taxRate = ref<number>(1.0);
|
||||
const loading = ref<boolean>(true);
|
||||
const stakeSnatchLoading = ref<boolean>(false);
|
||||
const stake = useStake();
|
||||
const claim = useClaim();
|
||||
const _claim = useClaim();
|
||||
const wallet = useWallet();
|
||||
const statCollection = useStatCollection();
|
||||
|
||||
const { activePositions } = usePositions();
|
||||
const { activePositions: _activePositions } = usePositions();
|
||||
|
||||
const minStake = ref(0n);
|
||||
const stakeSlots = ref();
|
||||
const supplyFreeze = ref<number>(0);
|
||||
let debounceTimer: ReturnType<typeof setTimeout>;
|
||||
watchEffect(() => {
|
||||
console.log("supplyFreeze");
|
||||
|
||||
if (!stake.stakingAmount) {
|
||||
supplyFreeze.value = 0;
|
||||
return;
|
||||
|
|
@ -149,17 +132,15 @@ watchEffect(() => {
|
|||
const stakeableSupplyNumber = bigInt2Number(statCollection.stakeableSupply, 18);
|
||||
minStake.value = await getMinStake();
|
||||
|
||||
console.log(stakingAmountSharesNumber / stakeableSupplyNumber);
|
||||
supplyFreeze.value = stakingAmountSharesNumber / stakeableSupplyNumber;
|
||||
}, 500); // Verzögerung von 500ms
|
||||
}, 500);
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
console.log("stakeSlots");
|
||||
stakeSlots.value = (supplyFreeze.value * 1000)?.toFixed(2);
|
||||
});
|
||||
|
||||
const tokenIssuance = computed(() => {
|
||||
const _tokenIssuance = computed(() => {
|
||||
if (statCollection.kraikenTotalSupply === 0n) {
|
||||
return 0n;
|
||||
}
|
||||
|
|
@ -175,7 +156,7 @@ async function stakeSnatch() {
|
|||
await stake.snatch(stake.stakingAmount, taxRate.value, snatchAblePositionsIds);
|
||||
}
|
||||
stakeSnatchLoading.value = true;
|
||||
await new Promise((resolve) => setTimeout(resolve, 10000));
|
||||
await new Promise(resolve => setTimeout(resolve, 10000));
|
||||
await loadPositions();
|
||||
await loadStats();
|
||||
stakeSnatchLoading.value = false;
|
||||
|
|
@ -183,36 +164,29 @@ async function stakeSnatch() {
|
|||
|
||||
watch(
|
||||
route,
|
||||
async (to) => {
|
||||
console.log("to", to.hash);
|
||||
if (to.hash === "#stake") {
|
||||
console.log("StakeMenuOpen", StakeMenuOpen.value);
|
||||
async to => {
|
||||
if (to.hash === '#stake') {
|
||||
StakeMenuOpen.value = true;
|
||||
}
|
||||
},
|
||||
{ flush: "pre", immediate: true, deep: true }
|
||||
{ flush: 'pre', immediate: true, deep: true }
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
minStake.value = await getMinStake();
|
||||
stake.stakingAmountNumber = minStakeAmount.value;
|
||||
} catch (error) {
|
||||
console.error("error", error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
const minStakeAmount = computed(() => {
|
||||
console.log("minStake", minStake.value);
|
||||
return bigInt2Number(minStake.value, 18);
|
||||
});
|
||||
|
||||
const maxStakeAmount = computed(() => {
|
||||
if (wallet.balance?.value) {
|
||||
console.log("wallet.balance.value", wallet.balance);
|
||||
console.log("formatBigIntDivision(wallet.balance.value, 10n ** 18n)", bigInt2Number(wallet.balance.value, 18));
|
||||
return bigInt2Number(wallet.balance.value, 18);
|
||||
} else {
|
||||
return 0;
|
||||
|
|
@ -221,8 +195,7 @@ const maxStakeAmount = computed(() => {
|
|||
|
||||
watch(
|
||||
minStakeAmount,
|
||||
async (newValue) => {
|
||||
console.log("newValue", newValue);
|
||||
async newValue => {
|
||||
if (newValue > stake.stakingAmountNumber && stake.stakingAmountNumber === 0) {
|
||||
stake.stakingAmountNumber = minStakeAmount.value;
|
||||
}
|
||||
|
|
@ -231,11 +204,10 @@ watch(
|
|||
);
|
||||
|
||||
function setMaxAmount() {
|
||||
console.log("maxStakeAmount.value", maxStakeAmount.value);
|
||||
stake.stakingAmountNumber = maxStakeAmount.value;
|
||||
}
|
||||
|
||||
const snatchSelection = useSnatchSelection(demo);
|
||||
const snatchSelection = useSnatchSelection(demo, taxRate);
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
<template>
|
||||
<div class="stats-output" :styles="styles">
|
||||
<f-card>
|
||||
<FCard>
|
||||
<h6>{{ props.headline }}</h6>
|
||||
<f-output :name="props.name" :price="props.price">
|
||||
<FOutput :name="props.name" :price="props.price">
|
||||
<template #price>
|
||||
<slot name="price"></slot>
|
||||
</template>
|
||||
</f-output>
|
||||
</f-card>
|
||||
</FOutput>
|
||||
</FCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import FCard from "@/components/fcomponents/FCard.vue"
|
||||
import FOutput from "@/components/fcomponents/FOutput.vue"
|
||||
import { computed } from 'vue';
|
||||
import FCard from '@/components/fcomponents/FCard.vue';
|
||||
import FOutput from '@/components/fcomponents/FOutput.vue';
|
||||
interface Props {
|
||||
name?: string;
|
||||
headline: string;
|
||||
|
|
@ -26,7 +26,10 @@ interface Styles {
|
|||
width?: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {});
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
name: '',
|
||||
width: undefined,
|
||||
});
|
||||
|
||||
const styles = computed(() => {
|
||||
const returnObject: Styles = {};
|
||||
|
|
|
|||
|
|
@ -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,29 +1,23 @@
|
|||
<template>
|
||||
<chart-js
|
||||
:snatchedPositions="snatchPositions.map((obj) => obj.id)"
|
||||
:positions="activePositions"
|
||||
:dark="darkTheme"
|
||||
></chart-js>
|
||||
<ChartJs :snatchedPositions="snatchPositions.map(obj => obj.id)" :positions="activePositions" :dark="darkTheme"></ChartJs>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import ChartJs from "@/components/chart/ChartJs.vue";
|
||||
import {bigInt2Number, formatBigIntDivision} from "@/utils/helper";
|
||||
import { computed, ref } from "vue";
|
||||
import { useStatCollection } from "@/composables/useStatCollection";
|
||||
import { useStake } from "@/composables/useStake";
|
||||
import { usePositions, type Position } from "@/composables/usePositions";
|
||||
import { useDark } from "@/composables/useDark";
|
||||
import ChartJs from '@/components/chart/ChartJs.vue';
|
||||
import { bigInt2Number, formatBigIntDivision } from '@/utils/helper';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useStatCollection } from '@/composables/useStatCollection';
|
||||
import { useStake } from '@/composables/useStake';
|
||||
import { usePositions, type Position } from '@/composables/usePositions';
|
||||
import { useDark } from '@/composables/useDark';
|
||||
const { darkTheme } = useDark();
|
||||
|
||||
const { activePositions, myActivePositions, tresholdValue, myClosedPositions, createRandomPosition } = usePositions();
|
||||
const { activePositions } = usePositions();
|
||||
const ignoreOwner = ref(false);
|
||||
|
||||
const taxRate = ref<number>(1.0);
|
||||
|
||||
const minStakeAmount = computed(() => {
|
||||
console.log("minStake", minStake.value);
|
||||
|
||||
return formatBigIntDivision(minStake.value, 10n ** 18n);
|
||||
});
|
||||
|
||||
|
|
@ -45,7 +39,6 @@ const difference =
|
|||
bigInt2Number(statCollection.outstandingStake, 18) +
|
||||
stake.stakingAmountNumber -
|
||||
bigInt2Number(statCollection.kraikenTotalSupply, 18) * 0.2;
|
||||
console.log("difference", difference);
|
||||
|
||||
//Division ohne Rest, um zu schauen wie viele Positionen gesnatched werden könnten
|
||||
const snatchAblePositionsCount = Math.floor(difference / minStakeAmount.value);
|
||||
|
|
@ -64,5 +57,4 @@ if (snatchAblePositionsCount > 0) {
|
|||
|
||||
return [];
|
||||
});
|
||||
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -4,15 +4,12 @@
|
|||
<div class="chart-inner" @click.stop>
|
||||
<div class="chart--header">
|
||||
<div class="chart--actions" v-if="props.positions?.length > 0">
|
||||
<reset-zoom-button @click="resetZoom"></reset-zoom-button>
|
||||
<fullscreen-button v-if="isMobile" @click="toggleFullscreen"></fullscreen-button>
|
||||
<ResetZoomButton @click="resetZoom"></ResetZoomButton>
|
||||
<FullscreenButton v-if="isMobile" @click="toggleFullscreen"></FullscreenButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="chart--body"
|
||||
:class="{ 'disable-actions': props.positions?.length === 0, dark: props.dark }"
|
||||
>
|
||||
<div class="chart--body" :class="{ 'disable-actions': props.positions?.length === 0, dark: props.dark }">
|
||||
<canvas ref="Chart1"></canvas>
|
||||
<template v-if="props.positions?.length === 0">
|
||||
<p class="chart--no-positions">No positions</p>
|
||||
|
|
@ -35,7 +32,7 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, watch, shallowRef, computed } from "vue";
|
||||
import { onMounted, ref, watch, shallowRef } from 'vue';
|
||||
import {
|
||||
BarController,
|
||||
BarElement,
|
||||
|
|
@ -46,25 +43,17 @@ import {
|
|||
LinearScale,
|
||||
PointElement,
|
||||
Tooltip,
|
||||
} from "chart.js";
|
||||
type TooltipModel,
|
||||
type ChartConfiguration,
|
||||
} from 'chart.js';
|
||||
// import { Chart } from "chart.js";
|
||||
import zoomPlugin from "chartjs-plugin-zoom";
|
||||
import { useAccount } from "@wagmi/vue";
|
||||
import { useMobile } from "@/composables/useMobile";
|
||||
import type { Position } from "@/composables/usePositions";
|
||||
import FullscreenButton from "@/components/chart/FullscreenButton.vue";
|
||||
import ResetZoomButton from "@/components/chart/ResetZoomButton.vue";
|
||||
Chart.register(
|
||||
zoomPlugin,
|
||||
LinearScale,
|
||||
CategoryScale,
|
||||
BarController,
|
||||
BarElement,
|
||||
LineController,
|
||||
LineElement,
|
||||
PointElement,
|
||||
Tooltip
|
||||
);
|
||||
import zoomPlugin from 'chartjs-plugin-zoom';
|
||||
import { useAccount } from '@wagmi/vue';
|
||||
import { useMobile } from '@/composables/useMobile';
|
||||
import type { Position } from '@/composables/usePositions';
|
||||
import FullscreenButton from '@/components/chart/FullscreenButton.vue';
|
||||
import ResetZoomButton from '@/components/chart/ResetZoomButton.vue';
|
||||
Chart.register(zoomPlugin, LinearScale, CategoryScale, BarController, BarElement, LineController, LineElement, PointElement, Tooltip);
|
||||
|
||||
interface Props {
|
||||
positions: Array<Position>;
|
||||
|
|
@ -81,23 +70,22 @@ const fullscreenOpen = ref(false);
|
|||
const myChart = ref();
|
||||
const activePosition = ref();
|
||||
const tooltipRef = ref();
|
||||
const positionSnatched = ref();
|
||||
const _positionSnatched = ref();
|
||||
|
||||
const account = useAccount();
|
||||
const isMobile = useMobile();
|
||||
|
||||
function resetZoom() {
|
||||
console.log("resetZoom", { Chart1, myChart });
|
||||
myChart.value.value.resetZoom();
|
||||
}
|
||||
|
||||
function toggleFullscreen() {
|
||||
if (fullscreenOpen.value) {
|
||||
document.body.style.position = "unset";
|
||||
document.body.style.overflow = "unset";
|
||||
document.body.style.position = 'unset';
|
||||
document.body.style.overflow = 'unset';
|
||||
} else {
|
||||
document.body.style.overflow = "hidden";
|
||||
document.body.style.position = "relative";
|
||||
document.body.style.overflow = 'hidden';
|
||||
document.body.style.position = 'relative';
|
||||
}
|
||||
fullscreenOpen.value = !fullscreenOpen.value;
|
||||
window.scrollTo(0, 0);
|
||||
|
|
@ -105,9 +93,7 @@ function toggleFullscreen() {
|
|||
|
||||
watch(
|
||||
() => props.positions,
|
||||
(newData) => {
|
||||
console.log("props.positions", props.positions);
|
||||
|
||||
() => {
|
||||
myChart.value.value.destroy();
|
||||
renderChart(props.positions);
|
||||
|
||||
|
|
@ -121,7 +107,7 @@ watch(
|
|||
|
||||
watch(
|
||||
() => props.dark,
|
||||
(newData) => {
|
||||
() => {
|
||||
myChart.value.value.destroy();
|
||||
renderChart(props.positions);
|
||||
|
||||
|
|
@ -132,12 +118,12 @@ watch(
|
|||
|
||||
watch(
|
||||
() => props.snatchedPositions,
|
||||
(newData) => {
|
||||
() => {
|
||||
// myChart.value.value.destroy();
|
||||
// renderChart(props.positions);
|
||||
let positionIndex = 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++) {
|
||||
const position: Position = myChart.value.value.data.datasets[0].data[index];
|
||||
|
|
@ -149,16 +135,16 @@ watch(
|
|||
positionIndex++;
|
||||
}
|
||||
if (positionIndex <= props.snatchedPositions.length && props.snatchedPositions.includes(position.id)) {
|
||||
backgroundColorArray[index] = "##7550AE";
|
||||
backgroundColorArray[index] = '##7550AE';
|
||||
} else {
|
||||
backgroundColorArray[index] = "##7550AE";
|
||||
backgroundColorArray[index] = '##7550AE';
|
||||
// const position = myChart.value.value.data.datasets[0].data[index];
|
||||
if (position.iAmOwner) {
|
||||
backgroundColorArray[index] = "#7550AE";
|
||||
backgroundColorArray[index] = '#7550AE';
|
||||
} else if (props.dark) {
|
||||
backgroundColorArray[index] = "white";
|
||||
backgroundColorArray[index] = 'white';
|
||||
} else {
|
||||
backgroundColorArray[index] = "black";
|
||||
backgroundColorArray[index] = 'black';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -173,49 +159,46 @@ watch(
|
|||
}
|
||||
);
|
||||
|
||||
const externalTooltipHandler = (context: any) => {
|
||||
const externalTooltipHandler = (context: { chart: Chart; tooltip: TooltipModel<'bar'> }) => {
|
||||
const { chart, tooltip } = context;
|
||||
const tooltipEl = tooltipRef.value;
|
||||
|
||||
// Tooltip ausblenden, wenn keine Daten angezeigt werden sollen
|
||||
if (!tooltip.opacity) {
|
||||
tooltipEl.style.opacity = "0";
|
||||
tooltipEl.style.display = "none";
|
||||
tooltipEl.style.opacity = '0';
|
||||
tooltipEl.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
// 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
|
||||
const { offsetLeft: chartX, offsetTop: chartY } = chart.canvas;
|
||||
|
||||
// Tooltip anpassen
|
||||
tooltipEl.style.opacity = "1";
|
||||
tooltipEl.style.display = "flex";
|
||||
tooltipEl.style.position = "absolute";
|
||||
tooltipEl.style.opacity = '1';
|
||||
tooltipEl.style.display = 'flex';
|
||||
tooltipEl.style.position = 'absolute';
|
||||
|
||||
// Tooltip mittig über dem Punkt platzieren
|
||||
tooltipEl.style.left = `${chartX + tooltip.caretX}px`;
|
||||
tooltipEl.style.top = `${chartY + tooltip.y}px`;
|
||||
|
||||
// Tooltip für saubere Mitte ausrichten
|
||||
tooltipEl.style.transform = "translateX(-50%)";
|
||||
tooltipEl.style.pointerEvents = "none";
|
||||
tooltipEl.style.transform = 'translateX(-50%)';
|
||||
tooltipEl.style.pointerEvents = 'none';
|
||||
};
|
||||
|
||||
function renderChart(data: any) {
|
||||
console.log("renderChart");
|
||||
|
||||
function renderChart(data: Position[]) {
|
||||
const backgroundColors = [];
|
||||
const data1 = data.map((obj: any) => {
|
||||
const data1 = data.map(obj => {
|
||||
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++) {
|
||||
const position = data1[index];
|
||||
|
|
@ -226,43 +209,41 @@ function renderChart(data: any) {
|
|||
|
||||
// }
|
||||
if (position.iAmOwner) {
|
||||
backgroundColors.push("rgba(117,80,174, 0.8)");
|
||||
backgroundColors.push('rgba(117,80,174, 0.8)');
|
||||
} else if (props.dark) {
|
||||
backgroundColors[index] = "white";
|
||||
backgroundColors[index] = 'white';
|
||||
} else {
|
||||
backgroundColors[index] = "black";
|
||||
backgroundColors[index] = 'black';
|
||||
}
|
||||
}
|
||||
|
||||
console.log("backgroundColors", backgroundColors);
|
||||
|
||||
myChart.value = shallowRef(
|
||||
new Chart(Chart1.value, {
|
||||
type: "bar",
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: data1.map((row: any) => row.id),
|
||||
labels: data1.map(row => row.id),
|
||||
datasets: [
|
||||
{
|
||||
type: "line",
|
||||
label: "TaxRate",
|
||||
type: 'line',
|
||||
label: 'TaxRate',
|
||||
data: data1,
|
||||
backgroundColor: ["#7550AE"],
|
||||
borderColor: ["#7550AE"],
|
||||
yAxisID: "y",
|
||||
backgroundColor: ['#7550AE'],
|
||||
borderColor: ['#7550AE'],
|
||||
yAxisID: 'y',
|
||||
parsing: {
|
||||
yAxisKey: "taxRatePercentage",
|
||||
xAxisKey: "id",
|
||||
yAxisKey: 'taxRatePercentage',
|
||||
xAxisKey: 'id',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "bar",
|
||||
label: "Amount",
|
||||
type: 'bar',
|
||||
label: 'Amount',
|
||||
data: data1,
|
||||
backgroundColor: backgroundColors,
|
||||
yAxisID: "y1",
|
||||
yAxisID: 'y1',
|
||||
parsing: {
|
||||
yAxisKey: "amount",
|
||||
xAxisKey: "id",
|
||||
yAxisKey: 'amount',
|
||||
xAxisKey: 'id',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
@ -275,7 +256,7 @@ function renderChart(data: any) {
|
|||
},
|
||||
interaction: {
|
||||
intersect: false,
|
||||
mode: "index",
|
||||
mode: 'index',
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
|
|
@ -283,13 +264,13 @@ function renderChart(data: any) {
|
|||
},
|
||||
tooltip: {
|
||||
enabled: false,
|
||||
position: "nearest",
|
||||
position: 'nearest',
|
||||
external: externalTooltipHandler,
|
||||
},
|
||||
zoom: {
|
||||
pan: {
|
||||
enabled: true,
|
||||
mode: "xy",
|
||||
mode: 'xy',
|
||||
},
|
||||
limits: {
|
||||
y: { min: 0 },
|
||||
|
|
@ -301,12 +282,12 @@ function renderChart(data: any) {
|
|||
pinch: {
|
||||
enabled: true,
|
||||
},
|
||||
mode: "xy",
|
||||
onZoomComplete(object: any) {
|
||||
mode: 'xy',
|
||||
onZoomComplete(object: { chart?: Chart }) {
|
||||
// This update is needed to display up to date zoom level in the title.
|
||||
// Without this, previous zoom level is displayed.
|
||||
// The reason is: title uses the same beforeUpdate hook, and is evaluated before zoom.
|
||||
object.chart?.update("none");
|
||||
object.chart?.update('none');
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -315,31 +296,31 @@ function renderChart(data: any) {
|
|||
y: {
|
||||
// type: "linear",
|
||||
display: true,
|
||||
position: "left",
|
||||
max: Math.max(...data.map((o: any) => o.taxRate)) * 100 * 1.5,
|
||||
position: 'left',
|
||||
max: Math.max(...data.map(o => o.taxRate)) * 100 * 1.5,
|
||||
// min: 0,
|
||||
title: {
|
||||
display: true,
|
||||
text: "Tax",
|
||||
color: "#7550AE",
|
||||
text: 'Tax',
|
||||
color: '#7550AE',
|
||||
font: {
|
||||
size: 16, // Hier die Schriftgröße ändern (z. B. 16px)
|
||||
weight: "bold", // Falls du den Text fett machen möchtest
|
||||
weight: 'bold', // Falls du den Text fett machen möchtest
|
||||
},
|
||||
},
|
||||
},
|
||||
y1: {
|
||||
// type: "linear",
|
||||
display: true,
|
||||
position: "right",
|
||||
max: Math.max(...data.map((o: any) => o.amount)) * 1.5,
|
||||
position: 'right',
|
||||
max: Math.max(...data.map(o => o.amount)) * 1.5,
|
||||
title: {
|
||||
display: true,
|
||||
text: "Slots",
|
||||
color: "white",
|
||||
text: 'Slots',
|
||||
color: 'white',
|
||||
font: {
|
||||
size: 16, // Hier die Schriftgröße ändern (z. B. 16px)
|
||||
weight: "bold", // Falls du den Text fett machen möchtest
|
||||
weight: 'bold', // Falls du den Text fett machen möchtest
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
|
|
@ -357,18 +338,18 @@ function renderChart(data: any) {
|
|||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: "Positions",
|
||||
color: "white",
|
||||
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
|
||||
weight: 'bold', // Falls du den Text fett machen möchtest
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
} as any)
|
||||
} as unknown as ChartConfiguration)
|
||||
);
|
||||
}
|
||||
onMounted(() => {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,7 @@
|
|||
<template>
|
||||
<button class="chart-button">
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<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"
|
||||
/>
|
||||
<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" />
|
||||
<path
|
||||
class="stroke"
|
||||
d="M16.6191 10.5273L16.6191 15.7266C16.6191 16.0679 16.3424 16.3447 16.001 16.3447L10.8018 16.3447"
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<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>
|
||||
<div class="collapse-header">
|
||||
<div class="collapse-header-row1">
|
||||
<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">
|
||||
<span class="subheader2">ID</span> <span class="number-small">{{ props.id }}</span>
|
||||
</div>
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="tags-list">
|
||||
<f-tag v-if="tag">{{ tag }}</f-tag>
|
||||
<FTag v-if="tag">{{ tag }}</FTag>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="collapse-amount">
|
||||
|
|
@ -44,66 +44,47 @@
|
|||
</div>
|
||||
<div class="collapsed-body--actions">
|
||||
<div :class="{ 'collapse-menu-open': showTaxMenu }">
|
||||
<f-button size="small" dense block outlined v-if="adjustTaxRate.state === 'SignTransaction'"
|
||||
>Sign Transaction ...</f-button
|
||||
<FButton size="small" dense block outlined v-if="adjustTaxRate.state === 'SignTransaction'">Sign Transaction ...</FButton>
|
||||
<FButton size="small" dense outlined block v-else-if="adjustTaxRate.state === 'Waiting'" @click="unstakePosition"
|
||||
>Waiting ...</FButton
|
||||
>
|
||||
<f-button
|
||||
size="small"
|
||||
dense
|
||||
outlined
|
||||
block
|
||||
v-else-if="adjustTaxRate.state === 'Waiting'"
|
||||
@click="unstakePosition"
|
||||
>Waiting ...</f-button
|
||||
>
|
||||
<f-button
|
||||
size="small"
|
||||
dense
|
||||
block
|
||||
v-else-if="adjustTaxRate.state === 'Action' && !showTaxMenu"
|
||||
@click="showTaxMenu = true"
|
||||
>Adjust Tax Rate</f-button
|
||||
<FButton size="small" dense block v-else-if="adjustTaxRate.state === 'Action' && !showTaxMenu" @click="showTaxMenu = true"
|
||||
>Adjust Tax Rate</FButton
|
||||
>
|
||||
<template v-else>
|
||||
<div class="collapse-menu-input">
|
||||
<f-select :items="filteredTaxRates" v-model="newTaxRate"> </f-select>
|
||||
<FSelect :items="filteredTaxRates" v-model="newTaxRate"> </FSelect>
|
||||
</div>
|
||||
<div>
|
||||
<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>
|
||||
<FButton size="small" dense @click="changeTax(props.id, newTaxRate)">Confirm</FButton>
|
||||
<FButton size="small" dense outlined @click="showTaxMenu = false">Cancel</FButton>
|
||||
</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
|
||||
>
|
||||
<FButton size="small" dense block outlined v-if="unstake.state === 'SignTransaction'">Sign Transaction ...</FButton>
|
||||
<FButton size="small" dense outlined block v-else-if="unstake.state === 'Waiting'">Waiting ...</FButton>
|
||||
<FButton size="small" dense block v-else-if="unstake.state === 'Unstakeable'" @click="unstakePosition">Unstake</FButton>
|
||||
</div>
|
||||
</div>
|
||||
</f-collapse>
|
||||
</FCollapse>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import FButton from "@/components/fcomponents/FButton.vue";
|
||||
import FTag from "@/components/fcomponents/FTag.vue";
|
||||
import FSelect from "@/components/fcomponents/FSelect.vue";
|
||||
import FCollapse from "@/components/fcomponents/FCollapse.vue";
|
||||
import { compactNumber, formatBigNumber } from "@/utils/helper";
|
||||
import { useUnstake } from "@/composables/useUnstake";
|
||||
import { useAdjustTaxRate } from "@/composables/useAdjustTaxRates";
|
||||
import { computed, ref, onMounted } from "vue";
|
||||
import { getTaxDue, payTax } from "@/contracts/stake";
|
||||
import { type Position, loadPositions } from "@/composables/usePositions";
|
||||
import { useStatCollection } from "@/composables/useStatCollection";
|
||||
import FButton from '@/components/fcomponents/FButton.vue';
|
||||
import FTag from '@/components/fcomponents/FTag.vue';
|
||||
import FSelect from '@/components/fcomponents/FSelect.vue';
|
||||
import FCollapse from '@/components/fcomponents/FCollapse.vue';
|
||||
import { compactNumber, formatBigNumber } from '@/utils/helper';
|
||||
import { useUnstake } from '@/composables/useUnstake';
|
||||
import { useAdjustTaxRate } from '@/composables/useAdjustTaxRates';
|
||||
import { computed, ref, onMounted } from 'vue';
|
||||
import { getTaxDue, payTax } from '@/contracts/stake';
|
||||
import { type Position, loadPositions } from '@/composables/usePositions';
|
||||
import { useStatCollection } from '@/composables/useStatCollection';
|
||||
|
||||
import { formatUnits } from "viem";
|
||||
import { formatUnits } from 'viem';
|
||||
|
||||
const unstake = useUnstake();
|
||||
const adjustTaxRate = useAdjustTaxRate();
|
||||
|
|
@ -126,9 +107,9 @@ const loading = ref<boolean>(false);
|
|||
|
||||
const tag = computed(() => {
|
||||
if (props.taxRate < props.treshold) {
|
||||
return "Low Tax!";
|
||||
return 'Low Tax!';
|
||||
}
|
||||
return "";
|
||||
return '';
|
||||
});
|
||||
|
||||
const total = computed(() => props.amount + profit.value! + -taxPaidGes.value!);
|
||||
|
|
@ -139,48 +120,37 @@ async function changeTax(id: bigint, newTaxRate: number) {
|
|||
}
|
||||
|
||||
async function unstakePosition() {
|
||||
console.log(props.id);
|
||||
await unstake.exitPosition(props.id);
|
||||
loading.value = true;
|
||||
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||
console.log("loadPositions begin");
|
||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
await loadPositions();
|
||||
console.log("loadPositions end");
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
async function loadActivePositionData() {
|
||||
console.log("loadActivePositionData", props.position);
|
||||
|
||||
//loadTaxDue
|
||||
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));
|
||||
console.log("props.position.totalSupplyInit", props.position.totalSupplyInit);
|
||||
|
||||
console.log("multiplier", multiplier);
|
||||
const multiplier = Number(formatUnits(props.position.totalSupplyInit, 18)) / Number(formatUnits(statCollection.kraikenTotalSupply, 18));
|
||||
|
||||
profit.value =
|
||||
Number(formatUnits(statCollection.kraikenTotalSupply, 18)) * multiplier -
|
||||
Number(formatUnits(statCollection.kraikenTotalSupply, 18));
|
||||
Number(formatUnits(statCollection.kraikenTotalSupply, 18)) * multiplier - Number(formatUnits(statCollection.kraikenTotalSupply, 18));
|
||||
}
|
||||
|
||||
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) {
|
||||
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>
|
||||
|
||||
<style lang="sass">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<f-collapse class="f-collapse-history">
|
||||
<FCollapse class="f-collapse-history">
|
||||
<template v-slot:header>
|
||||
<div class="collapse-header">
|
||||
<div><span class="subheader2">Tax</span> {{ props.taxRate }} %</div>
|
||||
|
|
@ -22,16 +22,16 @@
|
|||
><span class="caption"> $KRK</span>
|
||||
</div>
|
||||
</div>
|
||||
</f-collapse>
|
||||
</FCollapse>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Position } from "@/composables/usePositions";
|
||||
import FCollapse from "@/components/fcomponents/FCollapse.vue";
|
||||
import { compactNumber } from "@/utils/helper";
|
||||
import { formatUnits } from "viem";
|
||||
import type { Position } from '@/composables/usePositions';
|
||||
import FCollapse from '@/components/fcomponents/FCollapse.vue';
|
||||
import { compactNumber } from '@/utils/helper';
|
||||
import { formatUnits } from 'viem';
|
||||
|
||||
import { computed } from "vue";
|
||||
import { computed } from 'vue';
|
||||
const props = defineProps<{
|
||||
taxRate: number;
|
||||
taxPaid: string;
|
||||
|
|
@ -42,15 +42,8 @@ const props = defineProps<{
|
|||
}>();
|
||||
|
||||
const profit = computed(() => {
|
||||
const multiplier =
|
||||
Number(formatUnits(props.position.totalSupplyInit, 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))
|
||||
);
|
||||
const multiplier = Number(formatUnits(props.position.totalSupplyInit, 18)) / Number(formatUnits(props.position.totalSupplyEnd!, 18));
|
||||
return Number(formatUnits(props.position.totalSupplyEnd!, 18)) * multiplier - Number(formatUnits(props.position.totalSupplyEnd!, 18));
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -17,30 +17,31 @@ interface Props {
|
|||
dark?: boolean;
|
||||
}
|
||||
|
||||
import { computed } from "vue";
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
size: "medium",
|
||||
size: 'medium',
|
||||
bgColor: '',
|
||||
});
|
||||
|
||||
const classObject = computed(() => ({
|
||||
"f-btn--tiny": props.size === "tiny",
|
||||
"f-btn--small": props.size === "small",
|
||||
"f-btn--medium": props.size === "medium",
|
||||
"f-btn--large": props.size === "large",
|
||||
"f-btn--dense": props.dense,
|
||||
"f-btn--disabled": props.disabled,
|
||||
"f-btn--invert": props.invert,
|
||||
"f-btn--block": props.block,
|
||||
"f-btn--outlined": props.outlined,
|
||||
"f-btn--light": props.light,
|
||||
"f-btn--dark": props.dark,
|
||||
'f-btn--tiny': props.size === 'tiny',
|
||||
'f-btn--small': props.size === 'small',
|
||||
'f-btn--medium': props.size === 'medium',
|
||||
'f-btn--large': props.size === 'large',
|
||||
'f-btn--dense': props.dense,
|
||||
'f-btn--disabled': props.disabled,
|
||||
'f-btn--invert': props.invert,
|
||||
'f-btn--block': props.block,
|
||||
'f-btn--outlined': props.outlined,
|
||||
'f-btn--light': props.light,
|
||||
'f-btn--dark': props.dark,
|
||||
}));
|
||||
|
||||
const styleObject = computed(() => {
|
||||
const returnObject: any = {};
|
||||
const returnObject: Record<string, string> = {};
|
||||
if (props.bgColor) {
|
||||
returnObject["background-color"] = props.bgColor;
|
||||
returnObject['background-color'] = props.bgColor;
|
||||
}
|
||||
return returnObject;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, computed } from "vue";
|
||||
import { onMounted, ref, computed } from 'vue';
|
||||
|
||||
const fCard = ref();
|
||||
|
||||
|
|
@ -23,12 +23,17 @@ interface Props {
|
|||
title?: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {});
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
bgColor: '',
|
||||
borderWidth: undefined,
|
||||
boxShadow: '',
|
||||
title: '',
|
||||
});
|
||||
|
||||
const computedStyles = computed(() => ({
|
||||
"border-width": props.borderWidth,
|
||||
"background-color": props.bgColor,
|
||||
"box-shadow": props.boxShadow,
|
||||
'border-width': props.borderWidth,
|
||||
'background-color': props.bgColor,
|
||||
'box-shadow': props.boxShadow,
|
||||
}));
|
||||
|
||||
onMounted(() => {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<div class="f-collapse">
|
||||
<div class="f-collapse-wrapper" :class="{ loading: props.loading }">
|
||||
<template v-if="loading">
|
||||
<f-loader></f-loader>
|
||||
<FLoader></FLoader>
|
||||
</template>
|
||||
<div class="f-collapse-inner">
|
||||
<slot name="header"></slot>
|
||||
|
|
@ -27,23 +27,28 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import {Icon} from "@iconify/vue";
|
||||
import FLoader from "@/components/fcomponents/FLoader.vue";
|
||||
import { ref } from 'vue';
|
||||
import { Icon } from '@iconify/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<{
|
||||
loading?: boolean;
|
||||
}>();
|
||||
|
||||
const isShow = ref(false);
|
||||
const isShow = ref<boolean>(false);
|
||||
const openClose = () => {
|
||||
isShow.value = !isShow.value;
|
||||
if (isShow.value) {
|
||||
emit("collapse:opened")
|
||||
emit('collapse:opened');
|
||||
} else {
|
||||
emit("collapse:closed")
|
||||
emit('collapse:closed');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -92,6 +97,4 @@ const openClose = () => {
|
|||
to
|
||||
max-height: 0px
|
||||
overflow: hidden
|
||||
|
||||
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@
|
|||
<div class="f-input" :class="classObject">
|
||||
<div class="f-input-label subheader2">
|
||||
<label v-if="props.label" :for="name">{{ props.label }}</label>
|
||||
<icon>
|
||||
<Icon>
|
||||
<template v-slot:text v-if="slots.info">
|
||||
<slot name="info"></slot>
|
||||
</template>
|
||||
</icon>
|
||||
</Icon>
|
||||
</div>
|
||||
<div class="f-input__wrapper" ref="inputWrapper" @click="setFocus">
|
||||
<input
|
||||
|
|
@ -23,7 +23,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="f-input--details" v-if="slots.details">
|
||||
<slot name="details"> </slot>
|
||||
</div>
|
||||
|
|
@ -31,18 +30,18 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, getCurrentInstance, useSlots, ref } from "vue";
|
||||
import useClickOutside from "@/composables/useClickOutside"
|
||||
import { computed, getCurrentInstance, useSlots, ref } from 'vue';
|
||||
import useClickOutside from '@/composables/useClickOutside';
|
||||
|
||||
import Icon from "@/components/icons/IconInfo.vue";
|
||||
import Icon from '@/components/icons/IconInfo.vue';
|
||||
interface Props {
|
||||
size?: string;
|
||||
info?: string;
|
||||
label?: string;
|
||||
disabled?: boolean;
|
||||
modelValue?: any;
|
||||
modelValue?: string | number;
|
||||
type?: string;
|
||||
readonly?: boolean
|
||||
readonly?: boolean;
|
||||
}
|
||||
|
||||
const slots = useSlots();
|
||||
|
|
@ -53,40 +52,47 @@ const instance = getCurrentInstance();
|
|||
const name = `f-input-${instance!.uid}`;
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
size: "normal",
|
||||
size: 'normal',
|
||||
disabled: false,
|
||||
readonly: false,
|
||||
type: "string",
|
||||
type: 'string',
|
||||
info: '',
|
||||
label: '',
|
||||
modelValue: '',
|
||||
});
|
||||
|
||||
useClickOutside(inputWrapper, () => {
|
||||
removeFocus();
|
||||
})
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
interface Emits {
|
||||
(e: 'update:modelValue', value: string): void;
|
||||
}
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
function updateModelValue($event: any) {
|
||||
emit("update:modelValue", $event.target.value);
|
||||
function updateModelValue($event: Event) {
|
||||
const target = $event.target as HTMLInputElement;
|
||||
emit('update:modelValue', target.value);
|
||||
}
|
||||
|
||||
const classObject = computed(() => ({
|
||||
"f-input--normal": props.size === "normal",
|
||||
"f-input--big": props.size === "big",
|
||||
"f-input--disabled": props.disabled,
|
||||
'f-input--normal': props.size === 'normal',
|
||||
'f-input--big': props.size === 'big',
|
||||
'f-input--disabled': props.disabled,
|
||||
}));
|
||||
|
||||
function removeFocus() {
|
||||
const target = inputWrapper.value as HTMLElement
|
||||
target.classList.remove("f-input__wrapper--focused")
|
||||
const target = inputWrapper.value as HTMLElement;
|
||||
target.classList.remove('f-input__wrapper--focused');
|
||||
}
|
||||
|
||||
function setFocus(event:MouseEvent){
|
||||
console.log("setFocus");
|
||||
function setFocus(_event: MouseEvent) {
|
||||
// console.log("setFocus");
|
||||
if (props.disabled) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
const target = inputWrapper.value as HTMLElement
|
||||
target.classList.add("f-input__wrapper--focused")
|
||||
const target = inputWrapper.value as HTMLElement;
|
||||
target.classList.add('f-input__wrapper--focused');
|
||||
}
|
||||
</script>
|
||||
<style lang="sass">
|
||||
|
|
@ -131,5 +137,4 @@ function setFocus(event:MouseEvent){
|
|||
display: flex
|
||||
align-items: center
|
||||
margin-right: 10px
|
||||
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -23,15 +23,16 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useSlots } from "vue";
|
||||
const props = defineProps({
|
||||
name: String,
|
||||
price: [String, Number],
|
||||
variant: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 1,
|
||||
},
|
||||
import { useSlots, withDefaults } from 'vue';
|
||||
interface Props {
|
||||
name?: string;
|
||||
price?: string | number;
|
||||
variant?: number;
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
variant: 1,
|
||||
name: '',
|
||||
price: '',
|
||||
});
|
||||
|
||||
const slots = useSlots();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,14 @@
|
|||
<template>
|
||||
<div class="o-select" @click="clickSelect" ref="componentRef">
|
||||
<f-input hide-details :clearable="props.clearable" :label="props.label" :selectedable="false"
|
||||
:focus="showList" :modelValue="`${year} % yearly`" readonly>
|
||||
<FInput
|
||||
hide-details
|
||||
:clearable="props.clearable"
|
||||
:label="props.label ?? undefined"
|
||||
:selectedable="false"
|
||||
:focus="showList"
|
||||
:modelValue="`${year} % yearly`"
|
||||
readonly
|
||||
>
|
||||
<template #info v-if="slots.info">
|
||||
<slot name="info"></slot>
|
||||
</template>
|
||||
|
|
@ -9,11 +16,18 @@
|
|||
<Icon class="toggle-collapse" v-if="showList" icon="mdi:chevron-down"></Icon>
|
||||
<Icon class="toggle-collapse" v-else icon="mdi:chevron-up"></Icon>
|
||||
</template>
|
||||
</f-input>
|
||||
</FInput>
|
||||
<div class="select-list-wrapper" v-show="showList" ref="selectList">
|
||||
<div class="select-list-inner" @click.stop>
|
||||
<div class="select-list-item" v-for="(item, index) in props.items" :key="item.year" :class="{'active': year === item.year, 'hovered': activeIndex === index}"
|
||||
@click.stop="clickItem(item)" @mouseenter="mouseEnter($event, index)" @mouseleave="mouseLeave($event, index)">
|
||||
<div
|
||||
class="select-list-item"
|
||||
v-for="(item, index) in props.items"
|
||||
:key="item.year"
|
||||
:class="{ active: year === item.year, hovered: activeIndex === index }"
|
||||
@click.stop="clickItem(item)"
|
||||
@mouseenter="mouseEnter($event, index)"
|
||||
@mouseleave="mouseLeave($event, index)"
|
||||
>
|
||||
<div class="circle">
|
||||
<div class="active" v-if="year === item.year"></div>
|
||||
<div class="hovered" v-else-if="activeIndex === index"></div>
|
||||
|
|
@ -34,130 +48,94 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, getCurrentInstance, type ComputedRef, useSlots } from "vue"
|
||||
import FInput from "@/components/fcomponents/FInput.vue"
|
||||
import useClickOutside from "@/composables/useClickOutside"
|
||||
import {Icon} from "@iconify/vue";
|
||||
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const slots = useSlots();
|
||||
import { ref, computed, onMounted, useSlots, withDefaults } from 'vue';
|
||||
import FInput from '@/components/fcomponents/FInput.vue';
|
||||
import useClickOutside from '@/composables/useClickOutside';
|
||||
import { Icon } from '@iconify/vue';
|
||||
|
||||
interface Item {
|
||||
year: number;
|
||||
daily: number;
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
items: {
|
||||
type: [Array<Item>],
|
||||
required: false,
|
||||
default: []
|
||||
},
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
itemTitle: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "label"
|
||||
},
|
||||
itemValue: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "value"
|
||||
},
|
||||
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
|
||||
interface Props {
|
||||
items?: Item[];
|
||||
clearable?: boolean;
|
||||
itemTitle?: string;
|
||||
itemValue?: string;
|
||||
label?: string | null;
|
||||
modelValue?: number | null;
|
||||
value?: string | null;
|
||||
editor?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
items: () => [],
|
||||
clearable: false,
|
||||
itemTitle: 'label',
|
||||
itemValue: 'value',
|
||||
label: null,
|
||||
modelValue: null,
|
||||
value: null,
|
||||
editor: false,
|
||||
});
|
||||
|
||||
const showList = ref(<boolean>false);
|
||||
const slotElements = ref(<any[]>[])
|
||||
const componentRef = ref(<any>null);
|
||||
const selectList = ref();
|
||||
interface Emits {
|
||||
(e: 'update:modelValue', value: number): void;
|
||||
}
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
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, () => {
|
||||
|
||||
showList.value = false
|
||||
})
|
||||
|
||||
showList.value = false;
|
||||
});
|
||||
|
||||
const year = computed({
|
||||
// getter
|
||||
get() {
|
||||
return props.modelValue || props.items[0].year
|
||||
return props.modelValue || props.items[0].year;
|
||||
},
|
||||
// setter
|
||||
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;
|
||||
target.classList.add("active")
|
||||
target.classList.add('active');
|
||||
}
|
||||
|
||||
function mouseLeave(event:MouseEvent, index:number){
|
||||
const elements = selectList.value.querySelectorAll('.select-list-item');
|
||||
elements.forEach((element:HTMLElement) => {
|
||||
element.classList.remove("active")
|
||||
function mouseLeave(_event: MouseEvent, _index: number) {
|
||||
const elements = selectList.value?.querySelectorAll('.select-list-item');
|
||||
elements?.forEach(element => {
|
||||
(element as HTMLElement).classList.remove('active');
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
onMounted(() => {});
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function clickSelect(event: any) {
|
||||
function clickSelect(_event: unknown) {
|
||||
showList.value = !showList.value;
|
||||
}
|
||||
|
||||
|
||||
function clickItem(item: any) {
|
||||
console.log("item", item);
|
||||
year.value = item.year
|
||||
function clickItem(item: { year: number }) {
|
||||
// console.log("item", item);
|
||||
year.value = item.year;
|
||||
showList.value = false;
|
||||
console.log("showList.value", showList.value);
|
||||
// console.log("showList.value", showList.value);
|
||||
// emit('input', item)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="sass">
|
||||
.o-select
|
||||
position: relative
|
||||
|
|
@ -228,7 +206,4 @@ function clickItem(item: any) {
|
|||
&::-webkit-scrollbar-thumb
|
||||
border-radius: 10px
|
||||
background-color: lightgrey
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
|
|
@ -26,16 +26,19 @@
|
|||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<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<{
|
||||
modelValue: number;
|
||||
|
|
@ -45,29 +48,33 @@ const props = defineProps<{
|
|||
|
||||
const minPercentage = computed(() => {
|
||||
if (!props.min || !props.max) {
|
||||
return 0n;
|
||||
return 0;
|
||||
}
|
||||
return (props.min * 100) / props.max;
|
||||
});
|
||||
|
||||
function updateModelValue(event: any) {
|
||||
emit("update:modelValue", event.target.valueAsNumber);
|
||||
function updateModelValue(event: Event) {
|
||||
emit('update:modelValue', (event.target as HTMLInputElement).valueAsNumber);
|
||||
}
|
||||
|
||||
|
||||
const sliderValue = computed({
|
||||
// getter
|
||||
get() {
|
||||
return props.modelValue || props.min;
|
||||
return Number.isFinite(props.modelValue) ? props.modelValue : props.min;
|
||||
},
|
||||
// setter
|
||||
set(newValue) {
|
||||
emit("update:modelValue", newValue);
|
||||
set(newValue: number) {
|
||||
emit('update:modelValue', newValue);
|
||||
},
|
||||
});
|
||||
|
||||
const percentageDot = computed(() => {
|
||||
let percentage = (100 * (sliderValue.value - props.min)) / (props.max - props.min);
|
||||
const range = props.max - props.min;
|
||||
if (range <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let percentage = (100 * (sliderValue.value - props.min)) / range;
|
||||
|
||||
if (percentage < 20) {
|
||||
percentage = percentage + 1;
|
||||
|
|
@ -77,31 +84,34 @@ const percentageDot = computed(() => {
|
|||
percentage = percentage - 3;
|
||||
}
|
||||
|
||||
return percentage
|
||||
return percentage;
|
||||
});
|
||||
|
||||
const settings = {
|
||||
fill: "#7550AE",
|
||||
background: "#000000",
|
||||
fill: '#7550AE',
|
||||
background: '#000000',
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
sliderInput.value.addEventListener("input", setSliderValue);
|
||||
if (sliderInput.value) {
|
||||
sliderInput.value.addEventListener('input', setSliderValue);
|
||||
}
|
||||
});
|
||||
|
||||
function setSliderValue(event: any){
|
||||
sliderValue.value = event.target.value;
|
||||
function setSliderValue(event: Event) {
|
||||
const target = event.target as HTMLInputElement | null;
|
||||
if (target) {
|
||||
sliderValue.value = target.valueAsNumber;
|
||||
}
|
||||
}
|
||||
|
||||
function testMove(event: any) {
|
||||
}
|
||||
function testMove(_event: unknown) {}
|
||||
|
||||
onUnmounted(() => {
|
||||
console.log("sliderInput.value", sliderInput.value);
|
||||
if (sliderInput.value) {
|
||||
sliderInput.value.removeEventListener("input", setSliderValue);
|
||||
sliderInput.value.removeEventListener('input', setSliderValue);
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
|
@ -166,7 +176,7 @@ $range-label-width: 60px !default;
|
|||
.range-slider {
|
||||
width: $range-width;
|
||||
position: relative;
|
||||
accent-color: #7550AE;
|
||||
accent-color: #7550ae;
|
||||
}
|
||||
|
||||
.range-slider__range {
|
||||
|
|
@ -242,7 +252,7 @@ $range-label-width: 60px !default;
|
|||
border-top: 7px solid transparent;
|
||||
border-right: 7px solid $range-label-color;
|
||||
border-bottom: 7px solid transparent;
|
||||
content: "";
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,13 +5,20 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onBeforeMount, getCurrentInstance, ref, computed, watch, inject } from "vue";
|
||||
const instance = ref<any>();
|
||||
import { onMounted, onBeforeMount, getCurrentInstance, ref, watch, inject, type ComponentInternalInstance } from 'vue';
|
||||
|
||||
const isActive = ref<any>(null);
|
||||
interface Tab {
|
||||
uid: number;
|
||||
name: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
const value = inject("value");
|
||||
var updateTab: any = inject("updateTab");
|
||||
const instance = ref<ComponentInternalInstance | null>();
|
||||
|
||||
const isActive = ref<unknown>(null);
|
||||
|
||||
const value = inject('value');
|
||||
const updateTab: (tab: Tab) => void = inject('updateTab') as (tab: Tab) => void;
|
||||
|
||||
defineExpose({
|
||||
isActive,
|
||||
|
|
@ -21,9 +28,9 @@ onBeforeMount(() => {});
|
|||
onMounted(() => {
|
||||
instance.value = getCurrentInstance();
|
||||
|
||||
var addTab: any = inject("addTab");
|
||||
const addTab = inject('addTab') as (tab: Tab) => void;
|
||||
addTab({
|
||||
uid: instance.value!.uid,
|
||||
uid: instance.value?.uid ?? 0,
|
||||
name: props.name,
|
||||
label: props.label,
|
||||
});
|
||||
|
|
@ -38,25 +45,18 @@ onMounted(() => {
|
|||
// })
|
||||
|
||||
//Props
|
||||
const props = defineProps({
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: null,
|
||||
},
|
||||
name: {
|
||||
type: [String],
|
||||
required: true,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
interface Props {
|
||||
label: string;
|
||||
name: string;
|
||||
}
|
||||
const props = defineProps<Props>();
|
||||
|
||||
watch(props, (newValue, oldValue) => {
|
||||
console.log("newValue", newValue);
|
||||
console.log("instance", instance.value);
|
||||
watch(props, (_newValue, _oldValue) => {
|
||||
// console.log("newValue", newValue);
|
||||
// console.log("instance", instance.value);
|
||||
|
||||
updateTab({
|
||||
uid: instance.value!.uid,
|
||||
uid: instance.value?.uid ?? 0,
|
||||
name: props.name,
|
||||
label: props.label,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,14 +1,7 @@
|
|||
<template>
|
||||
<div class="f-tabs" ref="tabsRef">
|
||||
<div class="f-tabs__header" ref="headerRef">
|
||||
<div
|
||||
class="f-tab"
|
||||
ref="tabsRef1"
|
||||
:id="`f-tab-${tab.uid}`"
|
||||
v-for="tab in tabs"
|
||||
:key="tab.uid"
|
||||
@click="setActive(tab)"
|
||||
>
|
||||
<div class="f-tab" ref="tabsRef1" :id="`f-tab-${tab.uid}`" v-for="tab in tabs" :key="tab.uid" @click="setActive(tab)">
|
||||
<h5>{{ tab.label }}</h5>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -19,40 +12,49 @@
|
|||
</template>
|
||||
|
||||
<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({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
interface Props {
|
||||
modelValue?: string | null;
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
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 tabsRef1 = ref<HTMLElement>();
|
||||
const headerRef = ref<HTMLElement>();
|
||||
const tabsArray = ref<Array<HTMLElement>>();
|
||||
const tabsComponents = ref<Array<any>>();
|
||||
const content = ref<any>();
|
||||
const target = ref<any>();
|
||||
const slots = ref<any>();
|
||||
const value = ref<any>();
|
||||
const tabsTest = ref<any>();
|
||||
const tabs = ref<Array<any>>([]);
|
||||
provide("value", value);
|
||||
provide("addTab", (tab: any) => {
|
||||
const _tabsArray = ref<Array<HTMLElement>>();
|
||||
const _tabsComponents = ref<Array<unknown>>();
|
||||
const _content = ref<unknown>();
|
||||
const _target = ref<unknown>();
|
||||
const _slots = ref<unknown>();
|
||||
const value = ref<unknown>();
|
||||
const tabsTest = ref<unknown>();
|
||||
const tabs = ref<Array<Tab>>([]);
|
||||
provide('value', value);
|
||||
provide('addTab', (tab: Tab) => {
|
||||
tabs.value.push(tab);
|
||||
});
|
||||
|
||||
provide("updateTab", (tab: any) => {
|
||||
let changedTabIndex = tabs.value.findIndex((obj) => obj.uid === tab.uid);
|
||||
provide('updateTab', (tab: Tab) => {
|
||||
const changedTabIndex = tabs.value.findIndex(obj => obj.uid === tab.uid);
|
||||
if (changedTabIndex > -1) {
|
||||
tabs.value[changedTabIndex] = tab;
|
||||
}
|
||||
|
|
@ -62,27 +64,32 @@ provide("updateTab", (tab: any) => {
|
|||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
if (props.modelValue) {
|
||||
const tab = tabs.value.find((obj) => obj.name === props.modelValue)
|
||||
const tab = tabs.value.find(obj => obj.name === props.modelValue);
|
||||
if (tab) {
|
||||
setActive(tab);
|
||||
|
||||
}
|
||||
} else {
|
||||
setActive(tabs.value[0]);
|
||||
const firstTab = tabs.value[0];
|
||||
if (firstTab) {
|
||||
setActive(firstTab);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function setActive(tab: any) {
|
||||
|
||||
function setActive(tab: Tab) {
|
||||
nextTick(() => {
|
||||
const tabElement: any = headerRef.value?.querySelector(`#f-tab-${tab.uid}`);
|
||||
var array = Array.prototype.slice.call(tabsRef.value?.children[0].children);
|
||||
const tabElement = headerRef.value?.querySelector(`#f-tab-${tab.uid}`) as HTMLElement | null;
|
||||
const array = Array.prototype.slice.call(tabsRef.value?.children[0].children);
|
||||
|
||||
array.forEach((element: HTMLElement) => {
|
||||
element.classList.remove("f-tab--active");
|
||||
element.classList.remove('f-tab--active');
|
||||
});
|
||||
tabElement.classList.add("f-tab--active");
|
||||
if (tabElement) {
|
||||
tabElement.classList.add('f-tab--active');
|
||||
}
|
||||
value.value = tab.name;
|
||||
emit("update:modelValue", tab.name);
|
||||
emit('update:modelValue', tab.name);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ describe('FTabs.vue', () => {
|
|||
|
||||
expect(sections[0].attributes('style')).not.toContain('display: none');
|
||||
expect(sections[1].attributes('style')).toContain('display: none');
|
||||
|
||||
});
|
||||
|
||||
it('switches to the correct tab on click', async () => {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from "vue";
|
||||
import { ref } from 'vue';
|
||||
|
||||
const svgPath = ref();
|
||||
|
||||
|
|
@ -24,6 +24,7 @@ interface Props {
|
|||
color?: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {});
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
color: 'currentColor',
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<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">
|
||||
<svg :width="props.size" :height="props.size" viewBox="0 0 14 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
|
|
@ -17,26 +17,23 @@
|
|||
</div>
|
||||
<template #content>
|
||||
<slot name="text"></slot>
|
||||
|
||||
</template>
|
||||
</tippy>
|
||||
</Tippy>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useSlots, ref, onMounted } from "vue";
|
||||
import { Tippy } from "vue-tippy";
|
||||
import "tippy.js/dist/tippy.css"; // optional for styling
|
||||
import { useSlots } from 'vue';
|
||||
import { Tippy } from 'vue-tippy';
|
||||
import 'tippy.js/dist/tippy.css'; // optional for styling
|
||||
const slots = useSlots();
|
||||
|
||||
interface Props {
|
||||
size?: string;
|
||||
}
|
||||
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
size: "15px",
|
||||
size: '15px',
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
|
|
@ -58,5 +55,4 @@ const props = withDefaults(defineProps<Props>(), {
|
|||
border-radius: var(--border-radius)
|
||||
// @media (min-width: 768px)
|
||||
// max-width: 400px
|
||||
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
/>
|
||||
<path
|
||||
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>
|
||||
<defs>
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from "vue";
|
||||
import { ref } from 'vue';
|
||||
|
||||
const svgPath = ref();
|
||||
|
||||
|
|
@ -28,6 +28,7 @@ interface Props {
|
|||
color?: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {});
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
color: 'currentColor',
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
</svg>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from "vue";
|
||||
import { ref } from 'vue';
|
||||
|
||||
const svgPath = ref();
|
||||
|
||||
|
|
@ -25,6 +25,7 @@ interface Props {
|
|||
color?: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {});
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
color: 'currentColor',
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,23 +1,23 @@
|
|||
<template>
|
||||
<template v-if="status === 'disconnected'">
|
||||
<f-button class="connect-button--disconnected">Connect</f-button>
|
||||
<FButton class="connect-button--disconnected">Connect</FButton>
|
||||
</template>
|
||||
<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" />
|
||||
<div>{{ getAddressShortName(address!) }}</div>
|
||||
</f-button>
|
||||
</FButton>
|
||||
</template>
|
||||
<template v-else>
|
||||
<f-button class="connect-button--loading">loading</f-button>
|
||||
<FButton class="connect-button--loading">loading</FButton>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import FButton from "@/components/fcomponents/FButton.vue";
|
||||
import { getAddressShortName } from "@/utils/helper";
|
||||
import { useAccount } from "@wagmi/vue";
|
||||
import { getBlocky } from "@/utils/blockies";
|
||||
import FButton from '@/components/fcomponents/FButton.vue';
|
||||
import { getAddressShortName } from '@/utils/helper';
|
||||
import { useAccount } from '@wagmi/vue';
|
||||
import { getBlocky } from '@/utils/blockies';
|
||||
|
||||
const { address, status } = useAccount();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -14,18 +14,16 @@
|
|||
</div>
|
||||
<h6 class="connected-tokens-headline">Your Tokens</h6>
|
||||
<div class="connected-tokens">
|
||||
<f-output name="$KRK" :price="harbAmount" :variant="3">
|
||||
<FOutput name="$KRK" :price="harbAmount" :variant="3">
|
||||
<template #end>
|
||||
<f-button size="small" dense
|
||||
><a :href="chain.chainData?.uniswap" target="_blank">Buy</a></f-button
|
||||
>
|
||||
<FButton size="small" dense><a :href="chain.chainData?.uniswap" target="_blank">Buy</a></FButton>
|
||||
</template>
|
||||
</f-output>
|
||||
<f-output name="Staked $KRK" :price="compactNumber(stakedAmount)" :variant="3">
|
||||
</FOutput>
|
||||
<FOutput name="Staked $KRK" :price="compactNumber(stakedAmount)" :variant="3">
|
||||
<template #end>
|
||||
<f-button size="small" dense @click="stakeLink">Stake</f-button>
|
||||
<FButton size="small" dense @click="stakeLink">Stake</FButton>
|
||||
</template>
|
||||
</f-output>
|
||||
</FOutput>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="status === 'disconnected'">
|
||||
|
|
@ -56,32 +54,24 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getCurrentInstance, computed } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { getAddressShortName, compactNumber, formatBigIntDivision } from "@/utils/helper";
|
||||
import { getBlocky } from "@/utils/blockies";
|
||||
import FButton from "@/components/fcomponents/FButton.vue";
|
||||
import FOutput from "@/components/fcomponents/FOutput.vue";
|
||||
import { getCurrentInstance, computed } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { getAddressShortName, compactNumber, formatBigIntDivision } from '@/utils/helper';
|
||||
import { getBlocky } from '@/utils/blockies';
|
||||
import FButton from '@/components/fcomponents/FButton.vue';
|
||||
import FOutput from '@/components/fcomponents/FOutput.vue';
|
||||
|
||||
// import { usePositions } from "@/composables/usePositions";
|
||||
// import { useWallet } from "@/composables/useWallet";
|
||||
import { usePositions } from "@/composables/usePositions";
|
||||
import { useWallet } from "@/composables/useWallet";
|
||||
import { useChain } from "@/composables/useChain";
|
||||
import { usePositions } from '@/composables/usePositions';
|
||||
import { useWallet } from '@/composables/useWallet';
|
||||
import { useChain } from '@/composables/useChain';
|
||||
|
||||
import {
|
||||
useAccount,
|
||||
useDisconnect,
|
||||
useConnect,
|
||||
useChainId,
|
||||
useBalance,
|
||||
type CreateConnectorFn,
|
||||
type Connector,
|
||||
} from "@wagmi/vue";
|
||||
import { useAccount, useDisconnect, useConnect, useChainId, type CreateConnectorFn, type Connector } from '@wagmi/vue';
|
||||
|
||||
const { address, status } = useAccount();
|
||||
const { disconnect } = useDisconnect();
|
||||
const { connectors, connect, error } = useConnect();
|
||||
const { connectors, connect } = useConnect();
|
||||
const router = useRouter();
|
||||
const chainId = useChainId();
|
||||
const { myActivePositions } = usePositions();
|
||||
|
|
@ -100,7 +90,7 @@ const harbAmount = computed(() => {
|
|||
if (wallet.balance?.value) {
|
||||
return compactNumber(formatBigIntDivision(wallet.balance?.value, 10n ** BigInt(wallet.balance.decimals)));
|
||||
} else {
|
||||
return "0";
|
||||
return '0';
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -142,19 +132,19 @@ const instance = getCurrentInstance();
|
|||
// }
|
||||
|
||||
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
|
||||
async function connectWallet(connector: CreateConnectorFn | Connector, chainId: any) {
|
||||
console.log("connector", connector);
|
||||
console.log("connector", connector.name);
|
||||
async function connectWallet(connector: CreateConnectorFn | Connector, chainId: number) {
|
||||
// console.log("connector", connector);
|
||||
// console.log("connector", connector.name);
|
||||
connect({ connector, chainId });
|
||||
closeModal();
|
||||
}
|
||||
|
||||
function stakeLink() {
|
||||
router.push("/dashboard#stake");
|
||||
router.push('/dashboard#stake');
|
||||
closeModal();
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
<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-headline subheader2">Follow HARBERG Protocol</div>
|
||||
<div class="social-links">
|
||||
<social-button :dark="props.dark" type="discord" :href="discord"></social-button>
|
||||
<social-button :dark="props.dark" type="telegram" :href="telegram"></social-button>
|
||||
<social-button :dark="props.dark" type="twitter" :href="twitter"></social-button>
|
||||
<SocialButton :dark="props.dark" type="discord" :href="discord"></SocialButton>
|
||||
<SocialButton :dark="props.dark" type="telegram" :href="telegram"></SocialButton>
|
||||
<SocialButton :dark="props.dark" type="twitter" :href="twitter"></SocialButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import socialButton from '@/components/socialButton.vue';
|
||||
import SocialButton from '@/components/SocialButton.vue';
|
||||
|
||||
interface Props {
|
||||
dark?: boolean;
|
||||
}
|
||||
|
||||
const discord = import.meta.env.VITE_DISCORD
|
||||
const discord = import.meta.env.VITE_DISCORD;
|
||||
const telegram = import.meta.env.VITE_TELEGRAM;
|
||||
const twitter = import.meta.env.VITE_TWITTER;
|
||||
|
||||
|
|
|
|||
|
|
@ -21,17 +21,12 @@
|
|||
</nav>
|
||||
<template v-if="!isMobile">
|
||||
<div class="vertical-line"></div>
|
||||
<network-changer></network-changer>
|
||||
<NetworkChanger></NetworkChanger>
|
||||
<div class="vertical-line"></div>
|
||||
</template>
|
||||
<connect-button @click="showPanel = true" v-if="!isMobile"></connect-button>
|
||||
<icon-login @click="showPanel = true" v-else-if="isMobile && !address"></icon-login>
|
||||
<img
|
||||
@click="showPanel = true"
|
||||
v-else-if="isMobile && address"
|
||||
:src="getBlocky(address)"
|
||||
alt="avatar"
|
||||
/>
|
||||
<ConnectButton @click="showPanel = true" v-if="!isMobile"></ConnectButton>
|
||||
<IconLogin @click="showPanel = true" v-else-if="isMobile && !address"></IconLogin>
|
||||
<img @click="showPanel = true" v-else-if="isMobile && address" :src="getBlocky(address)" alt="avatar" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -39,27 +34,26 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getBlocky } from "@/utils/blockies";
|
||||
import { RouterLink, useRouter } from "vue-router";
|
||||
import IconLogin from "@/components/icons/IconLogin.vue";
|
||||
import ThemeToggle from "@/components/layouts/ThemeToggle.vue";
|
||||
import NetworkChanger from "@/components/layouts/NetworkChanger.vue";
|
||||
import ConnectButton from "@/components/layouts/ConnectButton.vue";
|
||||
import { computed, inject, ref } from "vue";
|
||||
import { useAccount } from "@wagmi/vue";
|
||||
import { useDark } from "@/composables/useDark";
|
||||
const {darkTheme} = useDark();
|
||||
import { getBlocky } from '@/utils/blockies';
|
||||
import { RouterLink, useRouter } from 'vue-router';
|
||||
import IconLogin from '@/components/icons/IconLogin.vue';
|
||||
// import ThemeToggle from "@/components/layouts/ThemeToggle.vue";
|
||||
import NetworkChanger from '@/components/layouts/NetworkChanger.vue';
|
||||
import ConnectButton from '@/components/layouts/ConnectButton.vue';
|
||||
import { inject, ref } from 'vue';
|
||||
import { useAccount } from '@wagmi/vue';
|
||||
import { useDark } from '@/composables/useDark';
|
||||
const { darkTheme: _darkTheme } = useDark();
|
||||
const { address } = useAccount();
|
||||
const router = useRouter();
|
||||
|
||||
const showPanel = inject<boolean>('showPanel', false);
|
||||
const isMobile = inject<boolean>('isMobile', false);
|
||||
|
||||
const showPanel = inject<boolean>("showPanel", false);
|
||||
const isMobile = inject<boolean>("isMobile", false);
|
||||
|
||||
const dark1 = ref(false)
|
||||
const _dark1 = ref(false);
|
||||
const routes = router.getRoutes();
|
||||
const navbarRoutes = routes.flatMap((obj) => {
|
||||
if (obj.meta.group === "navbar") {
|
||||
const navbarRoutes = routes.flatMap(obj => {
|
||||
if (obj.meta.group === 'navbar') {
|
||||
return { title: obj.meta.title, name: obj.name, path: obj.path };
|
||||
} else {
|
||||
return [];
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="network-changer" v-click-outside="closeMenu">
|
||||
<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-else class="toggle-icon" icon="mdi:chevron-up"></Icon>
|
||||
</div>
|
||||
|
|
@ -10,14 +10,14 @@
|
|||
<div class="list-inner" ref="listInner">
|
||||
<div class="list--element" v-for="chain in chains" :key="chain.id">
|
||||
<template v-if="chain.id === wallet.account.chainId">
|
||||
<icon-base></icon-base>
|
||||
<IconBase></IconBase>
|
||||
<div>{{ chain.name }}</div>
|
||||
<div>Connected</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<icon-base></icon-base>
|
||||
<IconBase></IconBase>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -27,12 +27,12 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import IconBase from "@/components/icons/IconBase.vue";
|
||||
import { ref } from "vue";
|
||||
import { useChains, useChainId, useSwitchChain } from "@wagmi/vue";
|
||||
import {Icon} from "@iconify/vue";
|
||||
import FButton from "@/components/fcomponents/FButton.vue";
|
||||
import { useWallet } from "@/composables/useWallet";
|
||||
import IconBase from '@/components/icons/IconBase.vue';
|
||||
import { ref } from 'vue';
|
||||
import { useChains, useSwitchChain } from '@wagmi/vue';
|
||||
import { Icon } from '@iconify/vue';
|
||||
import FButton from '@/components/fcomponents/FButton.vue';
|
||||
import { useWallet } from '@/composables/useWallet';
|
||||
const chains = useChains();
|
||||
const wallet = useWallet();
|
||||
const { switchChain } = useSwitchChain();
|
||||
|
|
@ -42,13 +42,12 @@ const listInner = ref();
|
|||
|
||||
function closeMenu() {
|
||||
if (showMenu.value) {
|
||||
console.log("tesat");
|
||||
// console.log("tesat");
|
||||
showMenu.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function switchNetwork(chain: any) {
|
||||
console.log("chain", chain);
|
||||
async function switchNetwork(chain: { id: number }) {
|
||||
switchChain({ chainId: chain.id });
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -8,15 +8,19 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { inject, watchEffect } from "vue";
|
||||
import { inject } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: Boolean,
|
||||
});
|
||||
interface Props {
|
||||
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>
|
||||
|
||||
<style lang="sass">
|
||||
|
|
|
|||
|
|
@ -13,104 +13,32 @@
|
|||
style="enable-background: new 0 0 496 496"
|
||||
xml:space="preserve"
|
||||
>
|
||||
<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"
|
||||
/>
|
||||
<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 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" />
|
||||
<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
|
||||
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 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
|
||||
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 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
|
||||
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 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
|
||||
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"
|
||||
/>
|
||||
<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"
|
||||
/>
|
||||
<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" />
|
||||
<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"
|
||||
/>
|
||||
<rect x="240" width="16" height="72" />
|
||||
<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"
|
||||
/>
|
||||
<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" />
|
||||
<rect y="240" width="72" height="16" />
|
||||
|
||||
<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"
|
||||
/>
|
||||
<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" />
|
||||
<rect x="240" y="424" width="16" height="72" />
|
||||
|
||||
<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"
|
||||
/>
|
||||
<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" />
|
||||
<rect x="424" y="240" width="72" height="16" />
|
||||
<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"
|
||||
/>
|
||||
<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" />
|
||||
</svg>
|
||||
<svg
|
||||
version="1.1"
|
||||
|
|
@ -137,14 +65,21 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
interface Emits {
|
||||
(event: 'update:modelValue', value: boolean): void;
|
||||
}
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: boolean;
|
||||
}>();
|
||||
|
||||
function toggleTheme(event: any) {
|
||||
emit("update:modelValue", event.target.checked);
|
||||
function toggleTheme(event: Event) {
|
||||
const target = event.target as HTMLInputElement | null;
|
||||
if (target) {
|
||||
emit('update:modelValue', target.checked);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,64 +1,64 @@
|
|||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { defineAsyncComponent } from "vue";
|
||||
import { mount, flushPromises } from "@vue/test-utils";
|
||||
import SocialBadge from "./socialButton.vue";
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { defineComponent } from 'vue';
|
||||
import SocialBadge from './SocialButton.vue';
|
||||
|
||||
|
||||
const mockIconComponent = (name:string) => ({
|
||||
default: {
|
||||
function mockIconComponent(name: string) {
|
||||
return {
|
||||
__esModule: true,
|
||||
default: defineComponent({
|
||||
name,
|
||||
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;
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = null;
|
||||
});
|
||||
|
||||
it("renders the correct link", () => {
|
||||
it('renders the correct link', () => {
|
||||
wrapper = mount(SocialBadge, {
|
||||
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.attributes("href")).toBe("https://example.com");
|
||||
expect(link.attributes("target")).toBe("_blank");
|
||||
expect(link.attributes('href')).toBe('https://example.com');
|
||||
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, {
|
||||
props: {
|
||||
dark: false,
|
||||
},
|
||||
});
|
||||
|
||||
const badge = wrapper.find(".social-badge");
|
||||
expect(badge.attributes("style")).toContain("color: black");
|
||||
expect(badge.attributes("style")).toContain("border-color: black");
|
||||
const badge = wrapper.find('.social-badge');
|
||||
expect(badge.attributes('style')).toContain('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, {
|
||||
props: {
|
||||
dark: true,
|
||||
},
|
||||
});
|
||||
|
||||
const badge = wrapper.find(".social-badge");
|
||||
expect(badge.attributes("style")).toContain("color: white");
|
||||
expect(badge.attributes("style")).toContain("border-color: white");
|
||||
const badge = wrapper.find('.social-badge');
|
||||
expect(badge.attributes('style')).toContain('color: white');
|
||||
expect(badge.attributes('style')).toContain('border-color: white');
|
||||
});
|
||||
|
||||
// it("renders the correct icon based on the type prop", async () => {
|
||||
|
|
@ -76,20 +76,20 @@ describe("SocialBadge.vue", () => {
|
|||
// // expect(icon.exists()).toBe(true);
|
||||
// });
|
||||
|
||||
it("does not render an icon if the type is unsupported", async () => {
|
||||
it('does not render an icon if the type is unsupported', async () => {
|
||||
wrapper = mount(SocialBadge, {
|
||||
props: {
|
||||
type: "unsupported",
|
||||
type: 'unsupported',
|
||||
},
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
it("renders without crashing when no props are provided", () => {
|
||||
it('renders without crashing when no props are provided', () => {
|
||||
wrapper = mount(SocialBadge);
|
||||
|
||||
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 { ref, nextTick } from 'vue'
|
||||
import { useSnatchSelection } from '../useSnatchSelection'
|
||||
import { usePositions } from '../usePositions'
|
||||
import { useStake } from '../useStake'
|
||||
import { useWallet } from '../useWallet'
|
||||
import { useStatCollection } from '../useStatCollection'
|
||||
import { useAdjustTaxRate } from '../useAdjustTaxRates'
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { ref, nextTick, type Ref } from 'vue';
|
||||
import { useSnatchSelection } from '../useSnatchSelection';
|
||||
import { usePositions, type Position } from '../usePositions';
|
||||
import { useStake } from '../useStake';
|
||||
import { useWallet } from '../useWallet';
|
||||
import { useStatCollection } from '../useStatCollection';
|
||||
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
|
||||
vi.mock('../usePositions', () => ({
|
||||
usePositions: vi.fn()
|
||||
}))
|
||||
usePositions: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../useStake', () => ({
|
||||
useStake: vi.fn()
|
||||
}))
|
||||
useStake: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../useWallet', () => ({
|
||||
useWallet: vi.fn()
|
||||
}))
|
||||
useWallet: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../useStatCollection', () => ({
|
||||
useStatCollection: vi.fn()
|
||||
}))
|
||||
useStatCollection: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../useAdjustTaxRates', () => ({
|
||||
useAdjustTaxRate: vi.fn()
|
||||
}))
|
||||
useAdjustTaxRate: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('kraiken-lib/staking', () => ({
|
||||
calculateSnatchShortfall: vi.fn((outstandingStake, stakingShares, stakeTotalSupply) => {
|
||||
return stakingShares > outstandingStake ? 0n : outstandingStake - stakingShares
|
||||
})
|
||||
}))
|
||||
calculateSnatchShortfall: vi.fn((outstandingStake, stakingShares, _stakeTotalSupply) => {
|
||||
return stakingShares > outstandingStake ? 0n : outstandingStake - stakingShares;
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('kraiken-lib/snatch', () => ({
|
||||
selectSnatchPositions: vi.fn((candidates, options) => {
|
||||
if (candidates.length === 0) {
|
||||
return { selected: [], remainingShortfall: options.shortfallShares }
|
||||
return { selected: [], remainingShortfall: options.shortfallShares };
|
||||
}
|
||||
return {
|
||||
selected: candidates,
|
||||
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', () => {
|
||||
beforeEach(() => {
|
||||
// Reset all mocks
|
||||
vi.clearAllMocks()
|
||||
vi.clearAllMocks();
|
||||
|
||||
// Setup default mock values with proper refs
|
||||
vi.mocked(usePositions).mockReturnValue({
|
||||
activePositions: ref([])
|
||||
} as any)
|
||||
activePositions: ref([]),
|
||||
} as MockPositionsReturn);
|
||||
|
||||
vi.mocked(useStake).mockReturnValue({
|
||||
stakingAmountShares: 0n,
|
||||
taxRate: 1.0
|
||||
} as any)
|
||||
taxRate: 1.0,
|
||||
} as MockStakeReturn);
|
||||
|
||||
vi.mocked(useWallet).mockReturnValue({
|
||||
account: { address: '0x123' }
|
||||
} as any)
|
||||
account: { address: '0x123' },
|
||||
} as MockWalletReturn);
|
||||
|
||||
// Mock with realistic values for local computation
|
||||
// stakeTotalSupply is typically 10^(18+7) = 10^25 from contract
|
||||
|
|
@ -73,40 +96,40 @@ describe('useSnatchSelection', () => {
|
|||
vi.mocked(useStatCollection).mockReturnValue({
|
||||
stakeTotalSupply: 10000000000000000000000000n, // 10^25
|
||||
kraikenTotalSupply: 10000000000000000000000000n, // 10^25
|
||||
outstandingStake: 500n
|
||||
} as any)
|
||||
outstandingStake: 500n,
|
||||
} as MockStatCollectionReturn);
|
||||
|
||||
vi.mocked(useAdjustTaxRate).mockReturnValue({
|
||||
taxRates: [{ year: 1 }]
|
||||
} as any)
|
||||
})
|
||||
taxRates: [{ year: 1 }],
|
||||
} as MockAdjustTaxRateReturn);
|
||||
});
|
||||
|
||||
it('should initialize with empty snatchable positions', () => {
|
||||
const { snatchablePositions } = useSnatchSelection()
|
||||
expect(snatchablePositions.value).toEqual([])
|
||||
})
|
||||
const { snatchablePositions } = useSnatchSelection();
|
||||
expect(snatchablePositions.value).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle no active positions', () => {
|
||||
const { snatchablePositions, floorTax } = useSnatchSelection()
|
||||
expect(snatchablePositions.value).toEqual([])
|
||||
expect(floorTax.value).toBe(1)
|
||||
})
|
||||
const { snatchablePositions, floorTax } = useSnatchSelection();
|
||||
expect(snatchablePositions.value).toEqual([]);
|
||||
expect(floorTax.value).toBe(1);
|
||||
});
|
||||
|
||||
it('should handle no shortfall', () => {
|
||||
vi.mocked(useStatCollection).mockReturnValue({
|
||||
stakeTotalSupply: 1000n,
|
||||
outstandingStake: 100n
|
||||
})
|
||||
outstandingStake: 100n,
|
||||
});
|
||||
|
||||
vi.mocked(useStake).mockReturnValue({
|
||||
stakingAmountShares: 900n,
|
||||
taxRate: 1.0
|
||||
})
|
||||
taxRate: 1.0,
|
||||
});
|
||||
|
||||
const { snatchablePositions, openPositionsAvailable } = useSnatchSelection()
|
||||
expect(snatchablePositions.value).toEqual([])
|
||||
expect(openPositionsAvailable.value).toBe(true)
|
||||
})
|
||||
const { snatchablePositions, openPositionsAvailable } = useSnatchSelection();
|
||||
expect(snatchablePositions.value).toEqual([]);
|
||||
expect(openPositionsAvailable.value).toBe(true);
|
||||
});
|
||||
|
||||
it('should filter out positions with higher tax rate', async () => {
|
||||
vi.mocked(usePositions).mockReturnValue({
|
||||
|
|
@ -117,23 +140,23 @@ describe('useSnatchSelection', () => {
|
|||
harbDeposit: 100n,
|
||||
taxRate: 2.0,
|
||||
taxRateIndex: 1,
|
||||
iAmOwner: false
|
||||
}
|
||||
])
|
||||
} as any)
|
||||
iAmOwner: false,
|
||||
},
|
||||
]) as Ref<Partial<Position>[]>,
|
||||
} as MockPositionsReturn);
|
||||
|
||||
vi.mocked(useStake).mockReturnValue({
|
||||
stakingAmountShares: 100n,
|
||||
taxRate: 1.0
|
||||
} as any)
|
||||
taxRate: 1.0,
|
||||
} as MockStakeReturn);
|
||||
|
||||
const { snatchablePositions } = useSnatchSelection()
|
||||
const { snatchablePositions } = useSnatchSelection();
|
||||
|
||||
// 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 () => {
|
||||
vi.mocked(usePositions).mockReturnValue({
|
||||
|
|
@ -144,15 +167,15 @@ describe('useSnatchSelection', () => {
|
|||
harbDeposit: 100n,
|
||||
taxRate: 0.5,
|
||||
taxRateIndex: 1,
|
||||
iAmOwner: true
|
||||
}
|
||||
])
|
||||
} as any)
|
||||
iAmOwner: true,
|
||||
},
|
||||
]) as Ref<Partial<Position>[]>,
|
||||
} as MockPositionsReturn);
|
||||
|
||||
const { snatchablePositions } = useSnatchSelection()
|
||||
await new Promise(resolve => setTimeout(resolve, 0))
|
||||
expect(snatchablePositions.value).toEqual([])
|
||||
})
|
||||
const { snatchablePositions } = useSnatchSelection();
|
||||
await new Promise(resolve => setTimeout(resolve, 0));
|
||||
expect(snatchablePositions.value).toEqual([]);
|
||||
});
|
||||
|
||||
it('should include owned positions when demo mode is enabled', async () => {
|
||||
const position = {
|
||||
|
|
@ -161,32 +184,32 @@ describe('useSnatchSelection', () => {
|
|||
harbDeposit: 100n,
|
||||
taxRate: 0.005, // 0.5% tax rate (less than maxTaxRate)
|
||||
taxRateIndex: 1,
|
||||
iAmOwner: true
|
||||
}
|
||||
iAmOwner: true,
|
||||
};
|
||||
|
||||
vi.mocked(usePositions).mockReturnValue({
|
||||
activePositions: ref([position])
|
||||
} as any)
|
||||
activePositions: ref([position]),
|
||||
} as MockPositionsReturn);
|
||||
|
||||
vi.mocked(useStake).mockReturnValue({
|
||||
stakingAmountShares: 100n,
|
||||
taxRate: 1.0 // Will be converted to 0.01 (1%) decimal
|
||||
} as any)
|
||||
taxRate: 1.0, // Will be converted to 0.01 (1%) decimal
|
||||
} as MockStakeReturn);
|
||||
|
||||
// Need outstandingStake > stakingAmountShares to create shortfall
|
||||
vi.mocked(useStatCollection).mockReturnValue({
|
||||
stakeTotalSupply: 10000000000000000000000000n,
|
||||
kraikenTotalSupply: 10000000000000000000000000n,
|
||||
outstandingStake: 500n
|
||||
} as any)
|
||||
outstandingStake: 500n,
|
||||
} as MockStatCollectionReturn);
|
||||
|
||||
const { snatchablePositions } = useSnatchSelection(true)
|
||||
const { snatchablePositions } = useSnatchSelection(true);
|
||||
|
||||
// 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 () => {
|
||||
const position1 = {
|
||||
|
|
@ -195,8 +218,8 @@ describe('useSnatchSelection', () => {
|
|||
harbDeposit: 100n,
|
||||
taxRate: 0.005, // 0.5% tax rate
|
||||
taxRateIndex: 1,
|
||||
iAmOwner: false
|
||||
}
|
||||
iAmOwner: false,
|
||||
};
|
||||
|
||||
const position2 = {
|
||||
positionId: 2n,
|
||||
|
|
@ -204,32 +227,32 @@ describe('useSnatchSelection', () => {
|
|||
harbDeposit: 200n,
|
||||
taxRate: 0.006, // 0.6% tax rate
|
||||
taxRateIndex: 2,
|
||||
iAmOwner: false
|
||||
}
|
||||
iAmOwner: false,
|
||||
};
|
||||
|
||||
vi.mocked(usePositions).mockReturnValue({
|
||||
activePositions: ref([position1, position2])
|
||||
} as any)
|
||||
activePositions: ref([position1, position2]) as Ref<Position[]>,
|
||||
} as MockPositionsReturn);
|
||||
|
||||
vi.mocked(useStake).mockReturnValue({
|
||||
stakingAmountShares: 150n,
|
||||
taxRate: 1.0 // Will be converted to 0.01 (1%) decimal
|
||||
} as any)
|
||||
taxRate: 1.0, // Will be converted to 0.01 (1%) decimal
|
||||
} as MockStakeReturn);
|
||||
|
||||
// Need outstandingStake > stakingAmountShares to create shortfall
|
||||
vi.mocked(useStatCollection).mockReturnValue({
|
||||
stakeTotalSupply: 10000000000000000000000000n,
|
||||
kraikenTotalSupply: 10000000000000000000000000n,
|
||||
outstandingStake: 500n
|
||||
} as any)
|
||||
outstandingStake: 500n,
|
||||
} as MockStatCollectionReturn);
|
||||
|
||||
const { snatchablePositions } = useSnatchSelection()
|
||||
const { snatchablePositions } = useSnatchSelection();
|
||||
|
||||
// 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 () => {
|
||||
const position = {
|
||||
|
|
@ -238,40 +261,36 @@ describe('useSnatchSelection', () => {
|
|||
harbDeposit: 100n,
|
||||
taxRate: 0.005, // 0.5% tax rate
|
||||
taxRateIndex: 1,
|
||||
iAmOwner: false
|
||||
}
|
||||
iAmOwner: false,
|
||||
};
|
||||
|
||||
vi.mocked(usePositions).mockReturnValue({
|
||||
activePositions: ref([position])
|
||||
} as any)
|
||||
activePositions: ref([position]),
|
||||
} as MockPositionsReturn);
|
||||
|
||||
vi.mocked(useStake).mockReturnValue({
|
||||
stakingAmountShares: 100n,
|
||||
taxRate: 1.0 // Will be converted to 0.01 (1%) decimal
|
||||
} as any)
|
||||
taxRate: 1.0, // Will be converted to 0.01 (1%) decimal
|
||||
} as MockStakeReturn);
|
||||
|
||||
// Need outstandingStake > stakingAmountShares to create shortfall
|
||||
vi.mocked(useStatCollection).mockReturnValue({
|
||||
stakeTotalSupply: 10000000000000000000000000n,
|
||||
kraikenTotalSupply: 10000000000000000000000000n,
|
||||
outstandingStake: 500n
|
||||
} as any)
|
||||
outstandingStake: 500n,
|
||||
} as MockStatCollectionReturn);
|
||||
|
||||
vi.mocked(useAdjustTaxRate).mockReturnValue({
|
||||
taxRates: [
|
||||
{ year: 1 },
|
||||
{ year: 2 },
|
||||
{ year: 3 }
|
||||
]
|
||||
} as any)
|
||||
taxRates: [{ year: 1 }, { year: 2 }, { year: 3 }],
|
||||
} as MockAdjustTaxRateReturn);
|
||||
|
||||
const { floorTax } = useSnatchSelection()
|
||||
const { floorTax } = useSnatchSelection();
|
||||
|
||||
// Wait for watchEffect to run
|
||||
await nextTick()
|
||||
await nextTick();
|
||||
|
||||
// Floor tax should be taxRates[maxSelectedTaxRateIndex + 1]
|
||||
// Position has taxRateIndex: 1, so nextIndex = 2, taxRates[2] = { year: 3 }
|
||||
expect(floorTax.value).toBe(3)
|
||||
})
|
||||
})
|
||||
expect(floorTax.value).toBe(3);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
import { ref, onMounted, onUnmounted, reactive, computed, type ComputedRef } from "vue";
|
||||
import * as StakeContract from "@/contracts/stake";
|
||||
import { waitForTransactionReceipt } from "@wagmi/core";
|
||||
import { config } from "@/wagmi";
|
||||
import { compactNumber, formatBigIntDivision } from "@/utils/helper";
|
||||
import { useContractToast } from "./useContractToast";
|
||||
import { TAX_RATE_OPTIONS } from "kraiken-lib/taxRates";
|
||||
import { ref, reactive, computed, type ComputedRef } from 'vue';
|
||||
import * as StakeContract from '@/contracts/stake';
|
||||
import { waitForTransactionReceipt, type Config } from '@wagmi/core';
|
||||
import { config } from '@/wagmi';
|
||||
// import { compactNumber, formatBigIntDivision } from "@/utils/helper";
|
||||
import { useContractToast } from './useContractToast';
|
||||
import { TAX_RATE_OPTIONS } from 'kraiken-lib/taxRates';
|
||||
|
||||
const contractToast = useContractToast();
|
||||
|
||||
enum State {
|
||||
SignTransaction = "SignTransaction",
|
||||
Waiting = "Waiting",
|
||||
Action = "Action",
|
||||
SignTransaction = 'SignTransaction',
|
||||
Waiting = 'Waiting',
|
||||
Action = 'Action',
|
||||
}
|
||||
|
||||
export const taxRates = TAX_RATE_OPTIONS.map(({ index, year, daily }) => ({
|
||||
|
|
@ -24,7 +24,6 @@ export function useAdjustTaxRate() {
|
|||
const loading = ref();
|
||||
const waiting = ref();
|
||||
|
||||
|
||||
const state: ComputedRef<State> = computed(() => {
|
||||
if (loading.value) {
|
||||
return State.SignTransaction;
|
||||
|
|
@ -37,31 +36,25 @@ export function useAdjustTaxRate() {
|
|||
|
||||
async function changeTax(positionId: bigint, taxRate: number) {
|
||||
try {
|
||||
console.log("changeTax", { positionId, taxRate });
|
||||
// console.log("changeTax", { positionId, taxRate });
|
||||
|
||||
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);
|
||||
console.log("hash", hash);
|
||||
// console.log("hash", hash);
|
||||
loading.value = false;
|
||||
waiting.value = true;
|
||||
const data = await waitForTransactionReceipt(config as any, {
|
||||
await waitForTransactionReceipt(config as Config, {
|
||||
hash: hash,
|
||||
});
|
||||
|
||||
contractToast.showSuccessToast(
|
||||
taxRate.toString(),
|
||||
"Success!",
|
||||
"You adjusted your position tax to",
|
||||
"",
|
||||
"%"
|
||||
);
|
||||
contractToast.showSuccessToast(taxRate.toString(), 'Success!', 'You adjusted your position tax to', '', '%');
|
||||
waiting.value = false;
|
||||
} catch (error: any) {
|
||||
console.error("error", error);
|
||||
console.log(JSON.stringify(error, (_, v) => (typeof v === "bigint" ? v.toString() : v)));
|
||||
} catch (error: unknown) {
|
||||
// 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 {
|
||||
loading.value = false;
|
||||
waiting.value = false;
|
||||
|
|
|
|||
|
|
@ -1,27 +1,22 @@
|
|||
import { ref, reactive, computed } from "vue";
|
||||
import { getChainId, watchChainId, getAccount, watchAccount } from "@wagmi/core";
|
||||
import { config } from "@/wagmi";
|
||||
import { setHarbContract } from "@/contracts/harb";
|
||||
import { setStakeContract } from "@/contracts/stake";
|
||||
import {chainsData} from "@/config"
|
||||
import logger from "@/utils/logger";
|
||||
const activeChain = ref()
|
||||
let unwatch: any = null;
|
||||
|
||||
|
||||
import { ref, reactive, computed } from 'vue';
|
||||
// import { getChainId, watchChainId, getAccount, watchAccount } from "@wagmi/core";
|
||||
// import { config } from "@/wagmi";
|
||||
// import { setHarbContract } from "@/contracts/harb";
|
||||
// import { setStakeContract } from "@/contracts/stake";
|
||||
import { chainsData } from '@/config';
|
||||
// import logger from "@/utils/logger";
|
||||
const activeChain = ref();
|
||||
|
||||
export const chainData = computed(() => {
|
||||
return chainsData.find((obj) => obj.id === activeChain.value)
|
||||
})
|
||||
return chainsData.find(obj => obj.id === activeChain.value);
|
||||
});
|
||||
|
||||
export function useChain() {
|
||||
|
||||
|
||||
// if (!unwatch) {
|
||||
// console.log("useChain function");
|
||||
// const chain = getChainId(config as any)
|
||||
// const chain = getChainId(config as Config)
|
||||
// activeChain.value = chain
|
||||
// unwatch = watchChainId(config as any, {
|
||||
// unwatch = watchChainId(config as Config, {
|
||||
// async onChange(chainId) {
|
||||
// console.log("Chain changed", chainId);
|
||||
// activeChain.value = chainId
|
||||
|
|
@ -34,7 +29,7 @@ export function useChain() {
|
|||
// if (!unwatch) {
|
||||
// console.log("useWallet function");
|
||||
|
||||
// unwatch = watchAccount(config as any, {
|
||||
// unwatch = watchAccount(config as Config, {
|
||||
// async onChange(data) {
|
||||
// console.log("watchaccount-useChain", data);
|
||||
|
||||
|
|
@ -46,6 +41,5 @@ export function useChain() {
|
|||
// });
|
||||
// }
|
||||
|
||||
|
||||
return reactive({ chainData });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
import { ref, onMounted, onUnmounted, reactive, computed } from "vue";
|
||||
import { type ComputedRef } from "vue";
|
||||
import { config } from "@/wagmi";
|
||||
import { AbiEncodingArrayLengthMismatchError, type WatchEventReturnType } from "viem";
|
||||
import axios from "axios";
|
||||
import { getAccount, watchContractEvent, readContract, waitForTransactionReceipt, watchAccount } from "@wagmi/core";
|
||||
import { type WatchAccountReturnType } from "@wagmi/core";
|
||||
import * as HarbContract from "@/contracts/harb";
|
||||
import { ref, reactive, computed } from 'vue';
|
||||
import { type ComputedRef } from 'vue';
|
||||
import { config } from '@/wagmi';
|
||||
// import axios from "axios";
|
||||
import { waitForTransactionReceipt, watchAccount, type Config } from '@wagmi/core';
|
||||
import { type WatchAccountReturnType } from '@wagmi/core';
|
||||
import * as HarbContract from '@/contracts/harb';
|
||||
// import HarbJson from "@/assets/contracts/harb.json";
|
||||
import { type Abi, type Address } from "viem";
|
||||
import { useWallet } from "./useWallet";
|
||||
import { compactNumber, formatBigIntDivision } from "@/utils/helper";
|
||||
import { useWallet } from './useWallet';
|
||||
// import { compactNumber, formatBigIntDivision } from "@/utils/helper";
|
||||
|
||||
const wallet = useWallet();
|
||||
|
||||
|
|
@ -19,15 +17,14 @@ const ubiDue = HarbContract.ubiDue;
|
|||
let unwatch: WatchAccountReturnType;
|
||||
|
||||
enum ClaimState {
|
||||
NothingToClaim = "NothingToClaim",
|
||||
StakeAble = "StakeAble",
|
||||
SignTransaction = "SignTransaction",
|
||||
Waiting = "Waiting",
|
||||
NotEnoughApproval = "NotEnoughApproval",
|
||||
NothingToClaim = 'NothingToClaim',
|
||||
StakeAble = 'StakeAble',
|
||||
SignTransaction = 'SignTransaction',
|
||||
Waiting = 'Waiting',
|
||||
NotEnoughApproval = 'NotEnoughApproval',
|
||||
}
|
||||
|
||||
export function useClaim() {
|
||||
|
||||
const state: ComputedRef<ClaimState> = computed(() => {
|
||||
if (loading.value) {
|
||||
return ClaimState.SignTransaction;
|
||||
|
|
@ -46,34 +43,29 @@ export function useClaim() {
|
|||
|
||||
loading.value = true;
|
||||
const hash = await HarbContract.claimUbi(address);
|
||||
console.log("hash", hash);
|
||||
// console.log("hash", hash);
|
||||
loading.value = false;
|
||||
waiting.value = true;
|
||||
const data = await waitForTransactionReceipt(config as any, {
|
||||
await waitForTransactionReceipt(config as Config, {
|
||||
hash: hash,
|
||||
});
|
||||
console.log("data.logs", data.logs);
|
||||
|
||||
} catch (error) {
|
||||
console.error("error", error);
|
||||
} catch (_error) {
|
||||
// console.error("error", error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
waiting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {});
|
||||
|
||||
if (!unwatch) {
|
||||
console.log("useClaim function");
|
||||
// console.log("useClaim function");
|
||||
|
||||
unwatch = watchAccount(config as any, {
|
||||
unwatch = watchAccount(config as Config, {
|
||||
async onChange(data) {
|
||||
console.log("watchAccount", data);
|
||||
// console.log("watchAccount", data);
|
||||
|
||||
if (data.address) {
|
||||
await HarbContract.setHarbContract();
|
||||
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,20 +1,21 @@
|
|||
import {onBeforeUnmount, onMounted} from 'vue'
|
||||
import { onBeforeUnmount, onMounted, type Ref } from 'vue';
|
||||
|
||||
export default function useClickOutside(component: any, callback: any) {
|
||||
|
||||
if (!component) return
|
||||
const listener = (event: any) => {
|
||||
|
||||
if (event.target !== component.value && event.composedPath().includes(component.value)) {
|
||||
|
||||
return
|
||||
export default function useClickOutside(component: Ref<HTMLElement | null>, callback: () => void) {
|
||||
if (!component) return;
|
||||
const listener = (event: MouseEvent) => {
|
||||
if (event.target !== component.value && event.composedPath().includes(component.value as EventTarget)) {
|
||||
return;
|
||||
}
|
||||
if (typeof callback === 'function') {
|
||||
callback()
|
||||
callback();
|
||||
}
|
||||
}
|
||||
onMounted(() => { window.addEventListener('click', listener) })
|
||||
onBeforeUnmount(() => {window.removeEventListener('click', listener)})
|
||||
};
|
||||
onMounted(() => {
|
||||
window.addEventListener('click', listener);
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('click', listener);
|
||||
});
|
||||
|
||||
return {listener}
|
||||
return { listener };
|
||||
}
|
||||
|
|
@ -1,37 +1,30 @@
|
|||
import Toast from "@/components/Toast.vue";
|
||||
import { POSITION, useToast } from "vue-toastification";
|
||||
import { reactive } from "vue";
|
||||
import ToastNotification from '@/components/ToastNotification.vue';
|
||||
import { POSITION, useToast } from 'vue-toastification';
|
||||
import { reactive } from 'vue';
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
export function useContractToast() {
|
||||
function showFailToast(name?: string) {
|
||||
console.log("name", name);
|
||||
if (name === "UserRejectedRequestError") {
|
||||
// console.log("name", name);
|
||||
if (name === 'UserRejectedRequestError') {
|
||||
//
|
||||
} else {
|
||||
showSuccessToast(
|
||||
"",
|
||||
"Failed!",
|
||||
"Your transaction didn’t go through. Please try again.",
|
||||
"If the issue persists, please send a message in #helpdesk on our Discord.",
|
||||
"",
|
||||
"error"
|
||||
'',
|
||||
'Failed!',
|
||||
'Your transaction didn’t go through. Please try again.',
|
||||
'If the issue persists, please send a message in #helpdesk on our Discord.',
|
||||
'',
|
||||
'error'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function showSuccessToast(
|
||||
value: string,
|
||||
header: string,
|
||||
subheader: string,
|
||||
info: string,
|
||||
unit: string,
|
||||
type: string = "info"
|
||||
) {
|
||||
function showSuccessToast(value: string, header: string, subheader: string, info: string, unit: string, type: string = 'info') {
|
||||
// Define the content object with the component, props and listeners
|
||||
const content = {
|
||||
component: Toast,
|
||||
component: ToastNotification,
|
||||
// Any prop can be passed, but don't expect them to be reactive
|
||||
props: {
|
||||
value: value,
|
||||
|
|
@ -51,7 +44,7 @@ export function useContractToast() {
|
|||
position: POSITION.TOP_RIGHT,
|
||||
icon: false,
|
||||
closeOnClick: false,
|
||||
toastClassName: "modal-overlay",
|
||||
toastClassName: 'modal-overlay',
|
||||
closeButton: false,
|
||||
hideProgressBar: true,
|
||||
timeout: 10000,
|
||||
|
|
|
|||
|
|
@ -1,38 +1,35 @@
|
|||
|
||||
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
// by convention, composable function names start with "use"
|
||||
const darkTheme = ref(false)
|
||||
const darkTheme = ref(false);
|
||||
export function useDark() {
|
||||
onMounted(() => {
|
||||
if(localStorage.getItem("theme") === "dark") {
|
||||
document.documentElement.classList.add("dark")
|
||||
darkTheme.value = true
|
||||
if (localStorage.getItem('theme') === 'dark') {
|
||||
document.documentElement.classList.add('dark');
|
||||
darkTheme.value = true;
|
||||
} else {
|
||||
darkTheme.value = false
|
||||
darkTheme.value = false;
|
||||
}
|
||||
|
||||
})
|
||||
});
|
||||
|
||||
watch(
|
||||
() => darkTheme.value,
|
||||
(newData) => {
|
||||
document.documentElement.removeAttribute("data-theme");
|
||||
localStorage.removeItem("theme")
|
||||
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");
|
||||
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")
|
||||
document.documentElement.setAttribute("data-theme", "default");
|
||||
localStorage.setItem("theme", "default");
|
||||
document.documentElement.classList.remove('dark');
|
||||
document.documentElement.setAttribute('data-theme', 'default');
|
||||
localStorage.setItem('theme', 'default');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return { darkTheme }
|
||||
return { darkTheme };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { ref, onMounted, onUnmounted } from "vue";
|
||||
import { ref, onMounted, onUnmounted } from 'vue';
|
||||
|
||||
// by convention, composable function names start with "use"
|
||||
export function useMobile() {
|
||||
|
|
@ -18,12 +18,12 @@ export function useMobile() {
|
|||
}
|
||||
|
||||
onMounted(async () => {
|
||||
window.addEventListener("resize", handleWindowSizeChange);
|
||||
window.addEventListener('resize', handleWindowSizeChange);
|
||||
handleWindowSizeChange();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener("resize", handleWindowSizeChange);
|
||||
window.removeEventListener('resize', handleWindowSizeChange);
|
||||
});
|
||||
|
||||
return isMobile;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
import { ref, onMounted, onUnmounted, reactive, computed, type ComputedRef } from "vue";
|
||||
import { config } from "@/wagmi";
|
||||
import { type WatchEventReturnType, toBytes, type Hex } from "viem";
|
||||
import axios from "axios";
|
||||
import { getAccount, watchContractEvent, watchChainId, watchAccount } from "@wagmi/core";
|
||||
import type { WatchChainIdReturnType, WatchAccountReturnType, GetAccountReturnType} from "@wagmi/core";
|
||||
import { ref, computed, type ComputedRef, onMounted, onUnmounted } from 'vue';
|
||||
import { config } from '@/wagmi';
|
||||
import { type WatchEventReturnType, type Hex, toBytes } from 'viem';
|
||||
import axios from 'axios';
|
||||
import { getAccount, watchContractEvent, watchChainId, watchAccount, type Config } from '@wagmi/core';
|
||||
import type { WatchChainIdReturnType, WatchAccountReturnType, GetAccountReturnType } from '@wagmi/core';
|
||||
|
||||
import { HarbContract } from "@/contracts/harb";
|
||||
import { bytesToUint256, uint256ToBytes } from "kraiken-lib";
|
||||
import { bigInt2Number } from "@/utils/helper";
|
||||
import { useChain } from "@/composables/useChain";
|
||||
import { taxRates } from "@/composables/useAdjustTaxRates";
|
||||
import { chainData } from "@/composables/useWallet";
|
||||
import logger from "@/utils/logger";
|
||||
import { HarbContract } from '@/contracts/harb';
|
||||
import { bytesToUint256 } from 'kraiken-lib';
|
||||
import { bigInt2Number } from '@/utils/helper';
|
||||
import { taxRates } from '@/composables/useAdjustTaxRates';
|
||||
import { chainData } from '@/composables/useWallet';
|
||||
import logger from '@/utils/logger';
|
||||
const rawActivePositions = ref<Array<Position>>([]);
|
||||
const rawClosedPositoins = ref<Array<Position>>([]);
|
||||
const loading = ref(false);
|
||||
|
|
@ -21,19 +20,18 @@ const POSITIONS_RETRY_MAX_DELAY = 60_000;
|
|||
const positionsRetryDelayMs = ref(POSITIONS_RETRY_BASE_DELAY);
|
||||
const GRAPHQL_TIMEOUT_MS = 15_000;
|
||||
let positionsRetryTimer: number | null = null;
|
||||
const chain = useChain();
|
||||
const activePositions = computed(() => {
|
||||
const account = getAccount(config as any);
|
||||
const account = getAccount(config as Config);
|
||||
|
||||
return rawActivePositions.value
|
||||
.map((obj: any) => {
|
||||
.map(obj => {
|
||||
return {
|
||||
...obj,
|
||||
positionId: formatId(obj.id),
|
||||
positionId: formatId(obj.id as Hex),
|
||||
amount: bigInt2Number(obj.harbDeposit, 18),
|
||||
taxRatePercentage: Number(obj.taxRate) * 100,
|
||||
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(),
|
||||
totalSupplyEnd: obj.totalSupplyEnd ? BigInt(obj.totalSupplyEnd) : undefined,
|
||||
totalSupplyInit: BigInt(obj.totalSupplyInit),
|
||||
|
|
@ -60,7 +58,7 @@ export interface Position {
|
|||
lastTaxTime: Date;
|
||||
taxPaid: bigint;
|
||||
taxRate: number;
|
||||
taxRateIndex: number;
|
||||
taxRateIndex?: number;
|
||||
taxRatePercentage: number;
|
||||
share: number;
|
||||
status: string;
|
||||
|
|
@ -72,24 +70,23 @@ export interface Position {
|
|||
}
|
||||
|
||||
const myClosedPositions: ComputedRef<Position[]> = computed(() => {
|
||||
const account = getAccount(config as any);
|
||||
const account = getAccount(config as Config);
|
||||
|
||||
return rawClosedPositoins.value.map((obj: any) => {
|
||||
const taxRatePosition = obj.taxRate * 100;
|
||||
console.log("taxRatePosition", taxRatePosition);
|
||||
return rawClosedPositoins.value.map(obj => {
|
||||
// console.log("taxRatePosition", taxRatePosition);
|
||||
|
||||
console.log("taxRates[taxRatePosition]", taxRates[taxRatePosition]);
|
||||
// console.log("taxRates[taxRatePosition]", taxRates[taxRatePosition]);
|
||||
|
||||
return {
|
||||
...obj,
|
||||
positionId: formatId(obj.id),
|
||||
positionId: formatId(obj.id as Hex),
|
||||
amount: obj.share * 1000000,
|
||||
// amount: bigInt2Number(obj.harbDeposit, 18),
|
||||
taxRatePercentage: Number(obj.taxRate) * 100,
|
||||
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(),
|
||||
totalSupplyEnd: BigInt(obj.totalSupplyEnd),
|
||||
totalSupplyEnd: obj.totalSupplyEnd !== undefined ? BigInt(obj.totalSupplyEnd) : undefined,
|
||||
totalSupplyInit: BigInt(obj.totalSupplyInit),
|
||||
taxPaid: BigInt(obj.taxPaid),
|
||||
share: Number(obj.share),
|
||||
|
|
@ -104,8 +101,8 @@ const myActivePositions: ComputedRef<Position[]> = computed(() =>
|
|||
);
|
||||
|
||||
const tresholdValue = computed(() => {
|
||||
const arrayTaxRatePositions = activePositions.value.map((obj) => obj.taxRatePercentage);
|
||||
const sortedPositions = arrayTaxRatePositions.sort((a: any, b: any) => (a > b ? 1 : -1));
|
||||
const arrayTaxRatePositions = activePositions.value.map(obj => obj.taxRatePercentage);
|
||||
const sortedPositions = arrayTaxRatePositions.sort((a, b) => (a > b ? 1 : -1));
|
||||
const sumq = sortedPositions.reduce((partialSum, a) => partialSum + a, 0);
|
||||
const avg = sumq / sortedPositions.length;
|
||||
|
||||
|
|
@ -116,12 +113,14 @@ export async function loadActivePositions(endpoint?: string) {
|
|||
logger.info(`loadActivePositions for chain: ${chainData.value?.path}`);
|
||||
const targetEndpoint = endpoint ?? chainData.value?.graphql?.trim();
|
||||
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(
|
||||
targetEndpoint,
|
||||
{
|
||||
query: `query ActivePositions {
|
||||
positionss(where: { status: "Active" }, orderBy: "taxRate", orderDirection: "asc", limit: 1000) {
|
||||
items {
|
||||
|
|
@ -140,17 +139,19 @@ export async function loadActivePositions(endpoint?: string) {
|
|||
}
|
||||
}
|
||||
}`,
|
||||
}, { timeout: GRAPHQL_TIMEOUT_MS });
|
||||
},
|
||||
{ timeout: GRAPHQL_TIMEOUT_MS }
|
||||
);
|
||||
|
||||
const errors = res.data?.errors;
|
||||
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 ?? [];
|
||||
return items.map((item: any) => ({
|
||||
return items.map((item: Record<string, unknown>) => ({
|
||||
...item,
|
||||
harbDeposit: item.kraikenDeposit ?? "0",
|
||||
harbDeposit: item.kraikenDeposit ?? '0',
|
||||
})) as Position[];
|
||||
}
|
||||
|
||||
|
|
@ -164,9 +165,11 @@ export async function loadMyClosedPositions(endpoint: string | undefined, accoun
|
|||
logger.info(`loadMyClosedPositions for chain: ${chainData.value?.path}`);
|
||||
const targetEndpoint = endpoint ?? chainData.value?.graphql?.trim();
|
||||
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(
|
||||
targetEndpoint,
|
||||
{
|
||||
query: `query ClosedPositions {
|
||||
positionss(where: { status: "Closed", owner: "${account.address?.toLowerCase()}" }, limit: 1000) {
|
||||
items {
|
||||
|
|
@ -185,15 +188,17 @@ export async function loadMyClosedPositions(endpoint: string | undefined, accoun
|
|||
}
|
||||
}
|
||||
}`,
|
||||
}, { timeout: GRAPHQL_TIMEOUT_MS });
|
||||
},
|
||||
{ timeout: GRAPHQL_TIMEOUT_MS }
|
||||
);
|
||||
const errors = res.data?.errors;
|
||||
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 ?? [];
|
||||
return items.map((item: any) => ({
|
||||
return items.map((item: Record<string, unknown>) => ({
|
||||
...item,
|
||||
harbDeposit: item.kraikenDeposit ?? "0",
|
||||
harbDeposit: item.kraikenDeposit ?? '0',
|
||||
})) as Position[];
|
||||
}
|
||||
|
||||
|
|
@ -204,7 +209,7 @@ export async function loadPositions() {
|
|||
if (!endpoint) {
|
||||
rawActivePositions.value = [];
|
||||
rawClosedPositoins.value = [];
|
||||
positionsError.value = "GraphQL endpoint not configured for this chain.";
|
||||
positionsError.value = 'GraphQL endpoint not configured for this chain.';
|
||||
clearPositionsRetryTimer();
|
||||
positionsRetryDelayMs.value = POSITIONS_RETRY_BASE_DELAY;
|
||||
loading.value = false;
|
||||
|
|
@ -213,7 +218,7 @@ export async function loadPositions() {
|
|||
|
||||
try {
|
||||
rawActivePositions.value = await loadActivePositions(endpoint);
|
||||
const account = getAccount(config as any);
|
||||
const account = getAccount(config as Config);
|
||||
if (account.address) {
|
||||
rawClosedPositoins.value = await loadMyClosedPositions(endpoint, account);
|
||||
} else {
|
||||
|
|
@ -223,7 +228,6 @@ export async function loadPositions() {
|
|||
positionsRetryDelayMs.value = POSITIONS_RETRY_BASE_DELAY;
|
||||
clearPositionsRetryTimer();
|
||||
} catch (error) {
|
||||
console.warn("[positions] loadPositions() failed", error);
|
||||
rawActivePositions.value = [];
|
||||
rawClosedPositoins.value = [];
|
||||
positionsError.value = formatGraphqlError(error);
|
||||
|
|
@ -240,9 +244,9 @@ let unwatchAccountChanged: WatchAccountReturnType | null;
|
|||
|
||||
function formatGraphqlError(error: unknown): string {
|
||||
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) {
|
||||
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) {
|
||||
return `GraphQL request failed with status ${error.response.status}`;
|
||||
|
|
@ -254,7 +258,7 @@ function formatGraphqlError(error: unknown): string {
|
|||
if (error instanceof Error && error.message) {
|
||||
return error.message;
|
||||
}
|
||||
return "Unknown GraphQL error";
|
||||
return 'Unknown GraphQL error';
|
||||
}
|
||||
|
||||
function clearPositionsRetryTimer() {
|
||||
|
|
@ -265,7 +269,7 @@ function clearPositionsRetryTimer() {
|
|||
}
|
||||
|
||||
function schedulePositionsRetry() {
|
||||
if (typeof window === "undefined") {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
if (positionsRetryTimer !== null) {
|
||||
|
|
@ -280,12 +284,12 @@ function schedulePositionsRetry() {
|
|||
}
|
||||
export function usePositions() {
|
||||
function watchEvent() {
|
||||
unwatch = watchContractEvent(config as any, {
|
||||
unwatch = watchContractEvent(config as Config, {
|
||||
address: HarbContract.contractAddress,
|
||||
abi: HarbContract.abi,
|
||||
eventName: "PositionCreated",
|
||||
async onLogs(logs) {
|
||||
console.log("new Position", logs);
|
||||
eventName: 'PositionCreated',
|
||||
async onLogs(_logs) {
|
||||
// console.log("new Position", logs);
|
||||
await loadPositions();
|
||||
// await getMinStake();
|
||||
},
|
||||
|
|
@ -293,12 +297,12 @@ export function usePositions() {
|
|||
}
|
||||
|
||||
function watchPositionRemoved() {
|
||||
unwatchPositionRemovedEvent = watchContractEvent(config as any, {
|
||||
unwatchPositionRemovedEvent = watchContractEvent(config as Config, {
|
||||
address: HarbContract.contractAddress,
|
||||
abi: HarbContract.abi,
|
||||
eventName: "PositionRemoved",
|
||||
async onLogs(logs) {
|
||||
console.log("Position removed", logs);
|
||||
eventName: 'PositionRemoved',
|
||||
async onLogs(_logs) {
|
||||
// console.log("Position removed", logs);
|
||||
await loadPositions();
|
||||
// await getMinStake();
|
||||
},
|
||||
|
|
@ -321,15 +325,15 @@ export function usePositions() {
|
|||
}
|
||||
|
||||
if (!unwatchChainSwitch) {
|
||||
unwatchChainSwitch = watchChainId(config as any, {
|
||||
async onChange(chainId) {
|
||||
unwatchChainSwitch = watchChainId(config as Config, {
|
||||
async onChange(_chainId) {
|
||||
await loadPositions();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (!unwatchAccountChanged) {
|
||||
unwatchAccountChanged = watchAccount(config as any, {
|
||||
unwatchAccountChanged = watchAccount(config as Config, {
|
||||
async onChange() {
|
||||
await loadPositions();
|
||||
},
|
||||
|
|
@ -361,16 +365,16 @@ export function usePositions() {
|
|||
for (let index = 0; index < amount; index++) {
|
||||
const newPosition: Position = {
|
||||
creationTime: new Date(),
|
||||
id: "123",
|
||||
id: '123',
|
||||
positionId: 123n,
|
||||
owner: "bla",
|
||||
owner: 'bla',
|
||||
lastTaxTime: new Date(),
|
||||
taxPaid: 100n,
|
||||
taxRate: randomInRange(0.01, 1),
|
||||
taxRateIndex: randomInRange(1, 30),
|
||||
taxRateIndex: Math.floor(randomInRange(1, 30)),
|
||||
taxRatePercentage: getRandomInt(1, 100),
|
||||
share: getRandomInt(0.001, 0.09),
|
||||
status: "active",
|
||||
status: 'active',
|
||||
totalSupplyEnd: undefined,
|
||||
totalSupplyInit: 1000000000000n,
|
||||
amount: 150,
|
||||
|
|
|
|||
|
|
@ -1,15 +1,11 @@
|
|||
import { ref, watchEffect, computed } from 'vue'
|
||||
import { usePositions, type Position } from './usePositions'
|
||||
import { useStake } from './useStake'
|
||||
import { useWallet } from './useWallet'
|
||||
import { useStatCollection } from './useStatCollection'
|
||||
import { useAdjustTaxRate } from './useAdjustTaxRates'
|
||||
import { calculateSnatchShortfall } from 'kraiken-lib/staking'
|
||||
import {
|
||||
selectSnatchPositions,
|
||||
minimumTaxRate,
|
||||
type SnatchablePosition,
|
||||
} from 'kraiken-lib/snatch'
|
||||
import { ref, watchEffect, computed, type Ref } from 'vue';
|
||||
import { usePositions, type Position } from './usePositions';
|
||||
import { useStake } from './useStake';
|
||||
import { useWallet } from './useWallet';
|
||||
import { useStatCollection } from './useStatCollection';
|
||||
import { useAdjustTaxRate } from './useAdjustTaxRates';
|
||||
import { calculateSnatchShortfall } from 'kraiken-lib/staking';
|
||||
import { selectSnatchPositions, minimumTaxRate, type SnatchablePosition } from 'kraiken-lib/snatch';
|
||||
|
||||
/**
|
||||
* 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)
|
||||
* @returns Number of shares corresponding to the assets
|
||||
*/
|
||||
function assetsToSharesLocal(
|
||||
assets: bigint,
|
||||
kraikenTotalSupply: bigint,
|
||||
stakeTotalSupply: bigint
|
||||
): bigint {
|
||||
function assetsToSharesLocal(assets: bigint, kraikenTotalSupply: bigint, stakeTotalSupply: bigint): bigint {
|
||||
if (kraikenTotalSupply === 0n) {
|
||||
return 0n
|
||||
return 0n;
|
||||
}
|
||||
// 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 { activePositions } = usePositions()
|
||||
const stake = useStake()
|
||||
const wallet = useWallet()
|
||||
const statCollection = useStatCollection()
|
||||
const adjustTaxRate = useAdjustTaxRate()
|
||||
const snatchablePositions = ref<Position[]>([]);
|
||||
const shortfallShares = ref<bigint>(0n);
|
||||
const floorTax = ref(1);
|
||||
let selectionRun = 0;
|
||||
|
||||
const snatchablePositions = ref<Position[]>([])
|
||||
const shortfallShares = ref<bigint>(0n)
|
||||
const floorTax = ref(1)
|
||||
let selectionRun = 0
|
||||
|
||||
const openPositionsAvailable = computed(() => shortfallShares.value <= 0n)
|
||||
const openPositionsAvailable = computed(() => shortfallShares.value <= 0n);
|
||||
|
||||
function getMinFloorTax() {
|
||||
const minRate = minimumTaxRate(activePositions.value, 0)
|
||||
return Math.round(minRate * 100)
|
||||
const minRate = minimumTaxRate(activePositions.value, 0);
|
||||
return Math.round(minRate * 100);
|
||||
}
|
||||
|
||||
watchEffect((onCleanup) => {
|
||||
const runId = ++selectionRun
|
||||
let cancelled = false
|
||||
watchEffect(onCleanup => {
|
||||
const runId = ++selectionRun;
|
||||
let cancelled = false;
|
||||
onCleanup(() => {
|
||||
cancelled = true
|
||||
})
|
||||
cancelled = true;
|
||||
});
|
||||
|
||||
// No longer async since we compute shares locally
|
||||
const compute = () => {
|
||||
if (statCollection.stakeTotalSupply === 0n) {
|
||||
shortfallShares.value = 0n
|
||||
shortfallShares.value = 0n;
|
||||
if (!cancelled && runId === selectionRun) {
|
||||
snatchablePositions.value = []
|
||||
floorTax.value = getMinFloorTax()
|
||||
snatchablePositions.value = [];
|
||||
floorTax.value = getMinFloorTax();
|
||||
}
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
const stakingShares = stake.stakingAmountShares ?? 0n
|
||||
const shortfall = calculateSnatchShortfall(
|
||||
statCollection.outstandingStake,
|
||||
stakingShares,
|
||||
statCollection.stakeTotalSupply,
|
||||
2n,
|
||||
10n
|
||||
)
|
||||
const stakingShares = stake.stakingAmountShares ?? 0n;
|
||||
const shortfall = calculateSnatchShortfall(statCollection.outstandingStake, stakingShares, statCollection.stakeTotalSupply, 2n, 10n);
|
||||
|
||||
shortfallShares.value = shortfall
|
||||
shortfallShares.value = shortfall;
|
||||
|
||||
if (shortfall <= 0n) {
|
||||
if (!cancelled && runId === selectionRun) {
|
||||
snatchablePositions.value = []
|
||||
floorTax.value = getMinFloorTax()
|
||||
snatchablePositions.value = [];
|
||||
floorTax.value = getMinFloorTax();
|
||||
}
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
const maxTaxRateDecimal = (stake.taxRate ?? 0) / 100
|
||||
const includeOwned = demo
|
||||
const recipient = wallet.account.address ?? null
|
||||
const stakeTaxRate = (stake as { taxRate?: number }).taxRate;
|
||||
const taxRatePercent = taxRate?.value ?? stakeTaxRate ?? Number.POSITIVE_INFINITY;
|
||||
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) => {
|
||||
if (position.taxRate >= maxTaxRateDecimal) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
if (!includeOwned && position.iAmOwner) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
return true
|
||||
})
|
||||
return true;
|
||||
});
|
||||
|
||||
if (eligiblePositions.length === 0) {
|
||||
if (!cancelled && runId === selectionRun) {
|
||||
snatchablePositions.value = []
|
||||
floorTax.value = getMinFloorTax()
|
||||
snatchablePositions.value = [];
|
||||
floorTax.value = getMinFloorTax();
|
||||
}
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
const candidates: SnatchablePosition[] = []
|
||||
const candidates: SnatchablePosition[] = [];
|
||||
|
||||
// Compute shares locally using the same formula as Stake.sol
|
||||
for (const position of eligiblePositions) {
|
||||
const shares = assetsToSharesLocal(
|
||||
position.harbDeposit,
|
||||
statCollection.kraikenTotalSupply,
|
||||
statCollection.stakeTotalSupply
|
||||
)
|
||||
const shares = assetsToSharesLocal(position.harbDeposit, statCollection.kraikenTotalSupply, statCollection.stakeTotalSupply);
|
||||
candidates.push({
|
||||
id: position.positionId,
|
||||
owner: position.owner,
|
||||
stakeShares: shares,
|
||||
taxRate: position.taxRate,
|
||||
taxRateIndex: position.taxRateIndex,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
const selection = selectSnatchPositions(candidates, {
|
||||
|
|
@ -134,47 +117,45 @@ export function useSnatchSelection(demo = false) {
|
|||
maxTaxRate: maxTaxRateDecimal,
|
||||
includeOwned,
|
||||
recipientAddress: recipient,
|
||||
})
|
||||
});
|
||||
|
||||
if (cancelled || runId !== selectionRun) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
if (selection.remainingShortfall > 0n) {
|
||||
snatchablePositions.value = []
|
||||
floorTax.value = getMinFloorTax()
|
||||
return
|
||||
snatchablePositions.value = [];
|
||||
floorTax.value = getMinFloorTax();
|
||||
return;
|
||||
}
|
||||
|
||||
const positionById = new Map<bigint, Position>()
|
||||
const positionById = new Map<bigint, Position>();
|
||||
for (const position of activePositions.value) {
|
||||
positionById.set(position.positionId, position)
|
||||
positionById.set(position.positionId, position);
|
||||
}
|
||||
|
||||
const selectedPositions = selection.selected
|
||||
.map((candidate) => positionById.get(candidate.id))
|
||||
.filter((value): value is Position => Boolean(value))
|
||||
.map(candidate => positionById.get(candidate.id))
|
||||
.filter((value): value is Position => Boolean(value));
|
||||
|
||||
snatchablePositions.value = selectedPositions
|
||||
snatchablePositions.value = selectedPositions;
|
||||
|
||||
if (selection.maxSelectedTaxRateIndex !== undefined) {
|
||||
const nextIndex = selection.maxSelectedTaxRateIndex + 1
|
||||
const option =
|
||||
adjustTaxRate.taxRates[nextIndex] ??
|
||||
adjustTaxRate.taxRates[selection.maxSelectedTaxRateIndex]
|
||||
floorTax.value = option ? option.year : getMinFloorTax()
|
||||
const nextIndex = selection.maxSelectedTaxRateIndex + 1;
|
||||
const option = adjustTaxRate.taxRates[nextIndex] ?? adjustTaxRate.taxRates[selection.maxSelectedTaxRateIndex];
|
||||
floorTax.value = option ? option.year : getMinFloorTax();
|
||||
} else {
|
||||
floorTax.value = getMinFloorTax()
|
||||
}
|
||||
floorTax.value = getMinFloorTax();
|
||||
}
|
||||
};
|
||||
|
||||
compute()
|
||||
})
|
||||
compute();
|
||||
});
|
||||
|
||||
return {
|
||||
snatchablePositions,
|
||||
shortfallShares,
|
||||
floorTax,
|
||||
openPositionsAvailable,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,42 +1,26 @@
|
|||
import { ref, onMounted, onUnmounted, reactive, computed } from "vue";
|
||||
import { type ComputedRef } from "vue";
|
||||
import { config } from "@/wagmi";
|
||||
import { AbiEncodingArrayLengthMismatchError, type WatchEventReturnType, decodeEventLog, type Hex } from "viem";
|
||||
import axios from "axios";
|
||||
import {
|
||||
getAccount,
|
||||
watchContractEvent,
|
||||
readContract,
|
||||
signTypedData,
|
||||
waitForTransactionReceipt,
|
||||
watchAccount,
|
||||
verifyTypedData,
|
||||
} 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";
|
||||
import { ref, onMounted, reactive, computed } from 'vue';
|
||||
import { type ComputedRef } from 'vue';
|
||||
import { config } from '@/wagmi';
|
||||
import { decodeEventLog, type Hex, type DecodeEventLogReturnType, type TypedDataDefinition } from 'viem';
|
||||
import { getAccount, signTypedData, waitForTransactionReceipt, type Config } from '@wagmi/core';
|
||||
import { HarbContract } from '@/contracts/harb';
|
||||
import { StakeContract, permitAndSnatch, minStake } 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 { taxRates } from '@/composables/useAdjustTaxRates';
|
||||
import { useContractToast } from './useContractToast';
|
||||
const wallet = useWallet();
|
||||
const toast = useToast();
|
||||
const contractToast = useContractToast();
|
||||
const wagmiConfig: Config = config;
|
||||
|
||||
enum StakeState {
|
||||
NoBalance = "NoBalance",
|
||||
StakeAble = "StakeAble",
|
||||
SignTransaction = "SignTransaction",
|
||||
Waiting = "Waiting",
|
||||
NotEnoughApproval = "NotEnoughApproval",
|
||||
}
|
||||
|
||||
interface PositionCreatedEvent {
|
||||
eventName: undefined;
|
||||
args: PositionCreatedArgs;
|
||||
NoBalance = 'NoBalance',
|
||||
StakeAble = 'StakeAble',
|
||||
SignTransaction = 'SignTransaction',
|
||||
Waiting = 'Waiting',
|
||||
NotEnoughApproval = 'NotEnoughApproval',
|
||||
}
|
||||
|
||||
interface PositionCreatedArgs {
|
||||
|
|
@ -60,9 +44,8 @@ export function useStake() {
|
|||
|
||||
const state: ComputedRef<StakeState> = computed(() => {
|
||||
const balance = wallet.balance.value;
|
||||
console.log("balance123", balance);
|
||||
console.log("wallet", wallet);
|
||||
|
||||
// console.log("balance123", balance);
|
||||
// console.log("wallet", wallet);
|
||||
|
||||
if (loading.value) {
|
||||
return StakeState.SignTransaction;
|
||||
|
|
@ -97,18 +80,16 @@ export function useStake() {
|
|||
},
|
||||
});
|
||||
|
||||
|
||||
// const stakingAmountNumber = computed(() => return staking)
|
||||
|
||||
async function snatch(stakingAmount: BigInt, taxRate: number, positions:Array<any> = []) {
|
||||
console.log("snatch", { stakingAmount, taxRate, positions });
|
||||
const account = getAccount(config as any);
|
||||
const taxRateObj = taxRates.find((obj) => obj.year === taxRate);
|
||||
async function snatch(stakingAmount: bigint, taxRate: number, positions: Array<bigint> = []) {
|
||||
// console.log("snatch", { stakingAmount, taxRate, positions });
|
||||
const account = getAccount(wagmiConfig);
|
||||
const taxRateObj = taxRates.find(obj => obj.year === taxRate);
|
||||
|
||||
try {
|
||||
const assets: BigInt = stakingAmount;
|
||||
const receiver = wallet.account.address!;
|
||||
console.log("receiver", receiver);
|
||||
const assets: bigint = stakingAmount;
|
||||
// console.log("receiver", receiver);
|
||||
|
||||
// await snatchService(assets, receiver, taxRate, []);
|
||||
// assets: BigInt, receiver: Address, taxRate: Number, positionsToSnatch: Array<BigInt>
|
||||
|
|
@ -126,58 +107,55 @@ export function useStake() {
|
|||
account.chainId!,
|
||||
name
|
||||
);
|
||||
console.log("resultPermitObject", { types, message, domain, primaryType });
|
||||
// console.log("resultPermitObject", { types, message, domain, primaryType });
|
||||
|
||||
const signature = await signTypedData(config as any, {
|
||||
domain: domain as any,
|
||||
message: message,
|
||||
primaryType: primaryType,
|
||||
types: types,
|
||||
});
|
||||
const typedData: TypedDataDefinition = {
|
||||
domain,
|
||||
message,
|
||||
primaryType,
|
||||
types,
|
||||
};
|
||||
|
||||
console.log("signature", {
|
||||
domain: domain as any,
|
||||
message: message,
|
||||
primaryType: primaryType,
|
||||
types: types,
|
||||
});
|
||||
const signature = await signTypedData(wagmiConfig, typedData);
|
||||
|
||||
// console.log("signature", {
|
||||
// domain,
|
||||
// message,
|
||||
// primaryType,
|
||||
// types,
|
||||
// });
|
||||
|
||||
const { r, s, v } = getSignatureRSV(signature);
|
||||
loading.value = true;
|
||||
console.log("permitAndSnatch", assets, account.address!, taxRateObj?.index!, positions, deadline, v, r, s);
|
||||
// console.log("permitAndSnatch", assets, account.address!, taxRateObj?.index!, positions, deadline, v, r, s);
|
||||
|
||||
const hash = await permitAndSnatch(assets, account.address!, taxRateObj?.index!, positions, deadline, v, r, s);
|
||||
console.log("hash", hash);
|
||||
const taxRateIndex = taxRateObj?.index ?? 0;
|
||||
const hash = await permitAndSnatch(assets, account.address!, taxRateIndex, positions, deadline, v, r, s);
|
||||
// console.log("hash", hash);
|
||||
loading.value = false;
|
||||
waiting.value = true;
|
||||
const data = await waitForTransactionReceipt(config as any, {
|
||||
const data = await waitForTransactionReceipt(wagmiConfig, {
|
||||
hash: hash,
|
||||
});
|
||||
|
||||
const topics: any = decodeEventLog({
|
||||
const topics = decodeEventLog({
|
||||
abi: StakeContract.abi,
|
||||
data: data.logs[3].data,
|
||||
topics: data.logs[3].topics,
|
||||
});
|
||||
}) as DecodeEventLogReturnType & { args: PositionCreatedArgs };
|
||||
const eventArgs: PositionCreatedArgs = topics.args;
|
||||
|
||||
const amount = compactNumber(
|
||||
formatBigIntDivision(eventArgs.harbergDeposit, 10n ** BigInt(wallet.balance.decimals))
|
||||
);
|
||||
contractToast.showSuccessToast(
|
||||
amount,
|
||||
"Success!",
|
||||
"You Staked",
|
||||
"Check your positions on the<br /> Staker Dashboard",
|
||||
"$KRK"
|
||||
);
|
||||
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: any) {
|
||||
console.error("error", error);
|
||||
console.log(JSON.parse(JSON.stringify(error)));
|
||||
contractToast.showFailToast(error.name);
|
||||
} 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;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import { ref, onMounted, onUnmounted, reactive, computed } from "vue";
|
||||
import axios from "axios";
|
||||
import { chainData } from "./useWallet";
|
||||
import { watchBlocks, watchChainId } from "@wagmi/core";
|
||||
import { config } from "@/wagmi";
|
||||
import logger from "@/utils/logger";
|
||||
import type { WatchBlocksReturnType } from "viem";
|
||||
import { bigInt2Number } from "@/utils/helper";
|
||||
const demo = sessionStorage.getItem("demo") === "true";
|
||||
import { ref, reactive, computed } from 'vue';
|
||||
import axios from 'axios';
|
||||
import { chainData } from './useWallet';
|
||||
import { watchChainId } from '@wagmi/core';
|
||||
import type { Config } from '@wagmi/core';
|
||||
import { config } from '@/wagmi';
|
||||
import logger from '@/utils/logger';
|
||||
import type { WatchBlocksReturnType } from 'viem';
|
||||
import { bigInt2Number } from '@/utils/helper';
|
||||
const demo = sessionStorage.getItem('demo') === 'true';
|
||||
|
||||
const GRAPHQL_TIMEOUT_MS = 15_000;
|
||||
const RETRY_BASE_DELAY_MS = 1_500;
|
||||
|
|
@ -37,9 +38,9 @@ let statsRetryTimer: number | null = null;
|
|||
|
||||
function formatGraphqlError(error: unknown): string {
|
||||
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) {
|
||||
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) {
|
||||
return `GraphQL request failed with status ${error.response.status}`;
|
||||
|
|
@ -51,7 +52,7 @@ function formatGraphqlError(error: unknown): string {
|
|||
if (error instanceof Error && error.message) {
|
||||
return error.message;
|
||||
}
|
||||
return "Unknown GraphQL error";
|
||||
return 'Unknown GraphQL error';
|
||||
}
|
||||
|
||||
function clearStatsRetryTimer() {
|
||||
|
|
@ -62,7 +63,7 @@ function clearStatsRetryTimer() {
|
|||
}
|
||||
|
||||
function scheduleStatsRetry() {
|
||||
if (typeof window === "undefined") {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
if (statsRetryTimer !== null) {
|
||||
|
|
@ -78,7 +79,9 @@ function scheduleStatsRetry() {
|
|||
|
||||
export async function loadStatsCollection(endpoint: string) {
|
||||
logger.info(`loadStatsCollection for chain: ${chainData.value?.path}`);
|
||||
const res = await axios.post(endpoint, {
|
||||
const res = await axios.post(
|
||||
endpoint,
|
||||
{
|
||||
query: `query StatsQuery {
|
||||
stats(id: "0x01") {
|
||||
burnNextHourProjected
|
||||
|
|
@ -96,16 +99,18 @@ export async function loadStatsCollection(endpoint: string) {
|
|||
totalMinted
|
||||
}
|
||||
}`,
|
||||
}, { timeout: GRAPHQL_TIMEOUT_MS });
|
||||
},
|
||||
{ timeout: GRAPHQL_TIMEOUT_MS }
|
||||
);
|
||||
|
||||
const errors = res.data?.errors;
|
||||
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;
|
||||
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 }];
|
||||
|
|
@ -172,10 +177,7 @@ const stakeTotalSupply = computed(() => {
|
|||
//Total Supply Change / 7d=mintedLastWeek−burnedLastWeek
|
||||
const totalSupplyChange7d = computed(() => {
|
||||
if (rawStatsCollections.value?.length > 0) {
|
||||
return (
|
||||
BigInt(rawStatsCollections.value[0].mintedLastWeek) -
|
||||
BigInt(rawStatsCollections.value[0].burnedLastWeek)
|
||||
);
|
||||
return BigInt(rawStatsCollections.value[0].mintedLastWeek) - BigInt(rawStatsCollections.value[0].burnedLastWeek);
|
||||
} else {
|
||||
return 0n;
|
||||
}
|
||||
|
|
@ -184,10 +186,7 @@ const totalSupplyChange7d = computed(() => {
|
|||
//totalsupply Change7d / harbtotalsupply
|
||||
const inflation7d = computed(() => {
|
||||
if (rawStatsCollections.value?.length > 0 && BigInt(rawStatsCollections.value[0].kraikenTotalSupply) > 0n) {
|
||||
return (
|
||||
BigInt(rawStatsCollections.value[0].mintedLastWeek) -
|
||||
BigInt(rawStatsCollections.value[0].burnedLastWeek)
|
||||
);
|
||||
return BigInt(rawStatsCollections.value[0].mintedLastWeek) - BigInt(rawStatsCollections.value[0].burnedLastWeek);
|
||||
} else {
|
||||
return 0n;
|
||||
}
|
||||
|
|
@ -195,7 +194,7 @@ const inflation7d = computed(() => {
|
|||
|
||||
const stakeableSupply = computed(() => {
|
||||
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;
|
||||
} else {
|
||||
|
|
@ -206,7 +205,7 @@ const stakeableSupply = computed(() => {
|
|||
//maxSlots
|
||||
const maxSlots = computed(() => {
|
||||
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;
|
||||
} else {
|
||||
|
|
@ -230,7 +229,7 @@ export async function loadStats() {
|
|||
const endpoint = chainData.value?.graphql?.trim();
|
||||
if (!endpoint) {
|
||||
rawStatsCollections.value = [];
|
||||
statsError.value = "GraphQL endpoint not configured for this chain.";
|
||||
statsError.value = 'GraphQL endpoint not configured for this chain.';
|
||||
clearStatsRetryTimer();
|
||||
statsRetryDelayMs.value = RETRY_BASE_DELAY_MS;
|
||||
loading.value = false;
|
||||
|
|
@ -244,7 +243,7 @@ export async function loadStats() {
|
|||
statsRetryDelayMs.value = RETRY_BASE_DELAY_MS;
|
||||
clearStatsRetryTimer();
|
||||
} catch (error) {
|
||||
console.warn("[stats] loadStats() failed", error);
|
||||
// console.warn('[stats] loadStats() failed', error);
|
||||
rawStatsCollections.value = [];
|
||||
statsError.value = formatGraphqlError(error);
|
||||
scheduleStatsRetry();
|
||||
|
|
@ -254,9 +253,9 @@ export async function loadStats() {
|
|||
}
|
||||
}
|
||||
|
||||
let unwatch: any = null;
|
||||
let unwatchBlock: WatchBlocksReturnType;
|
||||
const loadingWatchBlock = ref(false);
|
||||
import { onMounted, onUnmounted } from 'vue';
|
||||
|
||||
let unwatch: WatchBlocksReturnType | null = null;
|
||||
export function useStatCollection() {
|
||||
onMounted(async () => {
|
||||
//initial loading stats
|
||||
|
|
@ -265,16 +264,16 @@ export function useStatCollection() {
|
|||
}
|
||||
});
|
||||
if (!unwatch) {
|
||||
console.log("watchChain");
|
||||
// console.log("watchChain");
|
||||
|
||||
//chain Switch reload stats for other chain
|
||||
unwatch = watchChainId(config as any, {
|
||||
async onChange(chainId) {
|
||||
unwatch = watchChainId(config as Config, {
|
||||
async onChange(_chainId) {
|
||||
await loadStats();
|
||||
},
|
||||
});
|
||||
|
||||
// const unwatchBlock = watchBlocks(config as any, {
|
||||
// const unwatchBlock = watchBlocks(config as Config, {
|
||||
// async onBlock(block) {
|
||||
// console.log('Block changed!', block)
|
||||
// await loadStats();
|
||||
|
|
@ -283,7 +282,9 @@ export function useStatCollection() {
|
|||
}
|
||||
onUnmounted(() => {
|
||||
clearStatsRetryTimer();
|
||||
if (unwatch) {
|
||||
unwatch();
|
||||
}
|
||||
});
|
||||
|
||||
return reactive({
|
||||
|
|
|
|||
|
|
@ -1,19 +1,20 @@
|
|||
import { ref, onMounted, onUnmounted, reactive, computed, type ComputedRef } from "vue";
|
||||
import * as StakeContract from "@/contracts/stake";
|
||||
import { waitForTransactionReceipt } from "@wagmi/core";
|
||||
import { config } from "@/wagmi";
|
||||
import { useContractToast } from "./useContractToast";
|
||||
import { compactNumber, formatBigIntDivision } from "@/utils/helper";
|
||||
import { decodeEventLog } from "viem";
|
||||
import { useWallet } from "./useWallet";
|
||||
import { ref, reactive, computed, type ComputedRef } from 'vue';
|
||||
import * as StakeContract from '@/contracts/stake';
|
||||
import { waitForTransactionReceipt } from '@wagmi/core';
|
||||
import type { Config } from '@wagmi/core';
|
||||
import { config } from '@/wagmi';
|
||||
import { useContractToast } from './useContractToast';
|
||||
import { compactNumber, formatBigIntDivision } from '@/utils/helper';
|
||||
import { decodeEventLog, type DecodeEventLogReturnType } from 'viem';
|
||||
import { useWallet } from './useWallet';
|
||||
|
||||
const contractToast = useContractToast();
|
||||
const wallet = useWallet();
|
||||
|
||||
enum StakeState {
|
||||
SignTransaction = "SignTransaction",
|
||||
Waiting = "Waiting",
|
||||
Unstakeable = "Unstakeable",
|
||||
SignTransaction = 'SignTransaction',
|
||||
Waiting = 'Waiting',
|
||||
Unstakeable = 'Unstakeable',
|
||||
}
|
||||
|
||||
interface PositionRemovedArgs {
|
||||
|
|
@ -38,35 +39,33 @@ export function useUnstake() {
|
|||
|
||||
async function exitPosition(positionId: bigint) {
|
||||
try {
|
||||
console.log("positionId", positionId);
|
||||
// console.log("positionId", positionId);
|
||||
|
||||
loading.value = true;
|
||||
const hash = await StakeContract.exitPosition(positionId);
|
||||
console.log("hash", hash);
|
||||
// console.log("hash", hash);
|
||||
loading.value = false;
|
||||
waiting.value = true;
|
||||
const data = await waitForTransactionReceipt(config as any, {
|
||||
const data = await waitForTransactionReceipt(config as Config, {
|
||||
hash: hash,
|
||||
});
|
||||
|
||||
const topics: any = decodeEventLog({
|
||||
const topics = decodeEventLog({
|
||||
abi: StakeContract.StakeContract.abi,
|
||||
data: data.logs[2].data,
|
||||
topics: data.logs[2].topics,
|
||||
});
|
||||
}) as DecodeEventLogReturnType & { args: PositionRemovedArgs };
|
||||
|
||||
const eventArgs: PositionRemovedArgs = topics.args;
|
||||
|
||||
const amount = compactNumber(
|
||||
formatBigIntDivision(eventArgs.harbergPayout, 10n ** BigInt(wallet.balance.decimals))
|
||||
);
|
||||
contractToast.showSuccessToast(amount, "Success!", "You unstaked", "", "$KRK");
|
||||
const amount = compactNumber(formatBigIntDivision(eventArgs.harbergPayout, 10n ** BigInt(wallet.balance.decimals)));
|
||||
contractToast.showSuccessToast(amount, 'Success!', 'You unstaked', '', '$KRK');
|
||||
|
||||
waiting.value = false;
|
||||
wallet.loadBalance();
|
||||
} catch (error: any) {
|
||||
console.error("error", error);
|
||||
contractToast.showFailToast(error.name);
|
||||
} catch (error) {
|
||||
// console.error("error", error);
|
||||
contractToast.showFailToast((error as Error).name);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
waiting.value = false;
|
||||
|
|
|
|||
|
|
@ -1,45 +1,43 @@
|
|||
import { ref, onMounted, onUnmounted, reactive, computed } from "vue";
|
||||
import { type Ref } from "vue";
|
||||
import { getAccount, getBalance, watchAccount, watchChainId } from "@wagmi/core";
|
||||
import { type WatchAccountReturnType, type GetAccountReturnType, type GetBalanceReturnType } from "@wagmi/core";
|
||||
import { config } from "@/wagmi";
|
||||
import { getAllowance, HarbContract, getNonce } from "@/contracts/harb";
|
||||
import logger from "@/utils/logger";
|
||||
import { setHarbContract } from "@/contracts/harb";
|
||||
import { setStakeContract } from "@/contracts/stake";
|
||||
import {chainsData, DEFAULT_CHAIN_ID} from "@/config"
|
||||
import { ref, reactive, computed } from 'vue';
|
||||
import { getAccount, getBalance, watchAccount, watchChainId, type Config } from '@wagmi/core';
|
||||
import { type WatchAccountReturnType, type GetAccountReturnType, type GetBalanceReturnType } from '@wagmi/core';
|
||||
import { config } from '@/wagmi';
|
||||
import { getAllowance, HarbContract, getNonce } from '@/contracts/harb';
|
||||
import logger from '@/utils/logger';
|
||||
import { setHarbContract } from '@/contracts/harb';
|
||||
import { setStakeContract } from '@/contracts/stake';
|
||||
import { chainsData, DEFAULT_CHAIN_ID } from '@/config';
|
||||
|
||||
const balance = ref<GetBalanceReturnType>({
|
||||
value: 0n,
|
||||
decimals: 0,
|
||||
symbol: "",
|
||||
formatted: ""
|
||||
symbol: '',
|
||||
formatted: '',
|
||||
});
|
||||
const account = ref<GetAccountReturnType>(getAccount(config as any));
|
||||
const account = ref<GetAccountReturnType>(getAccount(config as Config));
|
||||
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);
|
||||
|
||||
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 unwatchChain: any = null;
|
||||
let unwatch: WatchAccountReturnType | null = null;
|
||||
let unwatchChain: WatchAccountReturnType | null = null;
|
||||
export function useWallet() {
|
||||
|
||||
async function loadBalance() {
|
||||
logger.contract("loadBalance")
|
||||
logger.contract('loadBalance');
|
||||
if (account.value.address) {
|
||||
console.log("HarbContract",HarbContract );
|
||||
// console.log("HarbContract",HarbContract );
|
||||
|
||||
balance.value = await getBalance(config as any, {
|
||||
balance.value = await getBalance(config as Config, {
|
||||
address: account.value.address,
|
||||
token: HarbContract.contractAddress,
|
||||
});
|
||||
console.log("balance.value", balance.value);
|
||||
// console.log("balance.value", balance.value);
|
||||
|
||||
return balance.value;
|
||||
} else {
|
||||
|
|
@ -48,20 +46,20 @@ export function useWallet() {
|
|||
}
|
||||
|
||||
if (!unwatch) {
|
||||
console.log("useWallet function");
|
||||
// console.log("useWallet function");
|
||||
|
||||
unwatch = watchAccount(config as any, {
|
||||
unwatch = watchAccount(config as Config, {
|
||||
async onChange(data) {
|
||||
console.log("watchaccount-useWallet", data);
|
||||
// console.log("watchaccount-useWallet", data);
|
||||
|
||||
if (!data.address) {
|
||||
logger.info(`disconnected`);
|
||||
balance.value = {
|
||||
value: 0n,
|
||||
decimals: 0,
|
||||
symbol: "",
|
||||
formatted: ""
|
||||
}
|
||||
symbol: '',
|
||||
formatted: '',
|
||||
};
|
||||
} else if (account.value.address !== data.address || account.value.chainId !== data.chainId) {
|
||||
logger.info(`Account changed!:`, data.address);
|
||||
account.value = data;
|
||||
|
|
@ -69,26 +67,23 @@ export function useWallet() {
|
|||
await getAllowance();
|
||||
// await loadPositions();
|
||||
await getNonce();
|
||||
setHarbContract()
|
||||
setStakeContract()
|
||||
setHarbContract();
|
||||
setStakeContract();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
//funzt nicht mehr-> library Änderung?
|
||||
if (!unwatchChain) {
|
||||
console.log("unwatchChain");
|
||||
|
||||
unwatchChain = watchChainId(config as any, {
|
||||
async onChange(chainId) {
|
||||
console.log("chainId123", chainId);
|
||||
// console.log("unwatchChain");
|
||||
|
||||
unwatchChain = watchChainId(config as Config, {
|
||||
async onChange(_chainId) {
|
||||
await loadBalance();
|
||||
await getAllowance();
|
||||
await getNonce();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,28 @@
|
|||
import deploymentsLocal from "../../onchain/deployments-local.json";
|
||||
import deploymentsLocal from '../../onchain/deployments-local.json';
|
||||
|
||||
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_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_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_RPC_URL = env.VITE_LOCAL_RPC_URL ?? '/rpc/anvil';
|
||||
|
||||
const localContracts = (deploymentsLocal as any)?.contracts ?? {};
|
||||
const localInfra = (deploymentsLocal as any)?.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();
|
||||
interface DeploymentContracts {
|
||||
Kraiken?: string;
|
||||
Stake?: string;
|
||||
LiquidityManager?: string;
|
||||
}
|
||||
|
||||
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 {
|
||||
const envValue = import.meta.env.VITE_DEFAULT_CHAIN_ID;
|
||||
|
|
@ -23,9 +33,9 @@ function detectDefaultChainId(): number {
|
|||
}
|
||||
}
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
if (typeof window !== 'undefined') {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -40,57 +50,58 @@ export const chainsData = [
|
|||
// local base sepolia fork
|
||||
id: 31337,
|
||||
graphql: LOCAL_PONDER_URL,
|
||||
path: "local",
|
||||
path: 'local',
|
||||
stake: LOCAL_STAKE,
|
||||
harb: LOCAL_KRAIKEN,
|
||||
uniswap: "",
|
||||
uniswap: '',
|
||||
cheats: {
|
||||
weth: LOCAL_WETH,
|
||||
swapRouter: LOCAL_ROUTER,
|
||||
liquidityManager: LOCAL_LM,
|
||||
txnBot: LOCAL_TXNBOT_URL,
|
||||
rpc: LOCAL_RPC_URL,
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
// sepolia
|
||||
id: 11155111,
|
||||
graphql: import.meta.env.VITE_PONDER_SEPOLIA ?? "",
|
||||
path: "sepolia",
|
||||
stake: "0xCd21a41a137BCAf8743E47D048F57D92398f7Da9",
|
||||
harb: "0x087F256D11fe533b0c7d372e44Ee0F9e47C89dF9",
|
||||
uniswap: "https://app.uniswap.org/swap?chain=mainnet&inputCurrency=NATIVE",
|
||||
cheats: null
|
||||
}, {
|
||||
graphql: import.meta.env.VITE_PONDER_SEPOLIA ?? '',
|
||||
path: 'sepolia',
|
||||
stake: '0xCd21a41a137BCAf8743E47D048F57D92398f7Da9',
|
||||
harb: '0x087F256D11fe533b0c7d372e44Ee0F9e47C89dF9',
|
||||
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,
|
||||
path: "sepoliabase",
|
||||
stake: "0xe28020BCdEeAf2779dd47c670A8eFC2973316EE2",
|
||||
harb: "0x22c264Ecf8D4E49D1E3CabD8DD39b7C4Ab51C1B8",
|
||||
uniswap: "https://app.uniswap.org/swap?chain=mainnet&inputCurrency=NATIVE",
|
||||
path: 'sepoliabase',
|
||||
stake: '0xe28020BCdEeAf2779dd47c670A8eFC2973316EE2',
|
||||
harb: '0x22c264Ecf8D4E49D1E3CabD8DD39b7C4Ab51C1B8',
|
||||
uniswap: 'https://app.uniswap.org/swap?chain=mainnet&inputCurrency=NATIVE',
|
||||
cheats: {
|
||||
weth: "0x4200000000000000000000000000000000000006",
|
||||
swapRouter: "0x94cC0AaC535CCDB3C01d6787D6413C739ae12bc4",
|
||||
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,
|
||||
graphql: import.meta.env.VITE_PONDER_BASE ?? "",
|
||||
path: "base",
|
||||
stake: "0xed70707fab05d973ad41eae8d17e2bcd36192cfc",
|
||||
harb: "0x45caa5929f6ee038039984205bdecf968b954820",
|
||||
uniswap: "https://app.uniswap.org/swap?chain=mainnet&inputCurrency=NATIVE",
|
||||
graphql: import.meta.env.VITE_PONDER_BASE ?? '',
|
||||
path: 'base',
|
||||
stake: '0xed70707fab05d973ad41eae8d17e2bcd36192cfc',
|
||||
harb: '0x45caa5929f6ee038039984205bdecf968b954820',
|
||||
uniswap: 'https://app.uniswap.org/swap?chain=mainnet&inputCurrency=NATIVE',
|
||||
cheats: {
|
||||
weth: "0x4200000000000000000000000000000000000006",
|
||||
swapRouter: "",
|
||||
txnBot: import.meta.env.VITE_TXNBOT_BASE ?? ""
|
||||
}
|
||||
weth: '0x4200000000000000000000000000000000000006',
|
||||
swapRouter: '',
|
||||
txnBot: import.meta.env.VITE_TXNBOT_BASE ?? '',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export function getChain(id: number) {
|
||||
return chainsData.find((obj) => obj.id === id)
|
||||
return chainsData.find(obj => obj.id === id);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,15 @@
|
|||
import { ref, onMounted, onUnmounted, reactive, computed, watch } from "vue";
|
||||
import { config } from "@/wagmi";
|
||||
import { type WatchEventReturnType } from "viem";
|
||||
import {
|
||||
getAccount,
|
||||
watchContractEvent,
|
||||
readContract,
|
||||
writeContract,
|
||||
waitForTransactionReceipt,
|
||||
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";
|
||||
import { ref } from 'vue';
|
||||
import { config } from '@/wagmi';
|
||||
import { getAccount, readContract, writeContract, waitForTransactionReceipt, getChainId } from '@wagmi/core';
|
||||
import type { Config } from '@wagmi/core';
|
||||
import { KraikenAbi } from 'kraiken-lib';
|
||||
import { type Abi, type Address, type Hash } from 'viem';
|
||||
import { StakeContract } from '@/contracts/stake';
|
||||
import { getChain } from '@/config';
|
||||
import logger from '@/utils/logger';
|
||||
// const chain1 = useChain();
|
||||
// console.log("chain1", chain1);
|
||||
|
||||
|
||||
interface Contract {
|
||||
abi: Abi;
|
||||
contractAddress: Address;
|
||||
|
|
@ -34,18 +25,18 @@ export const totalSupply = ref(0n);
|
|||
export let HarbContract = getHarbJson();
|
||||
|
||||
function getHarbJson() {
|
||||
console.log("getHarbJson");
|
||||
// console.log("getHarbJson");
|
||||
|
||||
const chainId = getChainId(config as any);
|
||||
console.log("chainId", chainId);
|
||||
const chainId = getChainId(config as Config);
|
||||
// console.log("chainId", chainId);
|
||||
|
||||
const chain = getChain(chainId)
|
||||
const chain = getChain(chainId);
|
||||
|
||||
return { abi: KraikenAbi as Abi, contractAddress: chain?.harb } as Contract;
|
||||
}
|
||||
|
||||
export function setHarbContract() {
|
||||
console.log("setHarbContract");
|
||||
// console.log("setHarbContract");
|
||||
|
||||
HarbContract = getHarbJson();
|
||||
}
|
||||
|
|
@ -55,18 +46,17 @@ export function setHarbContract(){
|
|||
|
||||
// });
|
||||
|
||||
|
||||
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) {
|
||||
return 0n;
|
||||
}
|
||||
const result = await readContract(config as any, {
|
||||
const result = await readContract(config as Config, {
|
||||
abi: HarbContract.abi,
|
||||
address: HarbContract.contractAddress,
|
||||
functionName: "allowance",
|
||||
functionName: 'allowance',
|
||||
args: [account.address, StakeContract.contractAddress],
|
||||
});
|
||||
allowance.value = result;
|
||||
|
|
@ -74,31 +64,31 @@ export async function getAllowance() {
|
|||
}
|
||||
|
||||
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,
|
||||
address: HarbContract.contractAddress,
|
||||
functionName: "minStake",
|
||||
functionName: 'minStake',
|
||||
args: [],
|
||||
}) as bigint;
|
||||
})) as bigint;
|
||||
allowance.value = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
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) {
|
||||
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,
|
||||
address: HarbContract.contractAddress,
|
||||
functionName: "nonces",
|
||||
functionName: 'nonces',
|
||||
args: [account.address],
|
||||
});
|
||||
nonce.value = result;
|
||||
|
|
@ -107,13 +97,12 @@ export async function getNonce() {
|
|||
}
|
||||
|
||||
export async function getName() {
|
||||
logger.contract("getName");
|
||||
logger.contract('getName');
|
||||
|
||||
|
||||
const result = await readContract(config as any, {
|
||||
const result = await readContract(config as Config, {
|
||||
abi: HarbContract.abi,
|
||||
address: HarbContract.contractAddress,
|
||||
functionName: "name",
|
||||
functionName: 'name',
|
||||
args: [],
|
||||
});
|
||||
name.value = result;
|
||||
|
|
@ -121,48 +110,45 @@ export async function getName() {
|
|||
return result as string;
|
||||
}
|
||||
|
||||
|
||||
export async function approve(amount: bigint): Promise<any> {
|
||||
const account = getAccount(config as any);
|
||||
export async function approve(amount: bigint): Promise<Hash> {
|
||||
const account = getAccount(config as Config);
|
||||
if (!account.address) {
|
||||
throw new Error("no address found");
|
||||
throw new Error('no address found');
|
||||
}
|
||||
const result = await writeContract(config as any, {
|
||||
const result = await writeContract(config as Config, {
|
||||
abi: HarbContract.abi,
|
||||
address: HarbContract.contractAddress,
|
||||
functionName: "approve",
|
||||
functionName: 'approve',
|
||||
args: [StakeContract.contractAddress, amount],
|
||||
});
|
||||
console.log("result", result);
|
||||
const transactionReceipt = waitForTransactionReceipt(config as any, {
|
||||
// console.log("result", result);
|
||||
await waitForTransactionReceipt(config as Config, {
|
||||
hash: result,
|
||||
});
|
||||
console.log("transactionReceipt", transactionReceipt);
|
||||
// console.log("transactionReceipt", transactionReceipt);
|
||||
|
||||
return transactionReceipt;
|
||||
return result;
|
||||
}
|
||||
|
||||
//claim
|
||||
export async function claimUbi(address: Address): Promise<any> {
|
||||
const result = await writeContract(config as any, {
|
||||
export async function claimUbi(address: Address): Promise<Hash> {
|
||||
const result = await writeContract(config as Config, {
|
||||
abi: HarbContract.abi,
|
||||
address: HarbContract.contractAddress,
|
||||
functionName: "claimUbi",
|
||||
functionName: 'claimUbi',
|
||||
args: [address],
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
export async function getTotalSupply() {
|
||||
logger.contract("getTotalSupply");
|
||||
logger.contract('getTotalSupply');
|
||||
|
||||
|
||||
const result = await readContract(config as any, {
|
||||
const result = await readContract(config as Config, {
|
||||
abi: HarbContract.abi,
|
||||
address: HarbContract.contractAddress,
|
||||
functionName: "totalSupply",
|
||||
functionName: 'totalSupply',
|
||||
args: [],
|
||||
});
|
||||
totalSupply.value = result as bigint;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import { ref } from "vue";
|
||||
import { config } from "@/wagmi";
|
||||
import { readContract, writeContract, getChainId } from "@wagmi/core";
|
||||
import { StakeAbi } from "kraiken-lib";
|
||||
import { type Abi, type Address } from "viem";
|
||||
import {getChain} from "@/config"
|
||||
import logger from "@/utils/logger";
|
||||
import { ref } from 'vue';
|
||||
import { config } from '@/wagmi';
|
||||
import { readContract, writeContract, getChainId } from '@wagmi/core';
|
||||
import type { Config } from '@wagmi/core';
|
||||
import { StakeAbi } from 'kraiken-lib';
|
||||
import { type Abi, type Address, type Hash, type Hex } from 'viem';
|
||||
import { getChain } from '@/config';
|
||||
import logger from '@/utils/logger';
|
||||
|
||||
const TAX_FLOOR_DURATION = 60 * 60 * 24 * 3;
|
||||
interface Contract {
|
||||
|
|
@ -12,51 +13,43 @@ interface Contract {
|
|||
contractAddress: Address;
|
||||
}
|
||||
|
||||
|
||||
export const minStake = ref();
|
||||
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 any);
|
||||
console.log("chainId", chainId);
|
||||
const chainId = getChainId(config as Config);
|
||||
// console.log("chainId", chainId);
|
||||
|
||||
const chain = getChain(chainId)
|
||||
const chain = getChain(chainId);
|
||||
|
||||
return { abi: StakeAbi as Abi, contractAddress: chain?.stake } as Contract;
|
||||
}
|
||||
|
||||
export function setStakeContract() {
|
||||
logger.contract("setStakeContract")
|
||||
logger.contract('setStakeContract');
|
||||
StakeContract = getStakeJson();
|
||||
}
|
||||
|
||||
export async function snatchService(assets: bigint, receiver: Address, taxRate: number, positionsToSnatch: Array<bigint>) {
|
||||
// console.log("StakeContract", StakeContract);
|
||||
|
||||
export async function snatchService(
|
||||
assets: BigInt,
|
||||
receiver: Address,
|
||||
taxRate: Number,
|
||||
positionsToSnatch: Array<BigInt>
|
||||
) {
|
||||
console.log("StakeContract", StakeContract);
|
||||
|
||||
const result = await writeContract(config as any, {
|
||||
const result = await writeContract(config as Config, {
|
||||
abi: StakeContract.abi,
|
||||
address: StakeContract.contractAddress,
|
||||
functionName: "snatch",
|
||||
functionName: 'snatch',
|
||||
args: [assets, receiver, taxRate, positionsToSnatch],
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function exitPosition(positionId: bigint): Promise<any> {
|
||||
const result = await writeContract(config as any, {
|
||||
export async function exitPosition(positionId: bigint): Promise<Hash> {
|
||||
const result = await writeContract(config as Config, {
|
||||
abi: StakeContract.abi,
|
||||
address: StakeContract.contractAddress,
|
||||
functionName: "exitPosition",
|
||||
functionName: 'exitPosition',
|
||||
args: [positionId],
|
||||
});
|
||||
|
||||
|
|
@ -64,11 +57,11 @@ export async function exitPosition(positionId: bigint): Promise<any> {
|
|||
}
|
||||
|
||||
//changeTax
|
||||
export async function changeTax(positionId: bigint, taxRate: number): Promise<any> {
|
||||
const result = await writeContract(config as any, {
|
||||
export async function changeTax(positionId: bigint, taxRate: number): Promise<Hash> {
|
||||
const result = await writeContract(config as Config, {
|
||||
abi: StakeContract.abi,
|
||||
address: StakeContract.contractAddress,
|
||||
functionName: "changeTax",
|
||||
functionName: 'changeTax',
|
||||
args: [positionId, taxRate],
|
||||
});
|
||||
|
||||
|
|
@ -79,48 +72,48 @@ export async function changeTax(positionId: bigint, taxRate: number): Promise<an
|
|||
* snatch/stake with permit
|
||||
*/
|
||||
export async function permitAndSnatch(
|
||||
assets: BigInt,
|
||||
assets: bigint,
|
||||
receiver: Address,
|
||||
taxRate: number,
|
||||
positionsToSnatch: Array<BigInt>,
|
||||
deadline: BigInt,
|
||||
v: any,
|
||||
r: any,
|
||||
s: any
|
||||
positionsToSnatch: Array<bigint>,
|
||||
deadline: bigint,
|
||||
v: number,
|
||||
r: Hex,
|
||||
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,
|
||||
address: StakeContract.contractAddress,
|
||||
functionName: "permitAndSnatch",
|
||||
functionName: 'permitAndSnatch',
|
||||
args: [assets, receiver, taxRate, positionsToSnatch, deadline, v, r, s],
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function getTotalSupply() {
|
||||
logger.contract("getTotalSupply")
|
||||
logger.contract('getTotalSupply');
|
||||
await setStakeContract();
|
||||
const result = await readContract(config as any, {
|
||||
const result = await readContract(config as Config, {
|
||||
abi: StakeContract.abi,
|
||||
address: StakeContract.contractAddress,
|
||||
functionName: "totalSupply",
|
||||
functionName: 'totalSupply',
|
||||
args: [],
|
||||
});
|
||||
console.log("result", result);
|
||||
// console.log("result", result);
|
||||
|
||||
totalSupply.value = result as bigint;
|
||||
return result;
|
||||
}
|
||||
|
||||
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,
|
||||
address: StakeContract.contractAddress,
|
||||
functionName: "outstandingStake",
|
||||
functionName: 'outstandingStake',
|
||||
args: [],
|
||||
});
|
||||
|
||||
|
|
@ -129,35 +122,35 @@ export async function getOutstandingSupply() {
|
|||
}
|
||||
|
||||
export async function getTaxDue(positionID: bigint) {
|
||||
logger.contract("getTaxDue")
|
||||
const result = await readContract(config as any, {
|
||||
logger.contract('getTaxDue');
|
||||
const result = await readContract(config as Config, {
|
||||
abi: StakeContract.abi,
|
||||
address: StakeContract.contractAddress,
|
||||
functionName: "taxDue",
|
||||
functionName: 'taxDue',
|
||||
args: [positionID, TAX_FLOOR_DURATION],
|
||||
});
|
||||
return result as 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,
|
||||
address: StakeContract.contractAddress,
|
||||
functionName: "payTax",
|
||||
functionName: 'payTax',
|
||||
args: [positionID],
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
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,
|
||||
address: StakeContract.contractAddress,
|
||||
functionName: "assetsToShares",
|
||||
functionName: 'assetsToShares',
|
||||
args: [asset],
|
||||
});
|
||||
return result as bigint;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,25 @@
|
|||
import type { DirectiveBinding } from 'vue';
|
||||
|
||||
interface ClickOutsideElement extends HTMLElement {
|
||||
clickOutsideEvent?: (event: MouseEvent) => void;
|
||||
}
|
||||
|
||||
export default {
|
||||
beforeMount(el: any, binding: any) {
|
||||
el.clickOutsideEvent = function (event: any) {
|
||||
beforeMount(el: ClickOutsideElement, binding: DirectiveBinding<(event: MouseEvent) => void>) {
|
||||
el.clickOutsideEvent = function (event: MouseEvent) {
|
||||
// Check if the clicked element is neither the element
|
||||
// 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
|
||||
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
|
||||
document.removeEventListener("click", el.clickOutsideEvent);
|
||||
if (el.clickOutsideEvent) {
|
||||
document.removeEventListener('click', el.clickOutsideEvent);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<!-- src/layouts/NavbarLayout.vue -->
|
||||
<template>
|
||||
<navbar></navbar>
|
||||
<slideout-panel v-model="showPanel">
|
||||
<connect-wallet></connect-wallet>
|
||||
<NavbarHeader></NavbarHeader>
|
||||
<SlideoutPanel v-model="showPanel">
|
||||
<ConnectWallet></ConnectWallet>
|
||||
<!-- <f-footer :dark="true"></f-footer> -->
|
||||
</slideout-panel>
|
||||
</SlideoutPanel>
|
||||
<div class="navbar-layout">
|
||||
<main>
|
||||
<slot></slot>
|
||||
|
|
@ -16,13 +16,13 @@
|
|||
<div class="mobile-navigation-bar" v-if="isMobile">
|
||||
<div class="mobile-navigation-tab" @click="router.push('/')">
|
||||
<div class="mobile-navigation-tab__icon">
|
||||
<icon-home />
|
||||
<IconHome />
|
||||
</div>
|
||||
<div class="mobile-navigation-tab__title">Stake</div>
|
||||
</div>
|
||||
<div class="mobile-navigation-tab" @click="openDocs">
|
||||
<div class="mobile-navigation-tab__icon">
|
||||
<icon-docs />
|
||||
<IconDocs />
|
||||
</div>
|
||||
<div class="mobile-navigation-tab__title">Docs</div>
|
||||
</div>
|
||||
|
|
@ -30,28 +30,24 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Navbar from "@/components/layouts/Navbar.vue";
|
||||
import SlideoutPanel from "@/components/layouts/SlideoutPanel.vue";
|
||||
import ConnectWallet from "@/components/layouts/ConnectWallet.vue";
|
||||
import ThemeToggle from "@/components/layouts/ThemeToggle.vue";
|
||||
import FFooter from "@/components/layouts/FFooter.vue";
|
||||
import { useMobile } from "@/composables/useMobile";
|
||||
import { useDark } from "@/composables/useDark";
|
||||
import IconHome from "@/components/icons/IconHome.vue";
|
||||
import IconDocs from "@/components/icons/IconDocs.vue";
|
||||
import { ref, provide } from "vue";
|
||||
import {useRouter } from "vue-router"
|
||||
import NavbarHeader from '@/components/layouts/NavbarHeader.vue';
|
||||
import SlideoutPanel from '@/components/layouts/SlideoutPanel.vue';
|
||||
import ConnectWallet from '@/components/layouts/ConnectWallet.vue';
|
||||
import { useMobile } from '@/composables/useMobile';
|
||||
import IconHome from '@/components/icons/IconHome.vue';
|
||||
import IconDocs from '@/components/icons/IconDocs.vue';
|
||||
import { ref, provide } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
const showPanel = ref(false);
|
||||
const { darkTheme } = useDark();
|
||||
|
||||
const router = useRouter()
|
||||
const router = useRouter();
|
||||
|
||||
const isMobile = useMobile();
|
||||
provide("isMobile", isMobile);
|
||||
provide("showPanel", showPanel);
|
||||
provide('isMobile', isMobile);
|
||||
provide('showPanel', showPanel);
|
||||
|
||||
function openDocs() {
|
||||
window.open("https://emberspirit007.github.io/KraikenLanding/#/docs/Introduction")
|
||||
window.open('https://emberspirit007.github.io/KraikenLanding/#/docs/Introduction');
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +1,25 @@
|
|||
import { WagmiPlugin } from "@wagmi/vue";
|
||||
import { QueryClient, VueQueryPlugin } from "@tanstack/vue-query";
|
||||
import { createApp } from "vue";
|
||||
import { config } from "./wagmi";
|
||||
import ClickOutSide from "@/directives/ClickOutsideDirective";
|
||||
import router from "./router";
|
||||
import { WagmiPlugin } from '@wagmi/vue';
|
||||
import { QueryClient, VueQueryPlugin } from '@tanstack/vue-query';
|
||||
import { createApp } from 'vue';
|
||||
import { config } from './wagmi';
|
||||
import ClickOutSide from '@/directives/ClickOutsideDirective';
|
||||
import router from './router';
|
||||
|
||||
import App from "./App.vue";
|
||||
import "./assets/styles/main.sass";
|
||||
import Toast from "vue-toastification";
|
||||
import "vue-toastification/dist/index.css";
|
||||
import App from './App.vue';
|
||||
import './assets/styles/main.sass';
|
||||
import Toast from 'vue-toastification';
|
||||
import 'vue-toastification/dist/index.css';
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
app.directive("click-outside", ClickOutSide);
|
||||
app.directive('click-outside', ClickOutSide);
|
||||
|
||||
app.use(WagmiPlugin, { config });
|
||||
app.use(VueQueryPlugin, { queryClient });
|
||||
app.use(router);
|
||||
app.use(Toast, {
|
||||
transition: "Vue-Toastification__fade",
|
||||
containerClassName: "harb-toast-container",
|
||||
transition: 'Vue-Toastification__fade',
|
||||
containerClassName: 'harb-toast-container',
|
||||
});
|
||||
app.mount("#app");
|
||||
app.mount('#app');
|
||||
|
|
|
|||
|
|
@ -1,42 +1,41 @@
|
|||
import { createRouter, createWebHashHistory } from "vue-router";
|
||||
import HomeView from "../views/HomeView.vue";
|
||||
import { createRouter, createWebHashHistory } from 'vue-router';
|
||||
import { authGuard } from './authGuard';
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes: [
|
||||
{
|
||||
path: "/",
|
||||
name: "home",
|
||||
redirect: "/stake",
|
||||
path: '/',
|
||||
name: 'home',
|
||||
redirect: '/stake',
|
||||
},
|
||||
{
|
||||
path: "/stake",
|
||||
name: "stake",
|
||||
path: '/stake',
|
||||
name: 'stake',
|
||||
meta: {
|
||||
title: "Stake",
|
||||
group: "navbar",
|
||||
layout: 'NavbarLayout'
|
||||
title: 'Stake',
|
||||
group: 'navbar',
|
||||
layout: 'NavbarLayout',
|
||||
},
|
||||
beforeEnter: authGuard,
|
||||
component: () => import("../views/StakeView.vue"),
|
||||
component: () => import('../views/StakeView.vue'),
|
||||
},
|
||||
{
|
||||
path: "/login",
|
||||
name: "login",
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
meta: {
|
||||
layout: 'DefaultLayout'
|
||||
, },
|
||||
component: () => import("../views/LoginView.vue"),
|
||||
layout: 'DefaultLayout',
|
||||
},
|
||||
component: () => import('../views/LoginView.vue'),
|
||||
},
|
||||
{
|
||||
path: "/cheats",
|
||||
name: "cheats",
|
||||
path: '/cheats',
|
||||
name: 'cheats',
|
||||
meta: {
|
||||
title: "Cheats",
|
||||
layout: 'NavbarLayout'
|
||||
title: 'Cheats',
|
||||
layout: 'NavbarLayout',
|
||||
},
|
||||
component: () => import("../views/CheatsView.vue"),
|
||||
component: () => import('../views/CheatsView.vue'),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
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(
|
||||
verifyingContract: Address,
|
||||
fromAddress: Address,
|
||||
spender: Address,
|
||||
nonce: BigInt,
|
||||
deadline: BigInt,
|
||||
value: BigInt,
|
||||
nonce: bigint,
|
||||
deadline: bigint,
|
||||
value: bigint,
|
||||
chain: number,
|
||||
domainName: string
|
||||
) {
|
||||
|
|
@ -19,45 +18,44 @@ export function createPermitObject(
|
|||
value: value,
|
||||
};
|
||||
|
||||
|
||||
const domainType = [
|
||||
{ name: "name", type: "string" },
|
||||
{ name: "version", type: "string" },
|
||||
{ name: "chainId", type: "uint256" },
|
||||
{ name: "verifyingContract", type: "address" },
|
||||
{ name: 'name', type: 'string' },
|
||||
{ name: 'version', type: 'string' },
|
||||
{ name: 'chainId', type: 'uint256' },
|
||||
{ name: 'verifyingContract', type: 'address' },
|
||||
];
|
||||
|
||||
const primaryType: "EIP712Domain" | "Permit" = "Permit";
|
||||
const primaryType: 'EIP712Domain' | 'Permit' = 'Permit';
|
||||
|
||||
const types = {
|
||||
EIP712Domain: domainType,
|
||||
Permit: [
|
||||
{
|
||||
name: "owner",
|
||||
type: "address",
|
||||
name: 'owner',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: "spender",
|
||||
type: "address",
|
||||
name: 'spender',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: "value",
|
||||
type: "uint256",
|
||||
name: 'value',
|
||||
type: 'uint256',
|
||||
},
|
||||
{
|
||||
name: "nonce",
|
||||
type: "uint256",
|
||||
name: 'nonce',
|
||||
type: 'uint256',
|
||||
},
|
||||
{
|
||||
name: "deadline",
|
||||
type: "uint256",
|
||||
name: 'deadline',
|
||||
type: 'uint256',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const domain: TypedDataDomain | undefined = {
|
||||
name: domainName,
|
||||
version: "1",
|
||||
version: '1',
|
||||
chainId: chain,
|
||||
verifyingContract: verifyingContract,
|
||||
};
|
||||
|
|
@ -80,6 +78,6 @@ export function getSignatureRSV2(sig: `0x${string}`) {
|
|||
export function getSignatureRSV(signature: `0x${string}`) {
|
||||
const r = signature.slice(0, 66) 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 };
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
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 {
|
||||
seed: string;
|
||||
|
|
@ -10,12 +10,11 @@ interface BlockiesOpt {
|
|||
}
|
||||
|
||||
export function getBlocky(address: string) {
|
||||
if (!address || typeof address !== "string" ) {
|
||||
if (!address || typeof address !== 'string') {
|
||||
return;
|
||||
}
|
||||
console.log("address", address);
|
||||
|
||||
var blockiesData = createIcon({
|
||||
const blockiesData = createIcon({
|
||||
seed: address?.toLowerCase(),
|
||||
size: 8,
|
||||
scale: 4,
|
||||
|
|
@ -25,17 +24,17 @@ export function getBlocky(address: string) {
|
|||
}
|
||||
|
||||
function seedrand(seed: string) {
|
||||
for (var i = 0; i < randseed.length; i++) {
|
||||
for (let i = 0; i < randseed.length; i++) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
function rand() {
|
||||
// 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[1] = randseed[2];
|
||||
|
|
@ -47,36 +46,36 @@ function rand() {
|
|||
|
||||
function createColor() {
|
||||
//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
|
||||
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%
|
||||
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;
|
||||
}
|
||||
|
||||
function createImageData(size: number) {
|
||||
var width = size; // Only support square icons for now
|
||||
var height = size;
|
||||
const width = size; // Only support square icons for now
|
||||
const height = size;
|
||||
|
||||
var dataWidth = Math.ceil(width / 2);
|
||||
var mirrorWidth = width - dataWidth;
|
||||
const dataWidth = Math.ceil(width / 2);
|
||||
const mirrorWidth = width - dataWidth;
|
||||
|
||||
var data = [];
|
||||
for (var y = 0; y < height; y++) {
|
||||
var row = [];
|
||||
for (var x = 0; x < dataWidth; x++) {
|
||||
const data = [];
|
||||
for (let y = 0; y < height; y++) {
|
||||
let row = [];
|
||||
for (let x = 0; x < dataWidth; x++) {
|
||||
// this makes foreground and background color to have a 43% (1/2.3) probability
|
||||
// spot color has 13% chance
|
||||
row[x] = Math.floor(rand() * 2.3);
|
||||
}
|
||||
var r = row.slice(0, mirrorWidth);
|
||||
const r = row.slice(0, mirrorWidth);
|
||||
r.reverse();
|
||||
row = row.concat(r);
|
||||
|
||||
for (var i = 0; i < row.length; i++) {
|
||||
for (let i = 0; i < row.length; i++) {
|
||||
data.push(row[i]);
|
||||
}
|
||||
}
|
||||
|
|
@ -84,50 +83,49 @@ function createImageData(size: number) {
|
|||
return data;
|
||||
}
|
||||
|
||||
function buildOpts(opts: any) {
|
||||
var newOpts: any = {};
|
||||
newOpts.seed = opts.seed || Math.floor(Math.random() * Math.pow(10, 16)).toString(16);
|
||||
function buildOpts(opts: Partial<BlockiesOpt>): BlockiesOpt {
|
||||
const seed = opts.seed || Math.floor(Math.random() * Math.pow(10, 16)).toString(16);
|
||||
seedrand(seed);
|
||||
|
||||
seedrand(newOpts.seed);
|
||||
|
||||
newOpts.size = opts.size || 8;
|
||||
newOpts.scale = opts.scale || 4;
|
||||
newOpts.color = opts.color || createColor();
|
||||
newOpts.bgcolor = opts.bgcolor || createColor();
|
||||
newOpts.spotcolor = opts.spotcolor || createColor();
|
||||
|
||||
return newOpts;
|
||||
return {
|
||||
seed,
|
||||
size: opts.size || 8,
|
||||
scale: opts.scale || 4,
|
||||
color: opts.color || createColor(),
|
||||
bgcolor: opts.bgcolor || createColor(),
|
||||
spotcolor: opts.spotcolor || createColor(),
|
||||
};
|
||||
}
|
||||
|
||||
function renderIcon(opts: BlockiesOpt, canvas: HTMLCanvasElement) {
|
||||
opts = buildOpts(opts || {});
|
||||
var imageData = createImageData(opts.size);
|
||||
var width = Math.sqrt(imageData.length);
|
||||
function renderIcon(opts: Partial<BlockiesOpt>, canvas: HTMLCanvasElement) {
|
||||
const fullOpts = buildOpts(opts);
|
||||
const imageData = createImageData(fullOpts.size);
|
||||
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")!;
|
||||
cc.fillStyle = opts.bgcolor!;
|
||||
const cc = canvas.getContext('2d')!;
|
||||
cc.fillStyle = fullOpts.bgcolor!;
|
||||
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 (imageData[i]) {
|
||||
var row = Math.floor(i / width);
|
||||
var col = i % width;
|
||||
const row = Math.floor(i / width);
|
||||
const col = i % width;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
export function createIcon(opts: BlockiesOpt) {
|
||||
var canvas = document.createElement("canvas");
|
||||
const canvas = document.createElement('canvas');
|
||||
|
||||
renderIcon(opts, canvas);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,17 @@
|
|||
import { formatUnits } from 'viem'
|
||||
|
||||
import { formatUnits } from 'viem';
|
||||
|
||||
export function getAddressShortName(address: string) {
|
||||
if (!address) {
|
||||
return "";
|
||||
return '';
|
||||
}
|
||||
const addressBegin = address.substring(0, 6);
|
||||
const addressEnd = address.substring(address.length - 4, address.length);
|
||||
return addressBegin + "..." + addressEnd;
|
||||
return addressBegin + '...' + addressEnd;
|
||||
}
|
||||
|
||||
export function compactNumber(number: number) {
|
||||
return Intl.NumberFormat("en-US", {
|
||||
notation: "compact",
|
||||
return Intl.NumberFormat('en-US', {
|
||||
notation: 'compact',
|
||||
// minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
}).format(number);
|
||||
|
|
@ -21,44 +20,41 @@ export function compactNumber(number: number) {
|
|||
export function formatBigNumber(number: bigint, decimals: number, digits: number = 5) {
|
||||
let bigIntNumber = number;
|
||||
if (!bigIntNumber) {
|
||||
bigIntNumber = BigInt(0)
|
||||
bigIntNumber = BigInt(0);
|
||||
}
|
||||
const formattedNumber = Number(formatUnits(bigIntNumber, decimals))
|
||||
const formattedNumber = Number(formatUnits(bigIntNumber, decimals));
|
||||
if (formattedNumber === 0) {
|
||||
return "0"
|
||||
return '0';
|
||||
}
|
||||
return formattedNumber.toFixed(digits)
|
||||
return formattedNumber.toFixed(digits);
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function bigInt2Number(number: bigint, decimals: number) {
|
||||
let bigIntNumber = number;
|
||||
if (!bigIntNumber) {
|
||||
bigIntNumber = BigInt(0)
|
||||
bigIntNumber = BigInt(0);
|
||||
}
|
||||
const formattedNumber = Number(formatUnits(bigIntNumber, decimals))
|
||||
return formattedNumber
|
||||
const formattedNumber = Number(formatUnits(bigIntNumber, decimals));
|
||||
return formattedNumber;
|
||||
}
|
||||
|
||||
export function InsertCommaNumber(number: any) {
|
||||
export function InsertCommaNumber(number: number) {
|
||||
if (!number) {
|
||||
return 0;
|
||||
}
|
||||
const formattedWithOptions = number.toLocaleString("en-US");
|
||||
const formattedWithOptions = number.toLocaleString('en-US');
|
||||
return formattedWithOptions;
|
||||
}
|
||||
|
||||
export function formatBigIntDivision(nominator: bigint, denominator: bigint, digits: number = 2) {
|
||||
export function formatBigIntDivision(nominator: bigint, denominator: bigint, _digits: number = 2) {
|
||||
if (!nominator) {
|
||||
return 0;
|
||||
}
|
||||
let display = nominator.toString();
|
||||
const display = nominator.toString();
|
||||
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
|
||||
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) {
|
||||
console.log(`%c ${text}`, 'color: #17a2b8', data);
|
||||
|
||||
// console.log(`%c ${text}`, 'color: #17a2b8', data);
|
||||
} 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) {
|
||||
console.log(`%c ${text}`, 'color: #8732a8', data);
|
||||
|
||||
// console.log(`%c ${text}`, 'color: #8732a8', data);
|
||||
} else {
|
||||
console.log(`%c ${text}`, 'color: #8732a8');
|
||||
// console.log(`%c ${text}`, 'color: #8732a8');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
info,
|
||||
contract
|
||||
}
|
||||
contract,
|
||||
};
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,28 +1,24 @@
|
|||
<template>
|
||||
<chart-js
|
||||
:snatchedPositions="snatchPositions.map((obj) => obj.id)"
|
||||
:positions="activePositions"
|
||||
:dark="darkTheme"
|
||||
></chart-js>
|
||||
<ChartJs :snatchedPositions="snatchPositions.map(obj => obj.id)" :positions="activePositions" :dark="darkTheme"></ChartJs>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import ChartJs from "@/components/chart/ChartJs.vue";
|
||||
import {bigInt2Number, formatBigIntDivision} from "@/utils/helper";
|
||||
import { computed, ref } from "vue";
|
||||
import { useStatCollection } from "@/composables/useStatCollection";
|
||||
import { useStake } from "@/composables/useStake";
|
||||
import { usePositions, type Position } from "@/composables/usePositions";
|
||||
import { useDark } from "@/composables/useDark";
|
||||
import ChartJs from '@/components/chart/ChartJs.vue';
|
||||
import { bigInt2Number, formatBigIntDivision } from '@/utils/helper';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useStatCollection } from '@/composables/useStatCollection';
|
||||
import { useStake } from '@/composables/useStake';
|
||||
import { usePositions, type Position } from '@/composables/usePositions';
|
||||
import { useDark } from '@/composables/useDark';
|
||||
const { darkTheme } = useDark();
|
||||
|
||||
const { activePositions, myActivePositions, tresholdValue, myClosedPositions, createRandomPosition } = usePositions();
|
||||
const { activePositions } = usePositions();
|
||||
const ignoreOwner = ref(false);
|
||||
|
||||
const taxRate = ref<number>(1.0);
|
||||
|
||||
const minStakeAmount = computed(() => {
|
||||
console.log("minStake", minStake.value);
|
||||
// console.log("minStake", minStake.value);
|
||||
|
||||
return formatBigIntDivision(minStake.value, 10n ** 18n);
|
||||
});
|
||||
|
|
@ -45,7 +41,7 @@ const snatchPositions = computed(() => {
|
|||
bigInt2Number(statCollection.outstandingStake, 18) +
|
||||
stake.stakingAmountNumber -
|
||||
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
|
||||
const snatchAblePositionsCount = Math.floor(difference / minStakeAmount.value);
|
||||
|
|
@ -64,5 +60,4 @@ const snatchPositions = computed(() => {
|
|||
|
||||
return [];
|
||||
});
|
||||
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
</script>
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<main>
|
||||
</main>
|
||||
<main></main>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@
|
|||
<div class="error-box" v-if="error">
|
||||
{{ error }}
|
||||
</div>
|
||||
<f-input type="password" @input="error = ''" v-model="inputPassword" label="Password" @keyup.enter="login"></f-input>
|
||||
<f-button size="large" @click="login">Login</f-button>
|
||||
<FInput type="password" @input="error = ''" v-model="inputPassword" label="Password" @keyup.enter="login"></FInput>
|
||||
<FButton size="large" @click="login">Login</FButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -16,22 +16,22 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import FButton from "@/components/fcomponents/FButton.vue";
|
||||
import FInput from "@/components/fcomponents/FInput.vue";
|
||||
import { onMounted, ref } from "vue";
|
||||
import {useRouter} from "vue-router"
|
||||
import FButton from '@/components/fcomponents/FButton.vue';
|
||||
import FInput from '@/components/fcomponents/FInput.vue';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
const router = useRouter();
|
||||
|
||||
const error = ref("")
|
||||
const passwords = ref(["lobsterDao", "test123", "lobster-x010syqe?412!"]);
|
||||
const inputPassword = ref("")
|
||||
const error = ref('');
|
||||
const passwords = ref(['lobsterDao', 'test123', 'lobster-x010syqe?412!']);
|
||||
const inputPassword = ref('');
|
||||
function login() {
|
||||
if (passwords.value.includes(inputPassword.value)) {
|
||||
localStorage.setItem('authentificated', "true");
|
||||
router.push("/")
|
||||
localStorage.setItem('authentificated', 'true');
|
||||
router.push('/');
|
||||
return true;
|
||||
} else {
|
||||
error.value = "wrong password"
|
||||
error.value = 'wrong password';
|
||||
// Token nach erfolgreichem Login speichern
|
||||
// Beim Logout Token entfernen
|
||||
return false;
|
||||
|
|
@ -39,10 +39,10 @@ function login(){
|
|||
}
|
||||
|
||||
onMounted(() => {
|
||||
if(localStorage.getItem('authentificated') === "true"){
|
||||
router.push("/")
|
||||
if (localStorage.getItem('authentificated') === 'true') {
|
||||
router.push('/');
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
|
|
|
|||
|
|
@ -5,52 +5,44 @@
|
|||
Staking Dashboard
|
||||
<IconInfo size="20px">
|
||||
<template #text>
|
||||
Stake your $KRK and claim owner slots. Owner slots give a return on every token buy that
|
||||
increases the liquidity of KrAIken. Owner slots are limited to 20,000 slots in total. To enable
|
||||
everyone and anyone to join staking is protected by a “Harberger Tax” mechanism.
|
||||
Stake your $KRK and claim owner slots. Owner slots give a return on every token buy that increases the liquidity of KrAIken.
|
||||
Owner slots are limited to 20,000 slots in total. To enable everyone and anyone to join staking is protected by a “Harberger
|
||||
Tax” mechanism.
|
||||
</template>
|
||||
</IconInfo>
|
||||
</h3>
|
||||
|
||||
<div class="stake-view-body">
|
||||
<chart-complete></chart-complete>
|
||||
<ChartComplete></ChartComplete>
|
||||
<div class="hold-stake-wrapper">
|
||||
<f-card class="inner-border">
|
||||
<template v-if="!isChainSupported">
|
||||
Chain not supported
|
||||
</template>
|
||||
<FCard class="inner-border">
|
||||
<template v-if="!isChainSupported"> Chain not supported </template>
|
||||
<template v-else-if="status !== 'connected'">
|
||||
<f-button @click="showPanel = true" size="large" block
|
||||
>Connect Wallet</f-button
|
||||
>
|
||||
<FButton @click="showPanel = true" size="large" block>Connect Wallet</FButton>
|
||||
</template>
|
||||
<template v-else>
|
||||
|
||||
<stake-holder ></stake-holder>
|
||||
<StakeHolder></StakeHolder>
|
||||
</template>
|
||||
</f-card>
|
||||
</FCard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="statistics-wrapper">
|
||||
<h3>Statistics</h3>
|
||||
<div class="statistics-outputs-wrapper">
|
||||
<stats-output headline="Average Slot Tax" :price="`${averageTaxRate.toFixed(2)} %`"></stats-output>
|
||||
<stats-output
|
||||
<StatsOutput headline="Average Slot Tax" :price="`${averageTaxRate.toFixed(2)} %`"></StatsOutput>
|
||||
<StatsOutput
|
||||
headline="Claimed Owner Slots"
|
||||
:price="`${InsertCommaNumber(stats.claimedSlots)} / ${InsertCommaNumber(stats.maxSlots)}`"
|
||||
></stats-output>
|
||||
<stats-output
|
||||
headline="Total Supply Change / 7d"
|
||||
:price="`+ ${stats.totalSupplyChange7d} $KRK`"
|
||||
></stats-output>
|
||||
<stats-output headline="Inflation / 7d" :price="`+${stats.inflation7d}%`"></stats-output>
|
||||
></StatsOutput>
|
||||
<StatsOutput headline="Total Supply Change / 7d" :price="`+ ${stats.totalSupplyChange7d} $KRK`"></StatsOutput>
|
||||
<StatsOutput headline="Inflation / 7d" :price="`+${stats.inflation7d}%`"></StatsOutput>
|
||||
</div>
|
||||
</div>
|
||||
<div class="active-positions-wrapper">
|
||||
<h3>Active Positions</h3>
|
||||
<div class="active-positions-list">
|
||||
<collapse-active
|
||||
<CollapseActive
|
||||
v-for="position in myActivePositions"
|
||||
:taxRate="position.taxRatePercentage"
|
||||
:amount="position.amount"
|
||||
|
|
@ -58,7 +50,7 @@
|
|||
:id="position.positionId"
|
||||
:position="position"
|
||||
:key="position.id"
|
||||
></collapse-active>
|
||||
></CollapseActive>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <f-button @click="getGraphData">graphql test</f-button>
|
||||
|
|
@ -69,44 +61,44 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import StakeHolder from "@/components/StakeHolder.vue";
|
||||
import ChartComplete from "@/components/chart/ChartComplete.vue";
|
||||
import StatsOutput from "@/components/StatsOutput.vue";
|
||||
import CollapseActive from "@/components/collapse/CollapseActive.vue";
|
||||
import { onMounted, computed, inject } from "vue";
|
||||
import { useStatCollection } from "@/composables/useStatCollection";
|
||||
import { useChains, useAccount } from "@wagmi/vue";
|
||||
import { useWallet } from "@/composables/useWallet";
|
||||
import FCard from "@/components/fcomponents/FCard.vue";
|
||||
import IconInfo from "@/components/icons/IconInfo.vue";
|
||||
import FButton from "@/components/fcomponents/FButton.vue";
|
||||
import { DEFAULT_CHAIN_ID } from "@/config";
|
||||
import StakeHolder from '@/components/StakeHolder.vue';
|
||||
import ChartComplete from '@/components/chart/ChartComplete.vue';
|
||||
import StatsOutput from '@/components/StatsOutput.vue';
|
||||
import CollapseActive from '@/components/collapse/CollapseActive.vue';
|
||||
import { onMounted, computed, inject } from 'vue';
|
||||
import { useStatCollection } from '@/composables/useStatCollection';
|
||||
import { useChains, useAccount } from '@wagmi/vue';
|
||||
import { useWallet } from '@/composables/useWallet';
|
||||
import FCard from '@/components/fcomponents/FCard.vue';
|
||||
import IconInfo from '@/components/icons/IconInfo.vue';
|
||||
import FButton from '@/components/fcomponents/FButton.vue';
|
||||
import { DEFAULT_CHAIN_ID } from '@/config';
|
||||
|
||||
// todo interface positions
|
||||
import { usePositions } from "@/composables/usePositions";
|
||||
import { usePositions } from '@/composables/usePositions';
|
||||
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 stats = useStatCollection();
|
||||
const wallet = useWallet();
|
||||
const chains = useChains();
|
||||
|
||||
function calculateAverageTaxRate(data: any): number {
|
||||
console.log("data", data);
|
||||
function calculateAverageTaxRate(data: Array<{ taxRate: number | string }>): number {
|
||||
// console.log("data", data);
|
||||
|
||||
if (data.length === 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;
|
||||
return averageTaxRate * 100;
|
||||
}
|
||||
|
||||
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 isChainSupported = computed(() => supportedChainIds.value.includes(currentChainId.value));
|
||||
onMounted(async () => {});
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
import { http, createConfig, createStorage } from "@wagmi/vue";
|
||||
import { baseSepolia } from "@wagmi/vue/chains";
|
||||
import { coinbaseWallet, walletConnect } from "@wagmi/vue/connectors";
|
||||
import { defineChain } from "viem";
|
||||
import { http, createConfig, createStorage } from '@wagmi/vue';
|
||||
import { baseSepolia } from '@wagmi/vue/chains';
|
||||
import { coinbaseWallet, walletConnect } from '@wagmi/vue/connectors';
|
||||
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({
|
||||
id: 31337,
|
||||
name: "Kraiken Local Fork",
|
||||
network: "kraiken-local",
|
||||
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
|
||||
name: 'Kraiken Local Fork',
|
||||
network: 'kraiken-local',
|
||||
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
||||
rpcUrls: {
|
||||
default: { http: [LOCAL_RPC_URL] },
|
||||
public: { http: [LOCAL_RPC_URL] },
|
||||
},
|
||||
blockExplorers: {
|
||||
default: { name: "Local Explorer", url: "" },
|
||||
default: { name: 'Local Explorer', url: '' },
|
||||
},
|
||||
testnet: true,
|
||||
});
|
||||
|
|
@ -26,19 +26,19 @@ export const config = createConfig({
|
|||
|
||||
connectors: [
|
||||
walletConnect({
|
||||
projectId: "d8e5ecb0353c02e21d4c0867d4473ac5",
|
||||
projectId: 'd8e5ecb0353c02e21d4c0867d4473ac5',
|
||||
metadata: {
|
||||
name: "Kraiken",
|
||||
description: "Connect your wallet with Kraiken",
|
||||
url: "https://kraiken.eth.limo",
|
||||
icons: [""],
|
||||
name: 'Kraiken',
|
||||
description: 'Connect your wallet with Kraiken',
|
||||
url: 'https://kraiken.eth.limo',
|
||||
icons: [''],
|
||||
},
|
||||
}),
|
||||
coinbaseWallet({
|
||||
appName: "Kraiken",
|
||||
appName: 'Kraiken',
|
||||
darkMode: true,
|
||||
preference: {
|
||||
options: "all",
|
||||
options: 'all',
|
||||
telemetry: false,
|
||||
},
|
||||
}),
|
||||
|
|
@ -49,6 +49,6 @@ export const config = createConfig({
|
|||
},
|
||||
});
|
||||
|
||||
if (typeof window !== "undefined" && config.state.chainId !== kraikenLocalFork.id) {
|
||||
config.setState((state) => ({ ...state, chainId: kraikenLocalFork.id }));
|
||||
if (typeof window !== 'undefined' && config.state.chainId !== kraikenLocalFork.id) {
|
||||
config.setState(state => ({ ...state, chainId: kraikenLocalFork.id }));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue