forked from cerc-io/cosmos-explorer
set pretttier to run on commit with husky
This commit is contained in:
@@ -13,8 +13,8 @@ function calculateValue(value: any) {
|
||||
if (Array.isArray(value)) {
|
||||
return (value[0] && value[0].amount) || '-';
|
||||
}
|
||||
if(String(value).search(/^\d+s$/g) > -1) {
|
||||
return formatSeconds(value)
|
||||
if (String(value).search(/^\d+s$/g) > -1) {
|
||||
return formatSeconds(value);
|
||||
}
|
||||
const newValue = Number(value);
|
||||
if (`${newValue}` === 'NaN' || typeof value === 'boolean') {
|
||||
@@ -28,8 +28,8 @@ function calculateValue(value: any) {
|
||||
}
|
||||
|
||||
function formatTitle(v: string) {
|
||||
if(!v) return ""
|
||||
return v.replace(/_/g, " ")
|
||||
if (!v) return '';
|
||||
return v.replace(/_/g, ' ');
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
@@ -46,7 +46,9 @@ function formatTitle(v: string) {
|
||||
:key="index"
|
||||
class="rounded-sm bg-active px-4 py-2"
|
||||
>
|
||||
<div class="text-xs mb-2 text-secondary capitalize">{{ formatTitle(item?.subtitle) }}</div>
|
||||
<div class="text-xs mb-2 text-secondary capitalize">
|
||||
{{ formatTitle(item?.subtitle) }}
|
||||
</div>
|
||||
<div class="text-base text-main">{{ calculateValue(item?.value) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { controlledComputed } from '@vueuse/core'
|
||||
import { controlledComputed } from '@vueuse/core';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
@@ -47,7 +47,7 @@ const isPositive = controlledComputed(
|
||||
|
||||
<div class="">
|
||||
<h6 class="text-lg text-center font-semibold mt-2 mb-1">
|
||||
{{ props.stats || '-'}}
|
||||
{{ props.stats || '-' }}
|
||||
</h6>
|
||||
<p class="text-sm text-center">
|
||||
{{ props.title }}
|
||||
|
||||
@@ -7,26 +7,26 @@ const props = defineProps({
|
||||
css: { type: String },
|
||||
});
|
||||
|
||||
const s = ref(0)
|
||||
|
||||
const s = ref(0);
|
||||
</script>
|
||||
<template>
|
||||
<Countdown
|
||||
v-if="time"
|
||||
:time="time > 0 ? time : 0"
|
||||
v-slot="{ days, hours, minutes, seconds }"
|
||||
class="countdown-container justify-items-center "
|
||||
class="countdown-container justify-items-center"
|
||||
>
|
||||
<span class="text-primary font-bold " :class="css">{{ days }}</span> days
|
||||
<span class="text-primary font-bold" :class="css">{{ hours }}</span> hours
|
||||
<span class="text-primary font-bold" :class="css">{{ minutes }}</span> minutes
|
||||
<span class="text-primary font-bold w-40" :class="css">
|
||||
<Transition name="slide-up">
|
||||
<span v-if="seconds % 2 === 0" class="countdown">{{ seconds }}</span>
|
||||
<span v-else="seconds % 2 === 1" class="countdown">{{ seconds }}</span>
|
||||
</Transition>
|
||||
</span>
|
||||
<span class="ml-10">seconds</span>
|
||||
<span class="text-primary font-bold" :class="css">{{ days }}</span> days
|
||||
<span class="text-primary font-bold" :class="css">{{ hours }}</span> hours
|
||||
<span class="text-primary font-bold" :class="css">{{ minutes }}</span>
|
||||
minutes
|
||||
<span class="text-primary font-bold w-40" :class="css">
|
||||
<Transition name="slide-up">
|
||||
<span v-if="seconds % 2 === 0" class="countdown">{{ seconds }}</span>
|
||||
<span v-else="seconds % 2 === 1" class="countdown">{{ seconds }}</span>
|
||||
</Transition>
|
||||
</span>
|
||||
<span class="ml-10">seconds</span>
|
||||
</Countdown>
|
||||
</template>
|
||||
|
||||
@@ -42,5 +42,4 @@ const s = ref(0)
|
||||
text-align: right;
|
||||
float: right;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -2,49 +2,57 @@
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
total: { type: String },
|
||||
limit: { type: Number },
|
||||
callback: { type: Function, required: true },
|
||||
total: { type: String },
|
||||
limit: { type: Number },
|
||||
callback: { type: Function, required: true },
|
||||
});
|
||||
const current = ref(1)
|
||||
const showSize = 3
|
||||
const current = ref(1);
|
||||
const showSize = 3;
|
||||
const pages = computed(() => {
|
||||
const pages: { color: string, page: number }[] = []
|
||||
const total = Number(props.total || 0)
|
||||
if (total > 0 && props.limit && total > props.limit) {
|
||||
let page = 0
|
||||
while (true) {
|
||||
if (page * props.limit >= total) break
|
||||
page += 1
|
||||
if (total / props.limit > 10 && page > showSize && page < (total / props.limit - showSize + 1)) {
|
||||
if (!(page >= current.value - 1 && page <= current.value + 1)) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
pages.push({
|
||||
color: page === current.value ? 'btn-primary' : '',
|
||||
page: page,
|
||||
})
|
||||
const pages: { color: string; page: number }[] = [];
|
||||
const total = Number(props.total || 0);
|
||||
if (total > 0 && props.limit && total > props.limit) {
|
||||
let page = 0;
|
||||
while (true) {
|
||||
if (page * props.limit >= total) break;
|
||||
page += 1;
|
||||
if (
|
||||
total / props.limit > 10 &&
|
||||
page > showSize &&
|
||||
page < total / props.limit - showSize + 1
|
||||
) {
|
||||
if (!(page >= current.value - 1 && page <= current.value + 1)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
pages.push({
|
||||
color: page === current.value ? 'btn-primary' : '',
|
||||
page: page,
|
||||
});
|
||||
}
|
||||
return pages
|
||||
})
|
||||
}
|
||||
return pages;
|
||||
});
|
||||
|
||||
function gotoPage(pageNum: number) {
|
||||
current.value = pageNum
|
||||
props.callback(pageNum)
|
||||
current.value = pageNum;
|
||||
props.callback(pageNum);
|
||||
}
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class="my-5 text-center">
|
||||
<div v-if="total && limit" class="btn-group">
|
||||
<button v-for="{ page, color } in pages" :key="page"
|
||||
class="btn bg-gray-100 text-gray-500 hover:text-white border-none dark:bg-gray-800 dark:text-white" :class="{
|
||||
'!btn-primary': color === 'btn-primary',
|
||||
}" @click="gotoPage(page)">
|
||||
{{ page }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="my-5 text-center">
|
||||
<div v-if="total && limit" class="btn-group">
|
||||
<button
|
||||
v-for="{ page, color } in pages"
|
||||
:key="page"
|
||||
class="btn bg-gray-100 text-gray-500 hover:text-white border-none dark:bg-gray-800 dark:text-white"
|
||||
:class="{
|
||||
'!btn-primary': color === 'btn-primary',
|
||||
}"
|
||||
@click="gotoPage(page)"
|
||||
>
|
||||
{{ page }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -39,10 +39,12 @@ const voterStatusMap: Record<string, string> = {
|
||||
|
||||
const proposalInfo = ref();
|
||||
|
||||
function metaItem(metadata: string|undefined): { title: string; summary: string } {
|
||||
return metadata ? JSON.parse(metadata) : {}
|
||||
function metaItem(metadata: string | undefined): {
|
||||
title: string;
|
||||
summary: string;
|
||||
} {
|
||||
return metadata ? JSON.parse(metadata) : {};
|
||||
}
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class="bg-white dark:bg-[#28334e] rounded text-sm">
|
||||
@@ -64,13 +66,17 @@ function metaItem(metadata: string|undefined): { title: string; summary: string
|
||||
:to="`/${chain.chainName}/gov/${item?.proposal_id}`"
|
||||
class="text-main text-base mb-1 block hover:text-indigo-400 truncate"
|
||||
>
|
||||
{{ item?.content?.title || item?.title || metaItem(item?.metadata)?.title }}
|
||||
{{
|
||||
item?.content?.title ||
|
||||
item?.title ||
|
||||
metaItem(item?.metadata)?.title
|
||||
}}
|
||||
</RouterLink>
|
||||
<div
|
||||
v-if="item.content"
|
||||
class="bg-[#f6f2ff] text-[#9c6cff] dark:bg-gray-600 dark:text-gray-300 inline-block rounded-full px-2 py-[1px] text-xs mb-1"
|
||||
>
|
||||
{{ showType(item.content['@type']) }}
|
||||
{{ showType(item.content['@type']) }}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@@ -149,7 +155,11 @@ function metaItem(metadata: string|undefined): { title: string; summary: string
|
||||
<RouterLink
|
||||
:to="`/${chain.chainName}/gov/${item?.proposal_id}`"
|
||||
class="flex-1 w-0 truncate mr-4"
|
||||
>{{ item?.content?.title || item?.title || metaItem(item?.metadata)?.title }}</RouterLink
|
||||
>{{
|
||||
item?.content?.title ||
|
||||
item?.title ||
|
||||
metaItem(item?.metadata)?.title
|
||||
}}</RouterLink
|
||||
>
|
||||
<label
|
||||
for="proposal-detail-modal"
|
||||
@@ -225,7 +235,6 @@ function metaItem(metadata: string|undefined): { title: string; summary: string
|
||||
|
||||
<span v-else>Vote</span></label
|
||||
>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -242,9 +251,24 @@ function metaItem(metadata: string|undefined): { title: string; summary: string
|
||||
<h3 class="font-bold text-lg">Description</h3>
|
||||
<p class="py-4">
|
||||
<Component
|
||||
v-if="proposalInfo?.content?.description || proposalInfo?.summary || metaItem(proposalInfo?.metadata)?.summary"
|
||||
:is="select(proposalInfo?.content?.description || proposalInfo?.summary || metaItem(proposalInfo?.metadata)?.summary, 'horizontal')"
|
||||
:value="proposalInfo?.content?.description || proposalInfo?.summary || metaItem(proposalInfo?.metadata)?.summary"
|
||||
v-if="
|
||||
proposalInfo?.content?.description ||
|
||||
proposalInfo?.summary ||
|
||||
metaItem(proposalInfo?.metadata)?.summary
|
||||
"
|
||||
:is="
|
||||
select(
|
||||
proposalInfo?.content?.description ||
|
||||
proposalInfo?.summary ||
|
||||
metaItem(proposalInfo?.metadata)?.summary,
|
||||
'horizontal'
|
||||
)
|
||||
"
|
||||
:value="
|
||||
proposalInfo?.content?.description ||
|
||||
proposalInfo?.summary ||
|
||||
metaItem(proposalInfo?.metadata)?.summary
|
||||
"
|
||||
>
|
||||
</Component>
|
||||
</p>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<script lang="ts" setup>
|
||||
import { useTxDialog, useBlockchain } from '@/stores';
|
||||
const store = useTxDialog();
|
||||
const chainStore = useBlockchain()
|
||||
const chainStore = useBlockchain();
|
||||
</script>
|
||||
<template>
|
||||
<ping-tx-dialog
|
||||
:type="store.type"
|
||||
:sender="store.sender"
|
||||
:endpoint="store.endpoint"
|
||||
:params='store.params'
|
||||
:params="store.params"
|
||||
:hd-path="store.hdPaths"
|
||||
:registry-name="chainStore.current?.prettyName || chainStore.chainName"
|
||||
@view="store.view"
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
import type { PropType } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
blocks: { type: Array as PropType<{height:string, color: string}[]> },
|
||||
blocks: { type: Array as PropType<{ height: string; color: string }[]> },
|
||||
});
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class="flex gap-0.5">
|
||||
<div class="cursor-default" v-for="(item, index) in blocks" :key="index">
|
||||
<div class="tooltip"
|
||||
:data-tip="item.height"
|
||||
:class="item.color"
|
||||
style="width: 3px;"
|
||||
<div
|
||||
class="tooltip"
|
||||
:data-tip="item.height"
|
||||
:class="item.color"
|
||||
style="width: 3px"
|
||||
>
|
||||
|
||||
</div>
|
||||
@@ -20,6 +20,4 @@ const props = defineProps({
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
<style></style>
|
||||
|
||||
@@ -5,34 +5,34 @@ import { useFormatter } from '@/stores';
|
||||
import type { CommissionRate } from '@/types';
|
||||
|
||||
const props = defineProps({
|
||||
commission: { type: Object as PropType<CommissionRate> },
|
||||
commission: { type: Object as PropType<CommissionRate> },
|
||||
});
|
||||
|
||||
let rate = computed(
|
||||
() => Number(props.commission?.commission_rates.rate || 0) * 100
|
||||
() => Number(props.commission?.commission_rates.rate || 0) * 100
|
||||
);
|
||||
let change = computed(
|
||||
() => Number(props.commission?.commission_rates.max_change_rate || 0) * 100
|
||||
() => Number(props.commission?.commission_rates.max_change_rate || 0) * 100
|
||||
);
|
||||
let max = computed(
|
||||
() => Number(props.commission?.commission_rates.max_rate || 1) * 100
|
||||
() => Number(props.commission?.commission_rates.max_rate || 1) * 100
|
||||
);
|
||||
|
||||
const left = rate;
|
||||
const right = computed(() => max.value - rate.value);
|
||||
|
||||
const s1 = computed(() =>
|
||||
left.value > change.value ? left.value - change.value : 0
|
||||
left.value > change.value ? left.value - change.value : 0
|
||||
);
|
||||
const s2 = computed(() =>
|
||||
left.value > change.value ? change.value : left.value
|
||||
left.value > change.value ? change.value : left.value
|
||||
);
|
||||
const s3 = 2;
|
||||
const s4 = computed(() =>
|
||||
right.value > change.value ? change.value : right.value
|
||||
right.value > change.value ? change.value : right.value
|
||||
);
|
||||
const s5 = computed(() =>
|
||||
right.value > change.value ? right.value - change.value : 0
|
||||
right.value > change.value ? right.value - change.value : 0
|
||||
);
|
||||
|
||||
const series = computed(() => [s1.value, s2.value, s3, s4.value, s5.value]);
|
||||
@@ -40,107 +40,112 @@ const series = computed(() => [s1.value, s2.value, s3, s4.value, s5.value]);
|
||||
const format = useFormatter();
|
||||
|
||||
const chartConfig = computed(() => {
|
||||
const secondaryText = `hsl(var(--bc))`;
|
||||
const primaryText = `hsl(var(--bc))`;
|
||||
|
||||
const secondaryText = `hsl(var(--bc))`;
|
||||
const primaryText = `hsl(var(--bc))`;
|
||||
|
||||
return {
|
||||
chart: {
|
||||
width: '200px',
|
||||
sparkline: { enabled: false },
|
||||
},
|
||||
colors: ['rgba(109,120,141,0.2)', 'rgba(114,225,40,0.2)', 'rgba(114,225,40,1)', 'rgba(114,225,40,0.2)', 'rgba(109,120,141,0.2)'],
|
||||
legend: { show: false },
|
||||
tooltip: { enabled: false },
|
||||
dataLabels: { enabled: false },
|
||||
stroke: {
|
||||
width: 3,
|
||||
lineCap: 'round',
|
||||
colors: ['hsl(var(--b1))'],
|
||||
},
|
||||
labels: [
|
||||
'Available',
|
||||
'Daily Change',
|
||||
'Commission Rate',
|
||||
'Daily Change',
|
||||
'Available',
|
||||
],
|
||||
states: {
|
||||
hover: {
|
||||
filter: { type: 'none' },
|
||||
return {
|
||||
chart: {
|
||||
width: '200px',
|
||||
sparkline: { enabled: false },
|
||||
},
|
||||
colors: [
|
||||
'rgba(109,120,141,0.2)',
|
||||
'rgba(114,225,40,0.2)',
|
||||
'rgba(114,225,40,1)',
|
||||
'rgba(114,225,40,0.2)',
|
||||
'rgba(109,120,141,0.2)',
|
||||
],
|
||||
legend: { show: false },
|
||||
tooltip: { enabled: false },
|
||||
dataLabels: { enabled: false },
|
||||
stroke: {
|
||||
width: 3,
|
||||
lineCap: 'round',
|
||||
colors: ['hsl(var(--b1))'],
|
||||
},
|
||||
labels: [
|
||||
'Available',
|
||||
'Daily Change',
|
||||
'Commission Rate',
|
||||
'Daily Change',
|
||||
'Available',
|
||||
],
|
||||
states: {
|
||||
hover: {
|
||||
filter: { type: 'none' },
|
||||
},
|
||||
active: {
|
||||
filter: { type: 'none' },
|
||||
},
|
||||
},
|
||||
plotOptions: {
|
||||
pie: {
|
||||
endAngle: 130,
|
||||
startAngle: -130,
|
||||
customScale: 0.9,
|
||||
donut: {
|
||||
size: '83%',
|
||||
labels: {
|
||||
show: true,
|
||||
name: {
|
||||
offsetY: 25,
|
||||
fontSize: '1rem',
|
||||
color: secondaryText,
|
||||
},
|
||||
active: {
|
||||
filter: { type: 'none' },
|
||||
value: {
|
||||
offsetY: -15,
|
||||
fontWeight: 500,
|
||||
fontSize: '2.125rem',
|
||||
formatter: (value: unknown) => `${rate.value.toFixed(1)}%`,
|
||||
color: primaryText,
|
||||
},
|
||||
total: {
|
||||
show: true,
|
||||
label: 'Commission Rate',
|
||||
fontSize: '1rem',
|
||||
color: secondaryText,
|
||||
formatter: () => `${rate.value.toFixed(1)}%`,
|
||||
},
|
||||
},
|
||||
},
|
||||
plotOptions: {
|
||||
pie: {
|
||||
endAngle: 130,
|
||||
startAngle: -130,
|
||||
customScale: 0.9,
|
||||
donut: {
|
||||
size: '83%',
|
||||
labels: {
|
||||
show: true,
|
||||
name: {
|
||||
offsetY: 25,
|
||||
fontSize: '1rem',
|
||||
color: secondaryText,
|
||||
},
|
||||
value: {
|
||||
offsetY: -15,
|
||||
fontWeight: 500,
|
||||
fontSize: '2.125rem',
|
||||
formatter: (value: unknown) => `${rate.value.toFixed(1)}%`,
|
||||
color: primaryText,
|
||||
},
|
||||
total: {
|
||||
show: true,
|
||||
label: 'Commission Rate',
|
||||
fontSize: '1rem',
|
||||
color: secondaryText,
|
||||
formatter: () => `${rate.value.toFixed(1)}%`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 1709,
|
||||
options: {
|
||||
chart: { height: 237 },
|
||||
},
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 1709,
|
||||
options: {
|
||||
chart: { height: 237 },
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-base-100 rounded shadow p-4">
|
||||
<div class="text-lg text-main font-semibold mb-1">Commission Rate</div>
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ `Updated at ${format.toDay(props.commission?.update_time, 'short')}` }}
|
||||
</div>
|
||||
<div class="w-80 m-auto">
|
||||
<ApexCharts type="donut" :options="chartConfig" :series="series" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex items-center justify-center flex-wrap gap-x-3">
|
||||
<div class="flex items-center gap-x-2">
|
||||
<div class="bg-success w-[6px] h-[6px] rounded-full"></div>
|
||||
<span class="text-caption">Rate:{{ rate.toFixed(0) }}%</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-x-2">
|
||||
<div class="bg-success w-[6px] h-[6px] rounded-full opacity-60"></div>
|
||||
<span class="text-caption">24h: ±{{ change }}%</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-x-2">
|
||||
<div class="bg-secondary w-[6px] h-[6px] rounded-full"></div>
|
||||
<span class="text-caption">Max:{{ max }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-base-100 rounded shadow p-4">
|
||||
<div class="text-lg text-main font-semibold mb-1">Commission Rate</div>
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ `Updated at ${format.toDay(props.commission?.update_time, 'short')}` }}
|
||||
</div>
|
||||
<div class="w-80 m-auto">
|
||||
<ApexCharts type="donut" :options="chartConfig" :series="series" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex items-center justify-center flex-wrap gap-x-3">
|
||||
<div class="flex items-center gap-x-2">
|
||||
<div class="bg-success w-[6px] h-[6px] rounded-full"></div>
|
||||
<span class="text-caption">Rate:{{ rate.toFixed(0) }}%</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-x-2">
|
||||
<div class="bg-success w-[6px] h-[6px] rounded-full opacity-60"></div>
|
||||
<span class="text-caption">24h: ±{{ change }}%</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-x-2">
|
||||
<div class="bg-secondary w-[6px] h-[6px] rounded-full"></div>
|
||||
<span class="text-caption">Max:{{ max }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
+252
-157
@@ -1,15 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import { get, post } from "@/libs/http"
|
||||
import { useBaseStore, useTxDialog } from "@/stores";
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import TextElement from "@/components/dynamic/TextElement.vue";
|
||||
import { get, post } from '@/libs/http';
|
||||
import { useBaseStore, useTxDialog } from '@/stores';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import TextElement from '@/components/dynamic/TextElement.vue';
|
||||
import DynamicComponent from '@/components/dynamic/DynamicComponent.vue';
|
||||
import { codeToHtml } from 'shiki'
|
||||
import { useWasmStore } from "@/modules/[chain]/cosmwasm/WasmStore";
|
||||
import { toBase64 } from "@cosmjs/encoding";
|
||||
import { codeToHtml } from 'shiki';
|
||||
import { useWasmStore } from '@/modules/[chain]/cosmwasm/WasmStore';
|
||||
import { toBase64 } from '@cosmjs/encoding';
|
||||
|
||||
import { JsonViewer } from "vue3-json-viewer"
|
||||
import { CosmjsOfflineSigner } from "@leapwallet/cosmos-snap-provider";
|
||||
import { JsonViewer } from 'vue3-json-viewer';
|
||||
import { CosmjsOfflineSigner } from '@leapwallet/cosmos-snap-provider';
|
||||
|
||||
interface Verification {
|
||||
chainId?: string;
|
||||
@@ -29,13 +29,13 @@ interface SourceCode<T> {
|
||||
interface Argument {
|
||||
format?: string;
|
||||
type: string;
|
||||
properties: Record<string, Argument>
|
||||
properties: Record<string, Argument>;
|
||||
}
|
||||
|
||||
interface Method {
|
||||
type: string;
|
||||
required: string[];
|
||||
properties: Record<string, Argument>
|
||||
properties: Record<string, Argument>;
|
||||
additionalProperties: boolean;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ const props = defineProps({
|
||||
contract: { type: String },
|
||||
});
|
||||
|
||||
const baseurl = "https://prod.compiler.welldonestudio.io"
|
||||
const baseurl = 'https://prod.compiler.welldonestudio.io';
|
||||
|
||||
const verification = ref<Verification>({});
|
||||
const sourceCode = ref<SourceCode<string>[]>([]);
|
||||
@@ -59,206 +59,301 @@ const baseStore = useBaseStore();
|
||||
const dialog = useTxDialog();
|
||||
const result = ref<Record<string, any>>({});
|
||||
|
||||
|
||||
function fetchVerification() {
|
||||
const url = `${baseurl}/deploy-histories/${chain_id.value}?contract=${props.contract}`
|
||||
get(url).then((x) => {
|
||||
console.log("verification:", x)
|
||||
verification.value = x
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
const url = `${baseurl}/deploy-histories/${chain_id.value}?contract=${props.contract}`;
|
||||
get(url)
|
||||
.then((x) => {
|
||||
console.log('verification:', x);
|
||||
verification.value = x;
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
function fetchSchema() {
|
||||
const base = useBaseStore()
|
||||
const chainId = base.latest?.block?.header?.chain_id || "neutron-1"
|
||||
const url = `${baseurl}/schemas/${chainId}?contract=${props.contract}`
|
||||
get(url).then(async (x) => {
|
||||
console.log("schema:", x)
|
||||
schemas.value = x.sourceCodes
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
}
|
||||
|
||||
function fetchSourceCode() {
|
||||
const base = useBaseStore()
|
||||
const chainId = base.latest?.block?.header?.chain_id || "neutron-1"
|
||||
const url = `${baseurl}/source-codes/${chainId}?contract=${props.contract}`
|
||||
const theme = baseStore.theme === 'dark' ? 'material-theme' : 'github-light'
|
||||
get(url).then(async (x) => {
|
||||
console.log("source codes:", x)
|
||||
for (let i = 0; i < x.sourceCodes.length; i++) {
|
||||
const sc = x.sourceCodes[i];
|
||||
sc.sourceCode = await codeToHtml(sc.sourceCode, {lang: sc.path.endsWith('.toml')?'toml':'rust', theme})
|
||||
}
|
||||
sourceCode.value = x.sourceCodes
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
const base = useBaseStore();
|
||||
const chainId = base.latest?.block?.header?.chain_id || 'neutron-1';
|
||||
const url = `${baseurl}/schemas/${chainId}?contract=${props.contract}`;
|
||||
get(url)
|
||||
.then(async (x) => {
|
||||
console.log('schema:', x);
|
||||
schemas.value = x.sourceCodes;
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
const base = useBaseStore()
|
||||
const chain_id = ref("")
|
||||
function fetchSourceCode() {
|
||||
const base = useBaseStore();
|
||||
const chainId = base.latest?.block?.header?.chain_id || 'neutron-1';
|
||||
const url = `${baseurl}/source-codes/${chainId}?contract=${props.contract}`;
|
||||
const theme = baseStore.theme === 'dark' ? 'material-theme' : 'github-light';
|
||||
get(url)
|
||||
.then(async (x) => {
|
||||
console.log('source codes:', x);
|
||||
for (let i = 0; i < x.sourceCodes.length; i++) {
|
||||
const sc = x.sourceCodes[i];
|
||||
sc.sourceCode = await codeToHtml(sc.sourceCode, {
|
||||
lang: sc.path.endsWith('.toml') ? 'toml' : 'rust',
|
||||
theme,
|
||||
});
|
||||
}
|
||||
sourceCode.value = x.sourceCodes;
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
const base = useBaseStore();
|
||||
const chain_id = ref('');
|
||||
base.$subscribe((m, s) => {
|
||||
if (chain_id.value !== s.latest?.block?.header?.chain_id) {
|
||||
chain_id.value = s.latest?.block?.header?.chain_id || "unknown"
|
||||
chain_id.value = s.latest?.block?.header?.chain_id || 'unknown';
|
||||
fetchVerification();
|
||||
fetchSchema();
|
||||
fetchSourceCode();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
function verify() {
|
||||
const base = useBaseStore();
|
||||
const id = base.latest?.block?.header?.chain_id || 'unknown';
|
||||
const data = { contractAddress: props.contract, chainId: id };
|
||||
|
||||
const base = useBaseStore()
|
||||
const id = base.latest?.block?.header?.chain_id || "unknown"
|
||||
const data = {"contractAddress": props.contract, "chainId": id}
|
||||
|
||||
post(`${baseurl}/verification/neutron`, data).then((x)=> {
|
||||
if(x.result) {
|
||||
verification.value = x.result
|
||||
fetchSchema();
|
||||
fetchSourceCode();
|
||||
}
|
||||
})
|
||||
post(`${baseurl}/verification/neutron`, data).then((x) => {
|
||||
if (x.result) {
|
||||
verification.value = x.result;
|
||||
fetchSchema();
|
||||
fetchSourceCode();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const tab = ref('verification')
|
||||
const tab = ref('verification');
|
||||
function selectTab(tabName: string) {
|
||||
tab.value = tabName
|
||||
tab.value = tabName;
|
||||
}
|
||||
|
||||
const executions = computed(() => {
|
||||
return schemas.value
|
||||
.filter(x => x.path.indexOf('execute_msg')>-1 || x.path.indexOf('query_msg')>-1)
|
||||
.map(x => JSON.parse(x.sourceCode||"{}") as Schema)
|
||||
.filter(
|
||||
(x) =>
|
||||
x.path.indexOf('execute_msg') > -1 || x.path.indexOf('query_msg') > -1
|
||||
)
|
||||
.map((x) => JSON.parse(x.sourceCode || '{}') as Schema);
|
||||
// if(raw && raw.sourceCode) {
|
||||
// return JSON.parse(raw.sourceCode) as Schema
|
||||
// }
|
||||
// }
|
||||
// return {} as Schema
|
||||
})
|
||||
});
|
||||
|
||||
const queries = computed(() => {
|
||||
let raw = schemas.value.find(x => x.path.indexOf('query_msg')>-1)
|
||||
if(raw && raw.sourceCode) {
|
||||
return JSON.parse(raw.sourceCode) as Schema
|
||||
}
|
||||
return {} as Schema
|
||||
})
|
||||
let raw = schemas.value.find((x) => x.path.indexOf('query_msg') > -1);
|
||||
if (raw && raw.sourceCode) {
|
||||
return JSON.parse(raw.sourceCode) as Schema;
|
||||
}
|
||||
return {} as Schema;
|
||||
});
|
||||
|
||||
function callFunction(title: string, method: string, arg: Argument) {
|
||||
if(!props.contract) return
|
||||
if (!props.contract) return;
|
||||
|
||||
// console.log("callFunction", title, method, arg)
|
||||
|
||||
let args = {} as Record<string, any>
|
||||
if(arg.properties) Object.keys(arg.properties).forEach(k => {
|
||||
const input = document.querySelector(`input[name="${method}-${k}"]`) as HTMLInputElement
|
||||
if (input) {
|
||||
args[k] = input.value
|
||||
}
|
||||
})
|
||||
let args = {} as Record<string, any>;
|
||||
if (arg.properties)
|
||||
Object.keys(arg.properties).forEach((k) => {
|
||||
const input = document.querySelector(
|
||||
`input[name="${method}-${k}"]`
|
||||
) as HTMLInputElement;
|
||||
if (input) {
|
||||
args[k] = input.value;
|
||||
}
|
||||
});
|
||||
|
||||
//console.log("args", arg.properties, JSON.stringify(args))
|
||||
|
||||
if(title === 'ExecuteMsg') {
|
||||
let execution = {} as Record<string, any>
|
||||
execution[method] = args
|
||||
console.log("execution", execution)
|
||||
dialog.open('wasm_execute_contract', { contract: props.contract, execution})
|
||||
if (title === 'ExecuteMsg') {
|
||||
let execution = {} as Record<string, any>;
|
||||
execution[method] = args;
|
||||
console.log('execution', execution);
|
||||
dialog.open('wasm_execute_contract', {
|
||||
contract: props.contract,
|
||||
execution,
|
||||
});
|
||||
} else {
|
||||
// QueryMsg
|
||||
wasmStore.wasmClient
|
||||
.getWasmContractSmartQuery(props.contract, `{"${method}": ${JSON.stringify(args)}}`)
|
||||
.getWasmContractSmartQuery(
|
||||
props.contract,
|
||||
`{"${method}": ${JSON.stringify(args)}}`
|
||||
)
|
||||
.then((x) => {
|
||||
result.value[`${title}-${method}`] = x;
|
||||
result.value[`${title}-${method}`] = x;
|
||||
})
|
||||
.catch((err) => {
|
||||
result.value[`${title}-${method}`] = err;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<div role="tablist" class="tabs tabs-boxed">
|
||||
<a role="tab" class="tab tooltip tooltip-right tooltip-success" data-tip="Powered By WELLDONE Studio">
|
||||
<div class="w-8 rounded">
|
||||
<img src="../assets/images/welldone-logo.svg" alt="Powered By WELLDONE Studio"/>
|
||||
</div>
|
||||
</a>
|
||||
<a role="tab" class="tab" :class="{'tab-active': tab==='verification'}" @click="selectTab('verification')">Verification</a>
|
||||
<a role="tab" class="tab" :class="{'tab-active': tab==='executions'}" @click="selectTab('executions')">Functions</a>
|
||||
<a role="tab" class="tab" :class="{'tab-active': tab==='source_code'}" @click="selectTab('source_code')">Source Code</a>
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<div role="tablist" class="tabs tabs-boxed">
|
||||
<a
|
||||
role="tab"
|
||||
class="tab tooltip tooltip-right tooltip-success"
|
||||
data-tip="Powered By WELLDONE Studio"
|
||||
>
|
||||
<div class="w-8 rounded">
|
||||
<img
|
||||
src="../assets/images/welldone-logo.svg"
|
||||
alt="Powered By WELLDONE Studio"
|
||||
/>
|
||||
</div>
|
||||
<div class="">
|
||||
<div v-if="tab === 'verification'"><DynamicComponent :value="verification"/></div>
|
||||
<div v-if="tab === 'executions'" class="">
|
||||
<div v-for="{title, oneOf} in executions" class="join join-vertical w-full mt-2">
|
||||
<div v-if="oneOf" v-for="m in oneOf">
|
||||
<div v-for="(props, method) in m.properties" class="collapse collapse-arrow join-item border border-base-300">
|
||||
<input type="radio" name="my-accordion-1" :checked="false"/>
|
||||
<div class="collapse-title font-medium">
|
||||
{{title}}::{{ method }}
|
||||
</div>
|
||||
<div class="collapse-content">
|
||||
<div v-for="(p, name) in props.properties" class="form-control pb-2">
|
||||
<label class="label">
|
||||
<span class="label-text">{{ name }}</span>
|
||||
<span></span>
|
||||
</label>
|
||||
<input :name="`${method}-${name}`" type="text" :placeholder="p.format" class="input input-sm border border-gray-300 dark:border-gray-600 w-full" />
|
||||
</div>
|
||||
<div>
|
||||
<label v-if="title==='ExecuteMsg'" for="wasm_execute_contract" class="btn btn-sm" @click="callFunction(title, method, props)">{{ method }}</label>
|
||||
<label v-else class="btn btn-sm" @click="callFunction(title, method, props)">{{ method }}</label>
|
||||
</div>
|
||||
<div v-if="result[`${title}-${method}`]" class="mt-2">
|
||||
<JsonViewer :value="result[`${title}-${method}`]" :theme="baseStore.theme||'dark'" style="background: transparent;" copyable boxed sort :expand-depth="5"/>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<a
|
||||
role="tab"
|
||||
class="tab"
|
||||
:class="{ 'tab-active': tab === 'verification' }"
|
||||
@click="selectTab('verification')"
|
||||
>Verification</a
|
||||
>
|
||||
<a
|
||||
role="tab"
|
||||
class="tab"
|
||||
:class="{ 'tab-active': tab === 'executions' }"
|
||||
@click="selectTab('executions')"
|
||||
>Functions</a
|
||||
>
|
||||
<a
|
||||
role="tab"
|
||||
class="tab"
|
||||
:class="{ 'tab-active': tab === 'source_code' }"
|
||||
@click="selectTab('source_code')"
|
||||
>Source Code</a
|
||||
>
|
||||
</div>
|
||||
<div class="">
|
||||
<div v-if="tab === 'verification'">
|
||||
<DynamicComponent :value="verification" />
|
||||
</div>
|
||||
<div v-if="tab === 'executions'" class="">
|
||||
<div
|
||||
v-for="{ title, oneOf } in executions"
|
||||
class="join join-vertical w-full mt-2"
|
||||
>
|
||||
<div v-if="oneOf" v-for="m in oneOf">
|
||||
<div
|
||||
v-for="(props, method) in m.properties"
|
||||
class="collapse collapse-arrow join-item border border-base-300"
|
||||
>
|
||||
<input type="radio" name="my-accordion-1" :checked="false" />
|
||||
<div class="collapse-title font-medium">
|
||||
{{ title }}::{{ method }}
|
||||
</div>
|
||||
<div class="collapse-content">
|
||||
<div
|
||||
v-for="(p, name) in props.properties"
|
||||
class="form-control pb-2"
|
||||
>
|
||||
<label class="label">
|
||||
<span class="label-text">{{ name }}</span>
|
||||
<span></span>
|
||||
</label>
|
||||
<input
|
||||
:name="`${method}-${name}`"
|
||||
type="text"
|
||||
:placeholder="p.format"
|
||||
class="input input-sm border border-gray-300 dark:border-gray-600 w-full"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
v-if="title === 'ExecuteMsg'"
|
||||
for="wasm_execute_contract"
|
||||
class="btn btn-sm"
|
||||
@click="callFunction(title, method, props)"
|
||||
>{{ method }}</label
|
||||
>
|
||||
<label
|
||||
v-else
|
||||
class="btn btn-sm"
|
||||
@click="callFunction(title, method, props)"
|
||||
>{{ method }}</label
|
||||
>
|
||||
</div>
|
||||
<div v-if="result[`${title}-${method}`]" class="mt-2">
|
||||
<JsonViewer
|
||||
:value="result[`${title}-${method}`]"
|
||||
:theme="baseStore.theme || 'dark'"
|
||||
style="background: transparent"
|
||||
copyable
|
||||
boxed
|
||||
sort
|
||||
:expand-depth="5"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="tab === 'source_code'" class="mt-2 join join-vertical w-full">
|
||||
<div v-for="sc in sourceCode.filter(x => !x.isDirectory)" class="collapse collapse-arrow join-item border border-base-300">
|
||||
<input type="radio" name="sourceCodeAccordion" :checked="false"/>
|
||||
<div class="collapse-title font-medium">{{sc.path}}</div>
|
||||
<div class="collapse-content overflow-auto" v-html="sc.sourceCode"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="tab === 'verification'" class="text-center">
|
||||
<div v-if="Object.keys(verification).length == 0" >
|
||||
Haven't found verification
|
||||
</div>
|
||||
<button class="btn btn-primary mt-5" @click="verify" v-show="tab === 'verification'" :disabled="verification.error !== undefined">verify</button>
|
||||
</div>
|
||||
|
||||
<!-- alert-info -->
|
||||
</div>
|
||||
<div v-if="tab === 'source_code'" class="mt-2 join join-vertical w-full">
|
||||
<div
|
||||
class="text-[#00cfe8] bg-[rgba(0,207,232,0.12)] rounded shadow mt-4 alert-info"
|
||||
v-for="sc in sourceCode.filter((x) => !x.isDirectory)"
|
||||
class="collapse collapse-arrow join-item border border-base-300"
|
||||
>
|
||||
<input type="radio" name="sourceCodeAccordion" :checked="false" />
|
||||
<div class="collapse-title font-medium">{{ sc.path }}</div>
|
||||
<div
|
||||
class="drop-shadow-md px-4 pt-2 pb-2"
|
||||
style="box-shadow: rgba(0, 207, 232, 0.4) 0px 6px 15px -7px"
|
||||
>
|
||||
<h2 class="text-base font-semibold">{{ $t('consensus.tips') }}</h2>
|
||||
</div>
|
||||
<div class="px-4 py-4">
|
||||
<ul style="list-style-type: disc" class="pl-8">
|
||||
<li>
|
||||
{{ $t('cosmwasm.tips_description_1') }}
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://docs.welldonestudio.io/code/verification-api/" target="_blank">Link to Verification API Manual</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
class="collapse-content overflow-auto"
|
||||
v-html="sc.sourceCode"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div v-show="tab === 'verification'" class="text-center">
|
||||
<div v-if="Object.keys(verification).length == 0">
|
||||
Haven't found verification
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-primary mt-5"
|
||||
@click="verify"
|
||||
v-show="tab === 'verification'"
|
||||
:disabled="verification.error !== undefined"
|
||||
>
|
||||
verify
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- alert-info -->
|
||||
<div
|
||||
class="text-[#00cfe8] bg-[rgba(0,207,232,0.12)] rounded shadow mt-4 alert-info"
|
||||
>
|
||||
<div
|
||||
class="drop-shadow-md px-4 pt-2 pb-2"
|
||||
style="box-shadow: rgba(0, 207, 232, 0.4) 0px 6px 15px -7px"
|
||||
>
|
||||
<h2 class="text-base font-semibold">{{ $t('consensus.tips') }}</h2>
|
||||
</div>
|
||||
<div class="px-4 py-4">
|
||||
<ul style="list-style-type: disc" class="pl-8">
|
||||
<li>
|
||||
{{ $t('cosmwasm.tips_description_1') }}
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://docs.welldonestudio.io/code/verification-api/"
|
||||
target="_blank"
|
||||
>Link to Verification API Manual</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import { useRoute } from 'vue-router'
|
||||
const route = useRoute()
|
||||
import { useRoute } from 'vue-router';
|
||||
const route = useRoute();
|
||||
|
||||
const aa = computed(() => {
|
||||
const hostname = location.hostname
|
||||
const hostname = location.hostname;
|
||||
if (hostname === 'testnet.ping.pub') {
|
||||
return '2396360'
|
||||
return '2396360';
|
||||
} else if (hostname === 'ping.pub') {
|
||||
return '2395639'
|
||||
return '2395639';
|
||||
} else {
|
||||
return '2396360'
|
||||
return '2396360';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
</template>
|
||||
<template></template>
|
||||
|
||||
@@ -8,51 +8,49 @@ import { useBaseStore } from '@/stores';
|
||||
const store = useIndexModule();
|
||||
const baseStore = useBaseStore();
|
||||
const chartConfig = computed(() => {
|
||||
const theme = baseStore.theme;
|
||||
const labels = store.marketData.prices.map((item: any) => item[0]);
|
||||
return getMarketPriceChartConfig(theme, labels);
|
||||
const theme = baseStore.theme;
|
||||
const labels = store.marketData.prices.map((item: any) => item[0]);
|
||||
return getMarketPriceChartConfig(theme, labels);
|
||||
});
|
||||
const kind = ref('price');
|
||||
const series = computed(() => {
|
||||
return [
|
||||
{
|
||||
name: kind.value === 'price' ? 'Price' : 'Volume',
|
||||
data:
|
||||
kind.value === 'price'
|
||||
? store.marketData.prices.map((item: any) => item[1])
|
||||
: store.marketData.total_volumes.map(
|
||||
(item: any) => item[1]
|
||||
),
|
||||
},
|
||||
];
|
||||
return [
|
||||
{
|
||||
name: kind.value === 'price' ? 'Price' : 'Volume',
|
||||
data:
|
||||
kind.value === 'price'
|
||||
? store.marketData.prices.map((item: any) => item[1])
|
||||
: store.marketData.total_volumes.map((item: any) => item[1]),
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
function changeChart(type: string) {
|
||||
kind.value = type;
|
||||
kind.value = type;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="tabs tabs-boxed bg-transparent justify-end">
|
||||
<a
|
||||
class="tab text-xs mr-2 text-gray-400 uppercase"
|
||||
:class="{ 'tab-active': kind === 'price' }"
|
||||
@click="changeChart('price')"
|
||||
>
|
||||
Price
|
||||
</a>
|
||||
<a
|
||||
class="tab text-xs text-gray-400 uppercase"
|
||||
:class="{ 'tab-active': kind === 'volume' }"
|
||||
@click="changeChart('volume')"
|
||||
>
|
||||
Volume
|
||||
</a>
|
||||
</div>
|
||||
<ApexCharts
|
||||
type="area"
|
||||
height="230"
|
||||
:options="chartConfig"
|
||||
:series="series"
|
||||
/>
|
||||
<div class="tabs tabs-boxed bg-transparent justify-end">
|
||||
<a
|
||||
class="tab text-xs mr-2 text-gray-400 uppercase"
|
||||
:class="{ 'tab-active': kind === 'price' }"
|
||||
@click="changeChart('price')"
|
||||
>
|
||||
Price
|
||||
</a>
|
||||
<a
|
||||
class="tab text-xs text-gray-400 uppercase"
|
||||
:class="{ 'tab-active': kind === 'volume' }"
|
||||
@click="changeChart('volume')"
|
||||
>
|
||||
Volume
|
||||
</a>
|
||||
</div>
|
||||
<ApexCharts
|
||||
type="area"
|
||||
height="230"
|
||||
:options="chartConfig"
|
||||
:series="series"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -6,39 +6,43 @@ import { useBaseStore } from '@/stores';
|
||||
const baseStore = useBaseStore();
|
||||
|
||||
const options = computed(() => {
|
||||
return {
|
||||
chart: {
|
||||
type: 'bar',
|
||||
height: 150
|
||||
},
|
||||
plotOptions: {
|
||||
bar: {
|
||||
// borderRadius: 4,
|
||||
horizontal: false,
|
||||
}
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false
|
||||
},
|
||||
colors: ['#5A67D8'],
|
||||
xaxis: {
|
||||
labels: {
|
||||
show: false,
|
||||
rotate: -45
|
||||
},
|
||||
show: false,
|
||||
categories: baseStore.recents.map((x) => x.block.header.height).concat(Array(50-baseStore.recents.length).fill('')),
|
||||
}
|
||||
};
|
||||
return {
|
||||
chart: {
|
||||
type: 'bar',
|
||||
height: 150,
|
||||
},
|
||||
plotOptions: {
|
||||
bar: {
|
||||
// borderRadius: 4,
|
||||
horizontal: false,
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
colors: ['#5A67D8'],
|
||||
xaxis: {
|
||||
labels: {
|
||||
show: false,
|
||||
rotate: -45,
|
||||
},
|
||||
show: false,
|
||||
categories: baseStore.recents
|
||||
.map((x) => x.block.header.height)
|
||||
.concat(Array(50 - baseStore.recents.length).fill('')),
|
||||
},
|
||||
};
|
||||
});
|
||||
const series = computed(() => {
|
||||
return [{
|
||||
name: 'Txs',
|
||||
data: baseStore.recents?.map((x) => x.block.data.txs.length) || []
|
||||
}]
|
||||
return [
|
||||
{
|
||||
name: 'Txs',
|
||||
data: baseStore.recents?.map((x) => x.block.data.txs.length) || [],
|
||||
},
|
||||
];
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ApexCharts type="bar" height="150" :options="options" :series="series" />
|
||||
<ApexCharts type="bar" height="150" :options="options" :series="series" />
|
||||
</template>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useBlockchain } from '@/stores';
|
||||
import numeral from 'numeral';
|
||||
|
||||
const chainStore = useBlockchain()
|
||||
const chainStore = useBlockchain();
|
||||
|
||||
const themeColors = (theme: string) => {
|
||||
if (theme === 'light') {
|
||||
@@ -260,14 +260,55 @@ export const getMarketPriceChartConfig = (
|
||||
};
|
||||
|
||||
// const donutColors = Array.from({length: 19}, () => (`#${Math.floor(Math.random()*16777215+100000).toString(16)}`))
|
||||
const donutColors = ["#bbe81a", "#ff5f0b", "#43ebef", "#1999e5", "#230b2c", "#628be8", "#aa5343", "#c9fa89", "#e88ea8", "#72e4a2", "#38cd87", "#515e13", "#7bf8f5", "#83dd6e", "#e8b203", "#7d11d5", "#3e4927", "#f303e2", "#249493", "#50e5e6", "#11deb2", "#a2f9c7", "#2a7bdc", "#47383a", "#226da4", "#966319", "#1bdf99", "#f3ab0c", "#961f50", "#832efd", "#875287", "#4bebe7", "#1d3d2e", "#9caea4", "#2772f5", "#938bf1", "#6228a5", "#24fea5", "#c9bbc8", "#e27225", "#54bd9f", "#babb2d", "#bcf591", "#803b36", "#124f03"]
|
||||
|
||||
|
||||
export const getDonutChartConfig = (
|
||||
theme: string,
|
||||
labels: string[]
|
||||
) => {
|
||||
const donutColors = [
|
||||
'#bbe81a',
|
||||
'#ff5f0b',
|
||||
'#43ebef',
|
||||
'#1999e5',
|
||||
'#230b2c',
|
||||
'#628be8',
|
||||
'#aa5343',
|
||||
'#c9fa89',
|
||||
'#e88ea8',
|
||||
'#72e4a2',
|
||||
'#38cd87',
|
||||
'#515e13',
|
||||
'#7bf8f5',
|
||||
'#83dd6e',
|
||||
'#e8b203',
|
||||
'#7d11d5',
|
||||
'#3e4927',
|
||||
'#f303e2',
|
||||
'#249493',
|
||||
'#50e5e6',
|
||||
'#11deb2',
|
||||
'#a2f9c7',
|
||||
'#2a7bdc',
|
||||
'#47383a',
|
||||
'#226da4',
|
||||
'#966319',
|
||||
'#1bdf99',
|
||||
'#f3ab0c',
|
||||
'#961f50',
|
||||
'#832efd',
|
||||
'#875287',
|
||||
'#4bebe7',
|
||||
'#1d3d2e',
|
||||
'#9caea4',
|
||||
'#2772f5',
|
||||
'#938bf1',
|
||||
'#6228a5',
|
||||
'#24fea5',
|
||||
'#c9bbc8',
|
||||
'#e27225',
|
||||
'#54bd9f',
|
||||
'#babb2d',
|
||||
'#bcf591',
|
||||
'#803b36',
|
||||
'#124f03',
|
||||
];
|
||||
|
||||
export const getDonutChartConfig = (theme: string, labels: string[]) => {
|
||||
const { themeSecondaryTextColor, themePrimaryTextColor } =
|
||||
colorVariables(theme);
|
||||
|
||||
@@ -359,4 +400,3 @@ export const getDonutChartConfig = (
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ const props = defineProps({
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div v-for="(item,index) of props.value" :key="index">
|
||||
<div v-for="(item, index) of props.value" :key="index">
|
||||
{{ toBase64(item) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@ import ArrayBytesElement from './ArrayBytesElement.vue';
|
||||
import ArrayObjectElement from './ArrayObjectElement.vue';
|
||||
import TextElement from './TextElement.vue';
|
||||
import ArrayCoinElement from './ArrayCoinElement.vue';
|
||||
import ArrayStringElement from './ArrayStringElement.vue'
|
||||
import ArrayStringElement from './ArrayStringElement.vue';
|
||||
|
||||
const props = defineProps({
|
||||
value: { type: Array<Object> },
|
||||
|
||||
@@ -18,7 +18,7 @@ const header = computed(() => {
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div class="overflow-auto max-h-96 ">
|
||||
<div class="overflow-auto max-h-96">
|
||||
<table class="table table-xs table-compact table-pin-rows w-full">
|
||||
<thead v-if="thead">
|
||||
<tr>
|
||||
|
||||
@@ -8,13 +8,11 @@ const props = defineProps(['value']);
|
||||
<table class="table table-compact w-full text-sm">
|
||||
<tbody>
|
||||
<tr v-for="(v, k) of value">
|
||||
<td
|
||||
class="capitalize whitespace-break-spaces min-w-max"
|
||||
>
|
||||
<td class="capitalize whitespace-break-spaces min-w-max">
|
||||
{{ String(k).replaceAll('_', ' ') }}
|
||||
</td>
|
||||
<td class="w-4/5">
|
||||
<div class="overflow-hidden w-auto whitespace-normal" >
|
||||
<div class="overflow-hidden w-auto whitespace-normal">
|
||||
<Component
|
||||
v-if="v"
|
||||
:is="select(v, 'horizontal')"
|
||||
|
||||
@@ -6,15 +6,16 @@ import { computed, onMounted, ref } from 'vue';
|
||||
|
||||
import { fromBase64, toHex } from '@cosmjs/encoding';
|
||||
|
||||
import { registry as nameMatcha } from "@leapwallet/name-matcha"
|
||||
import { registry as nameMatcha } from '@leapwallet/name-matcha';
|
||||
|
||||
const chainStore = useBlockchain()
|
||||
const chainStore = useBlockchain();
|
||||
const props = defineProps(['value']);
|
||||
const format = useFormatter();
|
||||
function isMD() {
|
||||
if (
|
||||
props.value &&
|
||||
(String(props.value).indexOf('\n') > -1 || String(props.value).indexOf('\\n') > -1)
|
||||
(String(props.value).indexOf('\n') > -1 ||
|
||||
String(props.value).indexOf('\\n') > -1)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
@@ -22,39 +23,44 @@ function isMD() {
|
||||
}
|
||||
|
||||
function isAddress() {
|
||||
return isBech32Address(props.value) && String(props.value).indexOf('valoper1') === -1
|
||||
return (
|
||||
isBech32Address(props.value) &&
|
||||
String(props.value).indexOf('valoper1') === -1
|
||||
);
|
||||
}
|
||||
|
||||
const text = computed(() => {
|
||||
if(!props.value) return ""
|
||||
const v = String(props.value)
|
||||
switch(true) {
|
||||
case v.length === 28 && v.endsWith("="): {
|
||||
return format.validator(v) || v
|
||||
if (!props.value) return '';
|
||||
const v = String(props.value);
|
||||
switch (true) {
|
||||
case v.length === 28 && v.endsWith('='): {
|
||||
return format.validator(v) || v;
|
||||
}
|
||||
// 2023-06-12T03:09:38.253756368Z
|
||||
case v.search(/^[1-9]\d{3}-\d{1,2}-\d{1,2}T\d{1,2}:\d{2}:\d{2}[.\d]*Z$/g) > -1: {
|
||||
return new Date(v).toLocaleString(navigator.language)
|
||||
case v.search(/^[1-9]\d{3}-\d{1,2}-\d{1,2}T\d{1,2}:\d{2}:\d{2}[.\d]*Z$/g) >
|
||||
-1: {
|
||||
return new Date(v).toLocaleString(navigator.language);
|
||||
}
|
||||
case toHexOutput.value:
|
||||
return toHex(fromBase64(v)).toUpperCase()
|
||||
return toHex(fromBase64(v)).toUpperCase();
|
||||
}
|
||||
return v
|
||||
})
|
||||
return v;
|
||||
});
|
||||
|
||||
const names = ref([] as {name?: any, provider?: string}[])
|
||||
const names = ref([] as { name?: any; provider?: string }[]);
|
||||
|
||||
onMounted(() => {
|
||||
if(isAddress()) nameMatcha.lookupAll(props.value).then(re => {
|
||||
|
||||
names.value = Object.keys(re).map(key => ({name: re[key], provider: key})).filter( x => x.name)
|
||||
})
|
||||
})
|
||||
const toHexOutput = ref(false)
|
||||
if (isAddress())
|
||||
nameMatcha.lookupAll(props.value).then((re) => {
|
||||
names.value = Object.keys(re)
|
||||
.map((key) => ({ name: re[key], provider: key }))
|
||||
.filter((x) => x.name);
|
||||
});
|
||||
});
|
||||
const toHexOutput = ref(false);
|
||||
const isConvertable = computed(() => {
|
||||
return String(props.value).endsWith('=') && props.value.length !== 28
|
||||
})
|
||||
|
||||
return String(props.value).endsWith('=') && props.value.length !== 28;
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<MdEditor
|
||||
@@ -64,32 +70,67 @@ const isConvertable = computed(() => {
|
||||
class="md-editor-recover"
|
||||
></MdEditor>
|
||||
<span v-else-if="isAddress()" class="flex">
|
||||
<RouterLink :to="`/${chainStore.chainName}/account/${text}`">{{ text }}</RouterLink>
|
||||
<div v-for="{name, provider} in names">
|
||||
<span class="text-xs truncate relative py-1 px-2 p2-4 w-fit ml-2 rounded text-success tooltip" :data-tip="provider" :title="provider">
|
||||
<RouterLink :to="`/${chainStore.chainName}/account/${text}`">{{
|
||||
text
|
||||
}}</RouterLink>
|
||||
<div v-for="{ name, provider } in names">
|
||||
<span
|
||||
class="text-xs truncate relative py-1 px-2 p2-4 w-fit ml-2 rounded text-success tooltip"
|
||||
:data-tip="provider"
|
||||
:title="provider"
|
||||
>
|
||||
<span class="inset-x-0 inset-y-0 opacity-10 absolute bg-success"></span>
|
||||
<button>{{ name }}</button>
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
<span v-else class="flex"><span class="break-words max-w-5xl">{{ text }}</span>
|
||||
<span v-if="isConvertable" @click="toHexOutput = !toHexOutput" class="ml-2 cursor-pointer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99" />
|
||||
</span>
|
||||
<span v-else class="flex"
|
||||
><span class="break-words max-w-5xl">{{ text }}</span>
|
||||
<span
|
||||
v-if="isConvertable"
|
||||
@click="toHexOutput = !toHexOutput"
|
||||
class="ml-2 cursor-pointer"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.md-editor-recover {
|
||||
.h1,h1 {
|
||||
.h1,
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
.h2,h2 {
|
||||
.h2,
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
|
||||
.h1,
|
||||
.h2,
|
||||
.h3,
|
||||
.h4,
|
||||
.h5,
|
||||
.h6,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
@@ -104,10 +145,15 @@ const isConvertable = computed(() => {
|
||||
margin-inline-end: 0px;
|
||||
padding-inline-start: 40px;
|
||||
}
|
||||
dl, ol, ul {
|
||||
dl,
|
||||
ol,
|
||||
ul {
|
||||
margin-top: 0;
|
||||
}
|
||||
address, dl, ol, ul {
|
||||
address,
|
||||
dl,
|
||||
ol,
|
||||
ul {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
p {
|
||||
@@ -115,11 +161,21 @@ const isConvertable = computed(() => {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
a {
|
||||
color: #666cff !important;
|
||||
color: #666cff !important;
|
||||
}
|
||||
.h1 > a, .h2> a, .h3> a, .h4> a, .h5> a, .h6> a, h1> a, h2> a, h3> a, h4> a, h5> a, h6> a {
|
||||
.h1 > a,
|
||||
.h2 > a,
|
||||
.h3 > a,
|
||||
.h4 > a,
|
||||
.h5 > a,
|
||||
.h6 > a,
|
||||
h1 > a,
|
||||
h2 > a,
|
||||
h3 > a,
|
||||
h4 > a,
|
||||
h5 > a,
|
||||
h6 > a {
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
|
||||
const props = defineProps(['value']);
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class="overflow-auto">
|
||||
{{ value["amount"] }} {{ value["denom"] }}
|
||||
</div>
|
||||
<div class="overflow-auto">{{ value['amount'] }} {{ value['denom'] }}</div>
|
||||
</template>
|
||||
|
||||
@@ -9,21 +9,23 @@ const props = defineProps({
|
||||
});
|
||||
|
||||
const txs = computed(() => {
|
||||
return props.value?.map((x) => {
|
||||
const tx_bytes = fromBase64(x);
|
||||
let tx = null
|
||||
let injected = 'Standard'
|
||||
try {
|
||||
tx = decodeTxRaw(fromBase64(x))
|
||||
} catch(e) {
|
||||
injected = 'Injected'
|
||||
}
|
||||
return {
|
||||
hash: hashTx(tx_bytes),
|
||||
tx,
|
||||
injected
|
||||
}
|
||||
}) || []
|
||||
return (
|
||||
props.value?.map((x) => {
|
||||
const tx_bytes = fromBase64(x);
|
||||
let tx = null;
|
||||
let injected = 'Standard';
|
||||
try {
|
||||
tx = decodeTxRaw(fromBase64(x));
|
||||
} catch (e) {
|
||||
injected = 'Injected';
|
||||
}
|
||||
return {
|
||||
hash: hashTx(tx_bytes),
|
||||
tx,
|
||||
injected,
|
||||
};
|
||||
}) || []
|
||||
);
|
||||
});
|
||||
|
||||
const format = useFormatter();
|
||||
@@ -35,7 +37,7 @@ const chain = useBlockchain();
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th style="position: relative; z-index: 2;">Hash</th>
|
||||
<th style="position: relative; z-index: 2">Hash</th>
|
||||
<th>Msgs</th>
|
||||
<th>Memo</th>
|
||||
</tr>
|
||||
@@ -44,10 +46,13 @@ const chain = useBlockchain();
|
||||
<tr v-for="item in txs">
|
||||
<td>{{ item.injected }}</td>
|
||||
<td>
|
||||
<span v-if="item.injected ==='Injected'">{{ item.hash }}</span>
|
||||
<RouterLink v-else :to="`/${chain.chainName}/tx/${item.hash}`" class="text-primary dark:invert">{{
|
||||
item.hash
|
||||
}}</RouterLink>
|
||||
<span v-if="item.injected === 'Injected'">{{ item.hash }}</span>
|
||||
<RouterLink
|
||||
v-else
|
||||
:to="`/${chain.chainName}/tx/${item.hash}`"
|
||||
class="text-primary dark:invert"
|
||||
>{{ item.hash }}</RouterLink
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="item.tx">
|
||||
@@ -58,7 +63,9 @@ const chain = useBlockchain();
|
||||
}}
|
||||
</span>
|
||||
</td>
|
||||
<td><span v-if="item.tx">{{ item.tx.body.memo }}</span></td>
|
||||
<td>
|
||||
<span v-if="item.tx">{{ item.tx.body.memo }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -31,7 +31,9 @@ function selectObject(v: Object, direct?: string) {
|
||||
return UInt8Array;
|
||||
case Array.isArray(v):
|
||||
return ArrayElement;
|
||||
case v && Object.keys(v).includes('amount') && Object.keys(v).includes('denom'): {
|
||||
case v &&
|
||||
Object.keys(v).includes('amount') &&
|
||||
Object.keys(v).includes('denom'): {
|
||||
return TokenElement;
|
||||
}
|
||||
case direct === 'horizontal':
|
||||
|
||||
@@ -1 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" tag="i" class="v-icon notranslate v-theme--light v-icon--size-default iconify iconify--mdi" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M16.36 14c.08-.66.14-1.32.14-2c0-.68-.06-1.34-.14-2h3.38c.16.64.26 1.31.26 2s-.1 1.36-.26 2m-5.15 5.56c.6-1.11 1.06-2.31 1.38-3.56h2.95a8.03 8.03 0 0 1-4.33 3.56M14.34 14H9.66c-.1-.66-.16-1.32-.16-2c0-.68.06-1.35.16-2h4.68c.09.65.16 1.32.16 2c0 .68-.07 1.34-.16 2M12 19.96c-.83-1.2-1.5-2.53-1.91-3.96h3.82c-.41 1.43-1.08 2.76-1.91 3.96M8 8H5.08A7.923 7.923 0 0 1 9.4 4.44C8.8 5.55 8.35 6.75 8 8m-2.92 8H8c.35 1.25.8 2.45 1.4 3.56A8.008 8.008 0 0 1 5.08 16m-.82-2C4.1 13.36 4 12.69 4 12s.1-1.36.26-2h3.38c-.08.66-.14 1.32-.14 2c0 .68.06 1.34.14 2M12 4.03c.83 1.2 1.5 2.54 1.91 3.97h-3.82c.41-1.43 1.08-2.77 1.91-3.97M18.92 8h-2.95a15.65 15.65 0 0 0-1.38-3.56c1.84.63 3.37 1.9 4.33 3.56M12 2C6.47 2 2 6.5 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2Z"></path></svg>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
tag="i"
|
||||
class="v-icon notranslate v-theme--light v-icon--size-default iconify iconify--mdi"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
><path fill="currentColor" d="M16.36 14c.08-.66.14-1.32.14-2c0-.68-.06-1.34-.14-2h3.38c.16.64.26 1.31.26 2s-.1 1.36-.26 2m-5.15 5.56c.6-1.11 1.06-2.31 1.38-3.56h2.95a8.03 8.03 0 0 1-4.33 3.56M14.34 14H9.66c-.1-.66-.16-1.32-.16-2c0-.68.06-1.35.16-2h4.68c.09.65.16 1.32.16 2c0 .68-.07 1.34-.16 2M12 19.96c-.83-1.2-1.5-2.53-1.91-3.96h3.82c-.41 1.43-1.08 2.76-1.91 3.96M8 8H5.08A7.923 7.923 0 0 1 9.4 4.44C8.8 5.55 8.35 6.75 8 8m-2.92 8H8c.35 1.25.8 2.45 1.4 3.56A8.008 8.008 0 0 1 5.08 16m-.82-2C4.1 13.36 4 12.69 4 12s.1-1.36.26-2h3.38c-.08.66-.14 1.32-.14 2c0 .68.06 1.34.14 2M12 4.03c.83 1.2 1.5 2.54 1.91 3.97h-3.82c.41-1.43 1.08-2.77 1.91-3.97M18.92 8h-2.95a15.65 15.65 0 0 0-1.38-3.56c1.84.63 3.37 1.9 4.33 3.56M12 2C6.47 2 2 6.5 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2Z"></path></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, resolveComponent, h } from 'vue'
|
||||
import { defineComponent, resolveComponent, h } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
|
||||
@@ -17,9 +17,10 @@ function changeEndpoint(item: Endpoint) {
|
||||
<div class="p-1 relative mr-3 cursor-pointer">
|
||||
<img v-lazy="chainStore.logo" class="w-9 h-9 rounded-full" />
|
||||
<div
|
||||
class="w-2 h-2 rounded-full absolute right-0 bottom-0 shadow" :class="{
|
||||
class="w-2 h-2 rounded-full absolute right-0 bottom-0 shadow"
|
||||
:class="{
|
||||
'bg-success': baseStore.connected,
|
||||
'bg-error': !baseStore.connected
|
||||
'bg-error': !baseStore.connected,
|
||||
}"
|
||||
></div>
|
||||
</div>
|
||||
@@ -32,11 +33,14 @@ function changeEndpoint(item: Endpoint) {
|
||||
"
|
||||
class="capitalize whitespace-nowrap text-base font-semibold text-gray-600 dark:text-gray-200 hidden md:!block"
|
||||
>
|
||||
{{
|
||||
{{
|
||||
baseStore.latest?.block?.header?.height
|
||||
? `#${baseStore.latest.block.header.height}`
|
||||
: chainStore.chainName || ''
|
||||
}} <span class="text-error">{{ baseStore.connected ? '' : 'disconnected' }}</span>
|
||||
: chainStore.chainName || ''
|
||||
}}
|
||||
<span class="text-error">{{
|
||||
baseStore.connected ? '' : 'disconnected'
|
||||
}}</span>
|
||||
</div>
|
||||
<div
|
||||
class="text-xs text-gray-500 dark:text-gray-400 whitespace-nowrap hidden md:!block"
|
||||
@@ -82,14 +86,20 @@ function changeEndpoint(item: Endpoint) {
|
||||
<div class="px-4 py-2 text-sm text-gray-400">Information</div>
|
||||
<div class="w-full">
|
||||
<div class="py-2 px-4">
|
||||
Chain Id: {{ baseStore.latest.block?.header.chain_id && baseStore.connected
|
||||
? baseStore.latest.block.header.chain_id
|
||||
: 'N/A' }}
|
||||
Chain Id:
|
||||
{{
|
||||
baseStore.latest.block?.header.chain_id && baseStore.connected
|
||||
? baseStore.latest.block.header.chain_id
|
||||
: 'N/A'
|
||||
}}
|
||||
</div>
|
||||
<div class="py-2 px-4">
|
||||
Height: {{ baseStore.latest.block?.header.height && baseStore.connected
|
||||
? baseStore.latest.block.header.height
|
||||
: '0' }}
|
||||
Height:
|
||||
{{
|
||||
baseStore.latest.block?.header.height && baseStore.connected
|
||||
? baseStore.latest.block.header.height
|
||||
: '0'
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<!-- bottom-->
|
||||
|
||||
@@ -14,7 +14,12 @@ import { useBaseStore, useBlockchain } from '@/stores';
|
||||
|
||||
import NavBarI18n from './NavBarI18n.vue';
|
||||
import NavBarWallet from './NavBarWallet.vue';
|
||||
import type { NavGroup, NavLink, NavSectionTitle, VerticalNavItems } from '../types';
|
||||
import type {
|
||||
NavGroup,
|
||||
NavLink,
|
||||
NavSectionTitle,
|
||||
VerticalNavItems,
|
||||
} from '../types';
|
||||
import dayjs from 'dayjs';
|
||||
import AdBanner from '@/components/ad/AdBanner.vue';
|
||||
|
||||
@@ -25,10 +30,10 @@ blockchain.randomSetupEndpoint();
|
||||
const baseStore = useBaseStore();
|
||||
|
||||
const current = ref(''); // the current chain
|
||||
const temp = ref('')
|
||||
const temp = ref('');
|
||||
blockchain.$subscribe((m, s) => {
|
||||
if (current.value === s.chainName && temp.value != s.endpoint.address) {
|
||||
temp.value = s.endpoint.address
|
||||
temp.value = s.endpoint.address;
|
||||
blockchain.initial();
|
||||
}
|
||||
if (current.value != s.chainName) {
|
||||
@@ -57,24 +62,26 @@ function isNavTitle(nav: VerticalNavItems | any): nav is NavSectionTitle {
|
||||
return (<NavSectionTitle>nav).heading !== undefined;
|
||||
}
|
||||
function selected(route: any, nav: NavLink) {
|
||||
const b = route.path === nav.to?.path || route.path.startsWith(nav.to?.path) && nav.title.indexOf('dashboard') === -1
|
||||
return b
|
||||
const b =
|
||||
route.path === nav.to?.path ||
|
||||
(route.path.startsWith(nav.to?.path) &&
|
||||
nav.title.indexOf('dashboard') === -1);
|
||||
return b;
|
||||
}
|
||||
const blocktime = computed(() => {
|
||||
return dayjs(baseStore.latest?.block?.header?.time)
|
||||
return dayjs(baseStore.latest?.block?.header?.time);
|
||||
});
|
||||
|
||||
const behind = computed(() => {
|
||||
const current = dayjs().subtract(10, 'minute')
|
||||
return blocktime.value.isBefore(current)
|
||||
const current = dayjs().subtract(10, 'minute');
|
||||
return blocktime.value.isBefore(current);
|
||||
});
|
||||
|
||||
dayjs()
|
||||
dayjs();
|
||||
|
||||
const show_ad = computed(() => {
|
||||
return location.hostname.indexOf('ping.pub') > -1
|
||||
})
|
||||
|
||||
return location.hostname.indexOf('ping.pub') > -1;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -82,7 +89,8 @@ const show_ad = computed(() => {
|
||||
<!-- sidebar -->
|
||||
<div
|
||||
class="w-64 fixed z-50 left-0 top-0 bottom-0 overflow-auto bg-base-100 border-r border-gray-100 dark:border-gray-700"
|
||||
:class="{ block: sidebarShow, 'hidden xl:!block': !sidebarShow }">
|
||||
:class="{ block: sidebarShow, 'hidden xl:!block': !sidebarShow }"
|
||||
>
|
||||
<div class="flex justify-between mt-1 pl-4 py-4 mb-1">
|
||||
<RouterLink to="/" class="flex items-center">
|
||||
<img class="w-10 h-10" src="../../assets/logo.svg" />
|
||||
@@ -90,63 +98,125 @@ const show_ad = computed(() => {
|
||||
Ping.pub
|
||||
</h1>
|
||||
</RouterLink>
|
||||
<div class="pr-4 cursor-pointer xl:!hidden" @click="sidebarShow = false">
|
||||
<div
|
||||
class="pr-4 cursor-pointer xl:!hidden"
|
||||
@click="sidebarShow = false"
|
||||
>
|
||||
<Icon icon="mdi-close" class="text-2xl" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="(item, index) of blockchain.computedChainMenu" :key="index" class="px-2">
|
||||
<div v-if="isNavGroup(item)" :tabindex="index" class="collapse" :class="{
|
||||
'collapse-arrow': index > 0 && item?.children?.length > 0,
|
||||
'collapse-open': index === 0 && sidebarOpen,
|
||||
'collapse-close': index === 0 && !sidebarOpen,
|
||||
}">
|
||||
<input v-if="index > 0" type="checkbox" class="cursor-pointer !h-10 block" @click="changeOpen(index)" />
|
||||
<div
|
||||
v-for="(item, index) of blockchain.computedChainMenu"
|
||||
:key="index"
|
||||
class="px-2"
|
||||
>
|
||||
<div
|
||||
v-if="isNavGroup(item)"
|
||||
:tabindex="index"
|
||||
class="collapse"
|
||||
:class="{
|
||||
'collapse-arrow': index > 0 && item?.children?.length > 0,
|
||||
'collapse-open': index === 0 && sidebarOpen,
|
||||
'collapse-close': index === 0 && !sidebarOpen,
|
||||
}"
|
||||
>
|
||||
<input
|
||||
v-if="index > 0"
|
||||
type="checkbox"
|
||||
class="cursor-pointer !h-10 block"
|
||||
@click="changeOpen(index)"
|
||||
/>
|
||||
<div
|
||||
class="collapse-title !py-0 px-4 flex items-center cursor-pointer hover:bg-gray-100 dark:hover:bg-[#373f59]">
|
||||
<Icon v-if="item?.icon?.icon" :icon="item?.icon?.icon" class="text-xl mr-2" :class="{
|
||||
'text-yellow-500': item?.title === 'Favorite',
|
||||
'text-blue-500': item?.title !== 'Favorite',
|
||||
}" />
|
||||
<img v-if="item?.icon?.image" :src="item?.icon?.image" class="w-6 h-6 rounded-full mr-3" />
|
||||
<div class="text-base capitalize flex-1 text-gray-700 dark:text-gray-200 whitespace-nowrap">
|
||||
class="collapse-title !py-0 px-4 flex items-center cursor-pointer hover:bg-gray-100 dark:hover:bg-[#373f59]"
|
||||
>
|
||||
<Icon
|
||||
v-if="item?.icon?.icon"
|
||||
:icon="item?.icon?.icon"
|
||||
class="text-xl mr-2"
|
||||
:class="{
|
||||
'text-yellow-500': item?.title === 'Favorite',
|
||||
'text-blue-500': item?.title !== 'Favorite',
|
||||
}"
|
||||
/>
|
||||
<img
|
||||
v-if="item?.icon?.image"
|
||||
:src="item?.icon?.image"
|
||||
class="w-6 h-6 rounded-full mr-3"
|
||||
/>
|
||||
<div
|
||||
class="text-base capitalize flex-1 text-gray-700 dark:text-gray-200 whitespace-nowrap"
|
||||
>
|
||||
{{ item?.title }}
|
||||
</div>
|
||||
<div v-if="item?.badgeContent" class="mr-6 badge badge-sm text-white border-none" :class="item?.badgeClass">
|
||||
<div
|
||||
v-if="item?.badgeContent"
|
||||
class="mr-6 badge badge-sm text-white border-none"
|
||||
:class="item?.badgeClass"
|
||||
>
|
||||
{{ item?.badgeContent }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse-content">
|
||||
<div v-for="(el, key) of item?.children" class="menu bg-base-100 w-full !p-0">
|
||||
<RouterLink v-if="isNavLink(el)" @click="sidebarShow = false"
|
||||
<div
|
||||
v-for="(el, key) of item?.children"
|
||||
class="menu bg-base-100 w-full !p-0"
|
||||
>
|
||||
<RouterLink
|
||||
v-if="isNavLink(el)"
|
||||
@click="sidebarShow = false"
|
||||
class="hover:bg-gray-100 dark:hover:bg-[#373f59] rounded cursor-pointer px-3 py-2 flex items-center"
|
||||
:class="{
|
||||
'!bg-primary': selected($route, el),
|
||||
}" :to="el.to">
|
||||
<Icon v-if="!el?.icon?.image" icon="mdi:chevron-right" class="mr-2 ml-3" :class="{
|
||||
'text-white':
|
||||
$route.path === el?.to?.path &&
|
||||
item?.title !== 'Favorite',
|
||||
}" />
|
||||
<img v-if="el?.icon?.image" :src="el?.icon?.image" class="w-6 h-6 rounded-full mr-3 ml-4 " :class="{
|
||||
'border border-gray-300 bg-white': selected($route, el),
|
||||
}" />
|
||||
<div class="text-base capitalize text-gray-500 dark:text-gray-300" :class="{
|
||||
'!text-white': selected($route, el),
|
||||
}">
|
||||
}"
|
||||
:to="el.to"
|
||||
>
|
||||
<Icon
|
||||
v-if="!el?.icon?.image"
|
||||
icon="mdi:chevron-right"
|
||||
class="mr-2 ml-3"
|
||||
:class="{
|
||||
'text-white':
|
||||
$route.path === el?.to?.path &&
|
||||
item?.title !== 'Favorite',
|
||||
}"
|
||||
/>
|
||||
<img
|
||||
v-if="el?.icon?.image"
|
||||
:src="el?.icon?.image"
|
||||
class="w-6 h-6 rounded-full mr-3 ml-4"
|
||||
:class="{
|
||||
'border border-gray-300 bg-white': selected($route, el),
|
||||
}"
|
||||
/>
|
||||
<div
|
||||
class="text-base capitalize text-gray-500 dark:text-gray-300"
|
||||
:class="{
|
||||
'!text-white': selected($route, el),
|
||||
}"
|
||||
>
|
||||
{{ item?.title === 'Favorite' ? el?.title : $t(el?.title) }}
|
||||
</div>
|
||||
</RouterLink>
|
||||
</div>
|
||||
<div v-if="index === 0 && dashboard.networkType === NetworkType.Testnet"
|
||||
class="menu bg-base-100 w-full !p-0">
|
||||
<div
|
||||
v-if="
|
||||
index === 0 && dashboard.networkType === NetworkType.Testnet
|
||||
"
|
||||
class="menu bg-base-100 w-full !p-0"
|
||||
>
|
||||
<RouterLink
|
||||
class="hover:bg-gray-100 dark:hover:bg-[#373f59] rounded cursor-pointer px-3 py-2 flex items-center"
|
||||
:to="`/${blockchain.chainName}/faucet`">
|
||||
:to="`/${blockchain.chainName}/faucet`"
|
||||
>
|
||||
<Icon icon="mdi:chevron-right" class="mr-2 ml-3"></Icon>
|
||||
<div class="text-base capitalize text-gray-500 dark:text-gray-300">
|
||||
<div
|
||||
class="text-base capitalize text-gray-500 dark:text-gray-300"
|
||||
>
|
||||
Faucet
|
||||
</div>
|
||||
<div class="badge badge-sm text-white border-none badge-error ml-auto">
|
||||
<div
|
||||
class="badge badge-sm text-white border-none badge-error ml-auto"
|
||||
>
|
||||
New
|
||||
</div>
|
||||
</RouterLink>
|
||||
@@ -154,59 +224,103 @@ const show_ad = computed(() => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<RouterLink v-if="isNavLink(item)" :to="item?.to" @click="sidebarShow = false"
|
||||
class="cursor-pointer rounded-lg px-4 flex items-center py-2 hover:bg-gray-100 dark:hover:bg-[#373f59]">
|
||||
<Icon v-if="item?.icon?.icon" :icon="item?.icon?.icon" class="text-xl mr-2" :class="{
|
||||
'text-yellow-500': item?.title === 'Favorite',
|
||||
'text-blue-500': item?.title !== 'Favorite',
|
||||
}" />
|
||||
<img v-if="item?.icon?.image" :src="item?.icon?.image"
|
||||
class="w-6 h-6 rounded-full mr-3 border border-blue-100" />
|
||||
<div class="text-base capitalize flex-1 text-gray-700 dark:text-gray-200 whitespace-nowrap">
|
||||
<RouterLink
|
||||
v-if="isNavLink(item)"
|
||||
:to="item?.to"
|
||||
@click="sidebarShow = false"
|
||||
class="cursor-pointer rounded-lg px-4 flex items-center py-2 hover:bg-gray-100 dark:hover:bg-[#373f59]"
|
||||
>
|
||||
<Icon
|
||||
v-if="item?.icon?.icon"
|
||||
:icon="item?.icon?.icon"
|
||||
class="text-xl mr-2"
|
||||
:class="{
|
||||
'text-yellow-500': item?.title === 'Favorite',
|
||||
'text-blue-500': item?.title !== 'Favorite',
|
||||
}"
|
||||
/>
|
||||
<img
|
||||
v-if="item?.icon?.image"
|
||||
:src="item?.icon?.image"
|
||||
class="w-6 h-6 rounded-full mr-3 border border-blue-100"
|
||||
/>
|
||||
<div
|
||||
class="text-base capitalize flex-1 text-gray-700 dark:text-gray-200 whitespace-nowrap"
|
||||
>
|
||||
{{ item?.title }}
|
||||
</div>
|
||||
<div v-if="item?.badgeContent" class="badge badge-sm text-white border-none" :class="item?.badgeClass">
|
||||
<div
|
||||
v-if="item?.badgeContent"
|
||||
class="badge badge-sm text-white border-none"
|
||||
:class="item?.badgeClass"
|
||||
>
|
||||
{{ item?.badgeContent }}
|
||||
</div>
|
||||
</RouterLink>
|
||||
<div v-if="isNavTitle(item)" class="px-4 text-sm text-gray-400 pb-2 uppercase">
|
||||
<div
|
||||
v-if="isNavTitle(item)"
|
||||
class="px-4 text-sm text-gray-400 pb-2 uppercase"
|
||||
>
|
||||
{{ item?.heading }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-2">
|
||||
<div class="px-4 text-sm pt-2 text-gray-400 pb-2 uppercase">
|
||||
Tools
|
||||
</div>
|
||||
<RouterLink to="/wallet/suggest"
|
||||
class="py-2 px-4 flex items-center cursor-pointer rounded-lg hover:bg-gray-100 dark:hover:bg-[#373f59]">
|
||||
<div class="px-4 text-sm pt-2 text-gray-400 pb-2 uppercase">Tools</div>
|
||||
<RouterLink
|
||||
to="/wallet/suggest"
|
||||
class="py-2 px-4 flex items-center cursor-pointer rounded-lg hover:bg-gray-100 dark:hover:bg-[#373f59]"
|
||||
>
|
||||
<Icon icon="mdi:frequently-asked-questions" class="text-xl mr-2" />
|
||||
<div class="text-base capitalize flex-1 text-gray-600 dark:text-gray-200">
|
||||
<div
|
||||
class="text-base capitalize flex-1 text-gray-600 dark:text-gray-200"
|
||||
>
|
||||
Wallet Helper
|
||||
</div>
|
||||
</RouterLink>
|
||||
<div v-if="showDiscord" class="px-4 text-sm pt-2 text-gray-400 pb-2 uppercase">
|
||||
<div
|
||||
v-if="showDiscord"
|
||||
class="px-4 text-sm pt-2 text-gray-400 pb-2 uppercase"
|
||||
>
|
||||
{{ $t('module.sponsors') }}
|
||||
</div>
|
||||
<Sponsors v-if="showDiscord" />
|
||||
<div class="px-4 text-sm pt-2 text-gray-400 pb-2 uppercase">{{ $t('module.links') }}</div>
|
||||
<a href="https://twitter.com/ping_pub" target="_blank"
|
||||
class="py-2 px-4 flex items-center cursor-pointer rounded-lg hover:bg-gray-100 dark:hover:bg-[#373f59]">
|
||||
<div class="px-4 text-sm pt-2 text-gray-400 pb-2 uppercase">
|
||||
{{ $t('module.links') }}
|
||||
</div>
|
||||
<a
|
||||
href="https://twitter.com/ping_pub"
|
||||
target="_blank"
|
||||
class="py-2 px-4 flex items-center cursor-pointer rounded-lg hover:bg-gray-100 dark:hover:bg-[#373f59]"
|
||||
>
|
||||
<Icon icon="mdi:twitter" class="text-xl mr-2" />
|
||||
<div class="text-base capitalize flex-1 text-gray-600 dark:text-gray-200">
|
||||
<div
|
||||
class="text-base capitalize flex-1 text-gray-600 dark:text-gray-200"
|
||||
>
|
||||
Twitter
|
||||
</div>
|
||||
</a>
|
||||
<a v-if="showDiscord" href="https://discord.com/invite/CmjYVSr6GW" target="_blank"
|
||||
class="py-2 px-4 flex items-center rounded-lg cursor-pointer hover:bg-gray-100 dark:hover:bg-[#373f59]">
|
||||
<a
|
||||
v-if="showDiscord"
|
||||
href="https://discord.com/invite/CmjYVSr6GW"
|
||||
target="_blank"
|
||||
class="py-2 px-4 flex items-center rounded-lg cursor-pointer hover:bg-gray-100 dark:hover:bg-[#373f59]"
|
||||
>
|
||||
<Icon icon="mdi:discord" class="text-xl mr-2" />
|
||||
<div class="text-base capitalize flex-1 text-gray-600 dark:text-gray-200">
|
||||
<div
|
||||
class="text-base capitalize flex-1 text-gray-600 dark:text-gray-200"
|
||||
>
|
||||
Discord
|
||||
</div>
|
||||
</a>
|
||||
<a href="https://github.com/ping-pub/explorer/discussions" target="_blank"
|
||||
class="py-2 px-4 flex items-center rounded-lg cursor-pointer hover:bg-gray-100 dark:hover:bg-[#373f59]">
|
||||
<a
|
||||
href="https://github.com/ping-pub/explorer/discussions"
|
||||
target="_blank"
|
||||
class="py-2 px-4 flex items-center rounded-lg cursor-pointer hover:bg-gray-100 dark:hover:bg-[#373f59]"
|
||||
>
|
||||
<Icon icon="mdi:frequently-asked-questions" class="text-xl mr-2" />
|
||||
<div class="text-base capitalize flex-1 text-gray-600 dark:text-gray-200">
|
||||
<div
|
||||
class="text-base capitalize flex-1 text-gray-600 dark:text-gray-200"
|
||||
>
|
||||
FAQ
|
||||
</div>
|
||||
</a>
|
||||
@@ -214,8 +328,13 @@ const show_ad = computed(() => {
|
||||
</div>
|
||||
<div class="xl:!ml-64 px-3 pt-4">
|
||||
<!-- header -->
|
||||
<div class="flex items-center py-3 bg-base-100 mb-4 rounded px-4 sticky top-0 z-10">
|
||||
<div class="text-2xl pr-3 cursor-pointer xl:!hidden" @click="sidebarShow = true">
|
||||
<div
|
||||
class="flex items-center py-3 bg-base-100 mb-4 rounded px-4 sticky top-0 z-10"
|
||||
>
|
||||
<div
|
||||
class="text-2xl pr-3 cursor-pointer xl:!hidden"
|
||||
@click="sidebarShow = true"
|
||||
>
|
||||
<Icon icon="mdi-menu" />
|
||||
</div>
|
||||
|
||||
@@ -231,15 +350,27 @@ const show_ad = computed(() => {
|
||||
</div>
|
||||
|
||||
<!-- 👉 Pages -->
|
||||
<div style="min-height: calc(100vh - 180px);">
|
||||
<div style="min-height: calc(100vh - 180px)">
|
||||
<div v-if="behind" class="alert alert-error mb-4">
|
||||
<div class="flex gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
class="stroke-current flex-shrink-0 w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
class="stroke-current flex-shrink-0 w-6 h-6"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
></path>
|
||||
</svg>
|
||||
<span>{{ $t('pages.out_of_sync') }} {{ blocktime.format() }} ({{ blocktime.fromNow() }})</span>
|
||||
<span
|
||||
>{{ $t('pages.out_of_sync') }} {{ blocktime.format() }} ({{
|
||||
blocktime.fromNow()
|
||||
}})</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<RouterView v-slot="{ Component }">
|
||||
|
||||
@@ -4,81 +4,79 @@ import { Icon } from '@iconify/vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const i18nLangs: Array<{ label: string; i18nLang: string }> = [
|
||||
{
|
||||
label: 'English',
|
||||
i18nLang: 'en',
|
||||
},
|
||||
{
|
||||
label: '中文',
|
||||
i18nLang: 'zh',
|
||||
},
|
||||
{
|
||||
label: 'Indonesian',
|
||||
i18nLang: 'id',
|
||||
},
|
||||
{
|
||||
label: '日本語',
|
||||
i18nLang: 'ja',
|
||||
},
|
||||
{
|
||||
label: '한국인',
|
||||
i18nLang: 'ko',
|
||||
},
|
||||
{
|
||||
label: 'Deutsch',
|
||||
i18nLang: 'de',
|
||||
},
|
||||
{
|
||||
label: 'Español',
|
||||
i18nLang: 'es',
|
||||
},
|
||||
{
|
||||
label: 'English',
|
||||
i18nLang: 'en',
|
||||
},
|
||||
{
|
||||
label: '中文',
|
||||
i18nLang: 'zh',
|
||||
},
|
||||
{
|
||||
label: 'Indonesian',
|
||||
i18nLang: 'id',
|
||||
},
|
||||
{
|
||||
label: '日本語',
|
||||
i18nLang: 'ja',
|
||||
},
|
||||
{
|
||||
label: '한국인',
|
||||
i18nLang: 'ko',
|
||||
},
|
||||
{
|
||||
label: 'Deutsch',
|
||||
i18nLang: 'de',
|
||||
},
|
||||
{
|
||||
label: 'Español',
|
||||
i18nLang: 'es',
|
||||
},
|
||||
];
|
||||
|
||||
let locale = ref(useI18n({ useScope: 'global' }).locale);
|
||||
watch(locale, (val) => {
|
||||
document.documentElement.setAttribute('lang', val as string);
|
||||
document.documentElement.setAttribute('lang', val as string);
|
||||
});
|
||||
|
||||
let currentLang = ref(localStorage.getItem('lang') || 'en');
|
||||
|
||||
watch(currentLang, (val: string) => {
|
||||
document.documentElement.setAttribute('lang', val as string);
|
||||
document.documentElement.setAttribute('lang', val as string);
|
||||
});
|
||||
|
||||
const handleLangChange = (lang: string) => {
|
||||
locale.value = lang;
|
||||
currentLang.value = lang;
|
||||
localStorage.setItem('lang', lang);
|
||||
locale.value = lang;
|
||||
currentLang.value = lang;
|
||||
localStorage.setItem('lang', lang);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="dropdown"
|
||||
:class="
|
||||
currentLang === 'ar'
|
||||
? 'dropdown-right'
|
||||
: 'dropdown-bottom dropdown-end'
|
||||
"
|
||||
<div
|
||||
class="dropdown"
|
||||
:class="
|
||||
currentLang === 'ar' ? 'dropdown-right' : 'dropdown-bottom dropdown-end'
|
||||
"
|
||||
>
|
||||
<label tabindex="0" class="btn btn-ghost btn-circle btn-sm mx-1">
|
||||
<Icon
|
||||
icon="mdi-translate"
|
||||
class="text-2xl text-gray-500 dark:text-gray-400"
|
||||
/>
|
||||
</label>
|
||||
<ul
|
||||
tabindex="0"
|
||||
class="dropdown-content menu p-2 shadow bg-base-100 rounded-box w-40"
|
||||
>
|
||||
<label tabindex="0" class="btn btn-ghost btn-circle btn-sm mx-1">
|
||||
<Icon
|
||||
icon="mdi-translate"
|
||||
class="text-2xl text-gray-500 dark:text-gray-400"
|
||||
/>
|
||||
</label>
|
||||
<ul
|
||||
tabindex="0"
|
||||
class="dropdown-content menu p-2 shadow bg-base-100 rounded-box w-40"
|
||||
<li v-for="lang in i18nLangs" :key="lang.i18nLang">
|
||||
<a
|
||||
class="hover:bg-gray-100 dark:hover:bg-[#373f59]"
|
||||
:class="{ 'text-primary': currentLang === lang.i18nLang }"
|
||||
@click="handleLangChange(lang.i18nLang)"
|
||||
>{{ lang.label }}</a
|
||||
>
|
||||
<li v-for="lang in i18nLangs" :key="lang.i18nLang">
|
||||
<a
|
||||
class="hover:bg-gray-100 dark:hover:bg-[#373f59]"
|
||||
:class="{ 'text-primary': currentLang === lang.i18nLang }"
|
||||
@click="handleLangChange(lang.i18nLang)"
|
||||
>{{ lang.label }}</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useBaseStore, useBlockchain, useWalletStore } from '@/stores';
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { ref, computed } from 'vue';
|
||||
@@ -39,44 +39,68 @@ const params = computed(() => {
|
||||
if (chainStore.chainName == 'side') {
|
||||
return JSON.stringify({
|
||||
wallet: ['okex', 'unisat'],
|
||||
});
|
||||
});
|
||||
}
|
||||
return "";
|
||||
return '';
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="dropdown dropdown-hover dropdown-end">
|
||||
<label tabindex="0" class="btn btn-sm btn-primary m-1 lowercase truncate !inline-flex text-xs md:!text-sm">
|
||||
<label
|
||||
tabindex="0"
|
||||
class="btn btn-sm btn-primary m-1 lowercase truncate !inline-flex text-xs md:!text-sm"
|
||||
>
|
||||
<Icon icon="mdi:wallet" />
|
||||
<span class="ml-1 hidden md:block">
|
||||
{{ walletStore.shortAddress || 'Wallet' }}</span>
|
||||
{{ walletStore.shortAddress || 'Wallet' }}</span
|
||||
>
|
||||
</label>
|
||||
<div tabindex="0" class="dropdown-content menu shadow p-2 bg-base-100 rounded w-52 md:!w-64 overflow-auto">
|
||||
<label v-if="!walletStore?.currentAddress" for="PingConnectWallet" class="btn btn-sm btn-primary">
|
||||
<div
|
||||
tabindex="0"
|
||||
class="dropdown-content menu shadow p-2 bg-base-100 rounded w-52 md:!w-64 overflow-auto"
|
||||
>
|
||||
<label
|
||||
v-if="!walletStore?.currentAddress"
|
||||
for="PingConnectWallet"
|
||||
class="btn btn-sm btn-primary"
|
||||
>
|
||||
<Icon icon="mdi:wallet" /><span class="ml-1 block">Connect Wallet</span>
|
||||
</label>
|
||||
<div class="px-2 mb-1 text-gray-500 dark:text-gray-400 font-semibold">
|
||||
{{ walletStore.connectedWallet?.wallet }}
|
||||
</div>
|
||||
<div>
|
||||
<a v-if="walletStore.currentAddress"
|
||||
<a
|
||||
v-if="walletStore.currentAddress"
|
||||
class="block py-2 px-2 hover:bg-gray-100 dark:hover:bg-[#353f5a] rounded cursor-pointer"
|
||||
style="overflow-wrap: anywhere" @click="copyAdress(walletStore.currentAddress)">
|
||||
style="overflow-wrap: anywhere"
|
||||
@click="copyAdress(walletStore.currentAddress)"
|
||||
>
|
||||
{{ walletStore.currentAddress }}
|
||||
</a>
|
||||
<div class="divider mt-1 mb-1"></div>
|
||||
<RouterLink to="/wallet/accounts">
|
||||
<div class="block py-2 px-2 hover:!bg-gray-100 rounded cursor-pointer">Accounts</div>
|
||||
<div
|
||||
class="block py-2 px-2 hover:!bg-gray-100 rounded cursor-pointer"
|
||||
>
|
||||
Accounts
|
||||
</div>
|
||||
</RouterLink>
|
||||
<RouterLink to="/wallet/portfolio">
|
||||
<div class="block py-2 px-2 hover:!bg-gray-100 rounded cursor-pointer">Portfolio</div>
|
||||
<div
|
||||
class="block py-2 px-2 hover:!bg-gray-100 rounded cursor-pointer"
|
||||
>
|
||||
Portfolio
|
||||
</div>
|
||||
</RouterLink>
|
||||
<div v-if="walletStore.currentAddress" class="divider mt-1 mb-1"></div>
|
||||
<a v-if="walletStore.currentAddress"
|
||||
<a
|
||||
v-if="walletStore.currentAddress"
|
||||
class="block py-2 px-2 hover:bg-gray-100 dark:hover:bg-[#353f5a] rounded cursor-pointer"
|
||||
@click="walletStore.disconnect()">Disconnect</a>
|
||||
@click="walletStore.disconnect()"
|
||||
>Disconnect</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="toast" v-show="showCopyToast === 1">
|
||||
@@ -95,9 +119,14 @@ const params = computed(() => {
|
||||
</div>
|
||||
</div>
|
||||
<Teleport to="body">
|
||||
<ping-connect-wallet :chain-id="baseStore.currentChainId || 'cosmoshub-4'" :hd-path="chainStore.defaultHDPath"
|
||||
:addr-prefix="chainStore.current?.bech32Prefix || 'cosmos'" @connect="walletStateChange"
|
||||
@keplr-config="walletStore.suggestChain()" :params="params" />
|
||||
<ping-connect-wallet
|
||||
:chain-id="baseStore.currentChainId || 'cosmoshub-4'"
|
||||
:hd-path="chainStore.defaultHDPath"
|
||||
:addr-prefix="chainStore.current?.bech32Prefix || 'cosmos'"
|
||||
@connect="walletStateChange"
|
||||
@keplr-config="walletStore.suggestChain()"
|
||||
:params="params"
|
||||
/>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -5,10 +5,8 @@
|
||||
>
|
||||
<div class="flex flex-1">
|
||||
©
|
||||
{{ new Date().getFullYear() }}
|
||||
Made With
|
||||
<img src="../../assets/images/heart.svg" />
|
||||
By
|
||||
{{ new Date().getFullYear() }} Made With
|
||||
<img src="../../assets/images/heart.svg" /> By
|
||||
<a
|
||||
class="link link-primary no-underline"
|
||||
href="https://ping.pub"
|
||||
@@ -17,9 +15,7 @@
|
||||
>Ping.pub</a
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="hidden md:!block"
|
||||
>
|
||||
<div class="hidden md:!block">
|
||||
<a
|
||||
class="link link-primary no-underline mr-4"
|
||||
href="https://github.com/ping-pub/explorer/blob/master/LICENSE"
|
||||
@@ -34,4 +30,4 @@
|
||||
>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -4,45 +4,45 @@ import { onMounted, computed } from 'vue';
|
||||
import { useBaseStore } from '@/stores';
|
||||
|
||||
const themeMap: Record<string, string> = {
|
||||
system: 'mdi-laptop',
|
||||
light: 'mdi-weather-sunny',
|
||||
dark: 'mdi-weather-night',
|
||||
system: 'mdi-laptop',
|
||||
light: 'mdi-weather-sunny',
|
||||
dark: 'mdi-weather-night',
|
||||
};
|
||||
const baseStore = useBaseStore();
|
||||
const theme = computed(() => {
|
||||
return baseStore.theme;
|
||||
return baseStore.theme;
|
||||
});
|
||||
|
||||
const changeMode = (val?: 'dark' | 'light') => {
|
||||
let value: 'dark' | 'light' = 'dark';
|
||||
const currentValue: 'dark' | 'light' = val || theme.value;
|
||||
if (currentValue === 'dark') {
|
||||
value = 'light';
|
||||
}
|
||||
if (value === 'light') {
|
||||
document.documentElement.classList.add('light');
|
||||
document.documentElement.classList.remove('dark');
|
||||
} else {
|
||||
document.documentElement.classList.add('dark');
|
||||
document.documentElement.classList.remove('light');
|
||||
}
|
||||
document.documentElement.setAttribute('data-theme', value);
|
||||
window.localStorage.setItem('theme', value);
|
||||
baseStore.theme = value;
|
||||
let value: 'dark' | 'light' = 'dark';
|
||||
const currentValue: 'dark' | 'light' = val || theme.value;
|
||||
if (currentValue === 'dark') {
|
||||
value = 'light';
|
||||
}
|
||||
if (value === 'light') {
|
||||
document.documentElement.classList.add('light');
|
||||
document.documentElement.classList.remove('dark');
|
||||
} else {
|
||||
document.documentElement.classList.add('dark');
|
||||
document.documentElement.classList.remove('light');
|
||||
}
|
||||
document.documentElement.setAttribute('data-theme', value);
|
||||
window.localStorage.setItem('theme', value);
|
||||
baseStore.theme = value;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
changeMode(theme.value === 'light' ? 'dark' : 'light');
|
||||
changeMode(theme.value === 'light' ? 'dark' : 'light');
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="tooltip tooltip-bottom delay-1000">
|
||||
<button
|
||||
class="btn btn-ghost btn-circle btn-sm mx-1"
|
||||
@click="changeMode()"
|
||||
>
|
||||
<Icon :icon="themeMap?.[theme]" class="text-2xl text-gray-500 dark:text-gray-400" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="tooltip tooltip-bottom delay-1000">
|
||||
<button class="btn btn-ghost btn-circle btn-sm mx-1" @click="changeMode()">
|
||||
<Icon
|
||||
:icon="themeMap?.[theme]"
|
||||
class="text-2xl text-gray-500 dark:text-gray-400"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,25 +1,43 @@
|
||||
<template>
|
||||
<div>
|
||||
<a href="https://cosmos.network" target="_blank"
|
||||
class="py-2 px-4 flex items-center cursor-pointer rounded-lg hover:bg-gray-100 dark:hover:bg-[#373f59]">
|
||||
<img src="https://ping.pub/logos/cosmos.svg" class="w-6 h-6 rounded-full mr-3" />
|
||||
<div class="text-sm capitalize flex-1 text-gray-600 dark:text-gray-200">
|
||||
Cosmos Hub
|
||||
</div>
|
||||
</a>
|
||||
<a href="https://osmosis.zone" target="_blank"
|
||||
class="py-2 px-4 flex items-center cursor-pointer rounded-lg hover:bg-gray-100 dark:hover:bg-[#373f59]">
|
||||
<img src="https://ping.pub/logos/osmosis.jpg" class="w-6 h-6 rounded-full mr-3" />
|
||||
<div class="text-sm capitalize flex-1 text-gray-600 dark:text-gray-200">
|
||||
Osmosis
|
||||
</div>
|
||||
</a>
|
||||
<a href="https://celestia.org" target="_blank"
|
||||
class="py-2 px-4 flex items-center cursor-pointer rounded-lg hover:bg-gray-100 dark:hover:bg-[#373f59]">
|
||||
<img src="https://ping.pub/logos/celestia.png" class="w-6 h-6 rounded-full mr-3" />
|
||||
<div class="text-sm capitalize flex-1 text-gray-600 dark:text-gray-200">
|
||||
Celestia
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<a
|
||||
href="https://cosmos.network"
|
||||
target="_blank"
|
||||
class="py-2 px-4 flex items-center cursor-pointer rounded-lg hover:bg-gray-100 dark:hover:bg-[#373f59]"
|
||||
>
|
||||
<img
|
||||
src="https://ping.pub/logos/cosmos.svg"
|
||||
class="w-6 h-6 rounded-full mr-3"
|
||||
/>
|
||||
<div class="text-sm capitalize flex-1 text-gray-600 dark:text-gray-200">
|
||||
Cosmos Hub
|
||||
</div>
|
||||
</a>
|
||||
<a
|
||||
href="https://osmosis.zone"
|
||||
target="_blank"
|
||||
class="py-2 px-4 flex items-center cursor-pointer rounded-lg hover:bg-gray-100 dark:hover:bg-[#373f59]"
|
||||
>
|
||||
<img
|
||||
src="https://ping.pub/logos/osmosis.jpg"
|
||||
class="w-6 h-6 rounded-full mr-3"
|
||||
/>
|
||||
<div class="text-sm capitalize flex-1 text-gray-600 dark:text-gray-200">
|
||||
Osmosis
|
||||
</div>
|
||||
</a>
|
||||
<a
|
||||
href="https://celestia.org"
|
||||
target="_blank"
|
||||
class="py-2 px-4 flex items-center cursor-pointer rounded-lg hover:bg-gray-100 dark:hover:bg-[#373f59]"
|
||||
>
|
||||
<img
|
||||
src="https://ping.pub/logos/celestia.png"
|
||||
class="w-6 h-6 rounded-full mr-3"
|
||||
/>
|
||||
<div class="text-sm capitalize flex-1 text-gray-600 dark:text-gray-200">
|
||||
Celestia
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Vendored
+44
-39
@@ -1,59 +1,64 @@
|
||||
// 👉 Vertical nav section title
|
||||
export interface NavSectionTitle extends Partial<AclProperties> {
|
||||
heading: string
|
||||
heading: string;
|
||||
}
|
||||
|
||||
// 👉 Vertical nav link
|
||||
declare type ATagTargetAttrValues = '_blank' | '_self' | '_parent' | '_top' | 'framename'
|
||||
declare type ATagTargetAttrValues =
|
||||
| '_blank'
|
||||
| '_self'
|
||||
| '_parent'
|
||||
| '_top'
|
||||
| 'framename';
|
||||
declare type ATagRelAttrValues =
|
||||
| 'alternate'
|
||||
| 'author'
|
||||
| 'bookmark'
|
||||
| 'external'
|
||||
| 'help'
|
||||
| 'license'
|
||||
| 'next'
|
||||
| 'nofollow'
|
||||
| 'noopener'
|
||||
| 'noreferrer'
|
||||
| 'prev'
|
||||
| 'search'
|
||||
| 'tag'
|
||||
| 'alternate'
|
||||
| 'author'
|
||||
| 'bookmark'
|
||||
| 'external'
|
||||
| 'help'
|
||||
| 'license'
|
||||
| 'next'
|
||||
| 'nofollow'
|
||||
| 'noopener'
|
||||
| 'noreferrer'
|
||||
| 'prev'
|
||||
| 'search'
|
||||
| 'tag';
|
||||
|
||||
export interface NavLinkProps {
|
||||
to?: RouteLocationRaw | string | null
|
||||
href?: string
|
||||
target?: ATagTargetAttrValues
|
||||
rel?: ATagRelAttrValues
|
||||
i18n?: boolean
|
||||
to?: RouteLocationRaw | string | null;
|
||||
href?: string;
|
||||
target?: ATagTargetAttrValues;
|
||||
rel?: ATagRelAttrValues;
|
||||
i18n?: boolean;
|
||||
}
|
||||
|
||||
export interface Icon {
|
||||
icon?: string,
|
||||
image?: string,
|
||||
size: string,
|
||||
icon?: string;
|
||||
image?: string;
|
||||
size: string;
|
||||
}
|
||||
|
||||
export interface NavLink extends NavLinkProps {
|
||||
title: string
|
||||
icon?: Icon
|
||||
badgeContent?: string | number
|
||||
badgeClass?: string
|
||||
disable?: boolean
|
||||
order?: number
|
||||
title: string;
|
||||
icon?: Icon;
|
||||
badgeContent?: string | number;
|
||||
badgeClass?: string;
|
||||
disable?: boolean;
|
||||
order?: number;
|
||||
}
|
||||
|
||||
// 👉 Vertical nav group
|
||||
export interface NavGroup {
|
||||
title: string
|
||||
icon?: Icon
|
||||
badgeContent?: string | number
|
||||
badgeClass?: string
|
||||
children: (NavLink | NavGroup)[]
|
||||
disable?: boolean
|
||||
order?: number
|
||||
i18n?: boolean
|
||||
title: string;
|
||||
icon?: Icon;
|
||||
badgeContent?: string | number;
|
||||
badgeClass?: string;
|
||||
children: (NavLink | NavGroup)[];
|
||||
disable?: boolean;
|
||||
order?: number;
|
||||
i18n?: boolean;
|
||||
}
|
||||
|
||||
export declare type VerticalNavItems = (NavLink | NavGroup | NavSectionTitle)[]
|
||||
export declare type HorizontalNavItems = (NavLink | NavGroup)[]
|
||||
export declare type VerticalNavItems = (NavLink | NavGroup | NavSectionTitle)[];
|
||||
export declare type HorizontalNavItems = (NavLink | NavGroup)[];
|
||||
|
||||
+5
-3
@@ -44,8 +44,9 @@ export function consensusPubkeyToHexAddress(consensusPubkey?: {
|
||||
}
|
||||
|
||||
// not work as expected, will fix later or remove
|
||||
export function consumerKeyToBase64Address(consumerKey?: Record<string, string>) {
|
||||
|
||||
export function consumerKeyToBase64Address(
|
||||
consumerKey?: Record<string, string>
|
||||
) {
|
||||
if (!consumerKey) return '';
|
||||
let raw = '';
|
||||
if (consumerKey.ed25519) {
|
||||
@@ -55,7 +56,8 @@ export function consumerKeyToBase64Address(consumerKey?: Record<string, string>)
|
||||
|
||||
if (consumerKey.secp256k1) {
|
||||
const pubkey = fromBase64(consumerKey.secp256k1);
|
||||
if (pubkey) return toBase64(new Ripemd160().update(sha256(pubkey)).digest());
|
||||
if (pubkey)
|
||||
return toBase64(new Ripemd160().update(sha256(pubkey)).digest());
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
|
||||
@@ -1,68 +1,72 @@
|
||||
import type{ RequestRegistry } from '@/libs/api/registry'
|
||||
import type { RequestRegistry } from '@/libs/api/registry';
|
||||
// import dayjs from 'dayjs'
|
||||
import { adapter } from '@/libs/api/registry'
|
||||
import type { GovProposal, PaginatedProposals } from '@/types'
|
||||
import { adapter } from '@/libs/api/registry';
|
||||
import type { GovProposal, PaginatedProposals } from '@/types';
|
||||
|
||||
// which registry is store
|
||||
export const store = 'name' // name or version
|
||||
export const store = 'name'; // name or version
|
||||
// Blockchain Name
|
||||
export const name = 'atomone'
|
||||
export const name = 'atomone';
|
||||
|
||||
function proposalAdapter(p: any): GovProposal {
|
||||
if(p) {
|
||||
if(p.messages && p.messages.length >= 1) p.content = p.messages[0].content || p.messages[0]
|
||||
//p.proposal_id = p.id
|
||||
p.final_tally_result = {
|
||||
yes: p.final_tally_result?.yes_count,
|
||||
no: p.final_tally_result?.no_count,
|
||||
no_with_veto: p.final_tally_result?.no_with_veto_count,
|
||||
abstain: p.final_tally_result?.abstain_count,
|
||||
}
|
||||
}
|
||||
return p
|
||||
if (p) {
|
||||
if (p.messages && p.messages.length >= 1)
|
||||
p.content = p.messages[0].content || p.messages[0];
|
||||
//p.proposal_id = p.id
|
||||
p.final_tally_result = {
|
||||
yes: p.final_tally_result?.yes_count,
|
||||
no: p.final_tally_result?.no_count,
|
||||
no_with_veto: p.final_tally_result?.no_with_veto_count,
|
||||
abstain: p.final_tally_result?.abstain_count,
|
||||
};
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
// osmosis custom request
|
||||
export const requests: Partial<RequestRegistry> = {
|
||||
// mint_inflation: {
|
||||
// url: `https://public-osmosis-api.numia.xyz/apr?start_date=${new Date(new Date().getTime() - 186400*1000).toISOString().split('T')[0]}&end_date=${new Date().toISOString().split('T')[0]}`,
|
||||
// adapter: async (data: any) => {
|
||||
// const [first] = data
|
||||
// return {inflation: String(Number(first?.apr|| "0")/100.0)}
|
||||
// }
|
||||
// },
|
||||
gov_params_voting: { url: '/atomone/gov/v1beta1/params/voting', adapter },
|
||||
gov_params_tally: { url: '/atomone/gov/v1beta1/params/tallying', adapter },
|
||||
gov_params_deposit: { url: '/atomone/gov/v1beta1/params/deposit', adapter },
|
||||
gov_proposals: { url: '/atomone/gov/v1beta1/proposals', adapter: async (source: any): Promise<PaginatedProposals> => {
|
||||
const proposals = source.proposals.map((p:any) => proposalAdapter(p))
|
||||
// mint_inflation: {
|
||||
// url: `https://public-osmosis-api.numia.xyz/apr?start_date=${new Date(new Date().getTime() - 186400*1000).toISOString().split('T')[0]}&end_date=${new Date().toISOString().split('T')[0]}`,
|
||||
// adapter: async (data: any) => {
|
||||
// const [first] = data
|
||||
// return {inflation: String(Number(first?.apr|| "0")/100.0)}
|
||||
// }
|
||||
// },
|
||||
gov_params_voting: { url: '/atomone/gov/v1beta1/params/voting', adapter },
|
||||
gov_params_tally: { url: '/atomone/gov/v1beta1/params/tallying', adapter },
|
||||
gov_params_deposit: { url: '/atomone/gov/v1beta1/params/deposit', adapter },
|
||||
gov_proposals: {
|
||||
url: '/atomone/gov/v1beta1/proposals',
|
||||
adapter: async (source: any): Promise<PaginatedProposals> => {
|
||||
const proposals = source.proposals.map((p: any) => proposalAdapter(p));
|
||||
return {
|
||||
proposals,
|
||||
pagination: source.pagination
|
||||
}
|
||||
}},
|
||||
gov_proposals_proposal_id: {
|
||||
url: '/atomone/gov/v1beta1/proposals/{proposal_id}',
|
||||
adapter: async (source: any): Promise<{proposal: GovProposal}> => {
|
||||
return {
|
||||
proposal: proposalAdapter(source.proposal)
|
||||
}
|
||||
},
|
||||
proposals,
|
||||
pagination: source.pagination,
|
||||
};
|
||||
},
|
||||
gov_proposals_deposits: {
|
||||
url: '/atomone/gov/v1beta1/proposals/{proposal_id}/deposits',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_proposal_id: {
|
||||
url: '/atomone/gov/v1beta1/proposals/{proposal_id}',
|
||||
adapter: async (source: any): Promise<{ proposal: GovProposal }> => {
|
||||
return {
|
||||
proposal: proposalAdapter(source.proposal),
|
||||
};
|
||||
},
|
||||
gov_proposals_tally: {
|
||||
url: '/atomone/gov/v1beta1/proposals/{proposal_id}/tally',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes: {
|
||||
url: '/atomone/gov/v1beta1/proposals/{proposal_id}/votes',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes_voter: {
|
||||
url: '/atomone/gov/v1beta1/proposals/{proposal_id}/votes/{voter}',
|
||||
adapter,
|
||||
},
|
||||
}
|
||||
},
|
||||
gov_proposals_deposits: {
|
||||
url: '/atomone/gov/v1beta1/proposals/{proposal_id}/deposits',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_tally: {
|
||||
url: '/atomone/gov/v1beta1/proposals/{proposal_id}/tally',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes: {
|
||||
url: '/atomone/gov/v1beta1/proposals/{proposal_id}/votes',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes_voter: {
|
||||
url: '/atomone/gov/v1beta1/proposals/{proposal_id}/votes/{voter}',
|
||||
adapter,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,59 +1,68 @@
|
||||
import type{ RequestRegistry } from '../registry'
|
||||
import { adapter } from '../registry'
|
||||
import type { GovProposal, PaginatedProposals } from '@/types'
|
||||
import type { RequestRegistry } from '../registry';
|
||||
import { adapter } from '../registry';
|
||||
import type { GovProposal, PaginatedProposals } from '@/types';
|
||||
// which registry is store
|
||||
export const store = 'name' // name or version
|
||||
export const store = 'name'; // name or version
|
||||
// Blockchain Name
|
||||
export const name = 'evmos'
|
||||
export const name = 'evmos';
|
||||
|
||||
function proposalAdapter(p: any): GovProposal {
|
||||
if(p) {
|
||||
if(p.messages && p.messages.length >= 1) p.content = p.messages[0].content || p.messages[0]
|
||||
p.proposal_id = p.id
|
||||
p.final_tally_result = {
|
||||
yes: p.final_tally_result?.yes_count,
|
||||
no: p.final_tally_result?.no_count,
|
||||
no_with_veto: p.final_tally_result?.no_with_veto_count,
|
||||
abstain: p.final_tally_result?.abstain_count,
|
||||
}
|
||||
}
|
||||
return p
|
||||
if (p) {
|
||||
if (p.messages && p.messages.length >= 1)
|
||||
p.content = p.messages[0].content || p.messages[0];
|
||||
p.proposal_id = p.id;
|
||||
p.final_tally_result = {
|
||||
yes: p.final_tally_result?.yes_count,
|
||||
no: p.final_tally_result?.no_count,
|
||||
no_with_veto: p.final_tally_result?.no_with_veto_count,
|
||||
abstain: p.final_tally_result?.abstain_count,
|
||||
};
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
export const requests: Partial<RequestRegistry> = {
|
||||
mint_inflation: { url: '/evmos/inflation/v1/inflation_rate', adapter: async (data: any) => ({inflation: (Number(data.inflation_rate || 0)/ 100 ).toFixed(2)}) },
|
||||
gov_params_voting: { url: '/cosmos/gov/v1/params/voting', adapter },
|
||||
gov_params_tally: { url: '/cosmos/gov/v1/params/tallying', adapter },
|
||||
gov_params_deposit: { url: '/cosmos/gov/v1/params/deposit', adapter },
|
||||
gov_proposals: { url: '/cosmos/gov/v1/proposals', adapter: async (source: any): Promise<PaginatedProposals> => {
|
||||
const proposals = source.proposals.map((p:any) => proposalAdapter(p))
|
||||
mint_inflation: {
|
||||
url: '/evmos/inflation/v1/inflation_rate',
|
||||
adapter: async (data: any) => ({
|
||||
inflation: (Number(data.inflation_rate || 0) / 100).toFixed(2),
|
||||
}),
|
||||
},
|
||||
gov_params_voting: { url: '/cosmos/gov/v1/params/voting', adapter },
|
||||
gov_params_tally: { url: '/cosmos/gov/v1/params/tallying', adapter },
|
||||
gov_params_deposit: { url: '/cosmos/gov/v1/params/deposit', adapter },
|
||||
gov_proposals: {
|
||||
url: '/cosmos/gov/v1/proposals',
|
||||
adapter: async (source: any): Promise<PaginatedProposals> => {
|
||||
const proposals = source.proposals.map((p: any) => proposalAdapter(p));
|
||||
return {
|
||||
proposals,
|
||||
pagination: source.pagination
|
||||
}
|
||||
}},
|
||||
gov_proposals_proposal_id: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}',
|
||||
adapter: async (source: any): Promise<{proposal: GovProposal}> => {
|
||||
return {
|
||||
proposal: proposalAdapter(source.proposal)
|
||||
}
|
||||
},
|
||||
proposals,
|
||||
pagination: source.pagination,
|
||||
};
|
||||
},
|
||||
gov_proposals_deposits: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/deposits',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_proposal_id: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}',
|
||||
adapter: async (source: any): Promise<{ proposal: GovProposal }> => {
|
||||
return {
|
||||
proposal: proposalAdapter(source.proposal),
|
||||
};
|
||||
},
|
||||
gov_proposals_tally: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/tally',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes_voter: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes/{voter}',
|
||||
adapter,
|
||||
},
|
||||
}
|
||||
},
|
||||
gov_proposals_deposits: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/deposits',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_tally: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/tally',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes_voter: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes/{voter}',
|
||||
adapter,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
import type { RequestRegistry } from '@/libs/api/registry'
|
||||
import type { GovProposal, PaginatedProposals } from '@/types'
|
||||
import type { RequestRegistry } from '@/libs/api/registry';
|
||||
import type { GovProposal, PaginatedProposals } from '@/types';
|
||||
import { CosmosRestClient } from '@/libs/client';
|
||||
import { useBlockchain } from '@/stores';
|
||||
import { adapter } from '@/libs/api/registry'
|
||||
import { adapter } from '@/libs/api/registry';
|
||||
|
||||
// Blockchain Name
|
||||
export const name = 'nolus';
|
||||
|
||||
function proposalAdapter(p: any): GovProposal {
|
||||
if (p) {
|
||||
if (p.messages && p.messages.length >= 1) p.content = p.messages[0].content || p.messages[0]
|
||||
p.proposal_id = p.id
|
||||
if (p.messages && p.messages.length >= 1)
|
||||
p.content = p.messages[0].content || p.messages[0];
|
||||
p.proposal_id = p.id;
|
||||
p.final_tally_result = {
|
||||
yes: p.final_tally_result?.yes_count,
|
||||
no: p.final_tally_result?.no_count,
|
||||
no_with_veto: p.final_tally_result?.no_with_veto_count,
|
||||
abstain: p.final_tally_result?.abstain_count,
|
||||
}
|
||||
};
|
||||
}
|
||||
return p
|
||||
return p;
|
||||
}
|
||||
|
||||
// nolus custom request
|
||||
@@ -27,39 +28,59 @@ export const requests: Partial<RequestRegistry> = {
|
||||
url: '/nolus/mint/v1beta1/annual_inflation',
|
||||
adapter: async (data: any): Promise<any> => {
|
||||
try {
|
||||
const client = CosmosRestClient.newDefault(useBlockchain().endpoint.address)
|
||||
const staking = await client.getStakingPool()
|
||||
const inflation = Number(data.annual_inflation) / Number(staking.pool.bonded_tokens) || 0;
|
||||
const client = CosmosRestClient.newDefault(
|
||||
useBlockchain().endpoint.address
|
||||
);
|
||||
const staking = await client.getStakingPool();
|
||||
const inflation =
|
||||
Number(data.annual_inflation) / Number(staking.pool.bonded_tokens) ||
|
||||
0;
|
||||
return { inflation: inflation.toString() };
|
||||
} catch (error) {
|
||||
console.log("Error in adapter:", error);
|
||||
return { inflation: "0" };
|
||||
console.log('Error in adapter:', error);
|
||||
return { inflation: '0' };
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
gov_proposals: {
|
||||
url: '/cosmos/gov/v1/proposals', adapter: async (source: any): Promise<PaginatedProposals> => {
|
||||
const proposals = source.proposals.map((p: any) => proposalAdapter(p))
|
||||
url: '/cosmos/gov/v1/proposals',
|
||||
adapter: async (source: any): Promise<PaginatedProposals> => {
|
||||
const proposals = source.proposals.map((p: any) => proposalAdapter(p));
|
||||
return {
|
||||
proposals,
|
||||
pagination: source.pagination
|
||||
}
|
||||
}
|
||||
pagination: source.pagination,
|
||||
};
|
||||
},
|
||||
},
|
||||
gov_proposals_proposal_id: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}',
|
||||
adapter: async (source: any): Promise<{ proposal: GovProposal }> => {
|
||||
return {
|
||||
proposal: proposalAdapter(source.proposal)
|
||||
}
|
||||
proposal: proposalAdapter(source.proposal),
|
||||
};
|
||||
},
|
||||
},
|
||||
gov_params_voting: { url: '/cosmos/gov/v1/params/voting', adapter },
|
||||
gov_params_tally: { url: '/cosmos/gov/v1/params/tallying', adapter },
|
||||
gov_params_deposit: { url: '/cosmos/gov/v1/params/deposit', adapter },
|
||||
gov_proposals_deposits: { url: '/cosmos/gov/v1/proposals/{proposal_id}/deposits', adapter },
|
||||
gov_proposals_tally: { url: '/cosmos/gov/v1/proposals/{proposal_id}/tally', adapter },
|
||||
gov_proposals_votes: { url: '/cosmos/gov/v1/proposals/{proposal_id}/votes', adapter },
|
||||
gov_proposals_votes_voter: { url: '/cosmos/gov/v1/proposals/{proposal_id}/votes/{voter}', adapter },
|
||||
bank_supply_by_denom: { url: "/cosmos/bank/v1beta1/supply/by_denom?denom={denom}", adapter }
|
||||
gov_proposals_deposits: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/deposits',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_tally: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/tally',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes_voter: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes/{voter}',
|
||||
adapter,
|
||||
},
|
||||
bank_supply_by_denom: {
|
||||
url: '/cosmos/bank/v1beta1/supply/by_denom?denom={denom}',
|
||||
adapter,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,68 +1,74 @@
|
||||
import type{ RequestRegistry } from '@/libs/api/registry'
|
||||
import type { RequestRegistry } from '@/libs/api/registry';
|
||||
// import dayjs from 'dayjs'
|
||||
import { adapter } from '@/libs/api/registry'
|
||||
import type { GovProposal, PaginatedProposals } from '@/types'
|
||||
import { adapter } from '@/libs/api/registry';
|
||||
import type { GovProposal, PaginatedProposals } from '@/types';
|
||||
|
||||
// which registry is store
|
||||
export const store = 'name' // name or version
|
||||
export const store = 'name'; // name or version
|
||||
// Blockchain Name
|
||||
export const name = 'osmosis'
|
||||
export const name = 'osmosis';
|
||||
|
||||
function proposalAdapter(p: any): GovProposal {
|
||||
if(p) {
|
||||
if(p.messages && p.messages.length >= 1) p.content = p.messages[0].content || p.messages[0]
|
||||
p.proposal_id = p.id
|
||||
p.final_tally_result = {
|
||||
yes: p.final_tally_result?.yes_count,
|
||||
no: p.final_tally_result?.no_count,
|
||||
no_with_veto: p.final_tally_result?.no_with_veto_count,
|
||||
abstain: p.final_tally_result?.abstain_count,
|
||||
}
|
||||
}
|
||||
return p
|
||||
if (p) {
|
||||
if (p.messages && p.messages.length >= 1)
|
||||
p.content = p.messages[0].content || p.messages[0];
|
||||
p.proposal_id = p.id;
|
||||
p.final_tally_result = {
|
||||
yes: p.final_tally_result?.yes_count,
|
||||
no: p.final_tally_result?.no_count,
|
||||
no_with_veto: p.final_tally_result?.no_with_veto_count,
|
||||
abstain: p.final_tally_result?.abstain_count,
|
||||
};
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
// osmosis custom request
|
||||
export const requests: Partial<RequestRegistry> = {
|
||||
mint_inflation: {
|
||||
url: `https://public-osmosis-api.numia.xyz/apr?start_date=${new Date(new Date().getTime() - 186400*1000).toISOString().split('T')[0]}&end_date=${new Date().toISOString().split('T')[0]}`,
|
||||
adapter: async (data: any) => {
|
||||
const [first] = data
|
||||
return {inflation: String(Number(first?.apr|| "0")/100.0)}
|
||||
}
|
||||
mint_inflation: {
|
||||
url: `https://public-osmosis-api.numia.xyz/apr?start_date=${
|
||||
new Date(new Date().getTime() - 186400 * 1000).toISOString().split('T')[0]
|
||||
}&end_date=${new Date().toISOString().split('T')[0]}`,
|
||||
adapter: async (data: any) => {
|
||||
const [first] = data;
|
||||
return { inflation: String(Number(first?.apr || '0') / 100.0) };
|
||||
},
|
||||
gov_params_voting: { url: '/cosmos/gov/v1/params/voting', adapter },
|
||||
gov_params_tally: { url: '/cosmos/gov/v1/params/tallying', adapter },
|
||||
gov_params_deposit: { url: '/cosmos/gov/v1/params/deposit', adapter },
|
||||
gov_proposals: { url: '/cosmos/gov/v1/proposals', adapter: async (source: any): Promise<PaginatedProposals> => {
|
||||
const proposals = source.proposals.map((p:any) => proposalAdapter(p))
|
||||
},
|
||||
gov_params_voting: { url: '/cosmos/gov/v1/params/voting', adapter },
|
||||
gov_params_tally: { url: '/cosmos/gov/v1/params/tallying', adapter },
|
||||
gov_params_deposit: { url: '/cosmos/gov/v1/params/deposit', adapter },
|
||||
gov_proposals: {
|
||||
url: '/cosmos/gov/v1/proposals',
|
||||
adapter: async (source: any): Promise<PaginatedProposals> => {
|
||||
const proposals = source.proposals.map((p: any) => proposalAdapter(p));
|
||||
return {
|
||||
proposals,
|
||||
pagination: source.pagination
|
||||
}
|
||||
}},
|
||||
gov_proposals_proposal_id: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}',
|
||||
adapter: async (source: any): Promise<{proposal: GovProposal}> => {
|
||||
return {
|
||||
proposal: proposalAdapter(source.proposal)
|
||||
}
|
||||
},
|
||||
proposals,
|
||||
pagination: source.pagination,
|
||||
};
|
||||
},
|
||||
gov_proposals_deposits: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/deposits',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_proposal_id: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}',
|
||||
adapter: async (source: any): Promise<{ proposal: GovProposal }> => {
|
||||
return {
|
||||
proposal: proposalAdapter(source.proposal),
|
||||
};
|
||||
},
|
||||
gov_proposals_tally: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/tally',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes_voter: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes/{voter}',
|
||||
adapter,
|
||||
},
|
||||
}
|
||||
},
|
||||
gov_proposals_deposits: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/deposits',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_tally: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/tally',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes_voter: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes/{voter}',
|
||||
adapter,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,51 +1,55 @@
|
||||
import type { RequestRegistry } from '@/libs/api/registry'
|
||||
import { adapter } from '@/libs/api/registry'
|
||||
import type { RequestRegistry } from '@/libs/api/registry';
|
||||
import { adapter } from '@/libs/api/registry';
|
||||
import type {
|
||||
GovParams,
|
||||
GovProposal,
|
||||
GovVote,
|
||||
PaginatedProposalDeposit,
|
||||
PaginatedProposalVotes,
|
||||
PaginatedProposals,
|
||||
Tally,
|
||||
} from '@/types/';
|
||||
GovParams,
|
||||
GovProposal,
|
||||
GovVote,
|
||||
PaginatedProposalDeposit,
|
||||
PaginatedProposalVotes,
|
||||
PaginatedProposals,
|
||||
Tally,
|
||||
} from '@/types/';
|
||||
|
||||
// which registry is store
|
||||
export const store = 'version' // name or version
|
||||
export const store = 'version'; // name or version
|
||||
// Cosmos SDK version
|
||||
export const name = 'v0.46.7'
|
||||
export const name = 'v0.46.7';
|
||||
|
||||
function proposalAdapter(p: any): GovProposal {
|
||||
if(p) {
|
||||
if(p.messages && p.messages.length >= 1) p.content = p.messages[0].content || p.messages[0]
|
||||
p.proposal_id = p.id
|
||||
p.final_tally_result = {
|
||||
yes: p.final_tally_result?.yes_count,
|
||||
no: p.final_tally_result?.no_count,
|
||||
no_with_veto: p.final_tally_result?.no_with_veto_count,
|
||||
abstain: p.final_tally_result?.abstain_count,
|
||||
}
|
||||
}
|
||||
return p
|
||||
if (p) {
|
||||
if (p.messages && p.messages.length >= 1)
|
||||
p.content = p.messages[0].content || p.messages[0];
|
||||
p.proposal_id = p.id;
|
||||
p.final_tally_result = {
|
||||
yes: p.final_tally_result?.yes_count,
|
||||
no: p.final_tally_result?.no_count,
|
||||
no_with_veto: p.final_tally_result?.no_with_veto_count,
|
||||
abstain: p.final_tally_result?.abstain_count,
|
||||
};
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
export const requests: Partial<RequestRegistry> = {
|
||||
gov_params_voting: { url: '/cosmos/gov/v1/params/voting', adapter },
|
||||
gov_params_tally: { url: '/cosmos/gov/v1/params/tallying', adapter },
|
||||
gov_params_deposit: { url: '/cosmos/gov/v1/params/deposit', adapter },
|
||||
gov_proposals: { url: '/cosmos/gov/v1/proposals', adapter: async (source: any): Promise<PaginatedProposals> => {
|
||||
const proposals = source.proposals.map((p:any) => proposalAdapter(p))
|
||||
return {
|
||||
gov_proposals: {
|
||||
url: '/cosmos/gov/v1/proposals',
|
||||
adapter: async (source: any): Promise<PaginatedProposals> => {
|
||||
const proposals = source.proposals.map((p: any) => proposalAdapter(p));
|
||||
return {
|
||||
proposals,
|
||||
pagination: source.pagination
|
||||
}
|
||||
}},
|
||||
pagination: source.pagination,
|
||||
};
|
||||
},
|
||||
},
|
||||
gov_proposals_proposal_id: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}',
|
||||
adapter: async (source: any): Promise<{proposal: GovProposal}> => {
|
||||
return {
|
||||
proposal: proposalAdapter(source.proposal)
|
||||
}
|
||||
adapter: async (source: any): Promise<{ proposal: GovProposal }> => {
|
||||
return {
|
||||
proposal: proposalAdapter(source.proposal),
|
||||
};
|
||||
},
|
||||
},
|
||||
gov_proposals_deposits: {
|
||||
@@ -64,4 +68,4 @@ export const requests: Partial<RequestRegistry> = {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes/{voter}',
|
||||
adapter,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,53 +1,59 @@
|
||||
import type { RequestRegistry } from '@/libs/api/registry'
|
||||
import { adapter } from '@/libs/api/registry'
|
||||
import type { RequestRegistry } from '@/libs/api/registry';
|
||||
import { adapter } from '@/libs/api/registry';
|
||||
import type {
|
||||
GovParams,
|
||||
GovProposal,
|
||||
GovVote,
|
||||
PaginatedProposalDeposit,
|
||||
PaginatedProposalVotes,
|
||||
PaginatedProposals,
|
||||
Tally,
|
||||
} from '@/types/';
|
||||
GovParams,
|
||||
GovProposal,
|
||||
GovVote,
|
||||
PaginatedProposalDeposit,
|
||||
PaginatedProposalVotes,
|
||||
PaginatedProposals,
|
||||
Tally,
|
||||
} from '@/types/';
|
||||
|
||||
// which registry is store
|
||||
export const store = 'version' // name or version
|
||||
export const store = 'version'; // name or version
|
||||
// Cosmos SDK version
|
||||
export const name = 'v0.50.0'
|
||||
export const name = 'v0.50.0';
|
||||
|
||||
function proposalAdapter(p: any): GovProposal {
|
||||
if(p) {
|
||||
if(p.messages && p.messages.length >= 1) p.content = p.messages[0].content || p.messages[0]
|
||||
p.proposal_id = p.id
|
||||
p.final_tally_result = {
|
||||
yes: p.final_tally_result?.yes_count,
|
||||
no: p.final_tally_result?.no_count,
|
||||
no_with_veto: p.final_tally_result?.no_with_veto_count,
|
||||
abstain: p.final_tally_result?.abstain_count,
|
||||
}
|
||||
}
|
||||
return p
|
||||
if (p) {
|
||||
if (p.messages && p.messages.length >= 1)
|
||||
p.content = p.messages[0].content || p.messages[0];
|
||||
p.proposal_id = p.id;
|
||||
p.final_tally_result = {
|
||||
yes: p.final_tally_result?.yes_count,
|
||||
no: p.final_tally_result?.no_count,
|
||||
no_with_veto: p.final_tally_result?.no_with_veto_count,
|
||||
abstain: p.final_tally_result?.abstain_count,
|
||||
};
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
export const requests: Partial<RequestRegistry> = {
|
||||
|
||||
bank_supply_by_denom: { url: '/cosmos/bank/v1beta1/supply/by_denom?denom={denom}', adapter },
|
||||
bank_supply_by_denom: {
|
||||
url: '/cosmos/bank/v1beta1/supply/by_denom?denom={denom}',
|
||||
adapter,
|
||||
},
|
||||
gov_params_voting: { url: '/cosmos/gov/v1/params/voting', adapter },
|
||||
gov_params_tally: { url: '/cosmos/gov/v1/params/tallying', adapter },
|
||||
gov_params_deposit: { url: '/cosmos/gov/v1/params/deposit', adapter },
|
||||
gov_proposals: { url: '/cosmos/gov/v1/proposals', adapter: async (source: any): Promise<PaginatedProposals> => {
|
||||
const proposals = source.proposals.map((p:any) => proposalAdapter(p))
|
||||
return {
|
||||
gov_proposals: {
|
||||
url: '/cosmos/gov/v1/proposals',
|
||||
adapter: async (source: any): Promise<PaginatedProposals> => {
|
||||
const proposals = source.proposals.map((p: any) => proposalAdapter(p));
|
||||
return {
|
||||
proposals,
|
||||
pagination: source.pagination
|
||||
}
|
||||
}},
|
||||
pagination: source.pagination,
|
||||
};
|
||||
},
|
||||
},
|
||||
gov_proposals_proposal_id: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}',
|
||||
adapter: async (source: any): Promise<{proposal: GovProposal}> => {
|
||||
return {
|
||||
proposal: proposalAdapter(source.proposal)
|
||||
}
|
||||
adapter: async (source: any): Promise<{ proposal: GovProposal }> => {
|
||||
return {
|
||||
proposal: proposalAdapter(source.proposal),
|
||||
};
|
||||
},
|
||||
},
|
||||
gov_proposals_deposits: {
|
||||
@@ -66,4 +72,4 @@ export const requests: Partial<RequestRegistry> = {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes/{voter}',
|
||||
adapter,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import type { RequestRegistry } from '@/libs/api/registry';
|
||||
import { adapter } from '@/libs/api/registry'
|
||||
import { adapter } from '@/libs/api/registry';
|
||||
import { CosmosRestClient } from '@/libs/client';
|
||||
import { useBlockchain } from '@/stores';
|
||||
import type {
|
||||
GovProposal,
|
||||
PaginatedProposals,
|
||||
} from '@/types/';
|
||||
import type { GovProposal, PaginatedProposals } from '@/types/';
|
||||
|
||||
// which registry is store
|
||||
export const store = 'name'; // name or version
|
||||
@@ -14,39 +11,44 @@ export const name = 'xion';
|
||||
|
||||
export function proposalAdapter(p: any): GovProposal {
|
||||
if (p) {
|
||||
if (p.messages && p.messages.length >= 1) p.content = p.messages[0].content || p.messages[0]
|
||||
p.proposal_id = p.id
|
||||
if (p.messages && p.messages.length >= 1)
|
||||
p.content = p.messages[0].content || p.messages[0];
|
||||
p.proposal_id = p.id;
|
||||
p.final_tally_result = {
|
||||
yes: p.final_tally_result?.yes_count,
|
||||
no: p.final_tally_result?.no_count,
|
||||
no_with_veto: p.final_tally_result?.no_with_veto_count,
|
||||
abstain: p.final_tally_result?.abstain_count,
|
||||
}
|
||||
};
|
||||
}
|
||||
return p
|
||||
return p;
|
||||
}
|
||||
|
||||
// xion custom request
|
||||
export const requests: Partial<RequestRegistry> = {
|
||||
bank_supply_by_denom: { url: '/cosmos/bank/v1beta1/supply/by_denom?denom={denom}', adapter },
|
||||
bank_supply_by_denom: {
|
||||
url: '/cosmos/bank/v1beta1/supply/by_denom?denom={denom}',
|
||||
adapter,
|
||||
},
|
||||
gov_params_voting: { url: '/cosmos/gov/v1/params/voting', adapter },
|
||||
gov_params_tally: { url: '/cosmos/gov/v1/params/tallying', adapter },
|
||||
gov_params_deposit: { url: '/cosmos/gov/v1/params/deposit', adapter },
|
||||
gov_proposals: {
|
||||
url: '/cosmos/gov/v1/proposals', adapter: async (source: any): Promise<PaginatedProposals> => {
|
||||
const proposals = source.proposals.map((p: any) => proposalAdapter(p))
|
||||
url: '/cosmos/gov/v1/proposals',
|
||||
adapter: async (source: any): Promise<PaginatedProposals> => {
|
||||
const proposals = source.proposals.map((p: any) => proposalAdapter(p));
|
||||
return {
|
||||
proposals,
|
||||
pagination: source.pagination
|
||||
}
|
||||
}
|
||||
pagination: source.pagination,
|
||||
};
|
||||
},
|
||||
},
|
||||
gov_proposals_proposal_id: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}',
|
||||
adapter: async (source: any): Promise<{ proposal: GovProposal }> => {
|
||||
return {
|
||||
proposal: proposalAdapter(source.proposal)
|
||||
}
|
||||
proposal: proposalAdapter(source.proposal),
|
||||
};
|
||||
},
|
||||
},
|
||||
gov_proposals_deposits: {
|
||||
@@ -99,4 +101,4 @@ export const requests: Partial<RequestRegistry> = {
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import {
|
||||
type RequestRegistry,
|
||||
adapter,
|
||||
} from './registry';
|
||||
import { type RequestRegistry, adapter } from './registry';
|
||||
|
||||
export const DEFAULT: RequestRegistry = {
|
||||
auth_params: { url: '/cosmos/auth/v1beta1/params', adapter },
|
||||
@@ -10,7 +7,10 @@ export const DEFAULT: RequestRegistry = {
|
||||
url: '/cosmos/auth/v1beta1/accounts/{address}',
|
||||
adapter,
|
||||
},
|
||||
params: { url: '/cosmos/params/v1beta1/params?subspace={subspace}&key={key}', adapter },
|
||||
params: {
|
||||
url: '/cosmos/params/v1beta1/params?subspace={subspace}&key={key}',
|
||||
adapter,
|
||||
},
|
||||
bank_params: { url: '/cosmos/bank/v1beta1/params', adapter },
|
||||
bank_balances_address: {
|
||||
url: '/cosmos/bank/v1beta1/balances/{address}',
|
||||
@@ -228,5 +228,4 @@ export const DEFAULT: RequestRegistry = {
|
||||
url: '/cosmos/group/v1/proposals_by_group_policy/{address}',
|
||||
adapter,
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
+34
-20
@@ -47,7 +47,7 @@ import type {
|
||||
Validator,
|
||||
} from '@/types/staking';
|
||||
import type { PaginatedTxs, Tx, TxResponse } from '@/types';
|
||||
import semver from 'semver'
|
||||
import semver from 'semver';
|
||||
export interface Request<T> {
|
||||
url: string;
|
||||
adapter: (source: any) => Promise<T>;
|
||||
@@ -80,10 +80,10 @@ export interface RequestRegistry extends AbstractRegistry {
|
||||
distribution_community_pool: Request<{ pool: Coin[] }>;
|
||||
distribution_delegator_rewards: Request<{
|
||||
rewards: {
|
||||
validator_address: string,
|
||||
reward: Coin[]
|
||||
}[],
|
||||
total: Coin[]
|
||||
validator_address: string;
|
||||
reward: Coin[];
|
||||
}[];
|
||||
total: Coin[];
|
||||
}>;
|
||||
|
||||
mint_inflation: Request<{ inflation: string }>;
|
||||
@@ -95,7 +95,7 @@ export interface RequestRegistry extends AbstractRegistry {
|
||||
}>;
|
||||
mint_annual_provisions: Request<{ annual_provisions: string }>;
|
||||
|
||||
slashing_params: Request<{params: SlashingParam}>;
|
||||
slashing_params: Request<{ params: SlashingParam }>;
|
||||
slashing_signing_info: Request<PaginatedSigningInfo>;
|
||||
|
||||
gov_params_voting: Request<GovParams>;
|
||||
@@ -129,7 +129,7 @@ export interface RequestRegistry extends AbstractRegistry {
|
||||
base_tendermint_validatorsets_latest: Request<PaginatedTendermintValidator>;
|
||||
base_tendermint_validatorsets_height: Request<PaginatedTendermintValidator>;
|
||||
|
||||
params: Request<{param: any}>;
|
||||
params: Request<{ param: any }>;
|
||||
|
||||
group_groups: Request<PaginatedGroups>;
|
||||
group_groups_by_admin: Request<PaginatedGroups>;
|
||||
@@ -163,9 +163,19 @@ export interface RequestRegistry extends AbstractRegistry {
|
||||
ibc_core_connection_connections: Request<PaginatedIBCConnections>;
|
||||
ibc_core_connection_connections_connection_id: Request<ConnectionWithProof>;
|
||||
ibc_core_connection_connections_connection_id_client_state: Request<ClientStateWithProof>;
|
||||
interchain_security_ccv_provider_validator_consumer_addr: Request<{consumer_address: string}>
|
||||
interchain_security_provider_opted_in_validators: Request<{validators_provider_addresses: string[]}>
|
||||
interchain_security_consumer_validators: Request<{validators: {provider_address: string, consumer_key: {ed25519: string}, power: string}[]}>
|
||||
interchain_security_ccv_provider_validator_consumer_addr: Request<{
|
||||
consumer_address: string;
|
||||
}>;
|
||||
interchain_security_provider_opted_in_validators: Request<{
|
||||
validators_provider_addresses: string[];
|
||||
}>;
|
||||
interchain_security_consumer_validators: Request<{
|
||||
validators: {
|
||||
provider_address: string;
|
||||
consumer_key: { ed25519: string };
|
||||
power: string;
|
||||
}[];
|
||||
}>;
|
||||
}
|
||||
|
||||
export function adapter<T>(source: any): Promise<T> {
|
||||
@@ -188,16 +198,20 @@ export const VERSION_REGISTRY: ApiProfileRegistry = {};
|
||||
// ChainName Profile Registory
|
||||
export const NAME_REGISTRY: ApiProfileRegistry = {};
|
||||
|
||||
export function registryVersionProfile(version: string, requests: RequestRegistry) {
|
||||
VERSION_REGISTRY[version] = requests
|
||||
export function registryVersionProfile(
|
||||
version: string,
|
||||
requests: RequestRegistry
|
||||
) {
|
||||
VERSION_REGISTRY[version] = requests;
|
||||
}
|
||||
|
||||
export function registryChainProfile(version: string, requests: RequestRegistry) {
|
||||
NAME_REGISTRY[version] = requests
|
||||
export function registryChainProfile(
|
||||
version: string,
|
||||
requests: RequestRegistry
|
||||
) {
|
||||
NAME_REGISTRY[version] = requests;
|
||||
}
|
||||
export function findApiProfileByChain(
|
||||
name: string,
|
||||
): RequestRegistry {
|
||||
export function findApiProfileByChain(name: string): RequestRegistry {
|
||||
const url = NAME_REGISTRY[name];
|
||||
// if (!url) {
|
||||
// throw new Error(`Unsupported version or name: ${name}`);
|
||||
@@ -206,12 +220,12 @@ export function findApiProfileByChain(
|
||||
}
|
||||
|
||||
export function findApiProfileBySDKVersion(
|
||||
version: string,
|
||||
version: string
|
||||
): RequestRegistry | undefined {
|
||||
let closestVersion: string | null = null;
|
||||
const chain_version = version.match(/(\d+\.\d+\.?\d*)/g) || [""];
|
||||
const chain_version = version.match(/(\d+\.\d+\.?\d*)/g) || [''];
|
||||
for (const k in VERSION_REGISTRY) {
|
||||
const key = k.replace('v', "")
|
||||
const key = k.replace('v', '');
|
||||
if (semver.lte(key, chain_version[0])) {
|
||||
if (!closestVersion || semver.gt(key, closestVersion)) {
|
||||
closestVersion = k;
|
||||
|
||||
+153
-79
@@ -11,8 +11,8 @@ import {
|
||||
registryVersionProfile,
|
||||
withCustomRequest,
|
||||
} from './api/registry';
|
||||
import { PageRequest,type Coin } from '@/types';
|
||||
import semver from 'semver'
|
||||
import { PageRequest, type Coin } from '@/types';
|
||||
import semver from 'semver';
|
||||
|
||||
export class BaseRestClient<R extends AbstractRegistry> {
|
||||
version: string;
|
||||
@@ -21,17 +21,26 @@ export class BaseRestClient<R extends AbstractRegistry> {
|
||||
constructor(endpoint: string, registry: R, version?: string) {
|
||||
this.endpoint = endpoint;
|
||||
this.registry = registry;
|
||||
this.version = version || 'v0.40'
|
||||
this.version = version || 'v0.40';
|
||||
}
|
||||
async request<T>(request: Request<T>, args: Record<string, any>, query = '', adapter?: (source: any) => Promise<T> ) {
|
||||
let url = `${request.url.startsWith("http")?'':this.endpoint}${request.url}${query}`;
|
||||
async request<T>(
|
||||
request: Request<T>,
|
||||
args: Record<string, any>,
|
||||
query = '',
|
||||
adapter?: (source: any) => Promise<T>
|
||||
) {
|
||||
let url = `${request.url.startsWith('http') ? '' : this.endpoint}${
|
||||
request.url
|
||||
}${query}`;
|
||||
Object.keys(args).forEach((k) => {
|
||||
url = url.replace(`{${k}}`, args[k] || '');
|
||||
});
|
||||
return fetchData<T>(url, adapter||request.adapter);
|
||||
return fetchData<T>(url, adapter || request.adapter);
|
||||
}
|
||||
async get<T>(request: Request<T>, args: Record<string, any>, query = '' ) {
|
||||
let url = `${request.url.startsWith("http")?'':this.endpoint}${request.url}${query}`;
|
||||
async get<T>(request: Request<T>, args: Record<string, any>, query = '') {
|
||||
let url = `${request.url.startsWith('http') ? '' : this.endpoint}${
|
||||
request.url
|
||||
}${query}`;
|
||||
Object.keys(args).forEach((k) => {
|
||||
url = url.replace(`{${k}}`, args[k] || '');
|
||||
});
|
||||
@@ -41,42 +50,47 @@ export class BaseRestClient<R extends AbstractRegistry> {
|
||||
|
||||
// dynamic all custom request implementations
|
||||
function registeCustomRequest() {
|
||||
const extensions: Record<string, any> = import.meta.glob('./api/customization/*.ts', { eager: true });
|
||||
Object.values(extensions).forEach(m => {
|
||||
if(m.store === 'version') {
|
||||
registryVersionProfile(m.name, withCustomRequest(DEFAULT, m.requests))
|
||||
const extensions: Record<string, any> = import.meta.glob(
|
||||
'./api/customization/*.ts',
|
||||
{ eager: true }
|
||||
);
|
||||
Object.values(extensions).forEach((m) => {
|
||||
if (m.store === 'version') {
|
||||
registryVersionProfile(m.name, withCustomRequest(DEFAULT, m.requests));
|
||||
} else {
|
||||
registryChainProfile(m.name, withCustomRequest(DEFAULT, m.requests));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
registeCustomRequest()
|
||||
|
||||
registeCustomRequest();
|
||||
|
||||
export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
|
||||
static newDefault(endpoint: string) {
|
||||
return new CosmosRestClient(endpoint, DEFAULT)
|
||||
return new CosmosRestClient(endpoint, DEFAULT);
|
||||
}
|
||||
|
||||
static newStrategy(endpoint: string, chain: any) {
|
||||
// sdk version of current chain
|
||||
const ver = localStorage.getItem(`sdk_version_${chain.chainName}`) || chain.versions?.cosmosSdk
|
||||
let profile
|
||||
if(chain) {
|
||||
const ver =
|
||||
localStorage.getItem(`sdk_version_${chain.chainName}`) ||
|
||||
chain.versions?.cosmosSdk;
|
||||
let profile;
|
||||
if (chain) {
|
||||
// find by name first
|
||||
profile = findApiProfileByChain(chain.chainName)
|
||||
profile = findApiProfileByChain(chain.chainName);
|
||||
// if not found. try sdk version
|
||||
if(!profile && chain.versions?.cosmosSdk) {
|
||||
profile = findApiProfileBySDKVersion(ver)
|
||||
if (!profile && chain.versions?.cosmosSdk) {
|
||||
profile = findApiProfileBySDKVersion(ver);
|
||||
}
|
||||
}
|
||||
return new CosmosRestClient(endpoint, profile || DEFAULT, ver)
|
||||
return new CosmosRestClient(endpoint, profile || DEFAULT, ver);
|
||||
}
|
||||
|
||||
// Auth Module
|
||||
async getAuthAccounts(page?: PageRequest) {
|
||||
if(!page) page = new PageRequest()
|
||||
const query =`?${page.toQueryString()}`;
|
||||
if (!page) page = new PageRequest();
|
||||
const query = `?${page.toQueryString()}`;
|
||||
return this.request(this.registry.auth_accounts, {}, query);
|
||||
}
|
||||
async getAuthAccount(address: string) {
|
||||
@@ -92,20 +106,28 @@ export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
|
||||
async getBankDenomMetadata() {
|
||||
return this.request(this.registry.bank_denoms_metadata, {});
|
||||
}
|
||||
async getBankSupply(page?: PageRequest) {
|
||||
if(!page) page = new PageRequest()
|
||||
const query =`?${page.toQueryString()}`;
|
||||
async getBankSupply(page?: PageRequest) {
|
||||
if (!page) page = new PageRequest();
|
||||
const query = `?${page.toQueryString()}`;
|
||||
return this.request(this.registry.bank_supply, {}, query);
|
||||
}
|
||||
async getBankSupplyByDenom(denom: string) {
|
||||
let supply;
|
||||
try{
|
||||
supply = await this.request(this.registry.bank_supply_by_denom, { denom });
|
||||
} catch(err) {
|
||||
try {
|
||||
supply = await this.request(this.registry.bank_supply_by_denom, {
|
||||
denom,
|
||||
});
|
||||
} catch (err) {
|
||||
// will move this to sdk version profile later
|
||||
supply = await this.request({url: "/cosmos/bank/v1beta1/supply/by_denom?denom={denom}", adapter } as Request<{ amount: Coin }>, { denom });
|
||||
supply = await this.request(
|
||||
{
|
||||
url: '/cosmos/bank/v1beta1/supply/by_denom?denom={denom}',
|
||||
adapter,
|
||||
} as Request<{ amount: Coin }>,
|
||||
{ denom }
|
||||
);
|
||||
}
|
||||
return supply
|
||||
return supply;
|
||||
}
|
||||
// Distribution Module
|
||||
async getDistributionParams() {
|
||||
@@ -145,8 +167,8 @@ export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
|
||||
}
|
||||
// Gov
|
||||
async getParams(subspace: string, key: string) {
|
||||
console.log(this.registry.params, subspace, key)
|
||||
return this.request(this.registry.params, {subspace, key});
|
||||
console.log(this.registry.params, subspace, key);
|
||||
return this.request(this.registry.params, { subspace, key });
|
||||
}
|
||||
async getGovParamsVoting() {
|
||||
return this.request(this.registry.gov_params_voting, {});
|
||||
@@ -158,9 +180,9 @@ export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
|
||||
return this.request(this.registry.gov_params_tally, {});
|
||||
}
|
||||
async getGovProposals(status: string, page?: PageRequest) {
|
||||
if(!page) page = new PageRequest()
|
||||
page.reverse = true
|
||||
const query =`?proposal_status={status}&${page.toQueryString()}`;
|
||||
if (!page) page = new PageRequest();
|
||||
page.reverse = true;
|
||||
const query = `?proposal_status={status}&${page.toQueryString()}`;
|
||||
return this.request(this.registry.gov_proposals, { status }, query);
|
||||
}
|
||||
async getGovProposal(proposal_id: string) {
|
||||
@@ -172,22 +194,32 @@ export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
|
||||
return this.request(this.registry.gov_proposals_deposits, { proposal_id });
|
||||
}
|
||||
async getGovProposalTally(proposal_id: string) {
|
||||
return this.request(this.registry.gov_proposals_tally, { proposal_id }, undefined, (source: any) => {
|
||||
return Promise.resolve({ tally: {
|
||||
yes: source.tally.yes || source.tally.yes_count,
|
||||
abstain: source.tally.abstain || source.tally.abstain_count,
|
||||
no: source.tally.no || source.tally.no_count,
|
||||
no_with_veto: source.tally.no_with_veto || source.tally.no_with_veto_count,
|
||||
},
|
||||
return this.request(
|
||||
this.registry.gov_proposals_tally,
|
||||
{ proposal_id },
|
||||
undefined,
|
||||
(source: any) => {
|
||||
return Promise.resolve({
|
||||
tally: {
|
||||
yes: source.tally.yes || source.tally.yes_count,
|
||||
abstain: source.tally.abstain || source.tally.abstain_count,
|
||||
no: source.tally.no || source.tally.no_count,
|
||||
no_with_veto:
|
||||
source.tally.no_with_veto || source.tally.no_with_veto_count,
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
async getGovProposalVotes(proposal_id: string, page?: PageRequest) {
|
||||
if(!page) page = new PageRequest()
|
||||
page.reverse = true
|
||||
const query =`?proposal_status={status}&${page.toQueryString()}`;
|
||||
return this.request(this.registry.gov_proposals_votes, { proposal_id }, query);
|
||||
if (!page) page = new PageRequest();
|
||||
page.reverse = true;
|
||||
const query = `?proposal_status={status}&${page.toQueryString()}`;
|
||||
return this.request(
|
||||
this.registry.gov_proposals_votes,
|
||||
{ proposal_id },
|
||||
query
|
||||
);
|
||||
}
|
||||
async getGovProposalVotesVoter(proposal_id: string, voter: string) {
|
||||
return this.request(this.registry.gov_proposals_votes_voter, {
|
||||
@@ -228,17 +260,24 @@ export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
|
||||
validator_addr,
|
||||
});
|
||||
}
|
||||
async getStakingValidatorsDelegations(validator_addr: string, page?: PageRequest) {
|
||||
if(!page) {
|
||||
page = new PageRequest()
|
||||
async getStakingValidatorsDelegations(
|
||||
validator_addr: string,
|
||||
page?: PageRequest
|
||||
) {
|
||||
if (!page) {
|
||||
page = new PageRequest();
|
||||
// page.reverse = true
|
||||
page.count_total = true
|
||||
page.offset = 0
|
||||
}
|
||||
const query =`?${page.toQueryString()}`;
|
||||
return this.request(this.registry.staking_validators_delegations, {
|
||||
validator_addr,
|
||||
}, query);
|
||||
page.count_total = true;
|
||||
page.offset = 0;
|
||||
}
|
||||
const query = `?${page.toQueryString()}`;
|
||||
return this.request(
|
||||
this.registry.staking_validators_delegations,
|
||||
{
|
||||
validator_addr,
|
||||
},
|
||||
query
|
||||
);
|
||||
}
|
||||
async getStakingValidatorsDelegationsDelegator(
|
||||
validator_addr: string,
|
||||
@@ -273,22 +312,34 @@ export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
|
||||
return this.request(this.registry.base_tendermint_node_info, {});
|
||||
}
|
||||
async getBaseValidatorsetAt(height: string | number, offset: number) {
|
||||
const query = `?pagination.limit=100&pagination.offset=${offset}`
|
||||
return this.request(this.registry.base_tendermint_validatorsets_height, {
|
||||
height,
|
||||
}, query);
|
||||
const query = `?pagination.limit=100&pagination.offset=${offset}`;
|
||||
return this.request(
|
||||
this.registry.base_tendermint_validatorsets_height,
|
||||
{
|
||||
height,
|
||||
},
|
||||
query
|
||||
);
|
||||
}
|
||||
async getBaseValidatorsetLatest(offset: number) {
|
||||
const query = `?pagination.limit=100&pagination.offset=${offset}`
|
||||
return this.request(this.registry.base_tendermint_validatorsets_latest, {}, query);
|
||||
const query = `?pagination.limit=100&pagination.offset=${offset}`;
|
||||
return this.request(
|
||||
this.registry.base_tendermint_validatorsets_latest,
|
||||
{},
|
||||
query
|
||||
);
|
||||
}
|
||||
// tx
|
||||
async getTxsBySender(sender: string, page?: PageRequest) {
|
||||
if(!page) page = new PageRequest()
|
||||
if (!page) page = new PageRequest();
|
||||
|
||||
let query = `?events=message.sender='${sender}'&pagination.limit=${page.limit}&pagination.offset=${page.offset||0}`;
|
||||
let query = `?events=message.sender='${sender}'&pagination.limit=${
|
||||
page.limit
|
||||
}&pagination.offset=${page.offset || 0}`;
|
||||
if (semver.gte(this.version.replaceAll('v', ''), '0.50.0')) {
|
||||
query = `?query=message.sender='${sender}'&pagination.limit=${page.limit}&pagination.offset=${page.offset||0}`;
|
||||
query = `?query=message.sender='${sender}'&pagination.limit=${
|
||||
page.limit
|
||||
}&pagination.offset=${page.offset || 0}`;
|
||||
}
|
||||
return this.request(this.registry.tx_txs, {}, query);
|
||||
}
|
||||
@@ -297,12 +348,20 @@ export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
|
||||
// query ibc receiving msgs
|
||||
// ?&pagination.reverse=true&events=recv_packet.packet_dst_channel='${channel}'&events=recv_packet.packet_dst_port='${port}'
|
||||
async getTxs(query: string, params: any, page?: PageRequest) {
|
||||
if(!page) page = new PageRequest()
|
||||
if (!page) page = new PageRequest();
|
||||
if (semver.gte(this.version.replaceAll('v', ''), '0.50.0')) {
|
||||
let query_edit = query.replaceAll('events=', 'query=')
|
||||
return this.request(this.registry.tx_txs, params, `${query_edit}&${page.toQueryString()}`);
|
||||
} else {
|
||||
return this.request(this.registry.tx_txs, params, `${query}&${page.toQueryString()}`);
|
||||
let query_edit = query.replaceAll('events=', 'query=');
|
||||
return this.request(
|
||||
this.registry.tx_txs,
|
||||
params,
|
||||
`${query_edit}&${page.toQueryString()}`
|
||||
);
|
||||
} else {
|
||||
return this.request(
|
||||
this.registry.tx_txs,
|
||||
params,
|
||||
`${query}&${page.toQueryString()}`
|
||||
);
|
||||
}
|
||||
}
|
||||
async getTxsAt(height: string | number) {
|
||||
@@ -330,9 +389,13 @@ export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
|
||||
});
|
||||
}
|
||||
async getIBCConnections(page?: PageRequest) {
|
||||
if(!page) page = new PageRequest()
|
||||
const query =`?${page.toQueryString()}`;
|
||||
return this.request(this.registry.ibc_core_connection_connections, {}, query);
|
||||
if (!page) page = new PageRequest();
|
||||
const query = `?${page.toQueryString()}`;
|
||||
return this.request(
|
||||
this.registry.ibc_core_connection_connections,
|
||||
{},
|
||||
query
|
||||
);
|
||||
}
|
||||
async getIBCConnectionsById(connection_id: string) {
|
||||
return this.request(
|
||||
@@ -366,13 +429,24 @@ export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
|
||||
port_id,
|
||||
});
|
||||
}
|
||||
async getInterchainSecurityValidatorRotatedKey(chain_id: string, provider_address: string) {
|
||||
return this.request(this.registry.interchain_security_ccv_provider_validator_consumer_addr, {chain_id, provider_address});
|
||||
async getInterchainSecurityValidatorRotatedKey(
|
||||
chain_id: string,
|
||||
provider_address: string
|
||||
) {
|
||||
return this.request(
|
||||
this.registry.interchain_security_ccv_provider_validator_consumer_addr,
|
||||
{ chain_id, provider_address }
|
||||
);
|
||||
}
|
||||
async getInterchainSecurityProviderOptedInValidators(chain_id: string) {
|
||||
return this.request(this.registry.interchain_security_provider_opted_in_validators, {chain_id});
|
||||
return this.request(
|
||||
this.registry.interchain_security_provider_opted_in_validators,
|
||||
{ chain_id }
|
||||
);
|
||||
}
|
||||
async getInterchainSecurityConsumerValidators(chain_id: string) {
|
||||
return this.request(this.registry.interchain_security_consumer_validators, {chain_id});
|
||||
return this.request(this.registry.interchain_security_consumer_validators, {
|
||||
chain_id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -29,11 +29,15 @@ try {
|
||||
}
|
||||
// */
|
||||
export async function get(url: string) {
|
||||
return (await fetch(url, {referrerPolicy: 'origin-when-cross-origin'})).json();
|
||||
return (
|
||||
await fetch(url, { referrerPolicy: 'origin-when-cross-origin' })
|
||||
).json();
|
||||
}
|
||||
|
||||
export async function getB(url: string) {
|
||||
return (await fetch(url, {referrerPolicy: 'origin-when-cross-origin'})).arrayBuffer();
|
||||
return (
|
||||
await fetch(url, { referrerPolicy: 'origin-when-cross-origin' })
|
||||
).arrayBuffer();
|
||||
}
|
||||
|
||||
export async function post(url: string, data: any) {
|
||||
|
||||
+18
-17
@@ -67,10 +67,11 @@ export function formatTokenAmount(
|
||||
tokenDenom = 'uatom',
|
||||
format = true
|
||||
) {
|
||||
const denom = typeof tokenDenom === 'string'
|
||||
? tokenDenom
|
||||
// @ts-ignore
|
||||
: tokenDenom?.denom_trace?.base_denom;
|
||||
const denom =
|
||||
typeof tokenDenom === 'string'
|
||||
? tokenDenom
|
||||
: // @ts-ignore
|
||||
tokenDenom?.denom_trace?.base_denom;
|
||||
let amount = 0;
|
||||
const asset = assets.find((a: any) => a.base === denom);
|
||||
let exp = asset
|
||||
@@ -120,24 +121,24 @@ export function isHexAddress(v: any) {
|
||||
}
|
||||
|
||||
export function isBech32Address(v?: string) {
|
||||
if(!v) return ""
|
||||
const pattern = /^[a-z\d]+1[a-z\d]{38}$/g
|
||||
return String(v).search(pattern) > -1
|
||||
if (!v) return '';
|
||||
const pattern = /^[a-z\d]+1[a-z\d]{38}$/g;
|
||||
return String(v).search(pattern) > -1;
|
||||
}
|
||||
|
||||
export function formatSeconds(value?: string) {
|
||||
if(!value) return ''
|
||||
const duration = Number(value.replace(/s/, ''))
|
||||
if(duration > 24*60*60) {
|
||||
return `${(duration / ( 24 * 60 * 60)).toFixed()} days`
|
||||
if (!value) return '';
|
||||
const duration = Number(value.replace(/s/, ''));
|
||||
if (duration > 24 * 60 * 60) {
|
||||
return `${(duration / (24 * 60 * 60)).toFixed()} days`;
|
||||
}
|
||||
if(duration > 60*60) {
|
||||
return `${(duration / (60 * 60)).toFixed()} hours`
|
||||
}
|
||||
if(duration > 60) {
|
||||
return `${duration / 60} mins`
|
||||
if (duration > 60 * 60) {
|
||||
return `${(duration / (60 * 60)).toFixed()} hours`;
|
||||
}
|
||||
return value
|
||||
if (duration > 60) {
|
||||
return `${duration / 60} mins`;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export function hexToRgb(hex: string) {
|
||||
|
||||
@@ -81,13 +81,15 @@ const totalValue = computed(() => {
|
||||
});
|
||||
unbonding.value?.forEach((x) => {
|
||||
x.entries?.forEach((y) => {
|
||||
value += format.tokenValueNumber({amount: y.balance, denom: stakingStore.params.bond_denom});
|
||||
value += format.tokenValueNumber({
|
||||
amount: y.balance,
|
||||
denom: stakingStore.params.bond_denom,
|
||||
});
|
||||
});
|
||||
});
|
||||
return format.formatNumber(value, '0,0.00');
|
||||
});
|
||||
|
||||
|
||||
function loadAccount(address: string) {
|
||||
blockchain.rpc.getAuthAccount(address).then((x) => {
|
||||
account.value = x.account;
|
||||
@@ -113,7 +115,7 @@ function loadAccount(address: string) {
|
||||
});
|
||||
});
|
||||
|
||||
const receivedQuery = `?&pagination.reverse=true&events=coin_received.receiver='${address}'&pagination.limit=5`;
|
||||
const receivedQuery = `?&pagination.reverse=true&events=coin_received.receiver='${address}'&pagination.limit=5`;
|
||||
blockchain.rpc.getTxs(receivedQuery, {}).then((x) => {
|
||||
recentReceived.value = x.tx_responses;
|
||||
});
|
||||
@@ -123,11 +125,16 @@ function updateEvent() {
|
||||
loadAccount(props.address);
|
||||
}
|
||||
|
||||
function mapAmount(events:{type: string, attributes: {key: string, value: string}[]}[]) {
|
||||
if(!events) return []
|
||||
return events.find(x => x.type==='coin_received')?.attributes
|
||||
.filter(x => x.key === 'YW1vdW50'|| x.key === `amount`)
|
||||
.map(x => x.key==='amount'? x.value : String.fromCharCode(...fromBase64(x.value)))
|
||||
function mapAmount(
|
||||
events: { type: string; attributes: { key: string; value: string }[] }[]
|
||||
) {
|
||||
if (!events) return [];
|
||||
return events
|
||||
.find((x) => x.type === 'coin_received')
|
||||
?.attributes.filter((x) => x.key === 'YW1vdW50' || x.key === `amount`)
|
||||
.map((x) =>
|
||||
x.key === 'amount' ? x.value : String.fromCharCode(...fromBase64(x.value))
|
||||
);
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
@@ -164,33 +171,33 @@ function mapAmount(events:{type: string, attributes: {key: string, value: string
|
||||
<h2 class="card-title mb-4">{{ $t('account.assets') }}</h2>
|
||||
<!-- button -->
|
||||
<div class="flex justify-end mb-4 pr-5">
|
||||
<label
|
||||
for="send"
|
||||
class="btn btn-primary btn-sm mr-2"
|
||||
@click="dialog.open('send', {}, updateEvent)"
|
||||
>{{ $t('account.btn_send') }}</label
|
||||
>
|
||||
<label
|
||||
for="transfer"
|
||||
class="btn btn-primary btn-sm"
|
||||
@click="
|
||||
dialog.open(
|
||||
'transfer',
|
||||
{
|
||||
chain_name: blockchain.current?.prettyName,
|
||||
},
|
||||
updateEvent
|
||||
)
|
||||
"
|
||||
>{{ $t('account.btn_transfer') }}</label
|
||||
>
|
||||
</div>
|
||||
<label
|
||||
for="send"
|
||||
class="btn btn-primary btn-sm mr-2"
|
||||
@click="dialog.open('send', {}, updateEvent)"
|
||||
>{{ $t('account.btn_send') }}</label
|
||||
>
|
||||
<label
|
||||
for="transfer"
|
||||
class="btn btn-primary btn-sm"
|
||||
@click="
|
||||
dialog.open(
|
||||
'transfer',
|
||||
{
|
||||
chain_name: blockchain.current?.prettyName,
|
||||
},
|
||||
updateEvent
|
||||
)
|
||||
"
|
||||
>{{ $t('account.btn_transfer') }}</label
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid md:!grid-cols-3">
|
||||
<div class="md:!col-span-1">
|
||||
<DonutChart :series="totalAmountByCategory" :labels="labels" />
|
||||
</div>
|
||||
<div class="mt-4 md:!col-span-2 md:!mt-0 md:!ml-4">
|
||||
<div class="mt-4 md:!col-span-2 md:!mt-0 md:!ml-4">
|
||||
<!-- list-->
|
||||
<div class="">
|
||||
<!--balances -->
|
||||
@@ -221,7 +228,7 @@ function mapAmount(events:{type: string, attributes: {key: string, value: string
|
||||
<span
|
||||
class="inset-x-0 inset-y-0 opacity-10 absolute bg-primary dark:invert text-sm"
|
||||
></span>
|
||||
${{ format.tokenValue(balanceItem) }}
|
||||
${{ format.tokenValue(balanceItem) }}
|
||||
</div>
|
||||
</div>
|
||||
<!--delegations -->
|
||||
@@ -257,7 +264,7 @@ function mapAmount(events:{type: string, attributes: {key: string, value: string
|
||||
<span
|
||||
class="inset-x-0 inset-y-0 opacity-10 absolute bg-primary dark:invert text-sm"
|
||||
></span>
|
||||
${{ format.tokenValue(delegationItem?.balance) }}
|
||||
${{ format.tokenValue(delegationItem?.balance) }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- rewards.total -->
|
||||
@@ -282,15 +289,17 @@ function mapAmount(events:{type: string, attributes: {key: string, value: string
|
||||
<div class="text-sm font-semibold">
|
||||
{{ format.formatToken(rewardItem) }}
|
||||
</div>
|
||||
<div class="text-xs">{{ format.calculatePercent(rewardItem.amount, totalAmount) }}</div>
|
||||
<div class="text-xs">
|
||||
{{ format.calculatePercent(rewardItem.amount, totalAmount) }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="text-xs truncate relative py-1 px-3 rounded-full w-fit text-primary dark:invert mr-2"
|
||||
>
|
||||
<span
|
||||
class="inset-x-0 inset-y-0 opacity-10 absolute bg-primary dark:invert text-sm"
|
||||
></span>${{ format.tokenValue(rewardItem) }}
|
||||
|
||||
class="inset-x-0 inset-y-0 opacity-10 absolute bg-primary dark:invert text-sm"
|
||||
></span
|
||||
>${{ format.tokenValue(rewardItem) }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- mdi-account-arrow-right -->
|
||||
@@ -323,16 +332,21 @@ function mapAmount(events:{type: string, attributes: {key: string, value: string
|
||||
<div
|
||||
class="text-xs truncate relative py-1 px-3 rounded-full w-fit text-primary dark:invert mr-2"
|
||||
>
|
||||
<span class="inset-x-0 inset-y-0 opacity-10 absolute bg-primary dark:invert"></span>
|
||||
${{format.tokenValue({
|
||||
amount: String(unbondingTotal),
|
||||
denom: stakingStore.params.bond_denom,
|
||||
})
|
||||
}}
|
||||
<span
|
||||
class="inset-x-0 inset-y-0 opacity-10 absolute bg-primary dark:invert"
|
||||
></span>
|
||||
${{
|
||||
format.tokenValue({
|
||||
amount: String(unbondingTotal),
|
||||
denom: stakingStore.params.bond_denom,
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 text-lg font-semibold mr-5 pl-5 border-t pt-4 text-right">
|
||||
<div
|
||||
class="mt-4 text-lg font-semibold mr-5 pl-5 border-t pt-4 text-right"
|
||||
>
|
||||
{{ $t('account.total_value') }}: ${{ totalValue }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -369,13 +383,21 @@ function mapAmount(events:{type: string, attributes: {key: string, value: string
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-sm">
|
||||
<tr v-if="delegations.length === 0"><td colspan="10"><div class="text-center">{{ $t('account.no_delegations') }}</div></td></tr>
|
||||
<tr v-if="delegations.length === 0">
|
||||
<td colspan="10">
|
||||
<div class="text-center">
|
||||
{{ $t('account.no_delegations') }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-for="(v, index) in delegations" :key="index">
|
||||
<td class="text-caption text-primary py-3">
|
||||
<RouterLink
|
||||
:to="`/${chain}/staking/${v.delegation.validator_address}`"
|
||||
>{{
|
||||
format.validatorFromBech32(v.delegation.validator_address) || v.delegation.validator_address
|
||||
format.validatorFromBech32(
|
||||
v.delegation.validator_address
|
||||
) || v.delegation.validator_address
|
||||
}}</RouterLink
|
||||
>
|
||||
</td>
|
||||
@@ -461,47 +483,52 @@ function mapAmount(events:{type: string, attributes: {key: string, value: string
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-sm" v-for="(v, index) in unbonding" :key="index">
|
||||
<tr>
|
||||
<td class="text-caption text-primary py-3 bg-slate-200" colspan="10">
|
||||
<RouterLink
|
||||
:to="`/${chain}/staking/${v.validator_address}`"
|
||||
>{{
|
||||
v.validator_address
|
||||
}}</RouterLink
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-for="entry in v.entries">
|
||||
<td class="py-3">{{ entry.creation_height }}</td>
|
||||
<td class="py-3">
|
||||
{{
|
||||
format.formatToken(
|
||||
{
|
||||
amount: entry.initial_balance,
|
||||
denom: stakingStore.params.bond_denom,
|
||||
},
|
||||
true,
|
||||
'0,0.[00]'
|
||||
)
|
||||
}}
|
||||
</td>
|
||||
<td class="py-3">
|
||||
{{
|
||||
format.formatToken(
|
||||
{
|
||||
amount: entry.balance,
|
||||
denom: stakingStore.params.bond_denom,
|
||||
},
|
||||
true,
|
||||
'0,0.[00]'
|
||||
)
|
||||
}}
|
||||
</td>
|
||||
<td class="py-3">
|
||||
<Countdown :time="new Date(entry.completion_time).getTime() - new Date().getTime()" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tr>
|
||||
<td
|
||||
class="text-caption text-primary py-3 bg-slate-200"
|
||||
colspan="10"
|
||||
>
|
||||
<RouterLink :to="`/${chain}/staking/${v.validator_address}`">{{
|
||||
v.validator_address
|
||||
}}</RouterLink>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-for="entry in v.entries">
|
||||
<td class="py-3">{{ entry.creation_height }}</td>
|
||||
<td class="py-3">
|
||||
{{
|
||||
format.formatToken(
|
||||
{
|
||||
amount: entry.initial_balance,
|
||||
denom: stakingStore.params.bond_denom,
|
||||
},
|
||||
true,
|
||||
'0,0.[00]'
|
||||
)
|
||||
}}
|
||||
</td>
|
||||
<td class="py-3">
|
||||
{{
|
||||
format.formatToken(
|
||||
{
|
||||
amount: entry.balance,
|
||||
denom: stakingStore.params.bond_denom,
|
||||
},
|
||||
true,
|
||||
'0,0.[00]'
|
||||
)
|
||||
}}
|
||||
</td>
|
||||
<td class="py-3">
|
||||
<Countdown
|
||||
:time="
|
||||
new Date(entry.completion_time).getTime() -
|
||||
new Date().getTime()
|
||||
"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -520,15 +547,26 @@ function mapAmount(events:{type: string, attributes: {key: string, value: string
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-sm">
|
||||
<tr v-if="txs.length === 0"><td colspan="10"><div class="text-center">{{ $t('account.no_transactions') }}</div></td></tr>
|
||||
<tr v-if="txs.length === 0">
|
||||
<td colspan="10">
|
||||
<div class="text-center">
|
||||
{{ $t('account.no_transactions') }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-for="(v, index) in txs" :key="index">
|
||||
<td class="text-sm py-3">
|
||||
<RouterLink :to="`/${chain}/block/${v.height}`" class="text-primary dark:invert">{{
|
||||
v.height
|
||||
}}</RouterLink>
|
||||
<RouterLink
|
||||
:to="`/${chain}/block/${v.height}`"
|
||||
class="text-primary dark:invert"
|
||||
>{{ v.height }}</RouterLink
|
||||
>
|
||||
</td>
|
||||
<td class="truncate py-3" style="max-width: 200px">
|
||||
<RouterLink :to="`/${chain}/tx/${v.txhash}`" class="text-primary dark:invert">
|
||||
<RouterLink
|
||||
:to="`/${chain}/tx/${v.txhash}`"
|
||||
class="text-primary dark:invert"
|
||||
>
|
||||
{{ v.txhash }}
|
||||
</RouterLink>
|
||||
</td>
|
||||
@@ -543,7 +581,12 @@ function mapAmount(events:{type: string, attributes: {key: string, value: string
|
||||
/>
|
||||
<Icon v-else icon="mdi-multiply" class="text-error text-lg" />
|
||||
</td>
|
||||
<td class="py-3">{{ format.toLocaleDate(v.timestamp) }} <span class=" text-xs">({{ format.toDay(v.timestamp, 'from') }})</span> </td>
|
||||
<td class="py-3">
|
||||
{{ format.toLocaleDate(v.timestamp) }}
|
||||
<span class="text-xs"
|
||||
>({{ format.toDay(v.timestamp, 'from') }})</span
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -564,21 +607,32 @@ function mapAmount(events:{type: string, attributes: {key: string, value: string
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-sm">
|
||||
<tr v-if="recentReceived.length === 0"><td colspan="10"><div class="text-center">{{ $t('account.no_transactions') }}</div></td></tr>
|
||||
<tr v-if="recentReceived.length === 0">
|
||||
<td colspan="10">
|
||||
<div class="text-center">
|
||||
{{ $t('account.no_transactions') }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-for="(v, index) in recentReceived" :key="index">
|
||||
<td class="text-sm py-3">
|
||||
<RouterLink :to="`/${chain}/block/${v.height}`" class="text-primary dark:invert">{{
|
||||
v.height
|
||||
}}</RouterLink>
|
||||
<RouterLink
|
||||
:to="`/${chain}/block/${v.height}`"
|
||||
class="text-primary dark:invert"
|
||||
>{{ v.height }}</RouterLink
|
||||
>
|
||||
</td>
|
||||
<td class="truncate py-3" style="max-width: 200px">
|
||||
<RouterLink :to="`/${chain}/tx/${v.txhash}`" class="text-primary dark:invert">
|
||||
<RouterLink
|
||||
:to="`/${chain}/tx/${v.txhash}`"
|
||||
class="text-primary dark:invert"
|
||||
>
|
||||
{{ v.txhash }}
|
||||
</RouterLink>
|
||||
</td>
|
||||
<td class="flex items-center py-3">
|
||||
<div class="mr-2">
|
||||
{{ mapAmount(v.events)?.join(", ")}}
|
||||
{{ mapAmount(v.events)?.join(', ') }}
|
||||
</div>
|
||||
<Icon
|
||||
v-if="v.code === 0"
|
||||
@@ -587,7 +641,12 @@ function mapAmount(events:{type: string, attributes: {key: string, value: string
|
||||
/>
|
||||
<Icon v-else icon="mdi-multiply" class="text-error text-lg" />
|
||||
</td>
|
||||
<td class="py-3">{{ format.toLocaleDate(v.timestamp) }} <span class=" text-xs">({{ format.toDay(v.timestamp, 'from') }})</span> </td>
|
||||
<td class="py-3">
|
||||
{{ format.toLocaleDate(v.timestamp) }}
|
||||
<span class="text-xs"
|
||||
>({{ format.toDay(v.timestamp, 'from') }})</span
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -6,72 +6,79 @@ import { onMounted } from 'vue';
|
||||
import PaginationBar from '@/components/PaginationBar.vue';
|
||||
const props = defineProps(['chain']);
|
||||
|
||||
const chainStore = useBlockchain()
|
||||
const chainStore = useBlockchain();
|
||||
|
||||
const accounts = ref([] as AuthAccount[])
|
||||
const pageRequest = ref(new PageRequest())
|
||||
const pageResponse = ref({} as Pagination)
|
||||
const accounts = ref([] as AuthAccount[]);
|
||||
const pageRequest = ref(new PageRequest());
|
||||
const pageResponse = ref({} as Pagination);
|
||||
|
||||
onMounted(() => {
|
||||
pageload(1)
|
||||
pageload(1);
|
||||
});
|
||||
|
||||
function pageload(p: number) {
|
||||
pageRequest.value.setPage(p)
|
||||
chainStore.rpc.getAuthAccounts(pageRequest.value).then(x => {
|
||||
accounts.value = x.accounts
|
||||
pageResponse.value = x.pagination
|
||||
pageRequest.value.setPage(p);
|
||||
chainStore.rpc.getAuthAccounts(pageRequest.value).then((x) => {
|
||||
accounts.value = x.accounts;
|
||||
pageResponse.value = x.pagination;
|
||||
});
|
||||
}
|
||||
|
||||
function showType(v: string) {
|
||||
return v.replace("/cosmos.auth.v1beta1.", "")
|
||||
return v.replace('/cosmos.auth.v1beta1.', '');
|
||||
}
|
||||
function findField(v: any, field: string) {
|
||||
if(!v || Array.isArray(v) || typeof v === 'string') return null
|
||||
const fields = Object.keys(v)
|
||||
if(fields.includes(field)) {
|
||||
return v[field]
|
||||
}
|
||||
for(let i= 0; i < fields.length; i++) {
|
||||
const re: any = findField(v[fields[i]], field)
|
||||
if(re) return re
|
||||
}
|
||||
if (!v || Array.isArray(v) || typeof v === 'string') return null;
|
||||
const fields = Object.keys(v);
|
||||
if (fields.includes(field)) {
|
||||
return v[field];
|
||||
}
|
||||
for (let i = 0; i < fields.length; i++) {
|
||||
const re: any = findField(v[fields[i]], field);
|
||||
if (re) return re;
|
||||
}
|
||||
}
|
||||
function showAddress(v: any) {
|
||||
return findField(v, 'address')
|
||||
return findField(v, 'address');
|
||||
}
|
||||
function showAccountNumber(v: any) {
|
||||
return findField(v, 'account_number')
|
||||
return findField(v, 'account_number');
|
||||
}
|
||||
function showSequence(v: any) {
|
||||
return findField(v, 'sequence')
|
||||
return findField(v, 'sequence');
|
||||
}
|
||||
function showPubkey(v: any) {
|
||||
return findField(v, 'pub_key')
|
||||
return findField(v, 'pub_key');
|
||||
}
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class=" overflow-x-auto">
|
||||
<table class="table table-compact">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>{{ $t('account.type') }}</td>
|
||||
<td>{{ $t('account.address') }}</td>
|
||||
<td>{{ $t('account.acc_num') }}</td>
|
||||
<td>{{ $t('account.sequence') }}</td>
|
||||
<td>{{ $t('account.pub_key') }}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="acc in accounts">
|
||||
<td>{{ showType(acc['@type']) }}</td>
|
||||
<td><RouterLink :to="`/${chain}/account/${showAddress(acc)}`">{{ showAddress(acc) }}</RouterLink></td>
|
||||
<td>{{ showAccountNumber(acc) }}</td>
|
||||
<td>{{ showSequence(acc) }}</td>
|
||||
<td>{{ showPubkey(acc) }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<PaginationBar :limit="pageRequest.limit" :total="pageResponse.total" :callback="pageload" />
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-compact">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>{{ $t('account.type') }}</td>
|
||||
<td>{{ $t('account.address') }}</td>
|
||||
<td>{{ $t('account.acc_num') }}</td>
|
||||
<td>{{ $t('account.sequence') }}</td>
|
||||
<td>{{ $t('account.pub_key') }}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="acc in accounts">
|
||||
<td>{{ showType(acc['@type']) }}</td>
|
||||
<td>
|
||||
<RouterLink :to="`/${chain}/account/${showAddress(acc)}`">{{
|
||||
showAddress(acc)
|
||||
}}</RouterLink>
|
||||
</td>
|
||||
<td>{{ showAccountNumber(acc) }}</td>
|
||||
<td>{{ showSequence(acc) }}</td>
|
||||
<td>{{ showPubkey(acc) }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<PaginationBar
|
||||
:limit="pageRequest.limit"
|
||||
:total="pageResponse.total"
|
||||
:callback="pageload"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -12,9 +12,9 @@ import Countdown from '@/components/Countdown.vue';
|
||||
const props = defineProps(['height', 'chain']);
|
||||
|
||||
const store = useBaseStore();
|
||||
const format = useFormatter()
|
||||
const current = ref({} as Block)
|
||||
const target = ref(Number(props.height || 0))
|
||||
const format = useFormatter();
|
||||
const current = ref({} as Block);
|
||||
const target = ref(Number(props.height || 0));
|
||||
|
||||
const height = computed(() => {
|
||||
return Number(current.value.block?.header?.height || props.height || 0);
|
||||
@@ -22,40 +22,41 @@ const height = computed(() => {
|
||||
|
||||
const isFutureBlock = computed({
|
||||
get: () => {
|
||||
const latest = store.latest?.block?.header.height
|
||||
const isFuture = latest ? target.value > Number(latest) : true
|
||||
if (!isFuture && !current.value.block_id) store.fetchBlock(target.value).then(x => current.value = x)
|
||||
return isFuture
|
||||
const latest = store.latest?.block?.header.height;
|
||||
const isFuture = latest ? target.value > Number(latest) : true;
|
||||
if (!isFuture && !current.value.block_id)
|
||||
store.fetchBlock(target.value).then((x) => (current.value = x));
|
||||
return isFuture;
|
||||
},
|
||||
set: val => {
|
||||
console.log(val)
|
||||
}
|
||||
})
|
||||
set: (val) => {
|
||||
console.log(val);
|
||||
},
|
||||
});
|
||||
|
||||
const remainingBlocks = computed(() => {
|
||||
const latest = store.latest?.block?.header.height
|
||||
return latest ? Number(target.value) - Number(latest) : 0
|
||||
})
|
||||
const latest = store.latest?.block?.header.height;
|
||||
return latest ? Number(target.value) - Number(latest) : 0;
|
||||
});
|
||||
|
||||
const estimateTime = computed(() => {
|
||||
const seconds = Number((remainingBlocks.value * store.blocktime).toFixed(2))
|
||||
return seconds
|
||||
})
|
||||
const seconds = Number((remainingBlocks.value * store.blocktime).toFixed(2));
|
||||
return seconds;
|
||||
});
|
||||
|
||||
const estimateDate = computed(() => {
|
||||
return new Date(new Date().getTime() + estimateTime.value)
|
||||
})
|
||||
return new Date(new Date().getTime() + estimateTime.value);
|
||||
});
|
||||
|
||||
const edit = ref(false)
|
||||
const newHeight = ref(props.height)
|
||||
const edit = ref(false);
|
||||
const newHeight = ref(props.height);
|
||||
function updateTarget() {
|
||||
target.value = Number(newHeight.value)
|
||||
console.log(target.value)
|
||||
target.value = Number(newHeight.value);
|
||||
console.log(target.value);
|
||||
}
|
||||
|
||||
onBeforeRouteUpdate(async (to, from, next) => {
|
||||
if (from.path !== to.path) {
|
||||
store.fetchBlock(String(to.params.height)).then(x => current.value = x);
|
||||
store.fetchBlock(String(to.params.height)).then((x) => (current.value = x));
|
||||
next();
|
||||
}
|
||||
});
|
||||
@@ -66,29 +67,48 @@ onBeforeRouteUpdate(async (to, from, next) => {
|
||||
<div v-if="remainingBlocks > 0">
|
||||
<div class="text-primary font-bold text-lg my-10">#{{ target }}</div>
|
||||
<Countdown :time="estimateTime" css="md:!text-5xl font-sans md:mx-5" />
|
||||
<div class="my-5">{{ $t('block.estimated_time') }}: <span class="text-xl font-bold">{{ format.toLocaleDate(estimateDate) }}</span>
|
||||
<div class="my-5">
|
||||
{{ $t('block.estimated_time') }}:
|
||||
<span class="text-xl font-bold">{{
|
||||
format.toLocaleDate(estimateDate)
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="pt-10 flex justify-center">
|
||||
<table class="table w-max rounded-lg bg-base-100">
|
||||
<tbody>
|
||||
<tr class="hover cursor-pointer" @click="edit = !edit">
|
||||
<td>{{ $t('block.countdown_for_block') }}:</td>
|
||||
<td class="text-right"><span class="md:!ml-40">{{ target }}</span></td>
|
||||
<td class="text-right">
|
||||
<span class="md:!ml-40">{{ target }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="edit">
|
||||
<td colspan="2" class="text-center">
|
||||
<h3 class="text-lg font-bold">{{ $t('block.countdown_for_block_input') }}</h3>
|
||||
<p class="py-4">
|
||||
<div class="join">
|
||||
<input class="input input-bordered join-item" v-model="newHeight" type="number" />
|
||||
<button class="btn btn-primary join-item" @click="updateTarget()">{{ $t('block.btn_update') }}</button>
|
||||
<h3 class="text-lg font-bold">
|
||||
{{ $t('block.countdown_for_block_input') }}
|
||||
</h3>
|
||||
<div class="py-4">
|
||||
<div class="join">
|
||||
<input
|
||||
class="input input-bordered join-item"
|
||||
v-model="newHeight"
|
||||
type="number"
|
||||
/>
|
||||
<button
|
||||
class="btn btn-primary join-item"
|
||||
@click="updateTarget()"
|
||||
>
|
||||
{{ $t('block.btn_update') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('block.current_height') }}:</td>
|
||||
<td class="text-right">#{{ store.latest?.block?.header.height }}</td>
|
||||
<td class="text-right">
|
||||
#{{ store.latest?.block?.header.height }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('block.remaining_blocks') }}:</td>
|
||||
@@ -96,12 +116,13 @@ onBeforeRouteUpdate(async (to, from, next) => {
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('block.average_block_time') }}:</td>
|
||||
<td class="text-right">{{ (store.blocktime / 1000).toFixed(1) }}s</td>
|
||||
<td class="text-right">
|
||||
{{ (store.blocktime / 1000).toFixed(1) }}s
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
@@ -109,12 +130,16 @@ onBeforeRouteUpdate(async (to, from, next) => {
|
||||
<h2 class="card-title flex flex-row justify-between">
|
||||
<p class="">#{{ current.block?.header?.height }}</p>
|
||||
<div class="flex" v-if="props.height">
|
||||
<RouterLink :to="`/${store.blockchain.chainName}/block/${height - 1}`"
|
||||
class="btn btn-primary btn-sm p-1 text-2xl mr-2">
|
||||
<RouterLink
|
||||
:to="`/${store.blockchain.chainName}/block/${height - 1}`"
|
||||
class="btn btn-primary btn-sm p-1 text-2xl mr-2"
|
||||
>
|
||||
<Icon icon="mdi-arrow-left" class="w-full h-full" />
|
||||
</RouterLink>
|
||||
<RouterLink :to="`/${store.blockchain.chainName}/block/${height + 1}`"
|
||||
class="btn btn-primary btn-sm p-1 text-2xl">
|
||||
<RouterLink
|
||||
:to="`/${store.blockchain.chainName}/block/${height + 1}`"
|
||||
class="btn btn-primary btn-sm p-1 text-2xl"
|
||||
>
|
||||
<Icon icon="mdi-arrow-right" class="w-full h-full" />
|
||||
</RouterLink>
|
||||
</div>
|
||||
@@ -125,18 +150,25 @@ onBeforeRouteUpdate(async (to, from, next) => {
|
||||
</div>
|
||||
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<h2 class="card-title flex flex-row justify-between">{{ $t('block.block_header') }}</h2>
|
||||
<h2 class="card-title flex flex-row justify-between">
|
||||
{{ $t('block.block_header') }}
|
||||
</h2>
|
||||
<DynamicComponent :value="current.block?.header" />
|
||||
</div>
|
||||
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<h2 class="card-title flex flex-row justify-between">{{ $t('account.transactions') }}</h2>
|
||||
<h2 class="card-title flex flex-row justify-between">
|
||||
{{ $t('account.transactions') }}
|
||||
</h2>
|
||||
<TxsElement :value="current.block?.data?.txs" />
|
||||
</div>
|
||||
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded shadow">
|
||||
<h2 class="card-title flex flex-row justify-between">{{ $t('block.last_commit') }}</h2>
|
||||
<h2 class="card-title flex flex-row justify-between">
|
||||
{{ $t('block.last_commit') }}
|
||||
</h2>
|
||||
<DynamicComponent :value="current.block?.last_commit" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div></template>
|
||||
</template>
|
||||
|
||||
@@ -7,52 +7,67 @@ const props = defineProps(['chain']);
|
||||
|
||||
const tab = ref('blocks');
|
||||
|
||||
const base = useBaseStore()
|
||||
const base = useBaseStore();
|
||||
|
||||
const format = useFormatter();
|
||||
|
||||
const list = computed(() => {
|
||||
// const recents = base.recents
|
||||
// return recents.sort((a, b) => (Number(b.block.header.height) - Number(a.block.header.height)))
|
||||
return base.recents
|
||||
})
|
||||
// const recents = base.recents
|
||||
// return recents.sort((a, b) => (Number(b.block.header.height) - Number(a.block.header.height)))
|
||||
return base.recents;
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div class="tabs tabs-boxed bg-transparent mb-4">
|
||||
<a class="tab text-gray-400 uppercase" :class="{ 'tab-active': tab === 'blocks' }"
|
||||
@click="tab = 'blocks'">{{ $t('block.recent') }}</a>
|
||||
<RouterLink class="tab text-gray-400 uppercase"
|
||||
:to="`/${chain}/block/${Number(base.latest?.block?.header.height||0) + 10000}`"
|
||||
>{{ $t('block.future') }}</RouterLink>
|
||||
</div>
|
||||
|
||||
<div v-show="tab === 'blocks'">
|
||||
|
||||
<TxsInBlocksChart />
|
||||
|
||||
<div class="grid xl:!grid-cols-6 md:!grid-cols-4 grid-cols-1 gap-3">
|
||||
<RouterLink v-for="item in list"
|
||||
class="flex flex-col justify-between rounded p-4 shadow bg-base-100"
|
||||
:to="`/${chain}/block/${item.block.header.height}`">
|
||||
<div class="flex justify-between">
|
||||
<h3 class="text-md font-bold sm:!text-lg">
|
||||
{{ item.block.header.height }}
|
||||
</h3>
|
||||
<span class="rounded text-xs whitespace-nowrap font-medium text-green-600">
|
||||
{{ format.toDay(item.block?.header?.time, 'from') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex justify-between tooltip" data-tip="Block Proposor">
|
||||
<div class="mt-2 hidden text-sm sm:!block truncate">
|
||||
<span>{{ format.validator(item.block?.header?.proposer_address) }}</span>
|
||||
</div>
|
||||
<span class="text-right mt-1 whitespace-nowrap"> {{ item.block?.data?.txs.length }} txs </span>
|
||||
</div>
|
||||
</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="tabs tabs-boxed bg-transparent mb-4">
|
||||
<a
|
||||
class="tab text-gray-400 uppercase"
|
||||
:class="{ 'tab-active': tab === 'blocks' }"
|
||||
@click="tab = 'blocks'"
|
||||
>{{ $t('block.recent') }}</a
|
||||
>
|
||||
<RouterLink
|
||||
class="tab text-gray-400 uppercase"
|
||||
:to="`/${chain}/block/${
|
||||
Number(base.latest?.block?.header.height || 0) + 10000
|
||||
}`"
|
||||
>{{ $t('block.future') }}</RouterLink
|
||||
>
|
||||
</div>
|
||||
|
||||
<div v-show="tab === 'blocks'">
|
||||
<TxsInBlocksChart />
|
||||
|
||||
<div class="grid xl:!grid-cols-6 md:!grid-cols-4 grid-cols-1 gap-3">
|
||||
<RouterLink
|
||||
v-for="item in list"
|
||||
class="flex flex-col justify-between rounded p-4 shadow bg-base-100"
|
||||
:to="`/${chain}/block/${item.block.header.height}`"
|
||||
>
|
||||
<div class="flex justify-between">
|
||||
<h3 class="text-md font-bold sm:!text-lg">
|
||||
{{ item.block.header.height }}
|
||||
</h3>
|
||||
<span
|
||||
class="rounded text-xs whitespace-nowrap font-medium text-green-600"
|
||||
>
|
||||
{{ format.toDay(item.block?.header?.time, 'from') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex justify-between tooltip" data-tip="Block Proposor">
|
||||
<div class="mt-2 hidden text-sm sm:!block truncate">
|
||||
<span>{{
|
||||
format.validator(item.block?.header?.proposer_address)
|
||||
}}</span>
|
||||
</div>
|
||||
<span class="text-right mt-1 whitespace-nowrap">
|
||||
{{ item.block?.data?.txs.length }} txs
|
||||
</span>
|
||||
</div>
|
||||
</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<route>
|
||||
|
||||
@@ -30,7 +30,7 @@ onMounted(async () => {
|
||||
rpc.value = rpcList.value[0].address + '/consensus_state';
|
||||
await fetchPosition();
|
||||
update();
|
||||
clearTime()
|
||||
clearTime();
|
||||
timer = setInterval(() => {
|
||||
update();
|
||||
}, 6000);
|
||||
@@ -80,7 +80,7 @@ function color(i: number, txt: string) {
|
||||
}
|
||||
return txt === 'nil-Vote' ? 'gray-700' : 'success';
|
||||
}
|
||||
async function onChange () {
|
||||
async function onChange() {
|
||||
httpstatus.value = 200;
|
||||
httpStatusText.value = '';
|
||||
roundState.value = {};
|
||||
@@ -170,7 +170,9 @@ async function update() {
|
||||
{{ item?.address }}/consensus_state
|
||||
</option>
|
||||
</select>
|
||||
<button class="btn btn-primary" @click="onChange">{{ $t('consensus.monitor') }}</button>
|
||||
<button class="btn btn-primary" @click="onChange">
|
||||
{{ $t('consensus.monitor') }}
|
||||
</button>
|
||||
</label>
|
||||
</div>
|
||||
<div v-if="httpstatus !== 200" class="text-error mt-1">
|
||||
@@ -191,7 +193,9 @@ async function update() {
|
||||
<div
|
||||
class="bg-rose-100 text-neutral-content rounded-full w-12 h-12"
|
||||
>
|
||||
<span class="text-2xl text-error font-semibold">{{ $t('consensus.o') }}</span>
|
||||
<span class="text-2xl text-error font-semibold">{{
|
||||
$t('consensus.o')
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -207,7 +211,9 @@ async function update() {
|
||||
<div
|
||||
class="bg-green-100 text-neutral-content rounded-full w-12 h-12"
|
||||
>
|
||||
<span class="text-2xl text-success font-semibold">{{ $t('consensus.h') }}</span>
|
||||
<span class="text-2xl text-success font-semibold">{{
|
||||
$t('consensus.h')
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -223,7 +229,9 @@ async function update() {
|
||||
<div
|
||||
class="bg-violet-100 text-neutral-content rounded-full w-12 h-12"
|
||||
>
|
||||
<span class="text-2xl text-primary font-semibold">{{ $t('consensus.r') }}</span>
|
||||
<span class="text-2xl text-primary font-semibold">{{
|
||||
$t('consensus.r')
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -239,7 +247,9 @@ async function update() {
|
||||
<div
|
||||
class="bg-blue-100 text-neutral-content rounded-full w-12 h-12"
|
||||
>
|
||||
<span class="text-2xl text-info font-semibold">{{ $t('consensus.s') }}</span>
|
||||
<span class="text-2xl text-info font-semibold">{{
|
||||
$t('consensus.s')
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -255,12 +265,14 @@ async function update() {
|
||||
{{ $t('consensus.updated_at') }} {{ newTime || '' }}
|
||||
</h2>
|
||||
<div v-for="item in roundState.height_vote_set" :key="item.round">
|
||||
<div class="text-xs mb-1">{{ $t('consensus.round') }}: {{ item.round }}</div>
|
||||
<div class="text-xs mb-1">
|
||||
{{ $t('consensus.round') }}: {{ item.round }}
|
||||
</div>
|
||||
<div class="text-xs break-words">{{ item.prevotes_bit_array }}</div>
|
||||
|
||||
<div class="flex flex-rows flex-wrap py-6">
|
||||
<div
|
||||
class=" w-48 rounded-3xl h-5 text-sm px-2 leading-5"
|
||||
class="w-48 rounded-3xl h-5 text-sm px-2 leading-5"
|
||||
v-for="(pre, i) in item.prevotes"
|
||||
:key="i"
|
||||
size="sm"
|
||||
@@ -269,17 +281,26 @@ async function update() {
|
||||
<span class="flex flex-rows justify-between">
|
||||
<span class="truncate">{{ showName(i, 'nil-Vote') }} </span>
|
||||
<span>
|
||||
<span class="tooltip " :data-tip="pre"
|
||||
:class="{
|
||||
'bg-green-400': String(pre).toLowerCase() !== 'nil-vote',
|
||||
'bg-red-400': String(pre).toLowerCase() === 'nil-vote'
|
||||
}"
|
||||
> </span>
|
||||
<span class="tooltip ml-1" :data-tip="item.precommits[i]"
|
||||
:class="{
|
||||
'bg-green-400': String(item.precommits[i]).toLowerCase() !== 'nil-vote',
|
||||
'bg-red-400': String(item.precommits[i]).toLowerCase() === 'nil-vote'
|
||||
}"> </span>
|
||||
<span
|
||||
class="tooltip"
|
||||
:data-tip="pre"
|
||||
:class="{
|
||||
'bg-green-400': String(pre).toLowerCase() !== 'nil-vote',
|
||||
'bg-red-400': String(pre).toLowerCase() === 'nil-vote',
|
||||
}"
|
||||
> </span
|
||||
>
|
||||
<span
|
||||
class="tooltip ml-1"
|
||||
:data-tip="item.precommits[i]"
|
||||
:class="{
|
||||
'bg-green-400':
|
||||
String(item.precommits[i]).toLowerCase() !== 'nil-vote',
|
||||
'bg-red-400':
|
||||
String(item.precommits[i]).toLowerCase() === 'nil-vote',
|
||||
}"
|
||||
> </span
|
||||
>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@@ -287,7 +308,6 @@ async function update() {
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- alert-info -->
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { BaseRestClient } from '@/libs/client';
|
||||
import { adapter, type AbstractRegistry, type Request } from '@/libs/api/registry';
|
||||
import {
|
||||
adapter,
|
||||
type AbstractRegistry,
|
||||
type Request,
|
||||
} from '@/libs/api/registry';
|
||||
import { defineStore } from 'pinia';
|
||||
import type {
|
||||
CodeInfo,
|
||||
@@ -16,115 +20,120 @@ import { PageRequest } from '@/types';
|
||||
import { get } from '@/libs';
|
||||
|
||||
export interface WasmRequestRegistry extends AbstractRegistry {
|
||||
cosmwasm_code: Request<PaginabledCodeInfos>;
|
||||
cosmwasm_code_id: Request<CodeInfo>;
|
||||
cosmwasm_code_id_contracts: Request<PaginabledContracts>;
|
||||
cosmwasm_param: Request<WasmParam>;
|
||||
cosmwasm_contract_address: Request<{
|
||||
address: string;
|
||||
contract_info: ContractInfo;
|
||||
}>;
|
||||
cosmwasm_contract_address_history: Request<PaginabledContractHistory>;
|
||||
cosmwasm_contract_address_raw_query_data: Request<any>;
|
||||
cosmwasm_contract_address_smart_query_data: Request<any>;
|
||||
cosmwasm_contract_address_state: Request<PaginabledContractStates>;
|
||||
cosmwasm_wasm_contracts_creator: Request<PaginabledContracts>;
|
||||
cosmwasm_code: Request<PaginabledCodeInfos>;
|
||||
cosmwasm_code_id: Request<CodeInfo>;
|
||||
cosmwasm_code_id_contracts: Request<PaginabledContracts>;
|
||||
cosmwasm_param: Request<WasmParam>;
|
||||
cosmwasm_contract_address: Request<{
|
||||
address: string;
|
||||
contract_info: ContractInfo;
|
||||
}>;
|
||||
cosmwasm_contract_address_history: Request<PaginabledContractHistory>;
|
||||
cosmwasm_contract_address_raw_query_data: Request<any>;
|
||||
cosmwasm_contract_address_smart_query_data: Request<any>;
|
||||
cosmwasm_contract_address_state: Request<PaginabledContractStates>;
|
||||
cosmwasm_wasm_contracts_creator: Request<PaginabledContracts>;
|
||||
}
|
||||
|
||||
export const DEFAULT_VERSION: WasmRequestRegistry = {
|
||||
cosmwasm_code: { url: '/cosmwasm/wasm/v1/code', adapter },
|
||||
cosmwasm_code_id: { url: '/cosmwasm/wasm/v1/code/{code_id}', adapter },
|
||||
cosmwasm_code_id_contracts: {
|
||||
url: '/cosmwasm/wasm/v1/code/{code_id}/contracts',
|
||||
adapter,
|
||||
},
|
||||
cosmwasm_param: { url: '/cosmwasm/wasm/v1/codes/params', adapter },
|
||||
cosmwasm_contract_address: {
|
||||
url: '/cosmwasm/wasm/v1/contract/{address}',
|
||||
adapter,
|
||||
},
|
||||
cosmwasm_contract_address_history: {
|
||||
url: '/cosmwasm/wasm/v1/contract/{address}/history',
|
||||
adapter,
|
||||
},
|
||||
cosmwasm_contract_address_raw_query_data: {
|
||||
url: '/cosmwasm/wasm/v1/contract/{address}/raw/{query_data}',
|
||||
adapter,
|
||||
},
|
||||
cosmwasm_contract_address_smart_query_data: {
|
||||
url: '/cosmwasm/wasm/v1/contract/{address}/smart/{query_data}',
|
||||
adapter,
|
||||
},
|
||||
cosmwasm_contract_address_state: {
|
||||
url: '/cosmwasm/wasm/v1/contract/{address}/state',
|
||||
adapter,
|
||||
},
|
||||
cosmwasm_wasm_contracts_creator: {
|
||||
url: '/cosmwasm/wasm/v1/contracts/creator/{creator_address}',
|
||||
adapter,
|
||||
},
|
||||
};
|
||||
|
||||
export class WasmRestClient extends BaseRestClient<WasmRequestRegistry> {
|
||||
getWasmCodeList(pr?: PageRequest) {
|
||||
// if(!pr) pr = new PageRequest()
|
||||
// const query = `?${pr.toQueryString()}`
|
||||
return this.request(this.registry.cosmwasm_code, {} /*query*/);
|
||||
}
|
||||
|
||||
export const DEFAULT_VERSION: WasmRequestRegistry = {
|
||||
cosmwasm_code: { url: '/cosmwasm/wasm/v1/code', adapter },
|
||||
cosmwasm_code_id: { url: '/cosmwasm/wasm/v1/code/{code_id}', adapter },
|
||||
cosmwasm_code_id_contracts: {
|
||||
url: '/cosmwasm/wasm/v1/code/{code_id}/contracts',
|
||||
adapter,
|
||||
},
|
||||
cosmwasm_param: { url: '/cosmwasm/wasm/v1/codes/params', adapter },
|
||||
cosmwasm_contract_address: {
|
||||
url: '/cosmwasm/wasm/v1/contract/{address}',
|
||||
adapter,
|
||||
},
|
||||
cosmwasm_contract_address_history: {
|
||||
url: '/cosmwasm/wasm/v1/contract/{address}/history',
|
||||
adapter,
|
||||
},
|
||||
cosmwasm_contract_address_raw_query_data: {
|
||||
url: '/cosmwasm/wasm/v1/contract/{address}/raw/{query_data}',
|
||||
adapter,
|
||||
},
|
||||
cosmwasm_contract_address_smart_query_data: {
|
||||
url: '/cosmwasm/wasm/v1/contract/{address}/smart/{query_data}',
|
||||
adapter,
|
||||
},
|
||||
cosmwasm_contract_address_state: {
|
||||
url: '/cosmwasm/wasm/v1/contract/{address}/state',
|
||||
adapter,
|
||||
},
|
||||
cosmwasm_wasm_contracts_creator: {
|
||||
url: '/cosmwasm/wasm/v1/contracts/creator/{creator_address}',
|
||||
adapter,
|
||||
},
|
||||
};
|
||||
|
||||
export class WasmRestClient extends BaseRestClient<WasmRequestRegistry> {
|
||||
getWasmCodeList(pr?: PageRequest) {
|
||||
// if(!pr) pr = new PageRequest()
|
||||
// const query = `?${pr.toQueryString()}`
|
||||
return this.request(this.registry.cosmwasm_code, {}, /*query*/);
|
||||
}
|
||||
getWasmCodeById(code_id: string) {
|
||||
return this.request(this.registry.cosmwasm_code, { code_id }); // `code_id` is a param in above url
|
||||
}
|
||||
getWasmCodeContracts(code_id: string, page?: PageRequest) {
|
||||
// if(!page) page = new PageRequest()
|
||||
// const query = `?${page.toQueryString()}`
|
||||
return this.request(this.registry.cosmwasm_code_id_contracts, { code_id });
|
||||
}
|
||||
getWasmParams() {
|
||||
return this.request(this.registry.cosmwasm_param, {});
|
||||
}
|
||||
getWasmContracts(address: string) {
|
||||
return this.request(this.registry.cosmwasm_contract_address, { address });
|
||||
}
|
||||
getWasmContractsByCreator(creator_address: string, page?: PageRequest) {
|
||||
// if(!page) page = new PageRequest()
|
||||
// const query = `?${page.toQueryString()}`
|
||||
return this.request(this.registry.cosmwasm_wasm_contracts_creator, { creator_address });
|
||||
}
|
||||
getWasmContractHistory(address: string) {
|
||||
return this.request(this.registry.cosmwasm_contract_address_history, {
|
||||
address,
|
||||
});
|
||||
}
|
||||
getWasmContractRawQuery(address: string, query: string) {
|
||||
const query_data = toBase64(new TextEncoder().encode(query));
|
||||
return this.request(
|
||||
this.registry.cosmwasm_contract_address_raw_query_data,
|
||||
{ address, query_data }
|
||||
);
|
||||
}
|
||||
getWasmContractSmartQuery(address: string, query: string) {
|
||||
const query_data = toBase64(new TextEncoder().encode(query));
|
||||
return this.get(
|
||||
this.registry.cosmwasm_contract_address_smart_query_data,
|
||||
{ address, query_data }
|
||||
);
|
||||
}
|
||||
async getWasmContractQueries(address: string) {
|
||||
const query_data = toBase64(new TextEncoder().encode('{"":""}'));
|
||||
const {code, message} = await this.get(
|
||||
this.registry.cosmwasm_contract_address_smart_query_data,
|
||||
{ address, query_data }
|
||||
);
|
||||
let re = /`(\w+)`/g
|
||||
let x = String(message).match(re)
|
||||
return code === 2 && x ? x.map(e => e.replaceAll('`', '')) : []
|
||||
}
|
||||
getWasmContractStates(address: string, pr: PageRequest) {
|
||||
if(!pr) pr = new PageRequest()
|
||||
const query = `?${pr.toQueryString()}`
|
||||
return this.request(this.registry.cosmwasm_contract_address_state, {
|
||||
address,
|
||||
}, query);
|
||||
}
|
||||
getWasmCodeById(code_id: string) {
|
||||
return this.request(this.registry.cosmwasm_code, { code_id }); // `code_id` is a param in above url
|
||||
}
|
||||
|
||||
getWasmCodeContracts(code_id: string, page?: PageRequest) {
|
||||
// if(!page) page = new PageRequest()
|
||||
// const query = `?${page.toQueryString()}`
|
||||
return this.request(this.registry.cosmwasm_code_id_contracts, { code_id });
|
||||
}
|
||||
getWasmParams() {
|
||||
return this.request(this.registry.cosmwasm_param, {});
|
||||
}
|
||||
getWasmContracts(address: string) {
|
||||
return this.request(this.registry.cosmwasm_contract_address, { address });
|
||||
}
|
||||
getWasmContractsByCreator(creator_address: string, page?: PageRequest) {
|
||||
// if(!page) page = new PageRequest()
|
||||
// const query = `?${page.toQueryString()}`
|
||||
return this.request(this.registry.cosmwasm_wasm_contracts_creator, {
|
||||
creator_address,
|
||||
});
|
||||
}
|
||||
getWasmContractHistory(address: string) {
|
||||
return this.request(this.registry.cosmwasm_contract_address_history, {
|
||||
address,
|
||||
});
|
||||
}
|
||||
getWasmContractRawQuery(address: string, query: string) {
|
||||
const query_data = toBase64(new TextEncoder().encode(query));
|
||||
return this.request(
|
||||
this.registry.cosmwasm_contract_address_raw_query_data,
|
||||
{ address, query_data }
|
||||
);
|
||||
}
|
||||
getWasmContractSmartQuery(address: string, query: string) {
|
||||
const query_data = toBase64(new TextEncoder().encode(query));
|
||||
return this.get(this.registry.cosmwasm_contract_address_smart_query_data, {
|
||||
address,
|
||||
query_data,
|
||||
});
|
||||
}
|
||||
async getWasmContractQueries(address: string) {
|
||||
const query_data = toBase64(new TextEncoder().encode('{"":""}'));
|
||||
const { code, message } = await this.get(
|
||||
this.registry.cosmwasm_contract_address_smart_query_data,
|
||||
{ address, query_data }
|
||||
);
|
||||
let re = /`(\w+)`/g;
|
||||
let x = String(message).match(re);
|
||||
return code === 2 && x ? x.map((e) => e.replaceAll('`', '')) : [];
|
||||
}
|
||||
getWasmContractStates(address: string, pr: PageRequest) {
|
||||
if (!pr) pr = new PageRequest();
|
||||
const query = `?${pr.toQueryString()}`;
|
||||
return this.request(
|
||||
this.registry.cosmwasm_contract_address_state,
|
||||
{
|
||||
address,
|
||||
},
|
||||
query
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { useWasmStore } from '../WasmStore';
|
||||
import { ref } from 'vue';
|
||||
import type {
|
||||
ContractInfo,
|
||||
PaginabledContracts,
|
||||
} from '../types';
|
||||
import type { ContractInfo, PaginabledContracts } from '../types';
|
||||
import DynamicComponent from '@/components/dynamic/DynamicComponent.vue';
|
||||
import PaginationBar from '@/components/PaginationBar.vue';
|
||||
import { PageRequest } from '@/types';
|
||||
@@ -22,31 +19,30 @@ const wasmStore = useWasmStore();
|
||||
function loadContract(pageNum: number) {
|
||||
const pr = new PageRequest();
|
||||
pr.setPage(pageNum);
|
||||
if(String(props.code_id).search(/^[\d]+$/) > -1){
|
||||
if (String(props.code_id).search(/^[\d]+$/) > -1) {
|
||||
// query with code id
|
||||
wasmStore.wasmClient.getWasmCodeContracts(props.code_id, pr).then((x) => {
|
||||
response.value = x;
|
||||
})
|
||||
});
|
||||
} else {
|
||||
// query by creator
|
||||
wasmStore.wasmClient.getWasmContractsByCreator(props.code_id, pr).then((x) => {
|
||||
response.value = {
|
||||
contracts: x.contract_addresses,
|
||||
pagination: x.pagination,
|
||||
};
|
||||
})
|
||||
wasmStore.wasmClient
|
||||
.getWasmContractsByCreator(props.code_id, pr)
|
||||
.then((x) => {
|
||||
response.value = {
|
||||
contracts: x.contract_addresses,
|
||||
pagination: x.pagination,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
loadContract(1);
|
||||
|
||||
|
||||
|
||||
function showInfo(address: string) {
|
||||
wasmStore.wasmClient.getWasmContracts(address).then((x) => {
|
||||
info.value = x.contract_info;
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
@@ -58,7 +54,9 @@ function showInfo(address: string) {
|
||||
<table class="table table-compact w-full mt-4">
|
||||
<thead class="bg-base-200">
|
||||
<tr>
|
||||
<th style="position: relative; z-index: 2">{{ $t('cosmwasm.contract_list') }}</th>
|
||||
<th style="position: relative; z-index: 2">
|
||||
{{ $t('cosmwasm.contract_list') }}
|
||||
</th>
|
||||
<th>{{ $t('account.action') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -80,7 +78,7 @@ function showInfo(address: string) {
|
||||
:to="`transactions?contract=${v}`"
|
||||
class="btn btn-primary btn-xs text-xs"
|
||||
>
|
||||
{{ $t('cosmwasm.btn_details') }}
|
||||
{{ $t('cosmwasm.btn_details') }}
|
||||
</RouterLink>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -125,6 +123,5 @@ function showInfo(address: string) {
|
||||
</div>
|
||||
</label>
|
||||
</label>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
<script lang="ts" setup>
|
||||
import PaginationBar from '@/components/PaginationBar.vue';
|
||||
import { useBaseStore, useBlockchain, useFormatter, useTxDialog } from '@/stores';
|
||||
import { PageRequest, type PaginatedBalances, type PaginatedTxs } from '@/types';
|
||||
import {
|
||||
useBaseStore,
|
||||
useBlockchain,
|
||||
useFormatter,
|
||||
useTxDialog,
|
||||
} from '@/stores';
|
||||
import {
|
||||
PageRequest,
|
||||
type PaginatedBalances,
|
||||
type PaginatedTxs,
|
||||
} from '@/types';
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useWasmStore } from '../WasmStore';
|
||||
@@ -9,301 +18,411 @@ import DynamicComponent from '@/components/dynamic/DynamicComponent.vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import type { ContractInfo, PaginabledContractStates } from '../types';
|
||||
|
||||
import { JsonViewer } from "vue3-json-viewer"
|
||||
import { JsonViewer } from 'vue3-json-viewer';
|
||||
// if you used v1.0.5 or latster ,you should add import "vue3-json-viewer/dist/index.css"
|
||||
import "vue3-json-viewer/dist/index.css";
|
||||
import 'vue3-json-viewer/dist/index.css';
|
||||
|
||||
const chainStore = useBlockchain();
|
||||
const baseStore = useBaseStore();
|
||||
const format = useFormatter();
|
||||
const wasmStore = useWasmStore();
|
||||
|
||||
const route = useRoute()
|
||||
const page = ref(new PageRequest())
|
||||
const route = useRoute();
|
||||
const page = ref(new PageRequest());
|
||||
const pageRequest = ref(new PageRequest());
|
||||
|
||||
const txs = ref<PaginatedTxs>({ txs: [], tx_responses: [], pagination: { total: "0" } });
|
||||
const txs = ref<PaginatedTxs>({
|
||||
txs: [],
|
||||
tx_responses: [],
|
||||
pagination: { total: '0' },
|
||||
});
|
||||
|
||||
const dialog = useTxDialog();
|
||||
const info = ref({} as ContractInfo);
|
||||
const state = ref({} as PaginabledContractStates);
|
||||
const selected = ref('');
|
||||
const balances = ref({} as PaginatedBalances)
|
||||
const balances = ref({} as PaginatedBalances);
|
||||
|
||||
const contractAddress = String(route.query.contract)
|
||||
const contractAddress = String(route.query.contract);
|
||||
|
||||
const history = JSON.parse(localStorage.getItem("contract_history") || "{}")
|
||||
const history = JSON.parse(localStorage.getItem('contract_history') || '{}');
|
||||
|
||||
if (history[chainStore.chainName]) {
|
||||
if (!history[chainStore.chainName].includes(contractAddress)) {
|
||||
history[chainStore.chainName].push(contractAddress)
|
||||
if (history[chainStore.chainName].length > 10) {
|
||||
history[chainStore.chainName].shift()
|
||||
}
|
||||
if (!history[chainStore.chainName].includes(contractAddress)) {
|
||||
history[chainStore.chainName].push(contractAddress);
|
||||
if (history[chainStore.chainName].length > 10) {
|
||||
history[chainStore.chainName].shift();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
history[chainStore.chainName] = [contractAddress]
|
||||
history[chainStore.chainName] = [contractAddress];
|
||||
}
|
||||
localStorage.setItem("contract_history", JSON.stringify(history))
|
||||
localStorage.setItem('contract_history', JSON.stringify(history));
|
||||
|
||||
onMounted(() => {
|
||||
const address = contractAddress
|
||||
wasmStore.wasmClient.getWasmContracts(address).then((x) => {
|
||||
info.value = x.contract_info;
|
||||
const address = contractAddress;
|
||||
wasmStore.wasmClient.getWasmContracts(address).then((x) => {
|
||||
info.value = x.contract_info;
|
||||
});
|
||||
chainStore.rpc
|
||||
.getTxs(
|
||||
"?order_by=2&events=execute._contract_address='{address}'",
|
||||
{ address },
|
||||
page.value
|
||||
)
|
||||
.then((res) => {
|
||||
txs.value = res;
|
||||
});
|
||||
chainStore.rpc.getTxs("?order_by=2&events=execute._contract_address='{address}'", { address }, page.value).then(res => {
|
||||
txs.value = res
|
||||
})
|
||||
|
||||
wasmStore.wasmClient.getWasmContractQueries(contractAddress).then(res => {
|
||||
console.log("queries: ", res)
|
||||
queries.value = res
|
||||
if (res && res.length > 0) {
|
||||
selectQuery(res[0])
|
||||
}
|
||||
})
|
||||
wasmStore.wasmClient.getWasmContractQueries(contractAddress).then((res) => {
|
||||
console.log('queries: ', res);
|
||||
queries.value = res;
|
||||
if (res && res.length > 0) {
|
||||
selectQuery(res[0]);
|
||||
}
|
||||
});
|
||||
|
||||
showFunds()
|
||||
showState()
|
||||
|
||||
})
|
||||
showFunds();
|
||||
showState();
|
||||
});
|
||||
|
||||
function pageload(pageNum: number) {
|
||||
page.value.setPage(pageNum)
|
||||
const address = String(route.query.contract)
|
||||
chainStore.rpc.getTxs("?order_by=2&events=execute._contract_address='{address}'", { address }, page.value).then(res => {
|
||||
txs.value = res
|
||||
})
|
||||
page.value.setPage(pageNum);
|
||||
const address = String(route.query.contract);
|
||||
chainStore.rpc
|
||||
.getTxs(
|
||||
"?order_by=2&events=execute._contract_address='{address}'",
|
||||
{ address },
|
||||
page.value
|
||||
)
|
||||
.then((res) => {
|
||||
txs.value = res;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function showFunds() {
|
||||
|
||||
const address = String(route.query.contract)
|
||||
chainStore.rpc.getBankBalances(address).then(res => {
|
||||
balances.value = res
|
||||
})
|
||||
const address = String(route.query.contract);
|
||||
chainStore.rpc.getBankBalances(address).then((res) => {
|
||||
balances.value = res;
|
||||
});
|
||||
}
|
||||
function showState() {
|
||||
const address = String(route.query.contract)
|
||||
selected.value = address;
|
||||
pageloadState(1);
|
||||
const address = String(route.query.contract);
|
||||
selected.value = address;
|
||||
pageloadState(1);
|
||||
}
|
||||
|
||||
function pageloadState(p: number) {
|
||||
pageRequest.value.setPage(p);
|
||||
wasmStore.wasmClient
|
||||
.getWasmContractStates(selected.value, pageRequest.value)
|
||||
.then((x) => {
|
||||
state.value = x;
|
||||
});
|
||||
pageRequest.value.setPage(p);
|
||||
wasmStore.wasmClient
|
||||
.getWasmContractStates(selected.value, pageRequest.value)
|
||||
.then((x) => {
|
||||
state.value = x;
|
||||
});
|
||||
}
|
||||
|
||||
function showQuery() {
|
||||
query.value = '';
|
||||
result.value = '';
|
||||
query.value = '';
|
||||
result.value = '';
|
||||
}
|
||||
|
||||
function selectQuery(method: string) {
|
||||
query.value = `{"${method}":{}}`
|
||||
query.value = `{"${method}":{}}`;
|
||||
}
|
||||
|
||||
function queryContract() {
|
||||
try {
|
||||
if (selectedRadio.value === 'raw') {
|
||||
wasmStore.wasmClient
|
||||
.getWasmContractRawQuery(contractAddress, query.value)
|
||||
.then((x) => {
|
||||
result.value = x;
|
||||
})
|
||||
.catch((err) => {
|
||||
result.value = err;
|
||||
});
|
||||
} else {
|
||||
wasmStore.wasmClient
|
||||
.getWasmContractSmartQuery(contractAddress, query.value)
|
||||
.then((x) => {
|
||||
result.value = x;
|
||||
})
|
||||
.catch((err) => {
|
||||
result.value = err;
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
result.value = JSON.stringify(err); // not works for now
|
||||
try {
|
||||
if (selectedRadio.value === 'raw') {
|
||||
wasmStore.wasmClient
|
||||
.getWasmContractRawQuery(contractAddress, query.value)
|
||||
.then((x) => {
|
||||
result.value = x;
|
||||
})
|
||||
.catch((err) => {
|
||||
result.value = err;
|
||||
});
|
||||
} else {
|
||||
wasmStore.wasmClient
|
||||
.getWasmContractSmartQuery(contractAddress, query.value)
|
||||
.then((x) => {
|
||||
result.value = x;
|
||||
})
|
||||
.catch((err) => {
|
||||
result.value = err;
|
||||
});
|
||||
}
|
||||
// TODO, show error in the result.
|
||||
} catch (err) {
|
||||
result.value = JSON.stringify(err); // not works for now
|
||||
}
|
||||
// TODO, show error in the result.
|
||||
}
|
||||
|
||||
const selectedRadio = ref('smart');
|
||||
const query = ref('');
|
||||
const result = ref({});
|
||||
const queries = ref<string[]>([])
|
||||
const tab = ref('detail')
|
||||
const queries = ref<string[]>([]);
|
||||
const tab = ref('detail');
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div class="tabs tabs-boxed bg-transparent mb-4">
|
||||
<a class="tab text-gray-400 uppercase" :class="{ 'tab-active': tab === 'detail' }"
|
||||
@click="tab = 'detail'">{{ $t('cosmwasm.contract_detail') }}</a>
|
||||
<a class="tab text-gray-400 uppercase" :class="{ 'tab-active': tab === 'transaction' }"
|
||||
@click="tab = 'transaction'">Transactions</a>
|
||||
<a class="tab text-gray-400 uppercase" :class="{ 'tab-active': tab === 'query' }"
|
||||
@click="tab = 'query'">Query</a>
|
||||
</div>
|
||||
|
||||
<div v-show="tab === 'detail'">
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<h2 class="card-title truncate w-full">
|
||||
{{ $t('cosmwasm.contract_detail') }}
|
||||
</h2>
|
||||
<DynamicComponent :value="info" />
|
||||
</div>
|
||||
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<div class="flex items-center justify-between px-3 pt-2">
|
||||
<div class="text-lg">{{ $t('cosmwasm.contract_balances') }}</div>
|
||||
</div>
|
||||
<ul class="menu mt-5">
|
||||
<li v-for="b in balances.balances">
|
||||
<a class="flex justify-between"><span>{{ format.formatToken(b) }}</span> {{ b.amount }}
|
||||
</a>
|
||||
</li>
|
||||
<li v-if="balances.pagination?.total === '0'" class="my-10 text-center">{{
|
||||
$t('cosmwasm.no_escrowed_assets') }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<div class="flex items-center justify-between px-3 pt-2 mb-4">
|
||||
<div class="text-lg">{{ $t('cosmwasm.contract_states') }}</div>
|
||||
</div>
|
||||
<div class="overflow-auto">
|
||||
<JsonViewer
|
||||
:value="state.models?.map(v => ({ key: format.hexToString(v.key), value: JSON.parse(format.base64ToString(v.value)) })) || ''"
|
||||
:theme="baseStore.theme || 'dark'" style="background: transparent;" copyable boxed sort
|
||||
:expand-depth="5" />
|
||||
<PaginationBar :limit="pageRequest.limit" :total="state.pagination?.total"
|
||||
:callback="pageloadState" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center mb-4">
|
||||
<RouterLink :to="`../${info.code_id}/contracts`"><span class="btn btn-xs text-xs mr-2"> Back </span>
|
||||
</RouterLink>
|
||||
|
||||
<label for="wasm_migrate_contract" class="btn btn-primary btn-xs text-xs mr-2"
|
||||
@click="dialog.open('wasm_migrate_contract', { contract: contractAddress })">
|
||||
{{ $t('cosmwasm.btn_migrate') }}
|
||||
</label>
|
||||
|
||||
<label for="wasm_update_admin" class="btn btn-primary btn-xs text-xs mr-2"
|
||||
@click="dialog.open('wasm_update_admin', { contract: contractAddress })">
|
||||
{{ $t('cosmwasm.btn_update_admin') }}
|
||||
</label>
|
||||
|
||||
<label for="wasm_clear_admin" class="btn btn-primary btn-xs text-xs mr-2"
|
||||
@click="dialog.open('wasm_clear_admin', { contract: contractAddress })">
|
||||
{{ $t('cosmwasm.btn_clear_admin') }}
|
||||
</label>
|
||||
|
||||
<label for="wasm_execute_contract" class="btn btn-primary btn-xs text-xs mr-2"
|
||||
@click="dialog.open('wasm_execute_contract', { contract: contractAddress })">
|
||||
{{ $t('cosmwasm.btn_execute') }}
|
||||
</label>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-show="tab === 'transaction'" class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<h2 class="card-title truncate w-full mt-4 mb-2">Transactions</h2>
|
||||
<table class="table">
|
||||
<thead class=" bg-base-200">
|
||||
<tr>
|
||||
<td> {{ $t('ibc.height') }}</td>
|
||||
<td>{{ $t('ibc.txhash') }}</td>
|
||||
<td> {{ $t('ibc.messages') }}</td>
|
||||
<td>{{ $t('ibc.time') }}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="resp in txs?.tx_responses">
|
||||
<td>{{ resp.height }}</td>
|
||||
<td>
|
||||
<div class="text-xs truncate text-primary dark:invert">
|
||||
<RouterLink :to="`/${chainStore.chainName}/tx/${resp.txhash}`">{{ resp.txhash }}
|
||||
</RouterLink>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex">
|
||||
{{ format.messages(resp.tx.body.messages) }}
|
||||
<Icon v-if="resp.code === 0" icon="mdi-check" class="text-success text-lg" />
|
||||
<Icon v-else icon="mdi-multiply" class="text-error text-lg" />
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ format.toLocaleDate(resp.timestamp) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<PaginationBar :limit="page.limit" :total="txs.pagination?.total" :callback="pageload" />
|
||||
</div>
|
||||
|
||||
<div v-show="tab === 'query'">
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<div class="flex items-center justify-between px-3 pt-2 mb-4">
|
||||
<div class="text-lg font-semibold">{{ $t('cosmwasm.suggested_messages') }}</div>
|
||||
</div>
|
||||
<div class="px-3">
|
||||
<div>
|
||||
<div>
|
||||
<span v-for="q in queries" class="btn btn-xs mx-1" @click="selectQuery(q)">{{ q }}</span>
|
||||
</div>
|
||||
<textarea v-model="query" placeholder="Query String, {}" label="Query String"
|
||||
class="my-2 textarea textarea-bordered w-full" />
|
||||
|
||||
<div class="mt-4 mb-4 text-center">
|
||||
<button class="btn btn-primary btn-sm px-4 text-white" @click="queryContract()">
|
||||
{{ $t('cosmwasm.btn_query') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-show="tab === 'execute'">
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<div class="flex items-center justify-between px-3 pt-2 mb-4">
|
||||
<div class="text-lg font-semibold">{{ $t('cosmwasm.suggested_messages') }}</div>
|
||||
</div>
|
||||
<div class="px-3">
|
||||
<div>
|
||||
<div>
|
||||
<span v-for="q in queries" class="btn btn-xs mx-1" @click="selectQuery(q)">{{ q }}</span>
|
||||
</div>
|
||||
<textarea v-model="query" placeholder="Query String, {}" label="Query String"
|
||||
class="my-2 textarea textarea-bordered w-full" />
|
||||
|
||||
<div class="mt-4 mb-4 text-center">
|
||||
<button class="btn btn-primary btn-sm px-4 text-white" @click="queryContract()">
|
||||
{{ $t('cosmwasm.btn_execute') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="tab === 'execute' || tab === 'query'" class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<div class="flex items-center justify-between px-3 pt-2 mb-4">
|
||||
<div class="text-lg font-semibold">{{ $t('cosmwasm.result') }}</div>
|
||||
</div>
|
||||
<JsonViewer :value="result" :theme="baseStore.theme" style="background: transparent;" copyable boxed sort
|
||||
:expand-depth="5" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="tabs tabs-boxed bg-transparent mb-4">
|
||||
<a
|
||||
class="tab text-gray-400 uppercase"
|
||||
:class="{ 'tab-active': tab === 'detail' }"
|
||||
@click="tab = 'detail'"
|
||||
>{{ $t('cosmwasm.contract_detail') }}</a
|
||||
>
|
||||
<a
|
||||
class="tab text-gray-400 uppercase"
|
||||
:class="{ 'tab-active': tab === 'transaction' }"
|
||||
@click="tab = 'transaction'"
|
||||
>Transactions</a
|
||||
>
|
||||
<a
|
||||
class="tab text-gray-400 uppercase"
|
||||
:class="{ 'tab-active': tab === 'query' }"
|
||||
@click="tab = 'query'"
|
||||
>Query</a
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-show="tab === 'detail'">
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<h2 class="card-title truncate w-full">
|
||||
{{ $t('cosmwasm.contract_detail') }}
|
||||
</h2>
|
||||
<DynamicComponent :value="info" />
|
||||
</div>
|
||||
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<div class="flex items-center justify-between px-3 pt-2">
|
||||
<div class="text-lg">{{ $t('cosmwasm.contract_balances') }}</div>
|
||||
</div>
|
||||
<ul class="menu mt-5">
|
||||
<li v-for="b in balances.balances">
|
||||
<a class="flex justify-between"
|
||||
><span>{{ format.formatToken(b) }}</span> {{ b.amount }}
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
v-if="balances.pagination?.total === '0'"
|
||||
class="my-10 text-center"
|
||||
>
|
||||
{{ $t('cosmwasm.no_escrowed_assets') }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<div class="flex items-center justify-between px-3 pt-2 mb-4">
|
||||
<div class="text-lg">{{ $t('cosmwasm.contract_states') }}</div>
|
||||
</div>
|
||||
<div class="overflow-auto">
|
||||
<JsonViewer
|
||||
:value="
|
||||
state.models?.map((v) => ({
|
||||
key: format.hexToString(v.key),
|
||||
value: JSON.parse(format.base64ToString(v.value)),
|
||||
})) || ''
|
||||
"
|
||||
:theme="baseStore.theme || 'dark'"
|
||||
style="background: transparent"
|
||||
copyable
|
||||
boxed
|
||||
sort
|
||||
:expand-depth="5"
|
||||
/>
|
||||
<PaginationBar
|
||||
:limit="pageRequest.limit"
|
||||
:total="state.pagination?.total"
|
||||
:callback="pageloadState"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center mb-4">
|
||||
<RouterLink :to="`../${info.code_id}/contracts`"
|
||||
><span class="btn btn-xs text-xs mr-2"> Back </span>
|
||||
</RouterLink>
|
||||
|
||||
<label
|
||||
for="wasm_migrate_contract"
|
||||
class="btn btn-primary btn-xs text-xs mr-2"
|
||||
@click="
|
||||
dialog.open('wasm_migrate_contract', { contract: contractAddress })
|
||||
"
|
||||
>
|
||||
{{ $t('cosmwasm.btn_migrate') }}
|
||||
</label>
|
||||
|
||||
<label
|
||||
for="wasm_update_admin"
|
||||
class="btn btn-primary btn-xs text-xs mr-2"
|
||||
@click="
|
||||
dialog.open('wasm_update_admin', { contract: contractAddress })
|
||||
"
|
||||
>
|
||||
{{ $t('cosmwasm.btn_update_admin') }}
|
||||
</label>
|
||||
|
||||
<label
|
||||
for="wasm_clear_admin"
|
||||
class="btn btn-primary btn-xs text-xs mr-2"
|
||||
@click="
|
||||
dialog.open('wasm_clear_admin', { contract: contractAddress })
|
||||
"
|
||||
>
|
||||
{{ $t('cosmwasm.btn_clear_admin') }}
|
||||
</label>
|
||||
|
||||
<label
|
||||
for="wasm_execute_contract"
|
||||
class="btn btn-primary btn-xs text-xs mr-2"
|
||||
@click="
|
||||
dialog.open('wasm_execute_contract', { contract: contractAddress })
|
||||
"
|
||||
>
|
||||
{{ $t('cosmwasm.btn_execute') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-show="tab === 'transaction'"
|
||||
class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow"
|
||||
>
|
||||
<h2 class="card-title truncate w-full mt-4 mb-2">Transactions</h2>
|
||||
<table class="table">
|
||||
<thead class="bg-base-200">
|
||||
<tr>
|
||||
<td>{{ $t('ibc.height') }}</td>
|
||||
<td>{{ $t('ibc.txhash') }}</td>
|
||||
<td>{{ $t('ibc.messages') }}</td>
|
||||
<td>{{ $t('ibc.time') }}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="resp in txs?.tx_responses">
|
||||
<td>{{ resp.height }}</td>
|
||||
<td>
|
||||
<div class="text-xs truncate text-primary dark:invert">
|
||||
<RouterLink :to="`/${chainStore.chainName}/tx/${resp.txhash}`"
|
||||
>{{ resp.txhash }}
|
||||
</RouterLink>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex">
|
||||
{{ format.messages(resp.tx.body.messages) }}
|
||||
<Icon
|
||||
v-if="resp.code === 0"
|
||||
icon="mdi-check"
|
||||
class="text-success text-lg"
|
||||
/>
|
||||
<Icon v-else icon="mdi-multiply" class="text-error text-lg" />
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ format.toLocaleDate(resp.timestamp) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<PaginationBar
|
||||
:limit="page.limit"
|
||||
:total="txs.pagination?.total"
|
||||
:callback="pageload"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-show="tab === 'query'">
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<div class="flex items-center justify-between px-3 pt-2 mb-4">
|
||||
<div class="text-lg font-semibold">
|
||||
{{ $t('cosmwasm.suggested_messages') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-3">
|
||||
<div>
|
||||
<div>
|
||||
<span
|
||||
v-for="q in queries"
|
||||
class="btn btn-xs mx-1"
|
||||
@click="selectQuery(q)"
|
||||
>{{ q }}</span
|
||||
>
|
||||
</div>
|
||||
<textarea
|
||||
v-model="query"
|
||||
placeholder="Query String, {}"
|
||||
label="Query String"
|
||||
class="my-2 textarea textarea-bordered w-full"
|
||||
/>
|
||||
|
||||
<div class="mt-4 mb-4 text-center">
|
||||
<button
|
||||
class="btn btn-primary btn-sm px-4 text-white"
|
||||
@click="queryContract()"
|
||||
>
|
||||
{{ $t('cosmwasm.btn_query') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-show="tab === 'execute'">
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<div class="flex items-center justify-between px-3 pt-2 mb-4">
|
||||
<div class="text-lg font-semibold">
|
||||
{{ $t('cosmwasm.suggested_messages') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-3">
|
||||
<div>
|
||||
<div>
|
||||
<span
|
||||
v-for="q in queries"
|
||||
class="btn btn-xs mx-1"
|
||||
@click="selectQuery(q)"
|
||||
>{{ q }}</span
|
||||
>
|
||||
</div>
|
||||
<textarea
|
||||
v-model="query"
|
||||
placeholder="Query String, {}"
|
||||
label="Query String"
|
||||
class="my-2 textarea textarea-bordered w-full"
|
||||
/>
|
||||
|
||||
<div class="mt-4 mb-4 text-center">
|
||||
<button
|
||||
class="btn btn-primary btn-sm px-4 text-white"
|
||||
@click="queryContract()"
|
||||
>
|
||||
{{ $t('cosmwasm.btn_execute') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="tab === 'execute' || tab === 'query'"
|
||||
class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow"
|
||||
>
|
||||
<div class="flex items-center justify-between px-3 pt-2 mb-4">
|
||||
<div class="text-lg font-semibold">{{ $t('cosmwasm.result') }}</div>
|
||||
</div>
|
||||
<JsonViewer
|
||||
:value="result"
|
||||
:theme="baseStore.theme"
|
||||
style="background: transparent"
|
||||
copyable
|
||||
boxed
|
||||
sort
|
||||
:expand-depth="5"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -11,88 +11,122 @@ const props = defineProps(['chain']);
|
||||
|
||||
const codes = ref({} as PaginabledCodeInfos);
|
||||
|
||||
const pageRequest = ref(new PageRequest())
|
||||
const pageRequest = ref(new PageRequest());
|
||||
const wasmStore = useWasmStore();
|
||||
const dialog = useTxDialog()
|
||||
const creator = ref("")
|
||||
const field = ref("contract")
|
||||
const history = ref([])
|
||||
const dialog = useTxDialog();
|
||||
const creator = ref('');
|
||||
const field = ref('contract');
|
||||
const history = ref([]);
|
||||
|
||||
function pageload(pageNum: number) {
|
||||
pageRequest.value.setPage(pageNum)
|
||||
wasmStore.wasmClient.getWasmCodeList(pageRequest.value).then((x) => {
|
||||
codes.value = x;
|
||||
});
|
||||
pageRequest.value.setPage(pageNum);
|
||||
wasmStore.wasmClient.getWasmCodeList(pageRequest.value).then((x) => {
|
||||
codes.value = x;
|
||||
});
|
||||
}
|
||||
pageload(1)
|
||||
pageload(1);
|
||||
|
||||
onMounted(() => {
|
||||
const historyStore = JSON.parse(localStorage.getItem("contract_history") || "{}")
|
||||
history.value = historyStore[props.chain] || []
|
||||
})
|
||||
const historyStore = JSON.parse(
|
||||
localStorage.getItem('contract_history') || '{}'
|
||||
);
|
||||
history.value = historyStore[props.chain] || [];
|
||||
});
|
||||
|
||||
function myContracts() {
|
||||
if(field.value === "contract")
|
||||
router.push(`/${props.chain}/cosmwasm/0/transactions?contract=${creator.value}`)
|
||||
else if(field.value === "creator")
|
||||
router.push(`/${props.chain}/cosmwasm/${creator.value}/contracts`)
|
||||
if (field.value === 'contract')
|
||||
router.push(
|
||||
`/${props.chain}/cosmwasm/0/transactions?contract=${creator.value}`
|
||||
);
|
||||
else if (field.value === 'creator')
|
||||
router.push(`/${props.chain}/cosmwasm/${creator.value}/contracts`);
|
||||
}
|
||||
const togo = ref("")
|
||||
const togo = ref('');
|
||||
function gotoHistory() {
|
||||
router.push(`/${props.chain}/cosmwasm/0/transactions?contract=${togo.value}`)
|
||||
router.push(`/${props.chain}/cosmwasm/0/transactions?contract=${togo.value}`);
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<h2 class="card-title truncate w-full mb-4">{{ $t('cosmwasm.title') }}</h2>
|
||||
<div class="grid grid-flow-col auto-cols-max gap-4 overflow-hidden">
|
||||
<div class="join w-full border border-primary">
|
||||
<select v-model="field" class="select select-primary"><option value="contract">Contract</option><option value="creator">Creator</option></select>
|
||||
<input v-model="creator" type=text class="input input-bordered w-full join-item" placeholder="address" />
|
||||
<button class="join-item btn btn-primary" @click="myContracts()">{{ $t('cosmwasm.btn_query') }}</button>
|
||||
</div>
|
||||
<div>
|
||||
<select v-model="togo" class="select select-primary" @change="gotoHistory()">
|
||||
<option value="">History</option>
|
||||
<option v-for="(v, index) in history" :key="index" :value="v" >...{{ String(v).substring(45) }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-compact w-full mt-4 text-sm">
|
||||
<thead class=" bg-base-200">
|
||||
<tr>
|
||||
<th>{{ $t('cosmwasm.code_id') }}</th>
|
||||
<th>{{ $t('cosmwasm.code_hash') }}</th>
|
||||
<th>{{ $t('cosmwasm.creator') }}</th>
|
||||
<th>{{ $t('cosmwasm.permissions') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(v, index) in codes.code_infos" :key="index">
|
||||
<td>{{ v.code_id }}</td>
|
||||
<td>
|
||||
<RouterLink :to="`/${props.chain}/cosmwasm/${v.code_id}/contracts`"
|
||||
class="truncate max-w-[200px] block text-primary dark:invert" :title="v.data_hash">
|
||||
{{ v.data_hash }}
|
||||
</RouterLink>
|
||||
</td>
|
||||
<td>{{ v.creator }}</td>
|
||||
<td>
|
||||
{{ v.instantiate_permission?.permission }}
|
||||
<span>{{ v.instantiate_permission?.address }}
|
||||
{{ v.instantiate_permission?.addresses.join(', ') }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="flex justify-between">
|
||||
<PaginationBar :limit="pageRequest.limit" :total="codes.pagination?.total" :callback="pageload" />
|
||||
<label for="wasm_store_code" class="btn btn-primary my-5" @click="dialog.open('wasm_store_code', {})">{{ $t('cosmwasm.btn_up_sc') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<h2 class="card-title truncate w-full mb-4">{{ $t('cosmwasm.title') }}</h2>
|
||||
<div class="grid grid-flow-col auto-cols-max gap-4 overflow-hidden">
|
||||
<div class="join w-full border border-primary">
|
||||
<select v-model="field" class="select select-primary">
|
||||
<option value="contract">Contract</option>
|
||||
<option value="creator">Creator</option>
|
||||
</select>
|
||||
<input
|
||||
v-model="creator"
|
||||
type="text"
|
||||
class="input input-bordered w-full join-item"
|
||||
placeholder="address"
|
||||
/>
|
||||
<button class="join-item btn btn-primary" @click="myContracts()">
|
||||
{{ $t('cosmwasm.btn_query') }}
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<select
|
||||
v-model="togo"
|
||||
class="select select-primary"
|
||||
@change="gotoHistory()"
|
||||
>
|
||||
<option value="">History</option>
|
||||
<option v-for="(v, index) in history" :key="index" :value="v">
|
||||
...{{ String(v).substring(45) }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-compact w-full mt-4 text-sm">
|
||||
<thead class="bg-base-200">
|
||||
<tr>
|
||||
<th>{{ $t('cosmwasm.code_id') }}</th>
|
||||
<th>{{ $t('cosmwasm.code_hash') }}</th>
|
||||
<th>{{ $t('cosmwasm.creator') }}</th>
|
||||
<th>{{ $t('cosmwasm.permissions') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(v, index) in codes.code_infos" :key="index">
|
||||
<td>{{ v.code_id }}</td>
|
||||
<td>
|
||||
<RouterLink
|
||||
:to="`/${props.chain}/cosmwasm/${v.code_id}/contracts`"
|
||||
class="truncate max-w-[200px] block text-primary dark:invert"
|
||||
:title="v.data_hash"
|
||||
>
|
||||
{{ v.data_hash }}
|
||||
</RouterLink>
|
||||
</td>
|
||||
<td>{{ v.creator }}</td>
|
||||
<td>
|
||||
{{ v.instantiate_permission?.permission }}
|
||||
<span
|
||||
>{{ v.instantiate_permission?.address }}
|
||||
{{ v.instantiate_permission?.addresses.join(', ') }}</span
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="flex justify-between">
|
||||
<PaginationBar
|
||||
:limit="pageRequest.limit"
|
||||
:total="codes.pagination?.total"
|
||||
:callback="pageload"
|
||||
/>
|
||||
<label
|
||||
for="wasm_store_code"
|
||||
class="btn btn-primary my-5"
|
||||
@click="dialog.open('wasm_store_code', {})"
|
||||
>{{ $t('cosmwasm.btn_up_sc') }}</label
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<route>
|
||||
|
||||
@@ -7,163 +7,217 @@ import { ref, onMounted, computed } from 'vue';
|
||||
const chainStore = useBlockchain();
|
||||
const format = useFormatter();
|
||||
interface FaucetResponse {
|
||||
status: string;
|
||||
result: any;
|
||||
message: string;
|
||||
status: string;
|
||||
result: any;
|
||||
message: string;
|
||||
}
|
||||
|
||||
const address = ref('');
|
||||
const faucet = ref('');
|
||||
const balances = ref([]);
|
||||
const faucetModal = ref(false);
|
||||
const ret = ref({} as FaucetResponse);
|
||||
const ret = ref({} as FaucetResponse);
|
||||
const configChecker = ref('');
|
||||
|
||||
const checklist = computed(() => {
|
||||
const endpoint = chainStore.current?.endpoints?.rest
|
||||
const bs = balances.value.length > 0 && balances.value.findIndex((v:any) => v.amount <= 10) === -1;
|
||||
return [
|
||||
{ title: 'Rest Endpoint', status: endpoint && endpoint[0].address !== '' },
|
||||
{ title: 'Faucet Configured', status: chainStore.current?.faucet !== undefined },
|
||||
{ title: 'Faucet Account', status: faucet.value !== ''},
|
||||
{ title: 'Faucet Balance', status: bs},
|
||||
];
|
||||
const endpoint = chainStore.current?.endpoints?.rest;
|
||||
const bs =
|
||||
balances.value.length > 0 &&
|
||||
balances.value.findIndex((v: any) => v.amount <= 10) === -1;
|
||||
return [
|
||||
{ title: 'Rest Endpoint', status: endpoint && endpoint[0].address !== '' },
|
||||
{
|
||||
title: 'Faucet Configured',
|
||||
status: chainStore.current?.faucet !== undefined,
|
||||
},
|
||||
{ title: 'Faucet Account', status: faucet.value !== '' },
|
||||
{ title: 'Faucet Balance', status: bs },
|
||||
];
|
||||
});
|
||||
|
||||
const notReady = computed(() => {
|
||||
for (const it of checklist.value) {
|
||||
if (!it.status) return true;
|
||||
}
|
||||
return false;
|
||||
for (const it of checklist.value) {
|
||||
if (!it.status) return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
const validAddress = computed(() => {
|
||||
if (!address.value) return true;
|
||||
return address.value.startsWith(chainStore.current?.bech32Prefix || '1');
|
||||
if (!address.value) return true;
|
||||
return address.value.startsWith(chainStore.current?.bech32Prefix || '1');
|
||||
});
|
||||
|
||||
const faucetUrl = computed(() => {
|
||||
return `https://faucet.ping.pub/${chainStore.current?.chainName}`;
|
||||
// return `http://localhost:3000/${chainStore.current?.chainName}`;
|
||||
return `https://faucet.ping.pub/${chainStore.current?.chainName}`;
|
||||
// return `http://localhost:3000/${chainStore.current?.chainName}`;
|
||||
});
|
||||
|
||||
|
||||
function claim() {
|
||||
|
||||
ret.value = {} as FaucetResponse;
|
||||
const prefix = chainStore.current?.bech32Prefix || 'cosmos';
|
||||
if (!address.value ) return;
|
||||
faucetModal.value = true;
|
||||
// @ts-ignore
|
||||
get(`${faucetUrl.value}/send/${address.value}`).then( (res: FaucetResponse) => {
|
||||
console.log(res);
|
||||
ret.value = res;
|
||||
});
|
||||
ret.value = {} as FaucetResponse;
|
||||
const prefix = chainStore.current?.bech32Prefix || 'cosmos';
|
||||
if (!address.value) return;
|
||||
faucetModal.value = true;
|
||||
// @ts-ignore
|
||||
get(`${faucetUrl.value}/send/${address.value}`).then(
|
||||
(res: FaucetResponse) => {
|
||||
console.log(res);
|
||||
ret.value = res;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function balance() {
|
||||
get(`${faucetUrl.value}/balance`).then(res => {
|
||||
if(res.status === 'error') {
|
||||
configChecker.value = res.message;
|
||||
return;
|
||||
}
|
||||
balances.value = res.result?.balance;
|
||||
faucet.value = res.result?.address;
|
||||
});
|
||||
get(`${faucetUrl.value}/balance`).then((res) => {
|
||||
if (res.status === 'error') {
|
||||
configChecker.value = res.message;
|
||||
return;
|
||||
}
|
||||
balances.value = res.result?.balance;
|
||||
faucet.value = res.result?.address;
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (chainStore.current && chainStore.current.faucet) {
|
||||
balance();
|
||||
}
|
||||
if (chainStore.current && chainStore.current.faucet) {
|
||||
balance();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex flex-col items-center justify-center mb-6 mt-14 gap-4">
|
||||
<img v-if="chainStore.current?.logo" :src="`${chainStore.current?.logo}`" class="w-16 rounded-md" />
|
||||
<div v-else class="w-16 rounded-full">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 150.000000 132.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<g transform="translate(0.000000,132.000000) scale(0.100000,-0.100000)" fill="#666CFF" class=""
|
||||
stroke="none">
|
||||
<path d="M500 1310 l-125 -5 -182 -315 c-100 -173 -182 -321 -182 -329 -1 -7
|
||||
<div>
|
||||
<div class="flex flex-col items-center justify-center mb-6 mt-14 gap-4">
|
||||
<img
|
||||
v-if="chainStore.current?.logo"
|
||||
:src="`${chainStore.current?.logo}`"
|
||||
class="w-16 rounded-md"
|
||||
/>
|
||||
<div v-else class="w-16 rounded-full">
|
||||
<svg
|
||||
version="1.0"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 150.000000 132.000000"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
>
|
||||
<g
|
||||
transform="translate(0.000000,132.000000) scale(0.100000,-0.100000)"
|
||||
fill="#666CFF"
|
||||
class=""
|
||||
stroke="none"
|
||||
>
|
||||
<path
|
||||
d="M500 1310 l-125 -5 -182 -315 c-100 -173 -182 -321 -182 -329 -1 -7
|
||||
81 -159 181 -337 l183 -324 372 0 371 0 186 325 c102 179 186 330 186 337 0 7
|
||||
-82 157 -182 335 l-183 323 -250 -2 c-137 -1 -306 -5 -375 -8z m588 -454 c61
|
||||
-106 112 -197 112 -201 0 -4 -50 -95 -111 -201 l-112 -194 -231 0 -231 0 -105
|
||||
181 c-58 100 -109 190 -114 200 -6 14 17 63 104 213 l112 196 232 0 231 0 113
|
||||
-194z" />
|
||||
<path d="M591 1001 l-54 -6 -87 -150 -88 -150 176 -3 c97 -1 181 -1 187 2 9 3
|
||||
165 267 183 308 4 9 -233 7 -317 -1z" />
|
||||
<path d="M872 824 l-90 -159 36 -66 c113 -201 147 -258 153 -251 8 8 179 302
|
||||
179 307 0 2 -37 68 -83 147 -46 78 -88 151 -94 162 -9 16 -24 -5 -101 -140z" />
|
||||
<path d="M360 625 c0 -7 148 -263 172 -297 l19 -28 186 0 c101 0 183 3 181 8
|
||||
-1 4 -43 78 -93 165 l-90 157 -187 0 c-104 0 -188 -2 -188 -5z" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<h1 class="text-primary text-3xl md:!text-6xl font-bold capitalize">
|
||||
{{ chainStore.chainName }} Faucet
|
||||
</h1>
|
||||
</div>
|
||||
<div class="bg-base-100 my-5 px-4 pt-3 pb-4 rounded shadow">
|
||||
<h2 class="card-title">Get Tokens</h2>
|
||||
<input type="text" v-model="address" class="mt-4 mb-4 w-full border border-gray-300 rounded-md p-2"
|
||||
:class="{'input-error' : !validAddress}"
|
||||
:disabled="notReady" placeholder="Enter your address" />
|
||||
<button class="btn btn-primary w-full bg-primary text-white" :disabled="notReady" @click="claim()">Get
|
||||
Tokens</button>
|
||||
</div>
|
||||
|
||||
<AdBanner id="home-banner-ad" unit="banner" />
|
||||
|
||||
<div class="bg-base-100 my-5 px-4 pt-3 pb-4 rounded shadow">
|
||||
<h2 class="card-title">Enable Faucet</h2>
|
||||
<div class="mt-4">
|
||||
<span class="text-base"> 1. Submit chain configuration</span>
|
||||
<div class="mockup-code bg-base-200 my-2 gap-4">
|
||||
<div v-for="it in checklist">
|
||||
<pre><code class="text-gray-800 dark:invert">{{ it.title }}: </code>{{ it.status ? '✅' : '❌' }} </pre>
|
||||
</div>
|
||||
|
||||
<pre class=" text-xs text-red-500">{{ configChecker }}</pre>
|
||||
<pre></pre>
|
||||
<a class=" btn-ghost text-white rounded-md p-2 ml-4"
|
||||
href="https://github.com/ping-pub/ping.pub/blob/main/faucet.md">Update</a>
|
||||
</div>
|
||||
|
||||
<span class="text-base"> 2. Fund the faucet account</span>
|
||||
<div class="mockup-code bg-base-200 my-2">
|
||||
<pre data-prefix=">"><code class=" text-gray-800 dark:invert"> Faucet Address: {{ faucet }} </code></pre>
|
||||
<pre
|
||||
data-prefix=">"><code class="text-gray-800 dark:invert"> Balances: {{ format.formatTokens(balances) }} </code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input type="checkbox" v-model="faucetModal" id="my_modal_6" class="modal-toggle" />
|
||||
<div class="modal" role="dialog">
|
||||
<div class="modal-box">
|
||||
<div v-if="ret.status === 'error'">
|
||||
<h3 class="font-bold text-red-500"> Error </h3>
|
||||
<div>{{ ret.message }}</div>
|
||||
</div>
|
||||
<div v-else-if="ret.status === 'ok'">
|
||||
<h3 class="font-bold text-green-500"> Token Sent! </h3>
|
||||
<div class=" text-center mt-4"><RouterLink :to="`/${chainStore.chainName}/tx/${ret.result.txhash}`">View Transaction</RouterLink></div>
|
||||
</div>
|
||||
<h3 v-else class="font-bold text-lg"> Processing <span class="loading loading-bars loading-sm"></span> </h3>
|
||||
|
||||
<div class="modal-action">
|
||||
<label for="my_modal_6" class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</label>
|
||||
</div>
|
||||
<p class="py-2">
|
||||
<div>
|
||||
<AdBanner id="popup-ad" unit="popup" />
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
-194z"
|
||||
/>
|
||||
<path
|
||||
d="M591 1001 l-54 -6 -87 -150 -88 -150 176 -3 c97 -1 181 -1 187 2 9 3
|
||||
165 267 183 308 4 9 -233 7 -317 -1z"
|
||||
/>
|
||||
<path
|
||||
d="M872 824 l-90 -159 36 -66 c113 -201 147 -258 153 -251 8 8 179 302
|
||||
179 307 0 2 -37 68 -83 147 -46 78 -88 151 -94 162 -9 16 -24 -5 -101 -140z"
|
||||
/>
|
||||
<path
|
||||
d="M360 625 c0 -7 148 -263 172 -297 l19 -28 186 0 c101 0 183 3 181 8
|
||||
-1 4 -43 78 -93 165 l-90 157 -187 0 c-104 0 -188 -2 -188 -5z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<h1 class="text-primary text-3xl md:!text-6xl font-bold capitalize">
|
||||
{{ chainStore.chainName }} Faucet
|
||||
</h1>
|
||||
</div>
|
||||
<div class="bg-base-100 my-5 px-4 pt-3 pb-4 rounded shadow">
|
||||
<h2 class="card-title">Get Tokens</h2>
|
||||
<input
|
||||
type="text"
|
||||
v-model="address"
|
||||
class="mt-4 mb-4 w-full border border-gray-300 rounded-md p-2"
|
||||
:class="{ 'input-error': !validAddress }"
|
||||
:disabled="notReady"
|
||||
placeholder="Enter your address"
|
||||
/>
|
||||
<button
|
||||
class="btn btn-primary w-full bg-primary text-white"
|
||||
:disabled="notReady"
|
||||
@click="claim()"
|
||||
>
|
||||
Get Tokens
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<AdBanner id="home-banner-ad" unit="banner" />
|
||||
|
||||
<div class="bg-base-100 my-5 px-4 pt-3 pb-4 rounded shadow">
|
||||
<h2 class="card-title">Enable Faucet</h2>
|
||||
<div class="mt-4">
|
||||
<span class="text-base"> 1. Submit chain configuration</span>
|
||||
<div class="mockup-code bg-base-200 my-2 gap-4">
|
||||
<div v-for="it in checklist">
|
||||
<pre><code class="text-gray-800 dark:invert">{{ it.title }}: </code>{{ it.status ? '✅' : '❌' }} </pre>
|
||||
</div>
|
||||
|
||||
<pre class="text-xs text-red-500">{{ configChecker }}</pre>
|
||||
<pre></pre>
|
||||
<a
|
||||
class="btn-ghost text-white rounded-md p-2 ml-4"
|
||||
href="https://github.com/ping-pub/ping.pub/blob/main/faucet.md"
|
||||
>Update</a
|
||||
>
|
||||
</div>
|
||||
|
||||
<span class="text-base"> 2. Fund the faucet account</span>
|
||||
<div class="mockup-code bg-base-200 my-2">
|
||||
<pre
|
||||
data-prefix=">"
|
||||
><code class=" text-gray-800 dark:invert"> Faucet Address: {{ faucet }} </code></pre>
|
||||
<pre
|
||||
data-prefix=">"
|
||||
><code class="text-gray-800 dark:invert"> Balances: {{ format.formatTokens(balances) }} </code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
type="checkbox"
|
||||
v-model="faucetModal"
|
||||
id="my_modal_6"
|
||||
class="modal-toggle"
|
||||
/>
|
||||
<div class="modal" role="dialog">
|
||||
<div class="modal-box">
|
||||
<div v-if="ret.status === 'error'">
|
||||
<h3 class="font-bold text-red-500">Error</h3>
|
||||
<div>{{ ret.message }}</div>
|
||||
</div>
|
||||
<div v-else-if="ret.status === 'ok'">
|
||||
<h3 class="font-bold text-green-500">Token Sent!</h3>
|
||||
<div class="text-center mt-4">
|
||||
<RouterLink :to="`/${chainStore.chainName}/tx/${ret.result.txhash}`"
|
||||
>View Transaction</RouterLink
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<h3 v-else class="font-bold text-lg">
|
||||
Processing <span class="loading loading-bars loading-sm"></span>
|
||||
</h3>
|
||||
|
||||
<div class="modal-action">
|
||||
<label
|
||||
for="my_modal_6"
|
||||
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
|
||||
>✕</label
|
||||
>
|
||||
</div>
|
||||
<div class="py-2">
|
||||
<div>
|
||||
<AdBanner id="popup-ad" unit="popup" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -22,7 +22,6 @@ import Countdown from '@/components/Countdown.vue';
|
||||
import PaginationBar from '@/components/PaginationBar.vue';
|
||||
import { fromBech32, toHex } from '@cosmjs/encoding';
|
||||
|
||||
|
||||
const props = defineProps(['proposal_id', 'chain']);
|
||||
const proposal = ref({} as GovProposal);
|
||||
const format = useFormatter();
|
||||
@@ -41,35 +40,35 @@ store.fetchProposal(props.proposal_id).then((res) => {
|
||||
}
|
||||
proposal.value = proposalDetail;
|
||||
// load origin params if the proposal is param change
|
||||
if(proposalDetail.content?.changes) {
|
||||
proposalDetail.content?.changes.forEach((item) => {
|
||||
chainStore.rpc.getParams(item.subspace, item.key).then((res) => {
|
||||
if(proposal.value.content && res.param) {
|
||||
if(proposal.value.content.current){
|
||||
proposal.value.content.current.push(res.param);
|
||||
} else {
|
||||
proposal.value.content.current = [res.param];
|
||||
};
|
||||
if (proposalDetail.content?.changes) {
|
||||
proposalDetail.content?.changes.forEach((item) => {
|
||||
chainStore.rpc.getParams(item.subspace, item.key).then((res) => {
|
||||
if (proposal.value.content && res.param) {
|
||||
if (proposal.value.content.current) {
|
||||
proposal.value.content.current.push(res.param);
|
||||
} else {
|
||||
proposal.value.content.current = [res.param];
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const msgType = proposalDetail.content?.['@type'] || '';
|
||||
if(msgType.endsWith('MsgUpdateParams')) {
|
||||
if(msgType.indexOf('staking') > -1) {
|
||||
if (msgType.endsWith('MsgUpdateParams')) {
|
||||
if (msgType.indexOf('staking') > -1) {
|
||||
chainStore.rpc.getStakingParams().then((res) => {
|
||||
addCurrentParams(res);
|
||||
});
|
||||
} else if(msgType.indexOf('gov') > -1) {
|
||||
} else if (msgType.indexOf('gov') > -1) {
|
||||
chainStore.rpc.getGovParamsVoting().then((res) => {
|
||||
addCurrentParams(res);
|
||||
});
|
||||
} else if(msgType.indexOf('distribution') > -1) {
|
||||
} else if (msgType.indexOf('distribution') > -1) {
|
||||
chainStore.rpc.getDistributionParams().then((res) => {
|
||||
addCurrentParams(res);
|
||||
});
|
||||
} else if(msgType.indexOf('slashing') > -1) {
|
||||
} else if (msgType.indexOf('slashing') > -1) {
|
||||
chainStore.rpc.getSlashingParams().then((res) => {
|
||||
addCurrentParams(res);
|
||||
});
|
||||
@@ -78,7 +77,7 @@ store.fetchProposal(props.proposal_id).then((res) => {
|
||||
});
|
||||
|
||||
function addCurrentParams(res: any) {
|
||||
if(proposal.value.content && res.params) {
|
||||
if (proposal.value.content && res.params) {
|
||||
proposal.value.content.params = [proposal.value.content?.params];
|
||||
proposal.value.content.current = [res.params];
|
||||
}
|
||||
@@ -128,7 +127,9 @@ const upgradeCountdown = computed((): number => {
|
||||
if (height > 0) {
|
||||
const base = useBaseStore();
|
||||
const current = Number(base.latest?.block?.header?.height || 0);
|
||||
return (height - current) * Number((base.blocktime / 1000).toFixed()) * 1000;
|
||||
return (
|
||||
(height - current) * Number((base.blocktime / 1000).toFixed()) * 1000
|
||||
);
|
||||
}
|
||||
const now = new Date();
|
||||
const end = new Date(proposal.value.content?.plan?.time || '');
|
||||
@@ -198,14 +199,14 @@ const processList = computed(() => {
|
||||
|
||||
function showValidatorName(voter: string) {
|
||||
try {
|
||||
const { data } = fromBech32(voter);
|
||||
const hex = toHex(data);
|
||||
const v = stakingStore.validators.find(
|
||||
(x) => toHex(fromBech32(x.operator_address).data) === hex
|
||||
);
|
||||
return v ? v.description.moniker : voter;
|
||||
} catch(e){
|
||||
return voter;
|
||||
const { data } = fromBech32(voter);
|
||||
const hex = toHex(data);
|
||||
const v = stakingStore.validators.find(
|
||||
(x) => toHex(fromBech32(x.operator_address).data) === hex
|
||||
);
|
||||
return v ? v.description.moniker : voter;
|
||||
} catch (e) {
|
||||
return voter;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,22 +218,32 @@ function pageload(p: number) {
|
||||
});
|
||||
}
|
||||
|
||||
function metaItem(metadata: string|undefined): { title: string; summary: string } {
|
||||
function metaItem(metadata: string | undefined): {
|
||||
title: string;
|
||||
summary: string;
|
||||
} {
|
||||
if (!metadata) {
|
||||
return { title: '', summary: '' }
|
||||
return { title: '', summary: '' };
|
||||
} else if (metadata.startsWith('{') && metadata.endsWith('}')) {
|
||||
return JSON.parse(metadata)
|
||||
return JSON.parse(metadata);
|
||||
}
|
||||
return { title: metadata, summary: '' }
|
||||
return { title: metadata, summary: '' };
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<h2 class="card-title flex flex-col md:!justify-between md:!flex-row mb-2">
|
||||
<h2
|
||||
class="card-title flex flex-col md:!justify-between md:!flex-row mb-2"
|
||||
>
|
||||
<p class="truncate w-full">
|
||||
{{ proposal_id }}. {{ proposal.title || proposal.content?.title || metaItem(proposal?.metadata)?.title }}
|
||||
{{ proposal_id }}.
|
||||
{{
|
||||
proposal.title ||
|
||||
proposal.content?.title ||
|
||||
metaItem(proposal?.metadata)?.title
|
||||
}}
|
||||
</p>
|
||||
<div
|
||||
class="badge badge-ghost"
|
||||
@@ -250,9 +261,18 @@ function metaItem(metadata: string|undefined): { title: string; summary: string
|
||||
<div class="">
|
||||
<ObjectElement :value="proposal.content" />
|
||||
</div>
|
||||
<div v-if="proposal.summary && !proposal.content?.description || metaItem(proposal?.metadata)?.summary ">
|
||||
<div
|
||||
v-if="
|
||||
(proposal.summary && !proposal.content?.description) ||
|
||||
metaItem(proposal?.metadata)?.summary
|
||||
"
|
||||
>
|
||||
<MdEditor
|
||||
:model-value="format.multiLine(proposal.summary || metaItem(proposal?.metadata)?.summary)"
|
||||
:model-value="
|
||||
format.multiLine(
|
||||
proposal.summary || metaItem(proposal?.metadata)?.summary
|
||||
)
|
||||
"
|
||||
previewOnly
|
||||
class="md-editor-recover"
|
||||
></MdEditor>
|
||||
@@ -308,7 +328,8 @@ function metaItem(metadata: string|undefined): { title: string; summary: string
|
||||
<div class="flex items-center mb-4 mt-2">
|
||||
<div class="w-2 h-2 rounded-full bg-error mr-3"></div>
|
||||
<div class="text-base flex-1 text-main">
|
||||
{{ $t('gov.submit_at') }}: {{ format.toDay(proposal.submit_time) }}
|
||||
{{ $t('gov.submit_at') }}:
|
||||
{{ format.toDay(proposal.submit_time) }}
|
||||
</div>
|
||||
<div class="text-sm">{{ shortTime(proposal.submit_time) }}</div>
|
||||
</div>
|
||||
@@ -338,7 +359,8 @@ function metaItem(metadata: string|undefined): { title: string; summary: string
|
||||
<div class="flex items-center">
|
||||
<div class="w-2 h-2 rounded-full bg-yes mr-3"></div>
|
||||
<div class="text-base flex-1 text-main">
|
||||
{{ $t('gov.vote_start_from') }} {{ format.toDay(proposal.voting_start_time) }}
|
||||
{{ $t('gov.vote_start_from') }}
|
||||
{{ format.toDay(proposal.voting_start_time) }}
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
{{ shortTime(proposal.voting_start_time) }}
|
||||
@@ -352,14 +374,16 @@ function metaItem(metadata: string|undefined): { title: string; summary: string
|
||||
<div class="flex items-center mb-1">
|
||||
<div class="w-2 h-2 rounded-full bg-success mr-3"></div>
|
||||
<div class="text-base flex-1 text-main">
|
||||
{{ $t('gov.vote_end') }} {{ format.toDay(proposal.voting_end_time) }}
|
||||
{{ $t('gov.vote_end') }}
|
||||
{{ format.toDay(proposal.voting_end_time) }}
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
{{ shortTime(proposal.voting_end_time) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pl-5 text-sm">
|
||||
{{ $t('gov.current_status') }}: {{ $t(`gov.proposal_statuses.${proposal.status}`) }}
|
||||
{{ $t('gov.current_status') }}:
|
||||
{{ $t(`gov.proposal_statuses.${proposal.status}`) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -409,11 +433,18 @@ function metaItem(metadata: string|undefined): { title: string; summary: string
|
||||
>
|
||||
{{ String(item.option).replace('VOTE_OPTION_', '') }}
|
||||
</td>
|
||||
<td
|
||||
v-if="item.options"
|
||||
class="py-2 text-sm"
|
||||
>
|
||||
{{ item.options.map(x => `${x.option.replace('VOTE_OPTION_', '')}:${format.percent(x.weight)}`).join(', ') }}
|
||||
<td v-if="item.options" class="py-2 text-sm">
|
||||
{{
|
||||
item.options
|
||||
.map(
|
||||
(x) =>
|
||||
`${x.option.replace(
|
||||
'VOTE_OPTION_',
|
||||
''
|
||||
)}:${format.percent(x.weight)}`
|
||||
)
|
||||
.join(', ')
|
||||
}}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -7,40 +7,57 @@ import { PageRequest } from '@/types';
|
||||
|
||||
const tab = ref('2');
|
||||
const store = useGovStore();
|
||||
const pageRequest = ref(new PageRequest())
|
||||
const pageRequest = ref(new PageRequest());
|
||||
|
||||
onMounted(() => {
|
||||
store.fetchProposals('2').then((x) => {
|
||||
if (x?.proposals?.length === 0) {
|
||||
tab.value = '3';
|
||||
store.fetchProposals('3');
|
||||
}
|
||||
store.fetchProposals('3');
|
||||
store.fetchProposals('4');
|
||||
});
|
||||
store.fetchProposals('2').then((x) => {
|
||||
if (x?.proposals?.length === 0) {
|
||||
tab.value = '3';
|
||||
store.fetchProposals('3');
|
||||
}
|
||||
store.fetchProposals('3');
|
||||
store.fetchProposals('4');
|
||||
});
|
||||
});
|
||||
|
||||
const changeTab = (val: '2' | '3' | '4') => {
|
||||
tab.value = val;
|
||||
tab.value = val;
|
||||
};
|
||||
|
||||
function page(p: number) {
|
||||
pageRequest.value.setPage(p)
|
||||
store.fetchProposals(tab.value, pageRequest.value)
|
||||
pageRequest.value.setPage(p);
|
||||
store.fetchProposals(tab.value, pageRequest.value);
|
||||
}
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div class="tabs tabs-boxed bg-transparent mb-4 text-center">
|
||||
<a class="tab text-gray-400 uppercase" :class="{ 'tab-active': tab === '2' }" @click="changeTab('2')">{{ $t('gov.voting') }}</a>
|
||||
<a class="tab text-gray-400 uppercase" :class="{ 'tab-active': tab === '3' }" @click="changeTab('3')">{{ $t('gov.passed') }}</a>
|
||||
<a class="tab text-gray-400 uppercase" :class="{ 'tab-active': tab === '4' }"
|
||||
@click="changeTab('4')">{{ $t('gov.rejected') }}</a>
|
||||
</div>
|
||||
<ProposalListItem :proposals="store?.proposals[tab]" />
|
||||
<PaginationBar :total="store?.proposals[tab]?.pagination?.total" :limit="pageRequest.limit" :callback="page" />
|
||||
<div>
|
||||
<div class="tabs tabs-boxed bg-transparent mb-4 text-center">
|
||||
<a
|
||||
class="tab text-gray-400 uppercase"
|
||||
:class="{ 'tab-active': tab === '2' }"
|
||||
@click="changeTab('2')"
|
||||
>{{ $t('gov.voting') }}</a
|
||||
>
|
||||
<a
|
||||
class="tab text-gray-400 uppercase"
|
||||
:class="{ 'tab-active': tab === '3' }"
|
||||
@click="changeTab('3')"
|
||||
>{{ $t('gov.passed') }}</a
|
||||
>
|
||||
<a
|
||||
class="tab text-gray-400 uppercase"
|
||||
:class="{ 'tab-active': tab === '4' }"
|
||||
@click="changeTab('4')"
|
||||
>{{ $t('gov.rejected') }}</a
|
||||
>
|
||||
</div>
|
||||
<ProposalListItem :proposals="store?.proposals[tab]" />
|
||||
<PaginationBar
|
||||
:total="store?.proposals[tab]?.pagination?.total"
|
||||
:limit="pageRequest.limit"
|
||||
:callback="page"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<route>
|
||||
{
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
<script lang="ts" setup>
|
||||
import { fromBech32, fromHex, toBech32 } from '@cosmjs/encoding';
|
||||
|
||||
let x = toBech32("neutronvaloper1", fromHex("3363E8F97B02ECC00289E72173D827543047ACDA"))
|
||||
let add = fromBech32("cosmosvaloper1jxv0u20scum4trha72c7ltfgfqef6nsch7q6cu")
|
||||
let op = toBech32("neutronvaloper1", add.data)
|
||||
console.log(x)
|
||||
console.log(op)
|
||||
let x = toBech32(
|
||||
'neutronvaloper1',
|
||||
fromHex('3363E8F97B02ECC00289E72173D827543047ACDA')
|
||||
);
|
||||
let add = fromBech32('cosmosvaloper1jxv0u20scum4trha72c7ltfgfqef6nsch7q6cu');
|
||||
let op = toBech32('neutronvaloper1', add.data);
|
||||
console.log(x);
|
||||
console.log(op);
|
||||
</script>
|
||||
<template>
|
||||
<div>address: </div>
|
||||
</template>
|
||||
<div>address:</div>
|
||||
</template>
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
import {useBlockchain} from '@/stores'
|
||||
import { useBlockchain } from '@/stores';
|
||||
import ChainRegistryClient from '@ping-pub/chain-registry-client';
|
||||
import type { IBCPath, IBCInfo, } from '@ping-pub/chain-registry-client/dist/types';
|
||||
import type {
|
||||
IBCPath,
|
||||
IBCInfo,
|
||||
} from '@ping-pub/chain-registry-client/dist/types';
|
||||
import type { Channel } from '@/types';
|
||||
import router from '@/router';
|
||||
|
||||
@@ -11,38 +13,51 @@ export const useIBCModule = defineStore('module-ibc', {
|
||||
state: () => {
|
||||
return {
|
||||
paths: [] as IBCPath[],
|
||||
connectionId: "" as string,
|
||||
connectionId: '' as string,
|
||||
registryConf: {} as IBCInfo,
|
||||
};
|
||||
},
|
||||
getters: {
|
||||
chain() {
|
||||
return useBlockchain()
|
||||
return useBlockchain();
|
||||
},
|
||||
commonIBCs(): any {
|
||||
return this.paths.filter((x: IBCPath) => x.path.search(this.chain.current?.prettyName || this.chain.chainName) > -1)
|
||||
return this.paths.filter(
|
||||
(x: IBCPath) =>
|
||||
x.path.search(
|
||||
this.chain.current?.prettyName || this.chain.chainName
|
||||
) > -1
|
||||
);
|
||||
},
|
||||
sourceField(): string {
|
||||
return this.registryConf?.chain_1?.chain_name === this.chain.current?.prettyName || this.chain.chainName ? 'chain_1': 'chain_2'
|
||||
return this.registryConf?.chain_1?.chain_name ===
|
||||
this.chain.current?.prettyName || this.chain.chainName
|
||||
? 'chain_1'
|
||||
: 'chain_2';
|
||||
},
|
||||
destField() : string {
|
||||
return this.registryConf?.chain_1?.chain_name === this.chain.current?.prettyName || this.chain.chainName ? 'chain_2': 'chain_1'
|
||||
destField(): string {
|
||||
return this.registryConf?.chain_1?.chain_name ===
|
||||
this.chain.current?.prettyName || this.chain.chainName
|
||||
? 'chain_2'
|
||||
: 'chain_1';
|
||||
},
|
||||
registryChannels(): any {
|
||||
return this.registryConf.channels
|
||||
}
|
||||
return this.registryConf.channels;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
load() {
|
||||
const client = new ChainRegistryClient();
|
||||
client.fetchIBCPaths().then(res => {
|
||||
this.paths = res
|
||||
client.fetchIBCPaths().then((res) => {
|
||||
this.paths = res;
|
||||
});
|
||||
},
|
||||
fetchConnection(path: string) {
|
||||
const client = new ChainRegistryClient();
|
||||
client.fetchIBCPathInfo(path).then(res => {
|
||||
const isFirstChain = res.chain_1.chain_name === this.chain.current?.prettyName || res.chain_1.chain_name === this.chain.chainName;
|
||||
client.fetchIBCPathInfo(path).then((res) => {
|
||||
const isFirstChain =
|
||||
res.chain_1.chain_name === this.chain.current?.prettyName ||
|
||||
res.chain_1.chain_name === this.chain.chainName;
|
||||
|
||||
const connId = isFirstChain
|
||||
? res.chain_1.connection_id
|
||||
@@ -50,14 +65,16 @@ export const useIBCModule = defineStore('module-ibc', {
|
||||
|
||||
this.registryConf = res;
|
||||
this.showConnection(connId);
|
||||
})
|
||||
});
|
||||
},
|
||||
showConnection(connId?: string | number) {
|
||||
if(!connId) {
|
||||
this.registryConf = {} as any
|
||||
if (!connId) {
|
||||
this.registryConf = {} as any;
|
||||
}
|
||||
const path = `/${this.chain.chainName}/ibc/connection/${connId || `connection-${this.connectionId || 0}`}`
|
||||
router.push(path)
|
||||
}
|
||||
const path = `/${this.chain.chainName}/ibc/connection/${
|
||||
connId || `connection-${this.connectionId || 0}`
|
||||
}`;
|
||||
router.push(path);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -12,50 +12,76 @@ import { useIBCModule } from './connStore';
|
||||
|
||||
const props = defineProps(['chain']);
|
||||
const chainStore = useBlockchain();
|
||||
const ibcStore = useIBCModule()
|
||||
const ibcStore = useIBCModule();
|
||||
const list = ref([] as Connection[]);
|
||||
const pageRequest = ref(new PageRequest())
|
||||
const pageResponse = ref({} as Pagination)
|
||||
const pageRequest = ref(new PageRequest());
|
||||
const pageResponse = ref({} as Pagination);
|
||||
const tab = ref('registry');
|
||||
|
||||
onMounted(() => {
|
||||
pageload(1)
|
||||
ibcStore.load()
|
||||
pageload(1);
|
||||
ibcStore.load();
|
||||
});
|
||||
|
||||
function pageload(p: number) {
|
||||
pageRequest.value.setPage(p)
|
||||
pageRequest.value.setPage(p);
|
||||
chainStore.rpc.getIBCConnections(pageRequest.value).then((x) => {
|
||||
list.value = x.connections;
|
||||
pageResponse.value = x.pagination
|
||||
if(x.pagination.total && Number(x.pagination.total) > 0) {
|
||||
ibcStore.showConnection(0)
|
||||
pageResponse.value = x.pagination;
|
||||
if (x.pagination.total && Number(x.pagination.total) > 0) {
|
||||
ibcStore.showConnection(0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded shadow">
|
||||
<div class="flex flex-wrap gap-4 items-center">
|
||||
<div class="flex flex-wrap gap-4 items-center">
|
||||
<h2 class="card-title py-4">{{ $t('ibc.title') }}</h2>
|
||||
<div class="tabs tabs-boxed">
|
||||
<a class="tab" :class="{ 'tab-active': tab === 'registry' }" @click="tab = 'registry'">{{ $t('ibc.registry') }}</a>
|
||||
<a class="tab" :class="{ 'tab-active': tab === 'favorite' }" @click="tab = 'favorite'">{{ $t('module.favorite') }}</a>
|
||||
<a
|
||||
class="tab"
|
||||
:class="{ 'tab-active': tab === 'registry' }"
|
||||
@click="tab = 'registry'"
|
||||
>{{ $t('ibc.registry') }}</a
|
||||
>
|
||||
<a
|
||||
class="tab"
|
||||
:class="{ 'tab-active': tab === 'favorite' }"
|
||||
@click="tab = 'favorite'"
|
||||
>{{ $t('module.favorite') }}</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div v-show="tab === 'registry'" class="flex flex-wrap gap-1 p-4 ">
|
||||
<span v-for="s in ibcStore.commonIBCs" class="btn btn-xs btn-link mr-1" @click="ibcStore.fetchConnection(s.path)">{{ s.from }}
|
||||
⇌ {{ s.to }}</span>
|
||||
<div v-show="tab === 'registry'" class="flex flex-wrap gap-1 p-4">
|
||||
<span
|
||||
v-for="s in ibcStore.commonIBCs"
|
||||
class="btn btn-xs btn-link mr-1"
|
||||
@click="ibcStore.fetchConnection(s.path)"
|
||||
>{{ s.from }} ⇌ {{ s.to }}</span
|
||||
>
|
||||
</div>
|
||||
<div v-show="tab === 'favorite'" class="flex flex-wrap gap-1 p-4 ">
|
||||
<div v-show="tab === 'favorite'" class="flex flex-wrap gap-1 p-4">
|
||||
<div class="join border border-primary">
|
||||
<button class="join-item px-2">{{ $t('ibc.connection_id') }}:</button>
|
||||
<input v-model="ibcStore.connectionId" type=number class="input input-bordered w-40 join-item" min="0"
|
||||
:max="pageResponse.total || 0" :placeholder="`0~${pageResponse.total}`" />
|
||||
<button class="join-item btn btn-primary" @click="ibcStore.showConnection()">{{ $t('ibc.btn_apply') }}</button>
|
||||
<button class="join-item px-2">
|
||||
{{ $t('ibc.connection_id') }}:
|
||||
</button>
|
||||
<input
|
||||
v-model="ibcStore.connectionId"
|
||||
type="number"
|
||||
class="input input-bordered w-40 join-item"
|
||||
min="0"
|
||||
:max="pageResponse.total || 0"
|
||||
:placeholder="`0~${pageResponse.total}`"
|
||||
/>
|
||||
<button
|
||||
class="join-item btn btn-primary"
|
||||
@click="ibcStore.showConnection()"
|
||||
>
|
||||
{{ $t('ibc.btn_apply') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
<script lang="ts" setup>
|
||||
import { formatSeconds } from '@/libs/utils';
|
||||
import { useBaseStore, useBlockchain, useFormatter } from '@/stores';
|
||||
import { type Connection, type ClientState, type Channel, PageRequest, type TxResponse, type PaginatedTxs } from '@/types';
|
||||
import {
|
||||
type Connection,
|
||||
type ClientState,
|
||||
type Channel,
|
||||
PageRequest,
|
||||
type TxResponse,
|
||||
type PaginatedTxs,
|
||||
} from '@/types';
|
||||
import { computed, onMounted } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import { useIBCModule } from '../connStore';
|
||||
@@ -12,33 +19,31 @@ const props = defineProps(['chain', 'connection_id']);
|
||||
const chainStore = useBlockchain();
|
||||
const baseStore = useBaseStore();
|
||||
const format = useFormatter();
|
||||
const ibcStore = useIBCModule()
|
||||
const ibcStore = useIBCModule();
|
||||
const conn = ref({} as Connection);
|
||||
const clientState = ref({} as { client_id: string; client_state: ClientState });
|
||||
const channels = ref([] as Channel[]);
|
||||
|
||||
const connId = computed(() => {
|
||||
return props.connection_id || 0
|
||||
})
|
||||
return props.connection_id || 0;
|
||||
});
|
||||
|
||||
const loading = ref(false)
|
||||
const txs = ref({} as PaginatedTxs)
|
||||
const direction = ref('')
|
||||
const channel_id = ref('')
|
||||
const port_id = ref('')
|
||||
const page = ref(new PageRequest())
|
||||
page.value.limit = 5
|
||||
const loading = ref(false);
|
||||
const txs = ref({} as PaginatedTxs);
|
||||
const direction = ref('');
|
||||
const channel_id = ref('');
|
||||
const port_id = ref('');
|
||||
const page = ref(new PageRequest());
|
||||
page.value.limit = 5;
|
||||
|
||||
onMounted(() => {
|
||||
if (connId.value) {
|
||||
chainStore.rpc.getIBCConnectionsById(connId.value).then((x) => {
|
||||
conn.value = x.connection;
|
||||
});
|
||||
chainStore.rpc
|
||||
.getIBCConnectionsClientState(connId.value)
|
||||
.then((x) => {
|
||||
clientState.value = x.identified_client_state;
|
||||
});
|
||||
chainStore.rpc.getIBCConnectionsClientState(connId.value).then((x) => {
|
||||
clientState.value = x.identified_client_state;
|
||||
});
|
||||
chainStore.rpc.getIBCConnectionsChannels(connId.value).then((x) => {
|
||||
channels.value = x.channels;
|
||||
});
|
||||
@@ -53,37 +58,47 @@ function loadChannel(channel: string, port: string) {
|
||||
|
||||
function pageload(pageNum: number) {
|
||||
if (direction.value === 'In') {
|
||||
fetchSendingTxs(channel_id.value, port_id.value, pageNum -1)
|
||||
fetchSendingTxs(channel_id.value, port_id.value, pageNum - 1);
|
||||
} else {
|
||||
fetchSendingTxs(channel_id.value, port_id.value, pageNum -1)
|
||||
fetchSendingTxs(channel_id.value, port_id.value, pageNum - 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function fetchSendingTxs(channel: string, port: string, pageNum = 0) {
|
||||
|
||||
page.value.setPage(pageNum)
|
||||
loading.value = true
|
||||
direction.value = 'Out'
|
||||
channel_id.value = channel
|
||||
port_id.value = port
|
||||
txs.value = {} as PaginatedTxs
|
||||
chainStore.rpc.getTxs("?order_by=2&events=send_packet.packet_src_channel='{channel}'&events=send_packet.packet_src_port='{port}'", { channel, port }, page.value).then(res => {
|
||||
txs.value = res
|
||||
})
|
||||
.finally(() => loading.value = false)
|
||||
page.value.setPage(pageNum);
|
||||
loading.value = true;
|
||||
direction.value = 'Out';
|
||||
channel_id.value = channel;
|
||||
port_id.value = port;
|
||||
txs.value = {} as PaginatedTxs;
|
||||
chainStore.rpc
|
||||
.getTxs(
|
||||
"?order_by=2&events=send_packet.packet_src_channel='{channel}'&events=send_packet.packet_src_port='{port}'",
|
||||
{ channel, port },
|
||||
page.value
|
||||
)
|
||||
.then((res) => {
|
||||
txs.value = res;
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
}
|
||||
function fetchRecevingTxs(channel: string, port: string, pageNum = 0) {
|
||||
page.value.setPage(pageNum)
|
||||
loading.value = true
|
||||
direction.value = 'In'
|
||||
channel_id.value = channel
|
||||
port_id.value = port
|
||||
txs.value = {} as PaginatedTxs
|
||||
chainStore.rpc.getTxs("?order_by=2&events=recv_packet.packet_dst_channel='{channel}'&events=recv_packet.packet_dst_port='{port}'", { channel, port }, page.value).then(res => {
|
||||
txs.value = res
|
||||
})
|
||||
.finally(() => loading.value = false)
|
||||
page.value.setPage(pageNum);
|
||||
loading.value = true;
|
||||
direction.value = 'In';
|
||||
channel_id.value = channel;
|
||||
port_id.value = port;
|
||||
txs.value = {} as PaginatedTxs;
|
||||
chainStore.rpc
|
||||
.getTxs(
|
||||
"?order_by=2&events=recv_packet.packet_dst_channel='{channel}'&events=recv_packet.packet_dst_port='{port}'",
|
||||
{ channel, port },
|
||||
page.value
|
||||
)
|
||||
.then((res) => {
|
||||
txs.value = res;
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
}
|
||||
|
||||
function color(v: string) {
|
||||
@@ -95,12 +110,14 @@ function color(v: string) {
|
||||
</script>
|
||||
<template>
|
||||
<div class="">
|
||||
<div class="px-4 pt-3 pb-4 bg-base-200 rounded mb-4 shadow ">
|
||||
<div class="px-4 pt-3 pb-4 bg-base-200 rounded mb-4 shadow">
|
||||
<div class="mx-auto max-w-7xl px-6 lg:!px-8">
|
||||
<dl class="grid grid-cols-1 gap-x-6 text-center lg:!grid-cols-3">
|
||||
<div class="mx-auto flex items-center">
|
||||
<div>
|
||||
<div class="order-first text-3xl font-semibold tracking-tight text-main mb-1">
|
||||
<div
|
||||
class="order-first text-3xl font-semibold tracking-tight text-main mb-1"
|
||||
>
|
||||
{{ baseStore.latest?.block?.header?.chain_id }}
|
||||
</div>
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">
|
||||
@@ -111,13 +128,15 @@ function color(v: string) {
|
||||
<div class="mx-auto flex items-center">
|
||||
<div :class="{ 'text-success': conn.state?.indexOf('_OPEN') > -1 }">
|
||||
<span class="text-lg rounded-full">⇌</span>
|
||||
<div class=" text-c">
|
||||
<div class="text-c">
|
||||
{{ conn.state }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mx-auto">
|
||||
<div class="order-first text-3xl font-semibold tracking-tight text-main mb-2">
|
||||
<div
|
||||
class="order-first text-3xl font-semibold tracking-tight text-main mb-2"
|
||||
>
|
||||
{{ clientState.client_state?.chain_id }}
|
||||
</div>
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">
|
||||
@@ -129,8 +148,12 @@ function color(v: string) {
|
||||
</div>
|
||||
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<h2 class="card-title mb-4 overflow-hidden">{{ $t('ibc.title_2') }}<span class="ml-2 text-sm">{{
|
||||
clientState.client_state?.['@type'] }}</span></h2>
|
||||
<h2 class="card-title mb-4 overflow-hidden">
|
||||
{{ $t('ibc.title_2')
|
||||
}}<span class="ml-2 text-sm">{{
|
||||
clientState.client_state?.['@type']
|
||||
}}</span>
|
||||
</h2>
|
||||
<div class="overflow-x-auto grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<table class="table table-sm capitalize">
|
||||
<thead class="bg-base-200">
|
||||
@@ -149,15 +172,21 @@ function color(v: string) {
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="w-52">{{ $t('ibc.trusting_period') }}:</td>
|
||||
<td>{{ formatSeconds(clientState.client_state?.trusting_period) }}</td>
|
||||
<td>
|
||||
{{ formatSeconds(clientState.client_state?.trusting_period) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="w-52">{{ $t('ibc.unbonding_period') }}:</td>
|
||||
<td>{{ formatSeconds(clientState.client_state?.unbonding_period) }}</td>
|
||||
<td>
|
||||
{{ formatSeconds(clientState.client_state?.unbonding_period) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="w-52">{{ $t('ibc.max_clock_drift') }}:</td>
|
||||
<td>{{ formatSeconds(clientState.client_state?.max_clock_drift) }}</td>
|
||||
<td>
|
||||
{{ formatSeconds(clientState.client_state?.max_clock_drift) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="w-52">{{ $t('ibc.frozen_height') }}:</td>
|
||||
@@ -178,23 +207,32 @@ function color(v: string) {
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="flex justify-between"><span>{{ $t('ibc.allow_update_after_expiry') }}:</span> <span>{{
|
||||
clientState.client_state?.allow_update_after_expiry }}</span></div>
|
||||
<div class="flex justify-between">
|
||||
<span>{{ $t('ibc.allow_update_after_expiry') }}:</span>
|
||||
<span>{{
|
||||
clientState.client_state?.allow_update_after_expiry
|
||||
}}</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="flex justify-between"><span>{{ $t('ibc.allow_update_after_misbehaviour') }}: </span> <span>{{
|
||||
clientState.client_state?.allow_update_after_misbehaviour }}</span></div>
|
||||
<div class="flex justify-between">
|
||||
<span>{{ $t('ibc.allow_update_after_misbehaviour') }}: </span>
|
||||
<span>{{
|
||||
clientState.client_state?.allow_update_after_misbehaviour
|
||||
}}</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="w-52">{{ $t('ibc.upgrade_path') }}:</td>
|
||||
<td class="text-right">{{ clientState.client_state?.upgrade_path.join(', ') }}</td>
|
||||
<td class="text-right">
|
||||
{{ clientState.client_state?.upgrade_path.join(', ') }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow overflow-hidden">
|
||||
@@ -204,7 +242,9 @@ function color(v: string) {
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $t('ibc.txs') }}</th>
|
||||
<th style="position: relative; z-index: 2">{{ $t('ibc.channel_id') }}</th>
|
||||
<th style="position: relative; z-index: 2">
|
||||
{{ $t('ibc.channel_id') }}
|
||||
</th>
|
||||
<th>{{ $t('ibc.port_id') }}</th>
|
||||
<th>{{ $t('ibc.state') }}</th>
|
||||
<th>{{ $t('ibc.counterparty') }}</th>
|
||||
@@ -217,36 +257,68 @@ function color(v: string) {
|
||||
<tr v-for="v in ibcStore.registryChannels">
|
||||
<td>
|
||||
<div class="flex gap-1">
|
||||
<button class="btn btn-xs"
|
||||
@click="fetchSendingTxs(v[ibcStore.sourceField].channel_id, v[ibcStore.sourceField].port_id)"
|
||||
:disabled="loading">
|
||||
<span v-if="loading" class="loading loading-spinner loading-sm"></span>
|
||||
<button
|
||||
class="btn btn-xs"
|
||||
@click="
|
||||
fetchSendingTxs(
|
||||
v[ibcStore.sourceField].channel_id,
|
||||
v[ibcStore.sourceField].port_id
|
||||
)
|
||||
"
|
||||
:disabled="loading"
|
||||
>
|
||||
<span
|
||||
v-if="loading"
|
||||
class="loading loading-spinner loading-sm"
|
||||
></span>
|
||||
{{ $t('ibc.btn_out') }}
|
||||
</button>
|
||||
<button class="btn btn-xs"
|
||||
@click="fetchRecevingTxs(v[ibcStore.sourceField].channel_id, v[ibcStore.sourceField].port_id)"
|
||||
:disabled="loading">
|
||||
<span v-if="loading" class="loading loading-spinner loading-sm"></span>
|
||||
<button
|
||||
class="btn btn-xs"
|
||||
@click="
|
||||
fetchRecevingTxs(
|
||||
v[ibcStore.sourceField].channel_id,
|
||||
v[ibcStore.sourceField].port_id
|
||||
)
|
||||
"
|
||||
:disabled="loading"
|
||||
>
|
||||
<span
|
||||
v-if="loading"
|
||||
class="loading loading-spinner loading-sm"
|
||||
></span>
|
||||
{{ $t('ibc.btn_in') }}
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a href="#">{{
|
||||
v[ibcStore.sourceField].channel_id
|
||||
}}</a>
|
||||
<a href="#">{{ v[ibcStore.sourceField].channel_id }}</a>
|
||||
</td>
|
||||
<td>{{ v[ibcStore.sourceField].port_id }}</td>
|
||||
</tr>
|
||||
<tr v-for="v in channels">
|
||||
<td>
|
||||
<div class="flex gap-1">
|
||||
<button class="btn btn-xs" @click="fetchSendingTxs(v.channel_id, v.port_id)" :disabled="loading">
|
||||
<span v-if="loading" class="loading loading-spinner loading-sm"></span>
|
||||
<button
|
||||
class="btn btn-xs"
|
||||
@click="fetchSendingTxs(v.channel_id, v.port_id)"
|
||||
:disabled="loading"
|
||||
>
|
||||
<span
|
||||
v-if="loading"
|
||||
class="loading loading-spinner loading-sm"
|
||||
></span>
|
||||
{{ $t('ibc.btn_out') }}
|
||||
</button>
|
||||
<button class="btn btn-xs" @click="fetchRecevingTxs(v.channel_id, v.port_id)" :disabled="loading">
|
||||
<span v-if="loading" class="loading loading-spinner loading-sm"></span>
|
||||
<button
|
||||
class="btn btn-xs"
|
||||
@click="fetchRecevingTxs(v.channel_id, v.port_id)"
|
||||
:disabled="loading"
|
||||
>
|
||||
<span
|
||||
v-if="loading"
|
||||
class="loading loading-spinner loading-sm"
|
||||
></span>
|
||||
{{ $t('ibc.btn_in') }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -258,8 +330,14 @@ function color(v: string) {
|
||||
</td>
|
||||
<td>{{ v.port_id }}</td>
|
||||
<td>
|
||||
<div class="text-xs truncate relative py-2 px-4 rounded-full w-fit" :class="`text-${color(v.state)}`">
|
||||
<span class="inset-x-0 inset-y-0 opacity-10 absolute" :class="`bg-${color(v.state)}`"></span>
|
||||
<div
|
||||
class="text-xs truncate relative py-2 px-4 rounded-full w-fit"
|
||||
:class="`text-${color(v.state)}`"
|
||||
>
|
||||
<span
|
||||
class="inset-x-0 inset-y-0 opacity-10 absolute"
|
||||
:class="`bg-${color(v.state)}`"
|
||||
></span>
|
||||
{{ v.state }}
|
||||
</div>
|
||||
</td>
|
||||
@@ -275,13 +353,15 @@ function color(v: string) {
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="channel_id">
|
||||
<h3 class=" card-title capitalize">Transactions ({{ channel_id }} {{ port_id }} {{ direction }}) </h3>
|
||||
<h3 class="card-title capitalize">
|
||||
Transactions ({{ channel_id }} {{ port_id }} {{ direction }})
|
||||
</h3>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td> {{ $t('ibc.height') }}</td>
|
||||
<td>{{ $t('ibc.height') }}</td>
|
||||
<td>{{ $t('ibc.txhash') }}</td>
|
||||
<td> {{ $t('ibc.messages') }}</td>
|
||||
<td>{{ $t('ibc.messages') }}</td>
|
||||
<td>{{ $t('ibc.time') }}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -290,13 +370,20 @@ function color(v: string) {
|
||||
<td>{{ resp.height }}</td>
|
||||
<td>
|
||||
<div class="text-xs truncate text-primary dark:invert">
|
||||
<RouterLink :to="`/${chainStore.chainName}/tx/${resp.txhash}`">{{ resp.txhash }}</RouterLink>
|
||||
<RouterLink
|
||||
:to="`/${chainStore.chainName}/tx/${resp.txhash}`"
|
||||
>{{ resp.txhash }}</RouterLink
|
||||
>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex">
|
||||
{{ format.messages(resp.tx.body.messages) }}
|
||||
<Icon v-if="resp.code === 0" icon="mdi-check" class="text-success text-lg" />
|
||||
<Icon
|
||||
v-if="resp.code === 0"
|
||||
icon="mdi-check"
|
||||
class="text-success text-lg"
|
||||
/>
|
||||
<Icon v-else icon="mdi-multiply" class="text-error text-lg" />
|
||||
</div>
|
||||
</td>
|
||||
@@ -304,7 +391,11 @@ function color(v: string) {
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<PaginationBar :limit="page.limit" :total="txs.pagination?.total" :callback="pageload" />
|
||||
<PaginationBar
|
||||
:limit="page.limit"
|
||||
:total="txs.pagination?.total"
|
||||
:callback="pageload"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// router.push(`/${props.chain}/ibc/connection/connection-0`)
|
||||
</script>
|
||||
<template>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</template>
|
||||
<route>
|
||||
{
|
||||
@@ -11,4 +11,4 @@
|
||||
order: 9
|
||||
}
|
||||
}
|
||||
</route>
|
||||
</route>
|
||||
|
||||
+242
-94
@@ -17,7 +17,7 @@ import { computed } from '@vue/reactivity';
|
||||
|
||||
import CardStatisticsVertical from '@/components/CardStatisticsVertical.vue';
|
||||
import ProposalListItem from '@/components/ProposalListItem.vue';
|
||||
import ArrayObjectElement from '@/components/dynamic/ArrayObjectElement.vue'
|
||||
import ArrayObjectElement from '@/components/dynamic/ArrayObjectElement.vue';
|
||||
|
||||
const props = defineProps(['chain']);
|
||||
|
||||
@@ -27,7 +27,7 @@ const walletStore = useWalletStore();
|
||||
const format = useFormatter();
|
||||
const dialog = useTxDialog();
|
||||
const stakingStore = useStakingStore();
|
||||
const paramStore = useParamStore()
|
||||
const paramStore = useParamStore();
|
||||
const coinInfo = computed(() => {
|
||||
return store.coinInfo;
|
||||
});
|
||||
@@ -35,19 +35,19 @@ const coinInfo = computed(() => {
|
||||
onMounted(() => {
|
||||
store.loadDashboard();
|
||||
walletStore.loadMyAsset();
|
||||
paramStore.handleAbciInfo()
|
||||
paramStore.handleAbciInfo();
|
||||
// if(!(coinInfo.value && coinInfo.value.name)) {
|
||||
// }
|
||||
});
|
||||
const ticker = computed(() => store.coinInfo.tickers[store.tickerIndex]);
|
||||
|
||||
const currName = ref("")
|
||||
const currName = ref('');
|
||||
blockchain.$subscribe((m, s) => {
|
||||
if (s.chainName !== currName.value) {
|
||||
currName.value = s.chainName
|
||||
currName.value = s.chainName;
|
||||
store.loadDashboard();
|
||||
walletStore.loadMyAsset();
|
||||
paramStore.handleAbciInfo()
|
||||
paramStore.handleAbciInfo();
|
||||
}
|
||||
});
|
||||
function shortName(name: string, id: string) {
|
||||
@@ -57,30 +57,30 @@ function shortName(name: string, id: string) {
|
||||
: name;
|
||||
}
|
||||
|
||||
const comLinks = computed(()=> {
|
||||
const comLinks = computed(() => {
|
||||
return [
|
||||
{
|
||||
name: 'Website',
|
||||
icon: 'mdi-web',
|
||||
href: store.homepage,
|
||||
},
|
||||
{
|
||||
name: 'Twitter',
|
||||
icon: 'mdi-twitter',
|
||||
href: store.twitter,
|
||||
},
|
||||
{
|
||||
name: 'Telegram',
|
||||
icon: 'mdi-telegram',
|
||||
href: store.telegram,
|
||||
},
|
||||
{
|
||||
name: 'Github',
|
||||
icon: 'mdi-github',
|
||||
href: store.github,
|
||||
},
|
||||
];
|
||||
})
|
||||
{
|
||||
name: 'Website',
|
||||
icon: 'mdi-web',
|
||||
href: store.homepage,
|
||||
},
|
||||
{
|
||||
name: 'Twitter',
|
||||
icon: 'mdi-twitter',
|
||||
href: store.twitter,
|
||||
},
|
||||
{
|
||||
name: 'Telegram',
|
||||
icon: 'mdi-telegram',
|
||||
href: store.telegram,
|
||||
},
|
||||
{
|
||||
name: 'Github',
|
||||
icon: 'mdi-github',
|
||||
href: store.github,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
// wallet box
|
||||
const change = computed(() => {
|
||||
@@ -99,31 +99,30 @@ const color = computed(() => {
|
||||
});
|
||||
|
||||
function updateState() {
|
||||
walletStore.loadMyAsset()
|
||||
walletStore.loadMyAsset();
|
||||
}
|
||||
|
||||
function trustColor(v: string) {
|
||||
return `text-${colorMap(v)}`
|
||||
return `text-${colorMap(v)}`;
|
||||
}
|
||||
|
||||
const quantity = ref(100)
|
||||
const quantity = ref(100);
|
||||
const qty = computed({
|
||||
get: () => {
|
||||
return parseFloat(quantity.value.toFixed(6))
|
||||
return parseFloat(quantity.value.toFixed(6));
|
||||
},
|
||||
set: val => {
|
||||
quantity.value = val
|
||||
}
|
||||
})
|
||||
set: (val) => {
|
||||
quantity.value = val;
|
||||
},
|
||||
});
|
||||
const amount = computed({
|
||||
get: () => {
|
||||
return quantity.value * ticker.value.converted_last.usd || 0
|
||||
return quantity.value * ticker.value.converted_last.usd || 0;
|
||||
},
|
||||
set: val => {
|
||||
quantity.value = val / ticker.value.converted_last.usd || 0
|
||||
}
|
||||
})
|
||||
|
||||
set: (val) => {
|
||||
quantity.value = val / ticker.value.converted_last.usd || 0;
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -134,18 +133,25 @@ const amount = computed({
|
||||
<div class="text-xl font-semibold text-main">
|
||||
{{ coinInfo.name }} (<span class="uppercase">{{
|
||||
coinInfo.symbol
|
||||
}}</span>)
|
||||
}}</span
|
||||
>)
|
||||
</div>
|
||||
<div class="text-xs mt-2">
|
||||
{{ $t('index.rank') }}:
|
||||
<div class="badge text-xs badge-error bg-[#fcebea] dark:bg-[#41384d] text-red-400">
|
||||
<div
|
||||
class="badge text-xs badge-error bg-[#fcebea] dark:bg-[#41384d] text-red-400"
|
||||
>
|
||||
#{{ coinInfo.market_cap_rank }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-4 flex flex-wrap items-center">
|
||||
<a v-for="(item, index) of comLinks" :key="index" :href="item.href"
|
||||
class="link link-primary px-2 py-1 rounded-sm no-underline hover:text-primary hover:bg-gray-100 dark:hover:bg-slate-800 flex items-center">
|
||||
<a
|
||||
v-for="(item, index) of comLinks"
|
||||
:key="index"
|
||||
:href="item.href"
|
||||
class="link link-primary px-2 py-1 rounded-sm no-underline hover:text-primary hover:bg-gray-100 dark:hover:bg-slate-800 flex items-center"
|
||||
>
|
||||
<Icon :icon="item?.icon" />
|
||||
<span class="ml-1 text-sm uppercase">{{ item?.name }}</span>
|
||||
</a>
|
||||
@@ -155,9 +161,12 @@ const amount = computed({
|
||||
<div class="dropdown dropdown-hover w-full">
|
||||
<label>
|
||||
<div
|
||||
class="bg-gray-100 dark:bg-[#384059] flex items-center justify-between px-4 py-2 cursor-pointer rounded">
|
||||
class="bg-gray-100 dark:bg-[#384059] flex items-center justify-between px-4 py-2 cursor-pointer rounded"
|
||||
>
|
||||
<div>
|
||||
<div class="font-semibold text-xl text-[#666] dark:text-white">
|
||||
<div
|
||||
class="font-semibold text-xl text-[#666] dark:text-white"
|
||||
>
|
||||
{{ ticker?.market?.name || '' }}
|
||||
</div>
|
||||
<div class="text-info text-sm">
|
||||
@@ -168,7 +177,9 @@ const amount = computed({
|
||||
</div>
|
||||
|
||||
<div class="text-right">
|
||||
<div class="text-xl font-semibold text-[#666] dark:text-white">
|
||||
<div
|
||||
class="text-xl font-semibold text-[#666] dark:text-white"
|
||||
>
|
||||
${{ ticker?.converted_last?.usd }}
|
||||
</div>
|
||||
<div class="text-sm" :class="store.priceColor">
|
||||
@@ -180,10 +191,19 @@ const amount = computed({
|
||||
<div class="dropdown-content pt-1">
|
||||
<div class="h-64 overflow-auto w-full shadow rounded">
|
||||
<ul class="menu w-full bg-gray-100 rounded dark:bg-[#384059]">
|
||||
<li v-for="(item, index) in store.coinInfo.tickers" :key="index" @click="store.selectTicker(index)">
|
||||
<div class="flex items-center justify-between hover:bg-base-100">
|
||||
<li
|
||||
v-for="(item, index) in store.coinInfo.tickers"
|
||||
:key="index"
|
||||
@click="store.selectTicker(index)"
|
||||
>
|
||||
<div
|
||||
class="flex items-center justify-between hover:bg-base-100"
|
||||
>
|
||||
<div class="flex-1">
|
||||
<div class="text-main text-sm" :class="trustColor(item.trust_score)">
|
||||
<div
|
||||
class="text-main text-sm"
|
||||
:class="trustColor(item.trust_score)"
|
||||
>
|
||||
{{ item?.market?.name }}
|
||||
</div>
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">
|
||||
@@ -194,7 +214,7 @@ const amount = computed({
|
||||
</div>
|
||||
|
||||
<div class="text-base text-main">
|
||||
${{ item?.converted_last?.usd }}
|
||||
${{ item?.converted_last?.usd }}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
@@ -205,37 +225,92 @@ const amount = computed({
|
||||
|
||||
<div class="flex">
|
||||
<label class="btn btn-primary !px-1 my-5 mr-2" for="calculator">
|
||||
<svg class="w-8 h-8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <rect x="4" y="2" width="16" height="20" rx="2"></rect> <line x1="8" x2="16" y1="6" y2="6"></line> <line x1="16" x2="16" y1="14" y2="18"></line> <path d="M16 10h.01"></path> <path d="M12 10h.01"></path> <path d="M8 10h.01"></path> <path d="M12 14h.01"></path> <path d="M8 14h.01"></path> <path d="M12 18h.01"></path> <path d="M8 18h.01"></path> </g></svg>
|
||||
<svg
|
||||
class="w-8 h-8"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="#ffffff"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"></g>
|
||||
<g
|
||||
id="SVGRepo_tracerCarrier"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
></g>
|
||||
<g id="SVGRepo_iconCarrier">
|
||||
<rect x="4" y="2" width="16" height="20" rx="2"></rect>
|
||||
<line x1="8" x2="16" y1="6" y2="6"></line>
|
||||
<line x1="16" x2="16" y1="14" y2="18"></line>
|
||||
<path d="M16 10h.01"></path>
|
||||
<path d="M12 10h.01"></path>
|
||||
<path d="M8 10h.01"></path>
|
||||
<path d="M12 14h.01"></path>
|
||||
<path d="M8 14h.01"></path>
|
||||
<path d="M12 18h.01"></path>
|
||||
<path d="M8 18h.01"></path>
|
||||
</g>
|
||||
</svg>
|
||||
</label>
|
||||
<!-- Put this part before </body> tag -->
|
||||
<input type="checkbox" id="calculator" class="modal-toggle" />
|
||||
<div class="modal">
|
||||
<div class="modal-box">
|
||||
<h3 class="text-lg font-bold">{{ $t('index.price_calculator') }}</h3>
|
||||
<h3 class="text-lg font-bold">
|
||||
{{ $t('index.price_calculator') }}
|
||||
</h3>
|
||||
<div class="flex flex-col w-full mt-5">
|
||||
<div class="grid h-20 flex-grow card rounded-box place-items-center">
|
||||
<div
|
||||
class="grid h-20 flex-grow card rounded-box place-items-center"
|
||||
>
|
||||
<div class="join w-full">
|
||||
<label class="join-item btn">
|
||||
<span class="uppercase">{{ coinInfo.symbol }}</span>
|
||||
</label>
|
||||
<input type="number" v-model="qty" min="0" placeholder="Input a number" class="input grow input-bordered join-item" />
|
||||
<input
|
||||
type="number"
|
||||
v-model="qty"
|
||||
min="0"
|
||||
placeholder="Input a number"
|
||||
class="input grow input-bordered join-item"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider">=</div>
|
||||
<div class="grid h-20 flex-grow card rounded-box place-items-center">
|
||||
<div
|
||||
class="grid h-20 flex-grow card rounded-box place-items-center"
|
||||
>
|
||||
<div class="join w-full">
|
||||
<label class="join-item btn">
|
||||
<span>USD</span>
|
||||
</label>
|
||||
<input type="number" v-model="amount" min="0" placeholder="Input amount" class="join-item grow input input-bordered" />
|
||||
<input
|
||||
type="number"
|
||||
v-model="amount"
|
||||
min="0"
|
||||
placeholder="Input amount"
|
||||
class="join-item grow input input-bordered"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<label class="modal-backdrop" for="calculator">{{ $t('index.close') }}</label>
|
||||
<label class="modal-backdrop" for="calculator">{{
|
||||
$t('index.close')
|
||||
}}</label>
|
||||
</div>
|
||||
<a class="my-5 !text-white btn grow" :class="{'!btn-success': store.trustColor === 'green', '!btn-warning': store.trustColor === 'yellow'}" :href="tickerUrl(ticker.trade_url)"
|
||||
target="_blank">
|
||||
<a
|
||||
class="my-5 !text-white btn grow"
|
||||
:class="{
|
||||
'!btn-success': store.trustColor === 'green',
|
||||
'!btn-warning': store.trustColor === 'yellow',
|
||||
}"
|
||||
:href="tickerUrl(ticker.trade_url)"
|
||||
target="_blank"
|
||||
>
|
||||
{{ $t('index.buy') }} {{ coinInfo.symbol || '' }}
|
||||
</a>
|
||||
</div>
|
||||
@@ -248,11 +323,16 @@ const amount = computed({
|
||||
</div>
|
||||
<div class="h-[1px] w-full bg-gray-100 dark:bg-[#384059]"></div>
|
||||
<div class="max-h-[250px] overflow-auto p-4 text-sm">
|
||||
<MdEditor :model-value="coinInfo.description?.en" previewOnly></MdEditor>
|
||||
<MdEditor
|
||||
:model-value="coinInfo.description?.en"
|
||||
previewOnly
|
||||
></MdEditor>
|
||||
</div>
|
||||
<div class="mx-4 flex flex-wrap items-center">
|
||||
<div v-for="tag in coinInfo.categories"
|
||||
class="mr-2 mb-4 text-xs bg-gray-100 dark:bg-[#384059] px-3 rounded-full py-1">
|
||||
<div
|
||||
v-for="tag in coinInfo.categories"
|
||||
class="mr-2 mb-4 text-xs bg-gray-100 dark:bg-[#384059] px-3 rounded-full py-1"
|
||||
>
|
||||
{{ tag }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -264,26 +344,41 @@ const amount = computed({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="blockchain.supportModule('governance')" class="bg-base-100 rounded mt-4 shadow">
|
||||
<div
|
||||
v-if="blockchain.supportModule('governance')"
|
||||
class="bg-base-100 rounded mt-4 shadow"
|
||||
>
|
||||
<div class="px-4 pt-4 pb-2 text-lg font-semibold text-main">
|
||||
{{ $t('index.active_proposals') }}
|
||||
</div>
|
||||
<div class="px-4 pb-4">
|
||||
<ProposalListItem :proposals="store?.proposals" />
|
||||
</div>
|
||||
<div class="pb-8 text-center" v-if="store.proposals?.proposals?.length === 0">
|
||||
<div
|
||||
class="pb-8 text-center"
|
||||
v-if="store.proposals?.proposals?.length === 0"
|
||||
>
|
||||
{{ $t('index.no_active_proposals') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-base-100 rounded mt-4 shadow">
|
||||
<div class="flex justify-between px-4 pt-4 pb-2 text-lg font-semibold text-main">
|
||||
<span class="truncate" >{{ walletStore.currentAddress || 'Not Connected' }}</span>
|
||||
<RouterLink v-if="walletStore.currentAddress"
|
||||
<div
|
||||
class="flex justify-between px-4 pt-4 pb-2 text-lg font-semibold text-main"
|
||||
>
|
||||
<span class="truncate">{{
|
||||
walletStore.currentAddress || 'Not Connected'
|
||||
}}</span>
|
||||
<RouterLink
|
||||
v-if="walletStore.currentAddress"
|
||||
class="float-right text-sm cursor-pointert link link-primary no-underline font-medium"
|
||||
:to="`/${chain}/account/${walletStore.currentAddress}`">{{ $t('index.more') }}</RouterLink>
|
||||
:to="`/${chain}/account/${walletStore.currentAddress}`"
|
||||
>{{ $t('index.more') }}</RouterLink
|
||||
>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:!grid-cols-4 auto-cols-auto gap-4 px-4 pb-6">
|
||||
<div
|
||||
class="grid grid-cols-1 md:!grid-cols-4 auto-cols-auto gap-4 px-4 pb-6"
|
||||
>
|
||||
<div class="bg-gray-100 dark:bg-[#373f59] rounded-sm px-4 py-3">
|
||||
<div class="text-sm mb-1">{{ $t('account.balance') }}</div>
|
||||
<div class="text-lg font-semibold text-main">
|
||||
@@ -322,7 +417,10 @@ const amount = computed({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="walletStore.delegations.length > 0" class="px-4 pb-4 overflow-auto">
|
||||
<div
|
||||
v-if="walletStore.delegations.length > 0"
|
||||
class="px-4 pb-4 overflow-auto"
|
||||
>
|
||||
<table class="table table-compact w-full table-zebra">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -335,12 +433,15 @@ const amount = computed({
|
||||
<tbody>
|
||||
<tr v-for="(item, index) in walletStore.delegations" :key="index">
|
||||
<td>
|
||||
<RouterLink class="link link-primary no-underline" :to="`/${chain}/staking/${item?.delegation?.validator_address}`">
|
||||
{{
|
||||
format.validatorFromBech32(
|
||||
item?.delegation?.validator_address
|
||||
)
|
||||
}}
|
||||
<RouterLink
|
||||
class="link link-primary no-underline"
|
||||
:to="`/${chain}/staking/${item?.delegation?.validator_address}`"
|
||||
>
|
||||
{{
|
||||
format.validatorFromBech32(
|
||||
item?.delegation?.validator_address
|
||||
)
|
||||
}}
|
||||
</RouterLink>
|
||||
</td>
|
||||
<td>{{ format.formatToken(item?.balance) }}</td>
|
||||
@@ -351,17 +452,40 @@ const amount = computed({
|
||||
(el) =>
|
||||
el?.validator_address ===
|
||||
item?.delegation?.validator_address
|
||||
)?.reward)
|
||||
)?.reward
|
||||
)
|
||||
}}
|
||||
</td>
|
||||
<td>
|
||||
<div>
|
||||
<label for="delegate" class="btn !btn-xs !btn-primary btn-ghost rounded-sm mr-2"
|
||||
@click="dialog.open('delegate', { validator_address: item.delegation.validator_address }, updateState)">
|
||||
<label
|
||||
for="delegate"
|
||||
class="btn !btn-xs !btn-primary btn-ghost rounded-sm mr-2"
|
||||
@click="
|
||||
dialog.open(
|
||||
'delegate',
|
||||
{
|
||||
validator_address: item.delegation.validator_address,
|
||||
},
|
||||
updateState
|
||||
)
|
||||
"
|
||||
>
|
||||
{{ $t('account.btn_delegate') }}
|
||||
</label>
|
||||
<label for="withdraw" class="btn !btn-xs !btn-primary btn-ghost rounded-sm"
|
||||
@click="dialog.open('withdraw', { validator_address: item.delegation.validator_address }, updateState)">
|
||||
<label
|
||||
for="withdraw"
|
||||
class="btn !btn-xs !btn-primary btn-ghost rounded-sm"
|
||||
@click="
|
||||
dialog.open(
|
||||
'withdraw',
|
||||
{
|
||||
validator_address: item.delegation.validator_address,
|
||||
},
|
||||
updateState
|
||||
)
|
||||
"
|
||||
>
|
||||
{{ $t('index.btn_withdraw_reward') }}
|
||||
</label>
|
||||
</div>
|
||||
@@ -372,15 +496,33 @@ const amount = computed({
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-3 gap-4 px-4 pb-6 mt-4">
|
||||
<label for="PingTokenConvert" class="btn btn-primary text-white">{{ $t('index.btn_swap') }}</label>
|
||||
<label for="send" class="btn !bg-yes !border-yes text-white" @click="dialog.open('send', {}, updateState)">{{ $t('account.btn_send') }}</label>
|
||||
<label for="delegate" class="btn !bg-info !border-info text-white"
|
||||
@click="dialog.open('delegate', {}, updateState)">{{ $t('account.btn_delegate') }}</label>
|
||||
<RouterLink to="/wallet/receive" class="btn !bg-info !border-info text-white hidden">{{ $t('index.receive') }}</RouterLink>
|
||||
<label for="PingTokenConvert" class="btn btn-primary text-white">{{
|
||||
$t('index.btn_swap')
|
||||
}}</label>
|
||||
<label
|
||||
for="send"
|
||||
class="btn !bg-yes !border-yes text-white"
|
||||
@click="dialog.open('send', {}, updateState)"
|
||||
>{{ $t('account.btn_send') }}</label
|
||||
>
|
||||
<label
|
||||
for="delegate"
|
||||
class="btn !bg-info !border-info text-white"
|
||||
@click="dialog.open('delegate', {}, updateState)"
|
||||
>{{ $t('account.btn_delegate') }}</label
|
||||
>
|
||||
<RouterLink
|
||||
to="/wallet/receive"
|
||||
class="btn !bg-info !border-info text-white hidden"
|
||||
>{{ $t('index.receive') }}</RouterLink
|
||||
>
|
||||
</div>
|
||||
<Teleport to="body">
|
||||
<ping-token-convert :chain-name="blockchain?.current?.prettyName" :endpoint="blockchain?.endpoint?.address"
|
||||
:hd-path="walletStore?.connectedWallet?.hdPath"></ping-token-convert>
|
||||
<ping-token-convert
|
||||
:chain-name="blockchain?.current?.prettyName"
|
||||
:endpoint="blockchain?.endpoint?.address"
|
||||
:hd-path="walletStore?.connectedWallet?.hdPath"
|
||||
></ping-token-convert>
|
||||
</Teleport>
|
||||
</div>
|
||||
|
||||
@@ -389,7 +531,10 @@ const amount = computed({
|
||||
{{ $t('index.app_versions') }}
|
||||
</div>
|
||||
<!-- Application Version -->
|
||||
<ArrayObjectElement :value="paramStore.appVersion?.items" :thead="false" />
|
||||
<ArrayObjectElement
|
||||
:value="paramStore.appVersion?.items"
|
||||
:thead="false"
|
||||
/>
|
||||
<div class="h-4"></div>
|
||||
</div>
|
||||
|
||||
@@ -397,7 +542,10 @@ const amount = computed({
|
||||
<div class="px-4 pt-4 pb-2 text-lg font-semibold text-main">
|
||||
{{ $t('index.node_info') }}
|
||||
</div>
|
||||
<ArrayObjectElement :value="paramStore.nodeVersion?.items" :thead="false" />
|
||||
<ArrayObjectElement
|
||||
:value="paramStore.nodeVersion?.items"
|
||||
:thead="false"
|
||||
/>
|
||||
<div class="h-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -25,11 +25,10 @@ export function colorMap(color: string) {
|
||||
}
|
||||
|
||||
const CODEMAP: Record<string, string[]> = {
|
||||
"binance.com": ["ref", "CPA_004JZGRX6A"],
|
||||
"gate.com": ["ref", "U1gVBl9a"],
|
||||
"bybit": ["affiliate_id", "JKRRZX9"],
|
||||
|
||||
}
|
||||
'binance.com': ['ref', 'CPA_004JZGRX6A'],
|
||||
'gate.com': ['ref', 'U1gVBl9a'],
|
||||
bybit: ['affiliate_id', 'JKRRZX9'],
|
||||
};
|
||||
|
||||
export const useIndexModule = defineStore('module-index', {
|
||||
state: () => {
|
||||
@@ -94,33 +93,33 @@ export const useIndexModule = defineStore('module-index', {
|
||||
return useBankStore();
|
||||
},
|
||||
twitter(): string {
|
||||
if (!this.coinInfo?.links?.twitter_screen_name) return ""
|
||||
if (!this.coinInfo?.links?.twitter_screen_name) return '';
|
||||
return `https://twitter.com/${this.coinInfo?.links.twitter_screen_name}`;
|
||||
},
|
||||
homepage(): string {
|
||||
if (!this.coinInfo?.links?.homepage) return ""
|
||||
if (!this.coinInfo?.links?.homepage) return '';
|
||||
const [page1, page2, page3] = this.coinInfo?.links?.homepage;
|
||||
return page1 || page2 || page3;
|
||||
},
|
||||
github(): string {
|
||||
if (!this.coinInfo?.links?.repos_url) return ""
|
||||
if (!this.coinInfo?.links?.repos_url) return '';
|
||||
const [page1, page2, page3] = this.coinInfo?.links?.repos_url?.github;
|
||||
return page1 || page2 || page3;
|
||||
},
|
||||
telegram(): string {
|
||||
if (!this.coinInfo?.links?.homepage) return ""
|
||||
if (!this.coinInfo?.links?.homepage) return '';
|
||||
return `https://t.me/${this.coinInfo?.links.telegram_channel_identifier}`;
|
||||
},
|
||||
|
||||
priceChange(): string {
|
||||
if (!this.coinInfo?.market_data?.price_change_percentage_24h) return ""
|
||||
if (!this.coinInfo?.market_data?.price_change_percentage_24h) return '';
|
||||
const change =
|
||||
this.coinInfo?.market_data?.price_change_percentage_24h || 0;
|
||||
return numeral(change).format('+0.[00]');
|
||||
},
|
||||
|
||||
priceColor(): string {
|
||||
if (!this.coinInfo?.market_data?.price_change_percentage_24h) return ""
|
||||
if (!this.coinInfo?.market_data?.price_change_percentage_24h) return '';
|
||||
const change =
|
||||
this.coinInfo?.market_data?.price_change_percentage_24h || 0;
|
||||
switch (true) {
|
||||
@@ -133,7 +132,7 @@ export const useIndexModule = defineStore('module-index', {
|
||||
}
|
||||
},
|
||||
trustColor(): string {
|
||||
if (!this.coinInfo?.tickers) return ""
|
||||
if (!this.coinInfo?.tickers) return '';
|
||||
const change = this.coinInfo?.tickers[this.tickerIndex]?.trust_score;
|
||||
return change;
|
||||
},
|
||||
@@ -144,8 +143,8 @@ export const useIndexModule = defineStore('module-index', {
|
||||
},
|
||||
|
||||
proposals() {
|
||||
const gov = useGovStore()
|
||||
return gov.proposals['2']
|
||||
const gov = useGovStore();
|
||||
return gov.proposals['2'];
|
||||
},
|
||||
|
||||
stats() {
|
||||
@@ -167,7 +166,9 @@ export const useIndexModule = defineStore('module-index', {
|
||||
title: 'Validators',
|
||||
color: 'error',
|
||||
icon: 'mdi-human-queue',
|
||||
stats: String(base?.latest?.block?.last_commit?.signatures.length || 0),
|
||||
stats: String(
|
||||
base?.latest?.block?.last_commit?.signatures.length || 0
|
||||
),
|
||||
change: 0,
|
||||
},
|
||||
{
|
||||
@@ -214,8 +215,8 @@ export const useIndexModule = defineStore('module-index', {
|
||||
this.tickerIndex = 0;
|
||||
// @ts-ignore
|
||||
const [firstAsset] = this.blockchain?.assets || [];
|
||||
return firstAsset.coingecko_id
|
||||
}
|
||||
return firstAsset.coingecko_id;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
async loadDashboard() {
|
||||
@@ -257,7 +258,7 @@ export const useIndexModule = defineStore('module-index', {
|
||||
},
|
||||
selectTicker(i: number) {
|
||||
this.tickerIndex = i;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -268,7 +269,11 @@ export const useIndexModule = defineStore('module-index', {
|
||||
* @param value - The value to set for the parameter.
|
||||
* @returns The new URL with the parameter added or replaced.
|
||||
*/
|
||||
export function addOrReplaceUrlParam(url: string, param: string, value: string): string {
|
||||
export function addOrReplaceUrlParam(
|
||||
url: string,
|
||||
param: string,
|
||||
value: string
|
||||
): string {
|
||||
// Parse the URL
|
||||
const urlObj = new URL(url, window.location.origin);
|
||||
|
||||
@@ -279,13 +284,12 @@ export function addOrReplaceUrlParam(url: string, param: string, value: string):
|
||||
return urlObj.toString();
|
||||
}
|
||||
|
||||
|
||||
export function tickerUrl(url: string) {
|
||||
for (const domain of Object.keys(CODEMAP)) {
|
||||
if (url.indexOf(domain) > -1) {
|
||||
const v = CODEMAP[domain];
|
||||
return addOrReplaceUrlParam(url, v[0], v[1])
|
||||
return addOrReplaceUrlParam(url, v[0], v[1]);
|
||||
}
|
||||
}
|
||||
return url
|
||||
return url;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
<!-- TODO: there's no mint msg at the moment. not sure how NFT comes from?-->
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
<script lang="ts" setup></script>
|
||||
|
||||
<template>
|
||||
<div>NFT</div>
|
||||
<div>NFT</div>
|
||||
</template>
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
import {useBlockchain} from '@/stores'
|
||||
import { DEFAULT, NFTRestClient, type PageinatedClasses, type PageinatedNFTs } from './types';
|
||||
import { useBlockchain } from '@/stores';
|
||||
import {
|
||||
DEFAULT,
|
||||
NFTRestClient,
|
||||
type PageinatedClasses,
|
||||
type PageinatedNFTs,
|
||||
} from './types';
|
||||
|
||||
export const useNFTModule = defineStore('module-nft', {
|
||||
state: () => {
|
||||
@@ -13,14 +17,12 @@ export const useNFTModule = defineStore('module-nft', {
|
||||
},
|
||||
getters: {
|
||||
chain() {
|
||||
return useBlockchain()
|
||||
return useBlockchain();
|
||||
},
|
||||
client() {
|
||||
const client = new NFTRestClient("", DEFAULT)
|
||||
return client
|
||||
}
|
||||
const client = new NFTRestClient('', DEFAULT);
|
||||
return client;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
|
||||
}
|
||||
actions: {},
|
||||
});
|
||||
|
||||
@@ -1,108 +1,118 @@
|
||||
|
||||
import { BaseRestClient } from '@/libs/client';
|
||||
import { adapter, type AbstractRegistry, type Request } from '@/libs/api/registry';
|
||||
import {
|
||||
adapter,
|
||||
type AbstractRegistry,
|
||||
type Request,
|
||||
} from '@/libs/api/registry';
|
||||
import { PageRequest, type PaginatedResponse } from '@/types';
|
||||
|
||||
export interface Classes {
|
||||
id: string,
|
||||
name: string,
|
||||
symbol: string,
|
||||
description: string,
|
||||
uri: string,
|
||||
uri_hash: string,
|
||||
data: {
|
||||
type_url: string,
|
||||
value: string
|
||||
}
|
||||
id: string;
|
||||
name: string;
|
||||
symbol: string;
|
||||
description: string;
|
||||
uri: string;
|
||||
uri_hash: string;
|
||||
data: {
|
||||
type_url: string;
|
||||
value: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface NFT {
|
||||
class_id: string,
|
||||
id: string,
|
||||
uri: string,
|
||||
uri_hash: string,
|
||||
data: {
|
||||
type_url: string,
|
||||
value: string
|
||||
}
|
||||
}
|
||||
class_id: string;
|
||||
id: string;
|
||||
uri: string;
|
||||
uri_hash: string;
|
||||
data: {
|
||||
type_url: string;
|
||||
value: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface Balance {
|
||||
amount: string,
|
||||
amount: string;
|
||||
}
|
||||
|
||||
export interface PageinatedClasses extends PaginatedResponse {
|
||||
classes: Classes[],
|
||||
classes: Classes[];
|
||||
}
|
||||
|
||||
export interface PageinatedNFTs extends PaginatedResponse {
|
||||
nfts: NFT[],
|
||||
nfts: NFT[];
|
||||
}
|
||||
|
||||
export interface NFTRequestRegistry extends AbstractRegistry {
|
||||
nft_classes: Request<PageinatedClasses>;
|
||||
nft_classes_class_id: Request<Classes>;
|
||||
nft_supply_class_id: Request<Balance>;
|
||||
nft_classes_nfts: Request<PageinatedNFTs>;
|
||||
nft_classes_nfts_class_id_nft_id: Request<NFT>;
|
||||
nft_classes_nfts_class_id_nft_id_owner: Request<{
|
||||
owner: string
|
||||
}>;
|
||||
nft_balance_owner: Request<Balance>;
|
||||
nft_classes: Request<PageinatedClasses>;
|
||||
nft_classes_class_id: Request<Classes>;
|
||||
nft_supply_class_id: Request<Balance>;
|
||||
nft_classes_nfts: Request<PageinatedNFTs>;
|
||||
nft_classes_nfts_class_id_nft_id: Request<NFT>;
|
||||
nft_classes_nfts_class_id_nft_id_owner: Request<{
|
||||
owner: string;
|
||||
}>;
|
||||
nft_balance_owner: Request<Balance>;
|
||||
}
|
||||
|
||||
export const DEFAULT: NFTRequestRegistry = {
|
||||
nft_classes: {
|
||||
url: '/cosmos/nft/v1beta1/classes',
|
||||
adapter,
|
||||
},
|
||||
nft_classes_class_id: {
|
||||
url: '/cosmos/nft/v1beta1/classes/{class_id}',
|
||||
adapter,
|
||||
},
|
||||
nft_supply_class_id: {
|
||||
url: '/cosmos/nft/v1beta1/supply/{class_id}',
|
||||
adapter,
|
||||
},
|
||||
nft_classes_nfts: {
|
||||
url: '/cosmos/nft/v1beta1/nfts',
|
||||
adapter,
|
||||
},
|
||||
nft_classes_nfts_class_id_nft_id: {
|
||||
url: '/cosmos/nft/v1beta1/{class_id}/{nft_id}',
|
||||
adapter,
|
||||
},
|
||||
nft_classes_nfts_class_id_nft_id_owner: {
|
||||
url: '/cosmos/nft/v1beta1/owner/{class_id}/{nft_id}',
|
||||
adapter,
|
||||
},
|
||||
nft_balance_owner: {
|
||||
url: '/cosmos/nft/v1beta1/balance/{owner}/{class_id}',
|
||||
adapter,
|
||||
}
|
||||
nft_classes: {
|
||||
url: '/cosmos/nft/v1beta1/classes',
|
||||
adapter,
|
||||
},
|
||||
nft_classes_class_id: {
|
||||
url: '/cosmos/nft/v1beta1/classes/{class_id}',
|
||||
adapter,
|
||||
},
|
||||
nft_supply_class_id: {
|
||||
url: '/cosmos/nft/v1beta1/supply/{class_id}',
|
||||
adapter,
|
||||
},
|
||||
nft_classes_nfts: {
|
||||
url: '/cosmos/nft/v1beta1/nfts',
|
||||
adapter,
|
||||
},
|
||||
nft_classes_nfts_class_id_nft_id: {
|
||||
url: '/cosmos/nft/v1beta1/{class_id}/{nft_id}',
|
||||
adapter,
|
||||
},
|
||||
nft_classes_nfts_class_id_nft_id_owner: {
|
||||
url: '/cosmos/nft/v1beta1/owner/{class_id}/{nft_id}',
|
||||
adapter,
|
||||
},
|
||||
nft_balance_owner: {
|
||||
url: '/cosmos/nft/v1beta1/balance/{owner}/{class_id}',
|
||||
adapter,
|
||||
},
|
||||
};
|
||||
|
||||
export class NFTRestClient extends BaseRestClient<NFTRequestRegistry> {
|
||||
getClasses(pr?: PageRequest) {
|
||||
if(!pr) pr = new PageRequest()
|
||||
const query = `?${pr.toQueryString()}`
|
||||
return this.request(this.registry.nft_classes, {}, query);
|
||||
}
|
||||
getClassesById(class_id: string) {
|
||||
return this.request(this.registry.nft_classes_class_id, { class_id }); // `code_id` is a param in above url
|
||||
}
|
||||
getNFTs(class_id: string, id: string, page?: PageRequest) {
|
||||
if(!page) page = new PageRequest()
|
||||
const query = `?${page.toQueryString()}`
|
||||
return this.request(this.registry.nft_classes_nfts_class_id_nft_id, { class_id, id }, query);
|
||||
}
|
||||
getNFTOwner(class_id: string, id: string) {
|
||||
return this.request(this.registry.nft_classes_nfts_class_id_nft_id_owner, {class_id, id});
|
||||
}
|
||||
getNFTSupply(class_id: string) {
|
||||
return this.request(this.registry.nft_supply_class_id, { class_id });
|
||||
}
|
||||
getBalanceOfNFT(class_id: string, owner: string) {
|
||||
return this.request(this.registry.nft_balance_owner, { class_id, owner });
|
||||
}
|
||||
}
|
||||
getClasses(pr?: PageRequest) {
|
||||
if (!pr) pr = new PageRequest();
|
||||
const query = `?${pr.toQueryString()}`;
|
||||
return this.request(this.registry.nft_classes, {}, query);
|
||||
}
|
||||
getClassesById(class_id: string) {
|
||||
return this.request(this.registry.nft_classes_class_id, { class_id }); // `code_id` is a param in above url
|
||||
}
|
||||
getNFTs(class_id: string, id: string, page?: PageRequest) {
|
||||
if (!page) page = new PageRequest();
|
||||
const query = `?${page.toQueryString()}`;
|
||||
return this.request(
|
||||
this.registry.nft_classes_nfts_class_id_nft_id,
|
||||
{ class_id, id },
|
||||
query
|
||||
);
|
||||
}
|
||||
getNFTOwner(class_id: string, id: string) {
|
||||
return this.request(this.registry.nft_classes_nfts_class_id_nft_id_owner, {
|
||||
class_id,
|
||||
id,
|
||||
});
|
||||
}
|
||||
getNFTSupply(class_id: string) {
|
||||
return this.request(this.registry.nft_supply_class_id, { class_id });
|
||||
}
|
||||
getBalanceOfNFT(class_id: string, owner: string) {
|
||||
return this.request(this.registry.nft_balance_owner, { class_id, owner });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,14 @@ import {
|
||||
operatorAddressToAccount,
|
||||
pubKeyToValcons,
|
||||
} from '@/libs';
|
||||
import { PageRequest, type Coin, type Delegation, type PaginatedDelegations, type PaginatedTxs, type Validator } from '@/types';
|
||||
import {
|
||||
PageRequest,
|
||||
type Coin,
|
||||
type Delegation,
|
||||
type PaginatedDelegations,
|
||||
type PaginatedTxs,
|
||||
type Validator,
|
||||
} from '@/types';
|
||||
import PaginationBar from '@/components/PaginationBar.vue';
|
||||
import { fromBase64, toBase64 } from '@cosmjs/encoding';
|
||||
import { stringToUint8Array, uint8ArrayToString } from '@/libs/utils';
|
||||
@@ -38,7 +45,7 @@ const avatars = ref(cache || {});
|
||||
const identity = ref('');
|
||||
const rewards = ref([] as Coin[] | undefined);
|
||||
const commission = ref([] as Coin[] | undefined);
|
||||
const delegations = ref({} as PaginatedDelegations)
|
||||
const delegations = ref({} as PaginatedDelegations);
|
||||
const addresses = ref(
|
||||
{} as {
|
||||
account: string;
|
||||
@@ -69,9 +76,12 @@ const apr = computed(() => {
|
||||
const rate = Number(v.value.commission?.commission_rates.rate || 0);
|
||||
const inflation = useMintStore().inflation;
|
||||
const communityTax = Number(useDistributionStore().params.community_tax);
|
||||
const bondedRatio = Number(staking.pool.bonded_tokens) / Number(useBankStore().supply.amount);
|
||||
const bondedRatio =
|
||||
Number(staking.pool.bonded_tokens) / Number(useBankStore().supply.amount);
|
||||
|
||||
return format.percent((1 - communityTax) * (1 - rate) * Number(inflation) / bondedRatio);
|
||||
return format.percent(
|
||||
((1 - communityTax) * (1 - rate) * Number(inflation)) / bondedRatio
|
||||
);
|
||||
});
|
||||
|
||||
const selfRate = computed(() => {
|
||||
@@ -127,14 +137,15 @@ onMounted(() => {
|
||||
staking.fetchValidator(validator).then((res) => {
|
||||
v.value = res.validator;
|
||||
identity.value = res.validator?.description?.identity || '';
|
||||
if (identity.value && !avatars.value[identity.value]) loadAvatar(identity.value);
|
||||
if (identity.value && !avatars.value[identity.value])
|
||||
loadAvatar(identity.value);
|
||||
|
||||
addresses.value.hex = consensusPubkeyToHexAddress(
|
||||
v.value.consensus_pubkey
|
||||
);
|
||||
addresses.value.valCons = pubKeyToValcons(
|
||||
v.value.consensus_pubkey,
|
||||
blockchain.current?.bech32ConsensusPrefix || "",
|
||||
blockchain.current?.bech32ConsensusPrefix || ''
|
||||
);
|
||||
});
|
||||
blockchain.rpc
|
||||
@@ -163,7 +174,6 @@ onMounted(() => {
|
||||
// Disable delegations due to its bad performance
|
||||
// Comment out the following code if you want to enable it
|
||||
// pageload(1)
|
||||
|
||||
}
|
||||
});
|
||||
let showCopyToast = ref(0);
|
||||
@@ -194,52 +204,71 @@ function pageload(p: number) {
|
||||
page.setPage(p);
|
||||
page.limit = 10;
|
||||
|
||||
blockchain.rpc.getStakingValidatorsDelegations(validator, page).then(res => {
|
||||
delegations.value = res
|
||||
})
|
||||
blockchain.rpc
|
||||
.getStakingValidatorsDelegations(validator, page)
|
||||
.then((res) => {
|
||||
delegations.value = res;
|
||||
});
|
||||
}
|
||||
|
||||
const events = ref({} as PaginatedTxs)
|
||||
const events = ref({} as PaginatedTxs);
|
||||
|
||||
enum EventType {
|
||||
Delegate = 'delegate',
|
||||
Unbond = 'unbond',
|
||||
}
|
||||
|
||||
const selectedEventType = ref(EventType.Delegate)
|
||||
const selectedEventType = ref(EventType.Delegate);
|
||||
|
||||
function loadPowerEvents(p: number, type: EventType) {
|
||||
selectedEventType.value = type
|
||||
selectedEventType.value = type;
|
||||
page.setPage(p);
|
||||
page.setPageSize(5);
|
||||
blockchain.rpc.getTxs("?order_by=2&events={type}.validator='{validator}'", { type: selectedEventType.value, validator }, page).then(res => {
|
||||
events.value = res
|
||||
})
|
||||
blockchain.rpc
|
||||
.getTxs(
|
||||
"?order_by=2&events={type}.validator='{validator}'",
|
||||
{ type: selectedEventType.value, validator },
|
||||
page
|
||||
)
|
||||
.then((res) => {
|
||||
events.value = res;
|
||||
});
|
||||
}
|
||||
|
||||
function pagePowerEvents(page: number) {
|
||||
loadPowerEvents(page, selectedEventType.value)
|
||||
loadPowerEvents(page, selectedEventType.value);
|
||||
}
|
||||
|
||||
pagePowerEvents(1)
|
||||
pagePowerEvents(1);
|
||||
|
||||
function mapEvents(events: {type: string, attributes: {key: string, value: string}[]}[]) {
|
||||
function mapEvents(
|
||||
events: { type: string; attributes: { key: string; value: string }[] }[]
|
||||
) {
|
||||
const attributes = events
|
||||
.filter(x => x.type === selectedEventType.value)
|
||||
.filter(x => x.attributes.findIndex(attr => attr.value === validator || attr.value === toBase64(stringToUint8Array(validator))) > -1)
|
||||
.map(x => {
|
||||
.filter((x) => x.type === selectedEventType.value)
|
||||
.filter(
|
||||
(x) =>
|
||||
x.attributes.findIndex(
|
||||
(attr) =>
|
||||
attr.value === validator ||
|
||||
attr.value === toBase64(stringToUint8Array(validator))
|
||||
) > -1
|
||||
)
|
||||
.map((x) => {
|
||||
// check if attributes need to decode
|
||||
const output = {} as {[key: string]: string }
|
||||
const output = {} as { [key: string]: string };
|
||||
|
||||
if (x.attributes.findIndex(a => a.key === `amount`) > -1) {
|
||||
x.attributes.forEach(attr => {
|
||||
output[attr.key] = attr.value
|
||||
})
|
||||
if (x.attributes.findIndex((a) => a.key === `amount`) > -1) {
|
||||
x.attributes.forEach((attr) => {
|
||||
output[attr.key] = attr.value;
|
||||
});
|
||||
} else {
|
||||
x.attributes.forEach(attr => {
|
||||
output[uint8ArrayToString(fromBase64(attr.key))] = uint8ArrayToString(fromBase64(attr.value))
|
||||
})
|
||||
};
|
||||
x.attributes.forEach((attr) => {
|
||||
output[uint8ArrayToString(fromBase64(attr.key))] = uint8ArrayToString(
|
||||
fromBase64(attr.value)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return output;
|
||||
});
|
||||
@@ -247,14 +276,15 @@ function mapEvents(events: {type: string, attributes: {key: string, value: strin
|
||||
const coinsAsString = attributes.map((x: any) => x.amount).join(',');
|
||||
const coins = parseCoins(coinsAsString);
|
||||
|
||||
return coins.map(coin => format.formatToken(coin)).join(', ');
|
||||
return coins.map((coin) => format.formatToken(coin)).join(', ');
|
||||
}
|
||||
|
||||
function mapDelegators(messages: any[]) {
|
||||
if(!messages) return []
|
||||
return Array.from(new Set(messages.map(x => x.delegator_address || x.grantee)))
|
||||
if (!messages) return [];
|
||||
return Array.from(
|
||||
new Set(messages.map((x) => x.delegator_address || x.grantee))
|
||||
);
|
||||
}
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
@@ -304,7 +334,9 @@ function mapDelegators(messages: any[]) {
|
||||
<div class="card-list">
|
||||
<div class="flex items-center mb-2">
|
||||
<Icon icon="mdi-web" class="text-xl mr-1" />
|
||||
<span class="font-bold mr-2">{{ $t('staking.website') }}: </span>
|
||||
<span class="font-bold mr-2"
|
||||
>{{ $t('staking.website') }}:
|
||||
</span>
|
||||
<a
|
||||
:href="v?.description?.website || '#'"
|
||||
:class="
|
||||
@@ -318,17 +350,21 @@ function mapDelegators(messages: any[]) {
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<Icon icon="mdi-email-outline" class="text-xl mr-1" />
|
||||
<span class="font-bold mr-2">{{ $t('staking.contact') }}: </span>
|
||||
<span class="font-bold mr-2"
|
||||
>{{ $t('staking.contact') }}:
|
||||
</span>
|
||||
<a
|
||||
v-if="v.description?.security_contact"
|
||||
:href="'mailto:' + v.description.security_contact || '#' "
|
||||
:href="'mailto:' + v.description.security_contact || '#'"
|
||||
class="cursor-pointer"
|
||||
>
|
||||
{{ v.description?.security_contact || '-' }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-sm mt-4 mb-3 font-medium">{{ $t('staking.validator_status') }}</p>
|
||||
<p class="text-sm mt-4 mb-3 font-medium">
|
||||
{{ $t('staking.validator_status') }}
|
||||
</p>
|
||||
<div class="card-list">
|
||||
<div class="flex items-center mb-2">
|
||||
<Icon icon="mdi-shield-account-outline" class="text-xl mr-1" />
|
||||
@@ -343,18 +379,42 @@ function mapDelegators(messages: any[]) {
|
||||
<span> {{ v.jailed || '-' }} </span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-sm mt-4 mb-3 font-medium">{{ $t('staking.liquid_staking') }}</p>
|
||||
<p class="text-sm mt-4 mb-3 font-medium">
|
||||
{{ $t('staking.liquid_staking') }}
|
||||
</p>
|
||||
<div class="card-list">
|
||||
<div class="flex items-center mb-2">
|
||||
<Icon icon="mdi-lock" class="text-xl mr-1" />
|
||||
<span class="font-bold mr-2">{{ $t('staking.validator_bond_share') }}: </span>
|
||||
<span> {{ format.formatToken( {amount: v.validator_bond_shares, denom: staking.params.bond_denom }, false) }} </span>
|
||||
<span class="font-bold mr-2"
|
||||
>{{ $t('staking.validator_bond_share') }}:
|
||||
</span>
|
||||
<span>
|
||||
{{
|
||||
format.formatToken(
|
||||
{
|
||||
amount: v.validator_bond_shares,
|
||||
denom: staking.params.bond_denom,
|
||||
},
|
||||
false
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<Icon icon="mdi-waves-arrow-right" class="text-xl mr-1" />
|
||||
<span class="font-bold mr-2">{{ $t('staking.liquid_staking_shares') }}: </span>
|
||||
<span class="font-bold mr-2"
|
||||
>{{ $t('staking.liquid_staking_shares') }}:
|
||||
</span>
|
||||
<span>
|
||||
{{ format.formatToken( {amount: v.liquid_shares, denom: staking.params.bond_denom }, false) }}
|
||||
{{
|
||||
format.formatToken(
|
||||
{
|
||||
amount: v.liquid_shares,
|
||||
denom: staking.params.bond_denom,
|
||||
},
|
||||
false
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -429,11 +489,16 @@ function mapDelegators(messages: any[]) {
|
||||
class="flex items-center justify-center rounded w-10 h-10"
|
||||
style="border: 1px solid #666"
|
||||
>
|
||||
<Icon icon="mdi:arrow-down-bold-circle-outline" class="text-3xl" />
|
||||
<Icon
|
||||
icon="mdi:arrow-down-bold-circle-outline"
|
||||
class="text-3xl"
|
||||
/>
|
||||
</div>
|
||||
<div class="ml-3 flex flex-col justify-center">
|
||||
<h4>{{ v.unbonding_height }}</h4>
|
||||
<span class="text-sm">{{ $t('staking.unbonding_height') }}</span>
|
||||
<span class="text-sm">{{
|
||||
$t('staking.unbonding_height')
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -445,7 +510,13 @@ function mapDelegators(messages: any[]) {
|
||||
<Icon icon="mdi-clock" class="text-3xl" />
|
||||
</div>
|
||||
<div class="ml-3 flex flex-col justify-center">
|
||||
<h4 v-if="v.unbonding_time && !v.unbonding_time.startsWith('1970')">{{ format.toDay(v.unbonding_time, 'from') }}</h4>
|
||||
<h4
|
||||
v-if="
|
||||
v.unbonding_time && !v.unbonding_time.startsWith('1970')
|
||||
"
|
||||
>
|
||||
{{ format.toDay(v.unbonding_time, 'from') }}
|
||||
</h4>
|
||||
<h4 v-else>-</h4>
|
||||
<span class="text-sm">{{ $t('staking.unbonding_time') }}</span>
|
||||
</div>
|
||||
@@ -480,7 +551,9 @@ function mapDelegators(messages: any[]) {
|
||||
>
|
||||
{{ format.formatToken2(i) }}
|
||||
</div>
|
||||
<div class="text-sm mb-2 mt-2">{{ $t('staking.outstanding') }} {{ $t('account.rewards') }}</div>
|
||||
<div class="text-sm mb-2 mt-2">
|
||||
{{ $t('staking.outstanding') }} {{ $t('account.rewards') }}
|
||||
</div>
|
||||
<div
|
||||
v-for="(i, k) in rewards"
|
||||
:key="`reward-${k}`"
|
||||
@@ -509,14 +582,15 @@ function mapDelegators(messages: any[]) {
|
||||
</div>
|
||||
<div class="px-4 pb-4">
|
||||
<div class="mb-3">
|
||||
<div class="text-sm flex">{{ $t('staking.account_addr') }}
|
||||
<div class="text-sm flex">
|
||||
{{ $t('staking.account_addr') }}
|
||||
<Icon
|
||||
icon="mdi:content-copy"
|
||||
class="ml-2 cursor-pointer"
|
||||
v-show="addresses.account"
|
||||
@click="copyWebsite(addresses.account || '')"
|
||||
/>
|
||||
</div>
|
||||
icon="mdi:content-copy"
|
||||
class="ml-2 cursor-pointer"
|
||||
v-show="addresses.account"
|
||||
@click="copyWebsite(addresses.account || '')"
|
||||
/>
|
||||
</div>
|
||||
<RouterLink
|
||||
class="text-xs text-primary"
|
||||
:to="`/${chain}/account/${addresses.account}`"
|
||||
@@ -525,57 +599,69 @@ function mapDelegators(messages: any[]) {
|
||||
</RouterLink>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="text-sm flex">{{ $t('staking.operator_addr') }}
|
||||
<div class="text-sm flex">
|
||||
{{ $t('staking.operator_addr') }}
|
||||
<Icon
|
||||
icon="mdi:content-copy"
|
||||
class="ml-2 cursor-pointer"
|
||||
v-show="v.operator_address"
|
||||
@click="copyWebsite(v.operator_address || '')"
|
||||
/></div>
|
||||
icon="mdi:content-copy"
|
||||
class="ml-2 cursor-pointer"
|
||||
v-show="v.operator_address"
|
||||
@click="copyWebsite(v.operator_address || '')"
|
||||
/>
|
||||
</div>
|
||||
<div class="text-xs">
|
||||
{{ v.operator_address }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="text-sm flex">{{ $t('staking.hex_addr') }}
|
||||
<div class="text-sm flex">
|
||||
{{ $t('staking.hex_addr') }}
|
||||
<Icon
|
||||
icon="mdi:content-copy"
|
||||
class="ml-2 cursor-pointer"
|
||||
v-show="addresses.hex"
|
||||
@click="copyWebsite(addresses.hex || '')"
|
||||
/>
|
||||
</div>
|
||||
icon="mdi:content-copy"
|
||||
class="ml-2 cursor-pointer"
|
||||
v-show="addresses.hex"
|
||||
@click="copyWebsite(addresses.hex || '')"
|
||||
/>
|
||||
</div>
|
||||
<div class="text-xs">{{ addresses.hex }}</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="text-sm flex">{{ $t('staking.signer_addr') }}
|
||||
<div class="text-sm flex">
|
||||
{{ $t('staking.signer_addr') }}
|
||||
<Icon
|
||||
icon="mdi:content-copy"
|
||||
class="ml-2 cursor-pointer"
|
||||
v-show="addresses.valCons"
|
||||
@click="copyWebsite(addresses.valCons || '')"
|
||||
/>
|
||||
</div>
|
||||
icon="mdi:content-copy"
|
||||
class="ml-2 cursor-pointer"
|
||||
v-show="addresses.valCons"
|
||||
@click="copyWebsite(addresses.valCons || '')"
|
||||
/>
|
||||
</div>
|
||||
<div class="text-xs">{{ addresses.valCons }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-sm flex">{{ $t('staking.consensus_pub_key') }}
|
||||
<div class="text-sm flex">
|
||||
{{ $t('staking.consensus_pub_key') }}
|
||||
<Icon
|
||||
icon="mdi:content-copy"
|
||||
class="ml-2 cursor-pointer"
|
||||
v-show="v.consensus_pubkey"
|
||||
@click="copyWebsite(JSON.stringify(v.consensus_pubkey) || '')"
|
||||
/>
|
||||
</div>
|
||||
icon="mdi:content-copy"
|
||||
class="ml-2 cursor-pointer"
|
||||
v-show="v.consensus_pubkey"
|
||||
@click="copyWebsite(JSON.stringify(v.consensus_pubkey) || '')"
|
||||
/>
|
||||
</div>
|
||||
<div class="text-xs">{{ v.consensus_pubkey }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="delegations.delegation_responses" class="mt-5 bg-base-100 shadow rounded p-4 ">
|
||||
<div class="text-lg mb-4 font-semibold">{{ $t('account.delegations') }}
|
||||
<span class="float-right"> {{ delegations.delegation_responses?.length || 0 }} / {{ delegations.pagination?.total || 0 }} </span>
|
||||
<div
|
||||
v-if="delegations.delegation_responses"
|
||||
class="mt-5 bg-base-100 shadow rounded p-4"
|
||||
>
|
||||
<div class="text-lg mb-4 font-semibold">
|
||||
{{ $t('account.delegations') }}
|
||||
<span class="float-right">
|
||||
{{ delegations.delegation_responses?.length || 0 }} /
|
||||
{{ delegations.pagination?.total || 0 }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="rounded overflow-auto">
|
||||
<table class="table validatore-table w-full">
|
||||
@@ -586,23 +672,33 @@ function mapDelegators(messages: any[]) {
|
||||
<th class="text-left pl-4">{{ $t('account.delegation') }}</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="{balance, delegation} in delegations.delegation_responses">
|
||||
<tr
|
||||
v-for="{
|
||||
balance,
|
||||
delegation,
|
||||
} in delegations.delegation_responses"
|
||||
>
|
||||
<td class="text-sm text-primary">
|
||||
{{ delegation.delegator_address }}
|
||||
</td>
|
||||
<td class="truncate text-primary">
|
||||
{{ format.formatToken(balance)}}
|
||||
{{ format.formatToken(balance) }}
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<PaginationBar :total="delegations.pagination?.total" :limit="page.limit" :callback="pageload"/>
|
||||
<PaginationBar
|
||||
:total="delegations.pagination?.total"
|
||||
:limit="page.limit"
|
||||
:callback="pageload"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-5 bg-base-100 shadow rounded p-4">
|
||||
<div class="text-lg mb-4 font-semibold">{{ $t('account.transactions') }}</div>
|
||||
<div class="text-lg mb-4 font-semibold">
|
||||
{{ $t('account.transactions') }}
|
||||
</div>
|
||||
<div class="rounded overflow-auto">
|
||||
<table class="table validatore-table w-full">
|
||||
<thead>
|
||||
@@ -610,7 +706,9 @@ function mapDelegators(messages: any[]) {
|
||||
{{ $t('account.height') }}
|
||||
</th>
|
||||
<th class="text-left pl-4">{{ $t('account.hash') }}</th>
|
||||
<th class="text-left pl-4" width="40%">{{ $t('account.messages') }}</th>
|
||||
<th class="text-left pl-4" width="40%">
|
||||
{{ $t('account.messages') }}
|
||||
</th>
|
||||
<th class="text-left pl-4">{{ $t('account.time') }}</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -648,46 +746,55 @@ function mapDelegators(messages: any[]) {
|
||||
<div class="mt-5 bg-base-100 shadow rounded p-4">
|
||||
<div class="text-lg mb-4 font-semibold">
|
||||
<div class="tabs tabs-boxed bg-transparent">
|
||||
|
||||
<span class="mr-10">Voting Power Events: </span>
|
||||
<a
|
||||
class="tab text-gray-400"
|
||||
:class="{ 'tab-active': selectedEventType === EventType.Delegate }"
|
||||
@click="loadPowerEvents(1, EventType.Delegate)"
|
||||
>{{ $t('account.btn_delegate') }}</a
|
||||
>
|
||||
<a
|
||||
class="tab text-gray-400"
|
||||
:class="{ 'tab-active': selectedEventType === EventType.Unbond }"
|
||||
@click="loadPowerEvents(1, EventType.Unbond)"
|
||||
>{{ $t('account.btn_unbond') }}</a
|
||||
>
|
||||
</div>
|
||||
<span class="mr-10">Voting Power Events: </span>
|
||||
<a
|
||||
class="tab text-gray-400"
|
||||
:class="{ 'tab-active': selectedEventType === EventType.Delegate }"
|
||||
@click="loadPowerEvents(1, EventType.Delegate)"
|
||||
>{{ $t('account.btn_delegate') }}</a
|
||||
>
|
||||
<a
|
||||
class="tab text-gray-400"
|
||||
:class="{ 'tab-active': selectedEventType === EventType.Unbond }"
|
||||
@click="loadPowerEvents(1, EventType.Unbond)"
|
||||
>{{ $t('account.btn_unbond') }}</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rounded overflow-auto">
|
||||
<table class="table validatore-table w-full">
|
||||
<thead>
|
||||
<th class="text-left pl-4">{{ $t('account.delegator') }}</th>
|
||||
<th class="text-left pl-4">{{ $t('account.amount') }}</th>
|
||||
<th class="text-left pl-4">{{ $t('account.height') }} / {{ $t('account.time') }}</th>
|
||||
<th class="text-left pl-4">
|
||||
{{ $t('account.height') }} / {{ $t('account.time') }}
|
||||
</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(item, i) in events.tx_responses">
|
||||
<td class="pr-2 truncate text-primary" style="max-width: 250px">
|
||||
<RouterLink v-for="d in mapDelegators(item.tx?.body?.messages)" :to="`/${props.chain}/account/${d}`">
|
||||
<RouterLink
|
||||
v-for="d in mapDelegators(item.tx?.body?.messages)"
|
||||
:to="`/${props.chain}/account/${d}`"
|
||||
>
|
||||
{{ d }}
|
||||
</RouterLink>
|
||||
</RouterLink>
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex items-center" :class="{
|
||||
'text-yes' : selectedEventType === EventType.Delegate,
|
||||
'text-no' : selectedEventType === EventType.Unbond,
|
||||
}">
|
||||
<div
|
||||
class="flex items-center"
|
||||
:class="{
|
||||
'text-yes': selectedEventType === EventType.Delegate,
|
||||
'text-no': selectedEventType === EventType.Unbond,
|
||||
}"
|
||||
>
|
||||
<RouterLink :to="`/${props.chain}/tx/${item.txhash}`">
|
||||
<span class="mr-2">
|
||||
{{ (selectedEventType === EventType.Delegate ? '+' : '-')}} {{
|
||||
mapEvents(item.events)
|
||||
}}</span>
|
||||
{{
|
||||
selectedEventType === EventType.Delegate ? '+' : '-'
|
||||
}}
|
||||
{{ mapEvents(item.events) }}</span
|
||||
>
|
||||
</RouterLink>
|
||||
<Icon
|
||||
v-if="item.code === 0"
|
||||
@@ -698,15 +805,23 @@ function mapDelegators(messages: any[]) {
|
||||
</div>
|
||||
</td>
|
||||
<td width="150">
|
||||
<RouterLink class="text-primary mb-0" :to="`/${props.chain}/block/${item.height}`">{{
|
||||
item.height
|
||||
}}</RouterLink><br>
|
||||
<span class="text-xs pt-0 mt-0">{{ format.toDay(item.timestamp, 'from') }}</span>
|
||||
<RouterLink
|
||||
class="text-primary mb-0"
|
||||
:to="`/${props.chain}/block/${item.height}`"
|
||||
>{{ item.height }}</RouterLink
|
||||
><br />
|
||||
<span class="text-xs pt-0 mt-0">{{
|
||||
format.toDay(item.timestamp, 'from')
|
||||
}}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<PaginationBar :total="events.pagination?.total" :limit="page.limit" :callback="pagePowerEvents"/>
|
||||
<PaginationBar
|
||||
:total="events.pagination?.total"
|
||||
:limit="page.limit"
|
||||
:callback="pagePowerEvents"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end -->
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
useBaseStore,
|
||||
useBlockchain,
|
||||
useFormatter,
|
||||
useMintStore,
|
||||
useStakingStore,
|
||||
useTxDialog,
|
||||
useBaseStore,
|
||||
useBlockchain,
|
||||
useFormatter,
|
||||
useMintStore,
|
||||
useStakingStore,
|
||||
useTxDialog,
|
||||
} from '@/stores';
|
||||
import { computed } from '@vue/reactivity';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { Icon } from '@iconify/vue';
|
||||
import type { Key, SlashingParam, Validator } from '@/types';
|
||||
import { formatSeconds} from '@/libs/utils'
|
||||
import { formatSeconds } from '@/libs/utils';
|
||||
import { diff } from 'semver';
|
||||
|
||||
const staking = useStakingStore();
|
||||
@@ -19,7 +19,7 @@ const base = useBaseStore();
|
||||
const format = useFormatter();
|
||||
const dialog = useTxDialog();
|
||||
const chainStore = useBlockchain();
|
||||
const mintStore = useMintStore()
|
||||
const mintStore = useMintStore();
|
||||
|
||||
const cache = JSON.parse(localStorage.getItem('avatars') || '{}');
|
||||
const avatars = ref(cache || {});
|
||||
@@ -27,130 +27,156 @@ const latest = ref({} as Record<string, number>);
|
||||
const yesterday = ref({} as Record<string, number>);
|
||||
const tab = ref('active');
|
||||
const unbondList = ref([] as Validator[]);
|
||||
const slashing = ref({} as SlashingParam)
|
||||
const slashing = ref({} as SlashingParam);
|
||||
|
||||
onMounted(() => {
|
||||
staking.fetchUnbondingValdiators().then((res) => {
|
||||
unbondList.value = res.concat(unbondList.value);
|
||||
});
|
||||
staking.fetchInacitveValdiators().then((res) => {
|
||||
unbondList.value = unbondList.value.concat(res);
|
||||
});
|
||||
chainStore.rpc.getSlashingParams().then(res => {
|
||||
slashing.value = res.params
|
||||
})
|
||||
staking.fetchUnbondingValdiators().then((res) => {
|
||||
unbondList.value = res.concat(unbondList.value);
|
||||
});
|
||||
staking.fetchInacitveValdiators().then((res) => {
|
||||
unbondList.value = unbondList.value.concat(res);
|
||||
});
|
||||
chainStore.rpc.getSlashingParams().then((res) => {
|
||||
slashing.value = res.params;
|
||||
});
|
||||
});
|
||||
|
||||
async function fetchChange(blockWindow: number = 14400) {
|
||||
let page = 0;
|
||||
let page = 0;
|
||||
|
||||
let height = Number(base.latest?.block?.header?.height || 0);
|
||||
if (height > blockWindow) {
|
||||
height -= blockWindow;
|
||||
} else {
|
||||
height = 1;
|
||||
}
|
||||
// voting power in 24h ago
|
||||
while (page < staking.validators.length && height > 0) {
|
||||
await base.fetchValidatorByHeight(height, page).then((x) => {
|
||||
x.validators.forEach((v) => {
|
||||
yesterday.value[v.pub_key.key] = Number(v.voting_power);
|
||||
});
|
||||
});
|
||||
page += 100;
|
||||
}
|
||||
let height = Number(base.latest?.block?.header?.height || 0);
|
||||
if (height > blockWindow) {
|
||||
height -= blockWindow;
|
||||
} else {
|
||||
height = 1;
|
||||
}
|
||||
// voting power in 24h ago
|
||||
while (page < staking.validators.length && height > 0) {
|
||||
await base.fetchValidatorByHeight(height, page).then((x) => {
|
||||
x.validators.forEach((v) => {
|
||||
yesterday.value[v.pub_key.key] = Number(v.voting_power);
|
||||
});
|
||||
});
|
||||
page += 100;
|
||||
}
|
||||
|
||||
page = 0;
|
||||
// voting power for now
|
||||
while (page < staking.validators.length) {
|
||||
await base.fetchLatestValidators(page).then((x) => {
|
||||
x.validators.forEach((v) => {
|
||||
latest.value[v.pub_key.key] = Number(v.voting_power);
|
||||
});
|
||||
});
|
||||
page += 100;
|
||||
}
|
||||
page = 0;
|
||||
// voting power for now
|
||||
while (page < staking.validators.length) {
|
||||
await base.fetchLatestValidators(page).then((x) => {
|
||||
x.validators.forEach((v) => {
|
||||
latest.value[v.pub_key.key] = Number(v.voting_power);
|
||||
});
|
||||
});
|
||||
page += 100;
|
||||
}
|
||||
}
|
||||
|
||||
const changes = computed(() => {
|
||||
const changes = {} as Record<string, number>;
|
||||
Object.keys(latest.value).forEach((k) => {
|
||||
const l = latest.value[k] || 0;
|
||||
const y = yesterday.value[k] || 0;
|
||||
changes[k] = l - y;
|
||||
});
|
||||
return changes;
|
||||
const changes = {} as Record<string, number>;
|
||||
Object.keys(latest.value).forEach((k) => {
|
||||
const l = latest.value[k] || 0;
|
||||
const y = yesterday.value[k] || 0;
|
||||
changes[k] = l - y;
|
||||
});
|
||||
return changes;
|
||||
});
|
||||
|
||||
const change24 = (entry: { consensus_pubkey: Key; tokens: string }) => {
|
||||
const txt = entry.consensus_pubkey.key;
|
||||
// const n: number = latest.value[txt];
|
||||
// const o: number = yesterday.value[txt];
|
||||
// // console.log( txt, n, o)
|
||||
// return n > 0 && o > 0 ? n - o : 0;
|
||||
const txt = entry.consensus_pubkey.key;
|
||||
// const n: number = latest.value[txt];
|
||||
// const o: number = yesterday.value[txt];
|
||||
// // console.log( txt, n, o)
|
||||
// return n > 0 && o > 0 ? n - o : 0;
|
||||
|
||||
const latestValue = latest.value[txt];
|
||||
if (!latestValue) {
|
||||
return 0;
|
||||
}
|
||||
const latestValue = latest.value[txt];
|
||||
if (!latestValue) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const displayTokens = format.tokenAmountNumber({
|
||||
amount: parseInt(entry.tokens, 10).toString(),
|
||||
denom: staking.params.bond_denom,
|
||||
});
|
||||
const coefficient = displayTokens / latestValue;
|
||||
return changes.value[txt] * coefficient;
|
||||
const displayTokens = format.tokenAmountNumber({
|
||||
amount: parseInt(entry.tokens, 10).toString(),
|
||||
denom: staking.params.bond_denom,
|
||||
});
|
||||
const coefficient = displayTokens / latestValue;
|
||||
return changes.value[txt] * coefficient;
|
||||
};
|
||||
|
||||
const change24Text = (entry: { consensus_pubkey: Key; tokens: string }) => {
|
||||
if (!entry) return '';
|
||||
const v = change24(entry);
|
||||
return v && v !== 0 ? format.showChanges(v) : '';
|
||||
if (!entry) return '';
|
||||
const v = change24(entry);
|
||||
return v && v !== 0 ? format.showChanges(v) : '';
|
||||
};
|
||||
|
||||
const change24Color = (entry: { consensus_pubkey: Key; tokens: string }) => {
|
||||
if (!entry) return '';
|
||||
const v = change24(entry);
|
||||
if (v > 0) return 'text-success';
|
||||
if (v < 0) return 'text-error';
|
||||
if (!entry) return '';
|
||||
const v = change24(entry);
|
||||
if (v > 0) return 'text-success';
|
||||
if (v < 0) return 'text-error';
|
||||
};
|
||||
|
||||
const calculateRank = function (position: number) {
|
||||
let sum = 0;
|
||||
for (let i = 0; i < position; i++) {
|
||||
sum += Number(staking.validators[i]?.delegator_shares);
|
||||
}
|
||||
const percent = sum / staking.totalPower;
|
||||
let sum = 0;
|
||||
for (let i = 0; i < position; i++) {
|
||||
sum += Number(staking.validators[i]?.delegator_shares);
|
||||
}
|
||||
const percent = sum / staking.totalPower;
|
||||
|
||||
switch (true) {
|
||||
case tab.value === 'active' && percent < 0.33:
|
||||
return 'error';
|
||||
case tab.value === 'active' && percent < 0.67:
|
||||
return 'warning';
|
||||
default:
|
||||
return 'primary';
|
||||
}
|
||||
switch (true) {
|
||||
case tab.value === 'active' && percent < 0.33:
|
||||
return 'error';
|
||||
case tab.value === 'active' && percent < 0.67:
|
||||
return 'warning';
|
||||
default:
|
||||
return 'primary';
|
||||
}
|
||||
};
|
||||
|
||||
function isFeatured(endpoints: string[], who?: {website?: string, moniker: string }) {
|
||||
if(!endpoints || !who) return false
|
||||
return endpoints.findIndex(x => who.website && who.website?.substring(0, who.website?.lastIndexOf('.')).endsWith(x) || who?.moniker?.toLowerCase().search(x.toLowerCase()) > -1) > -1
|
||||
function isFeatured(
|
||||
endpoints: string[],
|
||||
who?: { website?: string; moniker: string }
|
||||
) {
|
||||
if (!endpoints || !who) return false;
|
||||
return (
|
||||
endpoints.findIndex(
|
||||
(x) =>
|
||||
(who.website &&
|
||||
who.website
|
||||
?.substring(0, who.website?.lastIndexOf('.'))
|
||||
.endsWith(x)) ||
|
||||
who?.moniker?.toLowerCase().search(x.toLowerCase()) > -1
|
||||
) > -1
|
||||
);
|
||||
}
|
||||
|
||||
const list = computed(() => {
|
||||
if (tab.value === 'active') {
|
||||
return staking.validators.map((x, i) => ({v: x, rank: calculateRank(i), logo: logo(x.description.identity)}));
|
||||
} else if (tab.value === 'featured') {
|
||||
const endpoint = chainStore.current?.endpoints?.rest?.map(x => x.provider)
|
||||
if(endpoint) {
|
||||
endpoint.push('ping')
|
||||
return staking.validators
|
||||
.filter(x => isFeatured(endpoint, x.description))
|
||||
.map((x, i) => ({v: x, rank: 'primary', logo: logo(x.description.identity)}));
|
||||
}
|
||||
return []
|
||||
if (tab.value === 'active') {
|
||||
return staking.validators.map((x, i) => ({
|
||||
v: x,
|
||||
rank: calculateRank(i),
|
||||
logo: logo(x.description.identity),
|
||||
}));
|
||||
} else if (tab.value === 'featured') {
|
||||
const endpoint = chainStore.current?.endpoints?.rest?.map(
|
||||
(x) => x.provider
|
||||
);
|
||||
if (endpoint) {
|
||||
endpoint.push('ping');
|
||||
return staking.validators
|
||||
.filter((x) => isFeatured(endpoint, x.description))
|
||||
.map((x, i) => ({
|
||||
v: x,
|
||||
rank: 'primary',
|
||||
logo: logo(x.description.identity),
|
||||
}));
|
||||
}
|
||||
return unbondList.value.map((x, i) => ({v: x, rank: 'primary', logo: logo(x.description.identity)}));
|
||||
return [];
|
||||
}
|
||||
return unbondList.value.map((x, i) => ({
|
||||
v: x,
|
||||
rank: 'primary',
|
||||
logo: logo(x.description.identity),
|
||||
}));
|
||||
});
|
||||
|
||||
const fetchAvatar = (identity: string) => {
|
||||
@@ -202,294 +228,325 @@ const loadAvatars = () => {
|
||||
};
|
||||
|
||||
const logo = (identity?: string) => {
|
||||
if (!identity || !avatars.value[identity]) return '';
|
||||
const url = avatars.value[identity] || '';
|
||||
return url.startsWith('http')
|
||||
? url
|
||||
: `https://s3.amazonaws.com/keybase_processed_uploads/${url}`;
|
||||
if (!identity || !avatars.value[identity]) return '';
|
||||
const url = avatars.value[identity] || '';
|
||||
return url.startsWith('http')
|
||||
? url
|
||||
: `https://s3.amazonaws.com/keybase_processed_uploads/${url}`;
|
||||
};
|
||||
|
||||
const loaded = ref(false);
|
||||
base.$subscribe((_, s) => {
|
||||
if (s.recents.length >= 2 && loaded.value === false) {
|
||||
loaded.value = true;
|
||||
const diff_time = Date.parse(s.recents[1].block.header.time) - Date.parse(s.recents[0].block.header.time)
|
||||
const diff_height = Number(s.recents[1].block.header.height) - Number(s.recents[0].block.header.height)
|
||||
const block_window = Number(Number(86400 * 1000 * diff_height / diff_time).toFixed(0))
|
||||
fetchChange(block_window);
|
||||
}
|
||||
if (s.recents.length >= 2 && loaded.value === false) {
|
||||
loaded.value = true;
|
||||
const diff_time =
|
||||
Date.parse(s.recents[1].block.header.time) -
|
||||
Date.parse(s.recents[0].block.header.time);
|
||||
const diff_height =
|
||||
Number(s.recents[1].block.header.height) -
|
||||
Number(s.recents[0].block.header.height);
|
||||
const block_window = Number(
|
||||
Number((86400 * 1000 * diff_height) / diff_time).toFixed(0)
|
||||
);
|
||||
fetchChange(block_window);
|
||||
}
|
||||
});
|
||||
|
||||
loadAvatars();
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div class="bg-base-100 rounded-lg grid sm:grid-cols-1 md:grid-cols-4 p-4" >
|
||||
<div class="flex">
|
||||
<span>
|
||||
<div class="relative w-9 h-9 rounded overflow-hidden flex items-center justify-center mr-2">
|
||||
<Icon class="text-success" icon="mdi:trending-up" size="32" />
|
||||
<div class="absolute top-0 left-0 bottom-0 right-0 opacity-20 bg-success"></div>
|
||||
</div>
|
||||
</span>
|
||||
<span>
|
||||
<div class="font-bold">{{ format.percent(mintStore.inflation) }}</div>
|
||||
<div class="text-xs">{{ $t('staking.inflation') }}</div>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<span>
|
||||
<div class="relative w-9 h-9 rounded overflow-hidden flex items-center justify-center mr-2">
|
||||
<Icon class="text-primary" icon="mdi:lock-open-outline" size="32" />
|
||||
<div class="absolute top-0 left-0 bottom-0 right-0 opacity-20 bg-primary"></div>
|
||||
</div>
|
||||
</span>
|
||||
<span>
|
||||
<div class="font-bold">{{ formatSeconds(staking.params?.unbonding_time) }}</div>
|
||||
<div class="text-xs">{{ $t('staking.unbonding_time') }}</div>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<span>
|
||||
<div class="relative w-9 h-9 rounded overflow-hidden flex items-center justify-center mr-2">
|
||||
<Icon class="text-error" icon="mdi:alert-octagon-outline" size="32" />
|
||||
<div class="absolute top-0 left-0 bottom-0 right-0 opacity-20 bg-error"></div>
|
||||
</div>
|
||||
</span>
|
||||
<span>
|
||||
<div class="font-bold">{{ format.percent(slashing.slash_fraction_double_sign) }}</div>
|
||||
<div class="text-xs">{{ $t('staking.double_sign_slashing') }}</div>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<span>
|
||||
<div class="relative w-9 h-9 rounded overflow-hidden flex items-center justify-center mr-2">
|
||||
<Icon class="text-error" icon="mdi:pause" size="32" />
|
||||
<div class="absolute top-0 left-0 bottom-0 right-0 opacity-20 bg-error"></div>
|
||||
</div>
|
||||
</span>
|
||||
<span>
|
||||
<div class="font-bold">{{ format.percent(slashing.slash_fraction_downtime) }}</div>
|
||||
<div class="text-xs">{{ $t('staking.downtime_slashing') }}</div>
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="bg-base-100 rounded-lg grid sm:grid-cols-1 md:grid-cols-4 p-4">
|
||||
<div class="flex">
|
||||
<span>
|
||||
<div
|
||||
class="relative w-9 h-9 rounded overflow-hidden flex items-center justify-center mr-2"
|
||||
>
|
||||
<Icon class="text-success" icon="mdi:trending-up" size="32" />
|
||||
<div
|
||||
class="absolute top-0 left-0 bottom-0 right-0 opacity-20 bg-success"
|
||||
></div>
|
||||
</div>
|
||||
</span>
|
||||
<span>
|
||||
<div class="font-bold">{{ format.percent(mintStore.inflation) }}</div>
|
||||
<div class="text-xs">{{ $t('staking.inflation') }}</div>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<span>
|
||||
<div
|
||||
class="relative w-9 h-9 rounded overflow-hidden flex items-center justify-center mr-2"
|
||||
>
|
||||
<Icon class="text-primary" icon="mdi:lock-open-outline" size="32" />
|
||||
<div
|
||||
class="absolute top-0 left-0 bottom-0 right-0 opacity-20 bg-primary"
|
||||
></div>
|
||||
</div>
|
||||
</span>
|
||||
<span>
|
||||
<div class="font-bold">
|
||||
{{ formatSeconds(staking.params?.unbonding_time) }}
|
||||
</div>
|
||||
<div class="text-xs">{{ $t('staking.unbonding_time') }}</div>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<span>
|
||||
<div
|
||||
class="relative w-9 h-9 rounded overflow-hidden flex items-center justify-center mr-2"
|
||||
>
|
||||
<Icon
|
||||
class="text-error"
|
||||
icon="mdi:alert-octagon-outline"
|
||||
size="32"
|
||||
/>
|
||||
<div
|
||||
class="absolute top-0 left-0 bottom-0 right-0 opacity-20 bg-error"
|
||||
></div>
|
||||
</div>
|
||||
</span>
|
||||
<span>
|
||||
<div class="font-bold">
|
||||
{{ format.percent(slashing.slash_fraction_double_sign) }}
|
||||
</div>
|
||||
<div class="text-xs">{{ $t('staking.double_sign_slashing') }}</div>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<span>
|
||||
<div
|
||||
class="relative w-9 h-9 rounded overflow-hidden flex items-center justify-center mr-2"
|
||||
>
|
||||
<Icon class="text-error" icon="mdi:pause" size="32" />
|
||||
<div
|
||||
class="absolute top-0 left-0 bottom-0 right-0 opacity-20 bg-error"
|
||||
></div>
|
||||
</div>
|
||||
</span>
|
||||
<span>
|
||||
<div class="font-bold">
|
||||
{{ format.percent(slashing.slash_fraction_downtime) }}
|
||||
</div>
|
||||
<div class="text-xs">{{ $t('staking.downtime_slashing') }}</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="flex items-center justify-between py-1">
|
||||
<div class="tabs tabs-boxed bg-transparent">
|
||||
<a
|
||||
class="tab text-gray-400"
|
||||
:class="{ 'tab-active': tab === 'featured' }"
|
||||
@click="tab = 'featured'"
|
||||
>{{ $t('staking.popular') }}</a
|
||||
>
|
||||
<a
|
||||
class="tab text-gray-400"
|
||||
:class="{ 'tab-active': tab === 'active' }"
|
||||
@click="tab = 'active'"
|
||||
>{{ $t('staking.active') }}</a
|
||||
>
|
||||
<a
|
||||
class="tab text-gray-400"
|
||||
:class="{ 'tab-active': tab === 'inactive' }"
|
||||
@click="tab = 'inactive'"
|
||||
>{{ $t('staking.inactive') }}</a
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="text-lg font-semibold">
|
||||
{{ list.length }}/{{ staking.params.max_validators }}
|
||||
</div>
|
||||
<div class="flex items-center justify-between py-1">
|
||||
<div class="tabs tabs-boxed bg-transparent">
|
||||
<a
|
||||
class="tab text-gray-400"
|
||||
:class="{ 'tab-active': tab === 'featured' }"
|
||||
@click="tab = 'featured'"
|
||||
>{{ $t('staking.popular') }}</a
|
||||
>
|
||||
<a
|
||||
class="tab text-gray-400"
|
||||
:class="{ 'tab-active': tab === 'active' }"
|
||||
@click="tab = 'active'"
|
||||
>{{ $t('staking.active') }}</a
|
||||
>
|
||||
<a
|
||||
class="tab text-gray-400"
|
||||
:class="{ 'tab-active': tab === 'inactive' }"
|
||||
@click="tab = 'inactive'"
|
||||
>{{ $t('staking.inactive') }}</a
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded shadow">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table staking-table w-full">
|
||||
<thead class=" bg-base-200">
|
||||
<tr>
|
||||
<th
|
||||
scope="col"
|
||||
class="uppercase"
|
||||
style="width: 3rem; position: relative"
|
||||
>
|
||||
{{ $t('staking.rank') }}
|
||||
</th>
|
||||
<th scope="col" class="uppercase">{{ $t('staking.validator') }}</th>
|
||||
<th scope="col" class="text-right uppercase">{{ $t('staking.voting_power') }}</th>
|
||||
<th scope="col" class="text-right uppercase">{{ $t('staking.24h_changes') }}</th>
|
||||
<th scope="col" class="text-right uppercase">{{ $t('staking.commission') }}</th>
|
||||
<th scope="col" class="text-center uppercase">{{ $t('staking.actions') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="({v, rank, logo}, i) in list"
|
||||
:key="v.operator_address"
|
||||
class="hover:bg-gray-100 dark:hover:bg-[#384059]"
|
||||
<div class="text-lg font-semibold">
|
||||
{{ list.length }}/{{ staking.params.max_validators }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded shadow">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table staking-table w-full">
|
||||
<thead class="bg-base-200">
|
||||
<tr>
|
||||
<th
|
||||
scope="col"
|
||||
class="uppercase"
|
||||
style="width: 3rem; position: relative"
|
||||
>
|
||||
{{ $t('staking.rank') }}
|
||||
</th>
|
||||
<th scope="col" class="uppercase">
|
||||
{{ $t('staking.validator') }}
|
||||
</th>
|
||||
<th scope="col" class="text-right uppercase">
|
||||
{{ $t('staking.voting_power') }}
|
||||
</th>
|
||||
<th scope="col" class="text-right uppercase">
|
||||
{{ $t('staking.24h_changes') }}
|
||||
</th>
|
||||
<th scope="col" class="text-right uppercase">
|
||||
{{ $t('staking.commission') }}
|
||||
</th>
|
||||
<th scope="col" class="text-center uppercase">
|
||||
{{ $t('staking.actions') }}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="({ v, rank, logo }, i) in list"
|
||||
:key="v.operator_address"
|
||||
class="hover:bg-gray-100 dark:hover:bg-[#384059]"
|
||||
>
|
||||
<!-- 👉 rank -->
|
||||
<td>
|
||||
<div
|
||||
class="text-xs truncate relative px-2 py-1 rounded-full w-fit"
|
||||
:class="`text-${rank}`"
|
||||
>
|
||||
<span
|
||||
class="inset-x-0 inset-y-0 opacity-10 absolute"
|
||||
:class="`bg-${rank}`"
|
||||
></span>
|
||||
{{ i + 1 }}
|
||||
</div>
|
||||
</td>
|
||||
<!-- 👉 Validator -->
|
||||
<td>
|
||||
<div
|
||||
class="flex items-center overflow-hidden"
|
||||
style="max-width: 300px"
|
||||
>
|
||||
<div class="avatar mr-4 relative w-8 h-8 rounded-full">
|
||||
<div
|
||||
class="w-8 h-8 rounded-full bg-gray-400 absolute opacity-10"
|
||||
></div>
|
||||
<div class="w-8 h-8 rounded-full">
|
||||
<img
|
||||
v-if="logo"
|
||||
:src="logo"
|
||||
class="object-contain"
|
||||
@error="
|
||||
(e) => {
|
||||
const identity = v.description?.identity;
|
||||
if (identity) loadAvatar(identity);
|
||||
}
|
||||
"
|
||||
/>
|
||||
<Icon
|
||||
v-else
|
||||
class="text-3xl"
|
||||
:icon="`mdi-help-circle-outline`"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<span
|
||||
class="text-sm text-primary dark:invert whitespace-nowrap overflow-hidden"
|
||||
>
|
||||
<RouterLink
|
||||
:to="{
|
||||
name: 'chain-staking-validator',
|
||||
params: {
|
||||
validator: v.operator_address,
|
||||
},
|
||||
}"
|
||||
class="font-weight-medium"
|
||||
>
|
||||
<!-- 👉 rank -->
|
||||
<td>
|
||||
<div
|
||||
class="text-xs truncate relative px-2 py-1 rounded-full w-fit"
|
||||
:class="`text-${rank}`"
|
||||
>
|
||||
<span
|
||||
class="inset-x-0 inset-y-0 opacity-10 absolute"
|
||||
:class="`bg-${rank}`"
|
||||
></span>
|
||||
{{ i + 1 }}
|
||||
</div>
|
||||
</td>
|
||||
<!-- 👉 Validator -->
|
||||
<td>
|
||||
<div
|
||||
class="flex items-center overflow-hidden"
|
||||
style="max-width: 300px"
|
||||
>
|
||||
<div
|
||||
class="avatar mr-4 relative w-8 h-8 rounded-full"
|
||||
>
|
||||
<div
|
||||
class="w-8 h-8 rounded-full bg-gray-400 absolute opacity-10"
|
||||
></div>
|
||||
<div class="w-8 h-8 rounded-full">
|
||||
<img
|
||||
v-if="logo"
|
||||
:src="logo"
|
||||
class="object-contain"
|
||||
@error="
|
||||
(e) => {
|
||||
const identity = v.description?.identity;
|
||||
if (identity) loadAvatar(identity);
|
||||
}
|
||||
"
|
||||
/>
|
||||
<Icon
|
||||
v-else
|
||||
class="text-3xl"
|
||||
:icon="`mdi-help-circle-outline`"
|
||||
/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{{ v.description?.moniker }}
|
||||
</RouterLink>
|
||||
</span>
|
||||
<span class="text-xs">{{
|
||||
v.description?.website || v.description?.identity || '-'
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm text-primary dark:invert whitespace-nowrap overflow-hidden">
|
||||
<RouterLink
|
||||
:to="{
|
||||
name: 'chain-staking-validator',
|
||||
params: {
|
||||
validator:
|
||||
v.operator_address,
|
||||
},
|
||||
}"
|
||||
class="font-weight-medium"
|
||||
>
|
||||
{{ v.description?.moniker }}
|
||||
</RouterLink>
|
||||
</span>
|
||||
<span class="text-xs">{{
|
||||
v.description?.website ||
|
||||
v.description?.identity ||
|
||||
'-'
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<!-- 👉 Voting Power -->
|
||||
<td class="text-right">
|
||||
<div class="flex flex-col">
|
||||
<h6 class="text-sm font-weight-medium whitespace-nowrap ">
|
||||
{{
|
||||
format.formatToken(
|
||||
{
|
||||
amount: parseInt(
|
||||
v.tokens
|
||||
).toString(),
|
||||
denom: staking.params
|
||||
.bond_denom,
|
||||
},
|
||||
true,
|
||||
'0,0'
|
||||
)
|
||||
}}
|
||||
</h6>
|
||||
<span class="text-xs">{{
|
||||
format.calculatePercent(
|
||||
v.delegator_shares,
|
||||
staking.totalPower
|
||||
)
|
||||
}}</span>
|
||||
</div>
|
||||
</td>
|
||||
<!-- 👉 24h Changes -->
|
||||
<td
|
||||
class="text-right text-xs"
|
||||
:class="change24Color(v)"
|
||||
>
|
||||
{{ change24Text(v) }}
|
||||
</td>
|
||||
<!-- 👉 commission -->
|
||||
<td class="text-right text-xs">
|
||||
{{
|
||||
format.formatCommissionRate(
|
||||
v.commission?.commission_rates?.rate
|
||||
)
|
||||
}}
|
||||
</td>
|
||||
<!-- 👉 Action -->
|
||||
<td class="text-center">
|
||||
<div
|
||||
v-if="v.jailed"
|
||||
class="badge badge-error gap-2 text-white"
|
||||
>
|
||||
{{ $t('staking.jailed') }}
|
||||
</div>
|
||||
<label
|
||||
v-else
|
||||
for="delegate"
|
||||
class="btn btn-xs btn-primary rounded-sm capitalize"
|
||||
@click="
|
||||
dialog.open('delegate', {
|
||||
validator_address:
|
||||
v.operator_address,
|
||||
})
|
||||
"
|
||||
>{{ $t('account.btn_delegate') }}</label
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
<div class="flex flex-row items-center">
|
||||
<div
|
||||
class="text-xs truncate relative py-2 px-4 rounded-md w-fit text-error mr-2"
|
||||
>
|
||||
<span
|
||||
class="inset-x-0 inset-y-0 opacity-10 absolute bg-error"
|
||||
></span>
|
||||
{{ $t('staking.top') }} 33%
|
||||
</div>
|
||||
<div
|
||||
class="text-xs truncate relative py-2 px-4 rounded-md w-fit text-warning"
|
||||
>
|
||||
<span
|
||||
class="inset-x-0 inset-y-0 opacity-10 absolute bg-warning"
|
||||
></span>
|
||||
{{ $t('staking.top') }} 67%
|
||||
</div>
|
||||
<div class="text-xs hidden md:!block pl-2">
|
||||
{{ $t('staking.description') }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- 👉 Voting Power -->
|
||||
<td class="text-right">
|
||||
<div class="flex flex-col">
|
||||
<h6 class="text-sm font-weight-medium whitespace-nowrap">
|
||||
{{
|
||||
format.formatToken(
|
||||
{
|
||||
amount: parseInt(v.tokens).toString(),
|
||||
denom: staking.params.bond_denom,
|
||||
},
|
||||
true,
|
||||
'0,0'
|
||||
)
|
||||
}}
|
||||
</h6>
|
||||
<span class="text-xs">{{
|
||||
format.calculatePercent(
|
||||
v.delegator_shares,
|
||||
staking.totalPower
|
||||
)
|
||||
}}</span>
|
||||
</div>
|
||||
</td>
|
||||
<!-- 👉 24h Changes -->
|
||||
<td class="text-right text-xs" :class="change24Color(v)">
|
||||
{{ change24Text(v) }}
|
||||
</td>
|
||||
<!-- 👉 commission -->
|
||||
<td class="text-right text-xs">
|
||||
{{
|
||||
format.formatCommissionRate(
|
||||
v.commission?.commission_rates?.rate
|
||||
)
|
||||
}}
|
||||
</td>
|
||||
<!-- 👉 Action -->
|
||||
<td class="text-center">
|
||||
<div
|
||||
v-if="v.jailed"
|
||||
class="badge badge-error gap-2 text-white"
|
||||
>
|
||||
{{ $t('staking.jailed') }}
|
||||
</div>
|
||||
<label
|
||||
v-else
|
||||
for="delegate"
|
||||
class="btn btn-xs btn-primary rounded-sm capitalize"
|
||||
@click="
|
||||
dialog.open('delegate', {
|
||||
validator_address: v.operator_address,
|
||||
})
|
||||
"
|
||||
>{{ $t('account.btn_delegate') }}</label
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
<div class="flex flex-row items-center">
|
||||
<div
|
||||
class="text-xs truncate relative py-2 px-4 rounded-md w-fit text-error mr-2"
|
||||
>
|
||||
<span
|
||||
class="inset-x-0 inset-y-0 opacity-10 absolute bg-error"
|
||||
></span>
|
||||
{{ $t('staking.top') }} 33%
|
||||
</div>
|
||||
<div
|
||||
class="text-xs truncate relative py-2 px-4 rounded-md w-fit text-warning"
|
||||
>
|
||||
<span
|
||||
class="inset-x-0 inset-y-0 opacity-10 absolute bg-warning"
|
||||
></span>
|
||||
{{ $t('staking.top') }} 67%
|
||||
</div>
|
||||
<div class="text-xs hidden md:!block pl-2">
|
||||
{{ $t('staking.description') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<route>
|
||||
@@ -503,7 +560,7 @@ loadAvatars();
|
||||
|
||||
<style>
|
||||
.staking-table.table :where(th, td) {
|
||||
padding: 8px 5px;
|
||||
background: transparent;
|
||||
padding: 8px 5px;
|
||||
background: transparent;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -10,24 +10,22 @@ const blockchain = useBlockchain();
|
||||
const base = useBaseStore();
|
||||
const nodeInfo = ref({} as NodeInfo);
|
||||
|
||||
const height = ref(0)
|
||||
const hash = ref("")
|
||||
const height = ref(0);
|
||||
const hash = ref('');
|
||||
|
||||
base.$subscribe((m, { latest }) => {
|
||||
let h = Number(latest.block?.header?.height)
|
||||
h = Math.round((h - 2000) / 1000) * 1000
|
||||
let h = Number(latest.block?.header?.height);
|
||||
h = Math.round((h - 2000) / 1000) * 1000;
|
||||
if (h > height.value) {
|
||||
height.value = h
|
||||
base.fetchBlock(h).then(res => {
|
||||
hash.value = toHex(fromBase64(res.block_id.hash)).toUpperCase()
|
||||
})
|
||||
height.value = h;
|
||||
base.fetchBlock(h).then((res) => {
|
||||
hash.value = toHex(fromBase64(res.block_id.hash)).toUpperCase();
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const rpcs = computed(() => {
|
||||
return blockchain.current?.endpoints?.rpc
|
||||
?.map((x) => x.address)
|
||||
.join(',');
|
||||
return blockchain.current?.endpoints?.rpc?.map((x) => x.address).join(',');
|
||||
});
|
||||
|
||||
const appName = computed(() => {
|
||||
@@ -46,10 +44,12 @@ onMounted(() => {
|
||||
<h2 class="card-title truncate mb-2">{{ $t('statesync.title') }}</h2>
|
||||
<div class="text-sm">
|
||||
{{ $t('statesync.description') }}
|
||||
<a class="text-primary lowercase" href="https://blog.cosmos.network/cosmos-sdk-state-sync-guide-99e4cf43be2f">{{
|
||||
$t('statesync.here') }} </a>
|
||||
<a class="lowercase">
|
||||
{{ $t('statesync.for_more_info') }}.</a>
|
||||
<a
|
||||
class="text-primary lowercase"
|
||||
href="https://blog.cosmos.network/cosmos-sdk-state-sync-guide-99e4cf43be2f"
|
||||
>{{ $t('statesync.here') }} </a
|
||||
>
|
||||
<a class="lowercase"> {{ $t('statesync.for_more_info') }}.</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -58,34 +58,56 @@ onMounted(() => {
|
||||
{{ $t('statesync.title_2') }}
|
||||
</h2>
|
||||
<div class="text-sm">
|
||||
1. {{ $t('statesync.text_1') }} ({{ appName }} {{ $t('statesync.version') }}:
|
||||
1. {{ $t('statesync.text_1') }} ({{ appName }}
|
||||
{{ $t('statesync.version') }}:
|
||||
{{ nodeInfo.application_version?.version || '' }})
|
||||
<br />
|
||||
{{ $t('statesync.text_1_1') }}.
|
||||
<br />
|
||||
<br />
|
||||
2. {{ $t('statesync.text_2') }}<br />
|
||||
{{ $t('statesync.text_2_1') }}.
|
||||
<br /><br />
|
||||
{{ $t('statesync.text_2_1') }}. <br /><br />
|
||||
<div class="mockup-code bg-base-200 my-2">
|
||||
<pre data-prefix=">"><code class="text-gray-800 dark:invert">[state-sync]</code></pre>
|
||||
<pre data-prefix=">"><code class="text-gray-800 dark:invert">enable = true</code></pre>
|
||||
<pre
|
||||
data-prefix=">"
|
||||
><code class="text-gray-800 dark:invert">[state-sync]</code></pre>
|
||||
<pre
|
||||
data-prefix=">"
|
||||
><code class="text-gray-800 dark:invert">enable = true</code></pre>
|
||||
<pre data-prefix=">"><code class="text-gray-800"></code></pre>
|
||||
<pre data-prefix=">"><code class="text-gray-800 dark:invert">rpc_servers = "{{ rpcs }}"</code></pre>
|
||||
<pre data-prefix=">"><code class="text-gray-800 dark:invert">trust_height = {{ height }} </code></pre>
|
||||
<pre data-prefix=">"><code class="text-gray-800 dark:invert">trust_hash = "{{ hash }}"</code></pre>
|
||||
<pre data-prefix=">"><code class="text-gray-800 dark:invert"></code></pre>
|
||||
<pre data-prefix=">"><code class="text-green-400"># 2/3 of unbonding time</code></pre>
|
||||
<pre data-prefix=">"><code class="text-gray-800 dark:invert">trust_period = "168h"</code></pre>
|
||||
<pre
|
||||
data-prefix=">"
|
||||
><code class="text-gray-800 dark:invert">rpc_servers = "{{ rpcs }}"</code></pre>
|
||||
<pre
|
||||
data-prefix=">"
|
||||
><code class="text-gray-800 dark:invert">trust_height = {{ height }} </code></pre>
|
||||
<pre
|
||||
data-prefix=">"
|
||||
><code class="text-gray-800 dark:invert">trust_hash = "{{ hash }}"</code></pre>
|
||||
<pre
|
||||
data-prefix=">"
|
||||
><code class="text-gray-800 dark:invert"></code></pre>
|
||||
<pre
|
||||
data-prefix=">"
|
||||
><code class="text-green-400"># 2/3 of unbonding time</code></pre>
|
||||
<pre
|
||||
data-prefix=">"
|
||||
><code class="text-gray-800 dark:invert">trust_period = "168h"</code></pre>
|
||||
</div>
|
||||
<br />
|
||||
3. {{ $t('statesync.text_3') }}: <code
|
||||
class="bg-base-200 text-gray-600 px-2 py-px mx-1 rounded shadow">{{ appName }} start</code>
|
||||
3. {{ $t('statesync.text_3') }}:
|
||||
<code class="bg-base-200 text-gray-600 px-2 py-px mx-1 rounded shadow"
|
||||
>{{ appName }} start</code
|
||||
>
|
||||
<br />
|
||||
{{ $t('statesync.text_3_1') }}
|
||||
<code class="bg-base-200 text-gray-600 px-2 py-px mx-1 rounded shadow">{{ appName }} unsafe-reset-all</code> or
|
||||
<code
|
||||
class="bg-base-200 text-gray-600 px-2 py-px mx-1 rounded shadow">{{ appName }} tendermint unsafe-reset-all --home ~/.HOME</code>
|
||||
<code class="bg-base-200 text-gray-600 px-2 py-px mx-1 rounded shadow"
|
||||
>{{ appName }} unsafe-reset-all</code
|
||||
>
|
||||
or
|
||||
<code class="bg-base-200 text-gray-600 px-2 py-px mx-1 rounded shadow"
|
||||
>{{ appName }} tendermint unsafe-reset-all --home ~/.HOME</code
|
||||
>
|
||||
{{ $t('statesync.text_3_2') }}.
|
||||
</div>
|
||||
</div>
|
||||
@@ -96,16 +118,27 @@ onMounted(() => {
|
||||
{{ $t('statesync.text_title_3') }}
|
||||
<br /><br />
|
||||
<div class="mockup-code bg-base-200 my-2">
|
||||
<pre data-prefix=">"><code class="text-gray-800 dark:invert">[state-sync]</code></pre>
|
||||
<pre
|
||||
data-prefix=">"><code class="text-green-400"># snapshot-interval specifies the block interval at which local state sync snapshots are</code></pre>
|
||||
data-prefix=">"
|
||||
><code class="text-gray-800 dark:invert">[state-sync]</code></pre>
|
||||
<pre
|
||||
data-prefix=">"><code class="text-green-400"># taken (0 to disable). Must be a multiple of pruning-keep-every.</code></pre>
|
||||
<pre data-prefix=">"><code class="text-gray-800 dark:invert">snapshot-interval = 1000</code></pre>
|
||||
<pre data-prefix=">"><code class="text-gray-800 dark:invert"></code></pre>
|
||||
data-prefix=">"
|
||||
><code class="text-green-400"># snapshot-interval specifies the block interval at which local state sync snapshots are</code></pre>
|
||||
<pre
|
||||
data-prefix=">"><code class="text-green-400"># snapshot-keep-recent specifies the number of recent snapshots to keep and serve (0 to keep all). Each snapshot is about 500MiB</code></pre>
|
||||
<pre data-prefix=">"><code class="text-gray-800 dark:invert">snapshot-keep-recent = 2</code></pre>
|
||||
data-prefix=">"
|
||||
><code class="text-green-400"># taken (0 to disable). Must be a multiple of pruning-keep-every.</code></pre>
|
||||
<pre
|
||||
data-prefix=">"
|
||||
><code class="text-gray-800 dark:invert">snapshot-interval = 1000</code></pre>
|
||||
<pre
|
||||
data-prefix=">"
|
||||
><code class="text-gray-800 dark:invert"></code></pre>
|
||||
<pre
|
||||
data-prefix=">"
|
||||
><code class="text-green-400"># snapshot-keep-recent specifies the number of recent snapshots to keep and serve (0 to keep all). Each snapshot is about 500MiB</code></pre>
|
||||
<pre
|
||||
data-prefix=">"
|
||||
><code class="text-gray-800 dark:invert">snapshot-keep-recent = 2</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,76 +1,108 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from '@vue/reactivity';
|
||||
import { useBlockchain, useFormatter } from '@/stores';
|
||||
import { PageRequest, type Pagination, type Coin, type DenomMetadata } from '@/types';
|
||||
import {
|
||||
PageRequest,
|
||||
type Pagination,
|
||||
type Coin,
|
||||
type DenomMetadata,
|
||||
} from '@/types';
|
||||
import { onMounted } from 'vue';
|
||||
import type { Asset } from '@ping-pub/chain-registry-client/dist/types'
|
||||
import type { Asset } from '@ping-pub/chain-registry-client/dist/types';
|
||||
import PaginationBar from '@/components/PaginationBar.vue';
|
||||
const props = defineProps(['chain']);
|
||||
|
||||
const format = useFormatter();
|
||||
const chainStore = useBlockchain();
|
||||
|
||||
const list = ref([] as ({ denom: string, amount: string, base: string, info: string, logo: string | undefined })[])
|
||||
const list = ref(
|
||||
[] as {
|
||||
denom: string;
|
||||
amount: string;
|
||||
base: string;
|
||||
info: string;
|
||||
logo: string | undefined;
|
||||
}[]
|
||||
);
|
||||
|
||||
const pageRequest = ref(new PageRequest())
|
||||
const pageResponse = ref({} as Pagination)
|
||||
const pageRequest = ref(new PageRequest());
|
||||
const pageResponse = ref({} as Pagination);
|
||||
|
||||
interface SupplyAsset extends Asset {
|
||||
logo: string | undefined
|
||||
logo: string | undefined;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
pageload(1)
|
||||
pageload(1);
|
||||
});
|
||||
|
||||
function findGlobalAssetConfig(denom: string) {
|
||||
const assets = chainStore.current?.assets
|
||||
const assets = chainStore.current?.assets;
|
||||
if (assets) {
|
||||
const conf = assets.find(a => a.base === denom)
|
||||
const conf = assets.find((a) => a.base === denom);
|
||||
if (conf) {
|
||||
return conf
|
||||
return conf;
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async function mergeDenomMetadata(denom: string, denomsMetadatas: DenomMetadata[]): Promise<SupplyAsset> {
|
||||
const denomMetadata = denomsMetadatas.find(d => d.base.endsWith(denom));
|
||||
let asset = findGlobalAssetConfig(denom) as SupplyAsset
|
||||
async function mergeDenomMetadata(
|
||||
denom: string,
|
||||
denomsMetadatas: DenomMetadata[]
|
||||
): Promise<SupplyAsset> {
|
||||
const denomMetadata = denomsMetadatas.find((d) => d.base.endsWith(denom));
|
||||
let asset = findGlobalAssetConfig(denom) as SupplyAsset;
|
||||
if (asset && denomMetadata) {
|
||||
asset = { ...denomMetadata, ...asset }
|
||||
asset.display = denomMetadata.display
|
||||
asset.logo = asset.logo_URIs?.svg || asset.logo_URIs?.png || asset.logo_URIs?.jpeg || undefined
|
||||
asset = { ...denomMetadata, ...asset };
|
||||
asset.display = denomMetadata.display;
|
||||
asset.logo =
|
||||
asset.logo_URIs?.svg ||
|
||||
asset.logo_URIs?.png ||
|
||||
asset.logo_URIs?.jpeg ||
|
||||
undefined;
|
||||
} else if (denomMetadata) {
|
||||
return denomMetadata as SupplyAsset
|
||||
return denomMetadata as SupplyAsset;
|
||||
}
|
||||
return asset;
|
||||
}
|
||||
|
||||
function pageload(p: number) {
|
||||
pageRequest.value.setPage(p)
|
||||
pageRequest.value.setPage(p);
|
||||
chainStore.rpc.getBankDenomMetadata().then(async (denomsMetaResponse) => {
|
||||
const bankSupplyResponse = await chainStore.rpc.getBankSupply(pageRequest.value);
|
||||
list.value = await Promise.all(bankSupplyResponse.supply.map(async (coin: Coin) => {
|
||||
const asset = await mergeDenomMetadata(coin.denom, denomsMetaResponse.metadatas)
|
||||
const denom = (asset?.symbol || coin.denom)
|
||||
return {
|
||||
denom: denom.split('/')[denom.split('/').length - 1].toUpperCase(),
|
||||
amount: format.tokenAmountNumber({ amount: coin.amount, denom: denom }).toString(),
|
||||
base: asset.base || coin.denom,
|
||||
info: asset.display || coin.denom,
|
||||
logo: asset?.logo_URIs?.svg || asset?.logo_URIs?.png || asset?.logo_URIs?.jpeg || "/logo.svg",
|
||||
}
|
||||
}));
|
||||
pageResponse.value = bankSupplyResponse.pagination
|
||||
})
|
||||
const bankSupplyResponse = await chainStore.rpc.getBankSupply(
|
||||
pageRequest.value
|
||||
);
|
||||
list.value = await Promise.all(
|
||||
bankSupplyResponse.supply.map(async (coin: Coin) => {
|
||||
const asset = await mergeDenomMetadata(
|
||||
coin.denom,
|
||||
denomsMetaResponse.metadatas
|
||||
);
|
||||
const denom = asset?.symbol || coin.denom;
|
||||
return {
|
||||
denom: denom.split('/')[denom.split('/').length - 1].toUpperCase(),
|
||||
amount: format
|
||||
.tokenAmountNumber({ amount: coin.amount, denom: denom })
|
||||
.toString(),
|
||||
base: asset.base || coin.denom,
|
||||
info: asset.display || coin.denom,
|
||||
logo:
|
||||
asset?.logo_URIs?.svg ||
|
||||
asset?.logo_URIs?.png ||
|
||||
asset?.logo_URIs?.jpeg ||
|
||||
'/logo.svg',
|
||||
};
|
||||
})
|
||||
);
|
||||
pageResponse.value = bankSupplyResponse.pagination;
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class="overflow-auto bg-base-100">
|
||||
<table class="table table-compact">
|
||||
<thead class=" bg-base-200">
|
||||
<thead class="bg-base-200">
|
||||
<tr>
|
||||
<td>Logo</td>
|
||||
<td>Token</td>
|
||||
@@ -89,7 +121,11 @@ function pageload(p: number) {
|
||||
<td>{{ item.base }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<PaginationBar :limit="pageRequest.limit" :total="pageResponse.total" :callback="pageload" />
|
||||
<PaginationBar
|
||||
:limit="pageRequest.limit"
|
||||
:total="pageResponse.total"
|
||||
:callback="pageload"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
+142
-107
@@ -4,9 +4,9 @@ import DynamicComponent from '@/components/dynamic/DynamicComponent.vue';
|
||||
import { computed, ref } from '@vue/reactivity';
|
||||
import type { Tx, TxResponse } from '@/types';
|
||||
|
||||
import { JsonViewer } from "vue3-json-viewer"
|
||||
import { JsonViewer } from 'vue3-json-viewer';
|
||||
// if you used v1.0.5 or latster ,you should add import "vue3-json-viewer/dist/index.css"
|
||||
import "vue3-json-viewer/dist/index.css";
|
||||
import 'vue3-json-viewer/dist/index.css';
|
||||
|
||||
const props = defineProps(['hash', 'chain']);
|
||||
|
||||
@@ -14,117 +14,152 @@ const blockchain = useBlockchain();
|
||||
const baseStore = useBaseStore();
|
||||
const format = useFormatter();
|
||||
const tx = ref(
|
||||
{} as {
|
||||
tx: Tx;
|
||||
tx_response: TxResponse;
|
||||
}
|
||||
{} as {
|
||||
tx: Tx;
|
||||
tx_response: TxResponse;
|
||||
}
|
||||
);
|
||||
if (props.hash) {
|
||||
blockchain.rpc.getTx(props.hash).then((x) => (tx.value = x));
|
||||
blockchain.rpc.getTx(props.hash).then((x) => (tx.value = x));
|
||||
}
|
||||
const messages = computed(() => {
|
||||
return tx.value.tx?.body?.messages.map(x=> {
|
||||
if(x.packet?.data) {
|
||||
// @ts-ignore
|
||||
x.message = format.base64ToString(x.packet.data)
|
||||
}
|
||||
return x
|
||||
}) || [];
|
||||
return (
|
||||
tx.value.tx?.body?.messages.map((x) => {
|
||||
if (x.packet?.data) {
|
||||
// @ts-ignore
|
||||
x.message = format.base64ToString(x.packet.data);
|
||||
}
|
||||
return x;
|
||||
}) || []
|
||||
);
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div class="tabs tabs-boxed bg-transparent mb-4">
|
||||
<RouterLink class="tab text-gray-400 uppercase"
|
||||
:to="`/${chain}/tx/?tab=recent`"
|
||||
>{{ $t('block.recent') }}</RouterLink>
|
||||
<RouterLink class="tab text-gray-400 uppercase"
|
||||
:to="`/${chain}/tx/?tab=search`"
|
||||
>Search</RouterLink>
|
||||
<a class="tab text-gray-400 uppercase tab-active">Transaction</a>
|
||||
</div>
|
||||
|
||||
<div v-if="tx.tx_response" class="bg-base-100 px-4 pt-3 pb-4 rounded shadow mb-4">
|
||||
<h2 class="card-title truncate mb-2">{{ $t('tx.title') }}</h2>
|
||||
<div class="overflow-hidden">
|
||||
<table class="table text-sm">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ $t('tx.tx_hash') }}</td>
|
||||
<td class="overflow-hidden">{{ tx.tx_response.txhash }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('account.height') }}</td>
|
||||
<td>
|
||||
<RouterLink :to="`/${props.chain}/block/${tx.tx_response.height}`" class="text-primary dark:invert">{{ tx.tx_response.height
|
||||
}}
|
||||
</RouterLink>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('staking.status') }}</td>
|
||||
<td>
|
||||
<span class="text-xs truncate relative py-2 px-4 w-fit mr-2 rounded" :class="`text-${tx.tx_response.code === 0 ? 'success' : 'error'
|
||||
}`">
|
||||
<span class="inset-x-0 inset-y-0 opacity-10 absolute" :class="`bg-${tx.tx_response.code === 0 ? 'success' : 'error'
|
||||
}`"></span>
|
||||
{{ tx.tx_response.code === 0 ? 'Success' : 'Failed' }}
|
||||
</span>
|
||||
<span>
|
||||
{{ tx.tx_response.code === 0 ? '' : tx?.tx_response?.raw_log }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('account.time') }}</td>
|
||||
<td>
|
||||
{{ format.toLocaleDate(tx.tx_response.timestamp) }} ({{
|
||||
format.toDay(tx.tx_response.timestamp, 'from')
|
||||
}})
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('tx.gas') }}</td>
|
||||
<td>
|
||||
{{ tx.tx_response.gas_used }} / {{ tx.tx_response.gas_wanted }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('tx.fee') }}</td>
|
||||
<td>
|
||||
{{
|
||||
format.formatTokens(
|
||||
tx.tx?.auth_info?.fee?.amount,
|
||||
true,
|
||||
'0,0.[00]'
|
||||
)
|
||||
}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('tx.memo') }}</td>
|
||||
<td>{{ tx.tx.body.memo }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="tx.tx_response" class="bg-base-100 px-4 pt-3 pb-4 rounded shadow mb-4">
|
||||
<h2 class="card-title truncate mb-2">
|
||||
{{ $t('account.messages') }}: ({{ messages.length }})
|
||||
</h2>
|
||||
<div v-for="(msg, i) in messages">
|
||||
<div class="border border-slate-400 rounded-md mt-4">
|
||||
<DynamicComponent :value="msg" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="messages.length === 0">{{ $t('tx.no_messages') }}</div>
|
||||
</div>
|
||||
|
||||
<div v-if="tx.tx_response" class="bg-base-100 px-4 pt-3 pb-4 rounded shadow">
|
||||
<h2 class="card-title truncate mb-2">JSON</h2>
|
||||
<JsonViewer :value="tx" :theme="baseStore.theme" style="background: transparent;" copyable boxed sort expand-depth="5"/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="tabs tabs-boxed bg-transparent mb-4">
|
||||
<RouterLink
|
||||
class="tab text-gray-400 uppercase"
|
||||
:to="`/${chain}/tx/?tab=recent`"
|
||||
>{{ $t('block.recent') }}</RouterLink
|
||||
>
|
||||
<RouterLink
|
||||
class="tab text-gray-400 uppercase"
|
||||
:to="`/${chain}/tx/?tab=search`"
|
||||
>Search</RouterLink
|
||||
>
|
||||
<a class="tab text-gray-400 uppercase tab-active">Transaction</a>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="tx.tx_response"
|
||||
class="bg-base-100 px-4 pt-3 pb-4 rounded shadow mb-4"
|
||||
>
|
||||
<h2 class="card-title truncate mb-2">{{ $t('tx.title') }}</h2>
|
||||
<div class="overflow-hidden">
|
||||
<table class="table text-sm">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ $t('tx.tx_hash') }}</td>
|
||||
<td class="overflow-hidden">{{ tx.tx_response.txhash }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('account.height') }}</td>
|
||||
<td>
|
||||
<RouterLink
|
||||
:to="`/${props.chain}/block/${tx.tx_response.height}`"
|
||||
class="text-primary dark:invert"
|
||||
>{{ tx.tx_response.height }}
|
||||
</RouterLink>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('staking.status') }}</td>
|
||||
<td>
|
||||
<span
|
||||
class="text-xs truncate relative py-2 px-4 w-fit mr-2 rounded"
|
||||
:class="`text-${
|
||||
tx.tx_response.code === 0 ? 'success' : 'error'
|
||||
}`"
|
||||
>
|
||||
<span
|
||||
class="inset-x-0 inset-y-0 opacity-10 absolute"
|
||||
:class="`bg-${
|
||||
tx.tx_response.code === 0 ? 'success' : 'error'
|
||||
}`"
|
||||
></span>
|
||||
{{ tx.tx_response.code === 0 ? 'Success' : 'Failed' }}
|
||||
</span>
|
||||
<span>
|
||||
{{
|
||||
tx.tx_response.code === 0 ? '' : tx?.tx_response?.raw_log
|
||||
}}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('account.time') }}</td>
|
||||
<td>
|
||||
{{ format.toLocaleDate(tx.tx_response.timestamp) }} ({{
|
||||
format.toDay(tx.tx_response.timestamp, 'from')
|
||||
}})
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('tx.gas') }}</td>
|
||||
<td>
|
||||
{{ tx.tx_response.gas_used }} / {{ tx.tx_response.gas_wanted }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('tx.fee') }}</td>
|
||||
<td>
|
||||
{{
|
||||
format.formatTokens(
|
||||
tx.tx?.auth_info?.fee?.amount,
|
||||
true,
|
||||
'0,0.[00]'
|
||||
)
|
||||
}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('tx.memo') }}</td>
|
||||
<td>{{ tx.tx.body.memo }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="tx.tx_response"
|
||||
class="bg-base-100 px-4 pt-3 pb-4 rounded shadow mb-4"
|
||||
>
|
||||
<h2 class="card-title truncate mb-2">
|
||||
{{ $t('account.messages') }}: ({{ messages.length }})
|
||||
</h2>
|
||||
<div v-for="(msg, i) in messages">
|
||||
<div class="border border-slate-400 rounded-md mt-4">
|
||||
<DynamicComponent :value="msg" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="messages.length === 0">{{ $t('tx.no_messages') }}</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="tx.tx_response"
|
||||
class="bg-base-100 px-4 pt-3 pb-4 rounded shadow"
|
||||
>
|
||||
<h2 class="card-title truncate mb-2">JSON</h2>
|
||||
<JsonViewer
|
||||
:value="tx"
|
||||
:theme="baseStore.theme"
|
||||
style="background: transparent"
|
||||
copyable
|
||||
boxed
|
||||
sort
|
||||
expand-depth="5"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -8,80 +8,114 @@ const props = defineProps(['chain']);
|
||||
const vueRouters = useRouter();
|
||||
const tab = ref('recent');
|
||||
|
||||
const base = useBaseStore()
|
||||
const chainStore = useBlockchain()
|
||||
const base = useBaseStore();
|
||||
const chainStore = useBlockchain();
|
||||
|
||||
const format = useFormatter();
|
||||
const hashReg = /^[A-Z\d]{64}$/;
|
||||
const hash = ref('');
|
||||
const current = chainStore?.current?.chainName || '';
|
||||
onMounted(() => {
|
||||
tab.value = String(vueRouters.currentRoute.value.query.tab || 'recent');
|
||||
console.log(tab.value);
|
||||
tab.value = String(vueRouters.currentRoute.value.query.tab || 'recent');
|
||||
console.log(tab.value);
|
||||
});
|
||||
function search() {
|
||||
if (hashReg.test(hash.value)) {
|
||||
vueRouters.push({ path: `/${current}/tx/${hash.value}` });
|
||||
}
|
||||
if (hashReg.test(hash.value)) {
|
||||
vueRouters.push({ path: `/${current}/tx/${hash.value}` });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div class="tabs tabs-boxed bg-transparent mb-4">
|
||||
<a class="tab text-gray-400 uppercase" :class="{ 'tab-active': tab === 'recent' }"
|
||||
@click="tab = 'recent'">{{ $t('block.recent') }}</a>
|
||||
<a class="tab text-gray-400 uppercase" :class="{ 'tab-active': tab === 'search' }"
|
||||
@click="tab = 'search'">Search</a>
|
||||
</div>
|
||||
|
||||
<div v-show="tab === 'recent'" class="bg-base-100 rounded overflow-x-auto">
|
||||
<table class="table w-full table-compact">
|
||||
<thead class="bg-base-200">
|
||||
<tr>
|
||||
<th style="position: relative; z-index: 2;">{{ $t('account.height') }}</th>
|
||||
<th style="position: relative; z-index: 2;">{{ $t('account.hash') }}</th>
|
||||
<th>{{ $t('account.messages') }}</th>
|
||||
<th>{{ $t('block.fees') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(item, index) in base.txsInRecents" :index="index" class="hover">
|
||||
<td class="text-sm text-primary">
|
||||
<RouterLink :to="`/${props.chain}/block/${item.height}`">{{ item.height }}</RouterLink>
|
||||
</td>
|
||||
<td class="truncate text-primary" width="50%">
|
||||
<RouterLink :to="`/${props.chain}/tx/${item.hash}`">{{
|
||||
item.hash
|
||||
}}</RouterLink>
|
||||
</td>
|
||||
<td>{{ format.messages(item.tx.body.messages) }}</td>
|
||||
<td>{{ format.formatTokens(item.tx.authInfo.fee?.amount) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="p-4">
|
||||
<div class="alert relative bg-transparent">
|
||||
<div class="alert absolute inset-x-0 inset-y-0 w-full h-full bg-info opacity-10"></div>
|
||||
<div class="text-info flex gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
class="stroke-current flex-shrink-0 w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||
</svg>
|
||||
<span>{{ $t('block.only_tx') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-show="tab === 'search'" class="bg-base-100 rounded overflow-x-auto">
|
||||
<div class="p-4">
|
||||
<div class="form-control">
|
||||
<input v-model="hash" type="text" class="input input-bordered" placeholder="Search by Tx Hash" @blur="search"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="tabs tabs-boxed bg-transparent mb-4">
|
||||
<a
|
||||
class="tab text-gray-400 uppercase"
|
||||
:class="{ 'tab-active': tab === 'recent' }"
|
||||
@click="tab = 'recent'"
|
||||
>{{ $t('block.recent') }}</a
|
||||
>
|
||||
<a
|
||||
class="tab text-gray-400 uppercase"
|
||||
:class="{ 'tab-active': tab === 'search' }"
|
||||
@click="tab = 'search'"
|
||||
>Search</a
|
||||
>
|
||||
</div>
|
||||
|
||||
<div v-show="tab === 'recent'" class="bg-base-100 rounded overflow-x-auto">
|
||||
<table class="table w-full table-compact">
|
||||
<thead class="bg-base-200">
|
||||
<tr>
|
||||
<th style="position: relative; z-index: 2">
|
||||
{{ $t('account.height') }}
|
||||
</th>
|
||||
<th style="position: relative; z-index: 2">
|
||||
{{ $t('account.hash') }}
|
||||
</th>
|
||||
<th>{{ $t('account.messages') }}</th>
|
||||
<th>{{ $t('block.fees') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(item, index) in base.txsInRecents"
|
||||
:index="index"
|
||||
class="hover"
|
||||
>
|
||||
<td class="text-sm text-primary">
|
||||
<RouterLink :to="`/${props.chain}/block/${item.height}`">{{
|
||||
item.height
|
||||
}}</RouterLink>
|
||||
</td>
|
||||
<td class="truncate text-primary" width="50%">
|
||||
<RouterLink :to="`/${props.chain}/tx/${item.hash}`">{{
|
||||
item.hash
|
||||
}}</RouterLink>
|
||||
</td>
|
||||
<td>{{ format.messages(item.tx.body.messages) }}</td>
|
||||
<td>{{ format.formatTokens(item.tx.authInfo.fee?.amount) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="p-4">
|
||||
<div class="alert relative bg-transparent">
|
||||
<div
|
||||
class="alert absolute inset-x-0 inset-y-0 w-full h-full bg-info opacity-10"
|
||||
></div>
|
||||
<div class="text-info flex gap-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
class="stroke-current flex-shrink-0 w-6 h-6"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
></path>
|
||||
</svg>
|
||||
<span>{{ $t('block.only_tx') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-show="tab === 'search'" class="bg-base-100 rounded overflow-x-auto">
|
||||
<div class="p-4">
|
||||
<div class="form-control">
|
||||
<input
|
||||
v-model="hash"
|
||||
type="text"
|
||||
class="input input-bordered"
|
||||
placeholder="Search by Tx Hash"
|
||||
@blur="search"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<route>
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
useStakingStore,
|
||||
useBaseStore,
|
||||
useBlockchain,
|
||||
useDashboard,
|
||||
useDashboard,
|
||||
} from '@/stores';
|
||||
import UptimeBar from '@/components/UptimeBar.vue';
|
||||
import type { Block, Commit } from '@/types';
|
||||
@@ -20,131 +20,158 @@ const props = defineProps(['chain']);
|
||||
const stakingStore = useStakingStore();
|
||||
const format = useFormatter();
|
||||
const chainStore = useBlockchain();
|
||||
const dashboard = useDashboard()
|
||||
const dashboard = useDashboard();
|
||||
// storage local favorite validator ids
|
||||
const local = ref(JSON.parse(localStorage.getItem('uptime-validators') || '{}') as Record<string, {name: string, address: string}[]>)
|
||||
const local = ref(
|
||||
JSON.parse(localStorage.getItem('uptime-validators') || '{}') as Record<
|
||||
string,
|
||||
{ name: string; address: string }[]
|
||||
>
|
||||
);
|
||||
const signingInfo = ref({} as Record<string, SigningInfo[]>);
|
||||
const selected = ref([] as string[])
|
||||
const selectChain = ref(chainStore.chainName)
|
||||
const validators = ref(stakingStore.validators)
|
||||
const keyword = ref("")
|
||||
const selected = ref([] as string[]);
|
||||
const selectChain = ref(chainStore.chainName);
|
||||
const validators = ref(stakingStore.validators);
|
||||
const keyword = ref('');
|
||||
|
||||
function loadSigningInfo(chainName: string) {
|
||||
const chain = dashboard.chains[chainName]
|
||||
if(chain && chain.endpoints.rest) {
|
||||
const client = CosmosRestClient.newDefault(chain.endpoints.rest[0].address)
|
||||
client.getSlashingSigningInfos().then( resp => {
|
||||
signingInfo.value[chainName] = resp.info
|
||||
})
|
||||
const chain = dashboard.chains[chainName];
|
||||
if (chain && chain.endpoints.rest) {
|
||||
const client = CosmosRestClient.newDefault(chain.endpoints.rest[0].address);
|
||||
client.getSlashingSigningInfos().then((resp) => {
|
||||
signingInfo.value[chainName] = resp.info;
|
||||
});
|
||||
}
|
||||
}
|
||||
if(local.value) Object.keys(local.value).map(chainName => {
|
||||
loadSigningInfo(chainName)
|
||||
})
|
||||
if (local.value)
|
||||
Object.keys(local.value).map((chainName) => {
|
||||
loadSigningInfo(chainName);
|
||||
});
|
||||
|
||||
function initial() {
|
||||
const vals = local.value[selectChain.value]
|
||||
if(vals) {
|
||||
selected.value = vals.map(x => x.address)
|
||||
const vals = local.value[selectChain.value];
|
||||
if (vals) {
|
||||
selected.value = vals.map((x) => x.address);
|
||||
}
|
||||
}
|
||||
|
||||
const filterValidators = computed(() => {
|
||||
if(keyword.value) {
|
||||
return validators.value.filter(x => x.description.moniker.indexOf(keyword.value) > -1)
|
||||
if (keyword.value) {
|
||||
return validators.value.filter(
|
||||
(x) => x.description.moniker.indexOf(keyword.value) > -1
|
||||
);
|
||||
}
|
||||
return validators.value
|
||||
})
|
||||
return validators.value;
|
||||
});
|
||||
|
||||
const list = computed(() => {
|
||||
const list = [] as any[]
|
||||
if(local.value) Object.keys(local.value).map( chainName => {
|
||||
const vals = local.value[chainName]
|
||||
const info = signingInfo.value[chainName]
|
||||
if(vals && info) {
|
||||
vals.forEach(v => {
|
||||
const sigingInfo = info.find(x => valconsToBase64(x.address) === v.address)
|
||||
list.push( {
|
||||
chainName,
|
||||
v,
|
||||
sigingInfo,
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
return list
|
||||
})
|
||||
const list = [] as any[];
|
||||
if (local.value)
|
||||
Object.keys(local.value).map((chainName) => {
|
||||
const vals = local.value[chainName];
|
||||
const info = signingInfo.value[chainName];
|
||||
if (vals && info) {
|
||||
vals.forEach((v) => {
|
||||
const sigingInfo = info.find(
|
||||
(x) => valconsToBase64(x.address) === v.address
|
||||
);
|
||||
list.push({
|
||||
chainName,
|
||||
v,
|
||||
sigingInfo,
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
return list;
|
||||
});
|
||||
|
||||
function add() {
|
||||
if(!signingInfo.value[selectChain.value]) {
|
||||
loadSigningInfo(selectChain.value)
|
||||
if (!signingInfo.value[selectChain.value]) {
|
||||
loadSigningInfo(selectChain.value);
|
||||
}
|
||||
const newList = [] as { name: string; address: string; }[]
|
||||
selected.value.forEach(x => {
|
||||
const validator = validators.value.find(v => (consensusPubkeyToHexAddress(v.consensus_pubkey) === x))
|
||||
if(validator) newList.push({
|
||||
name: validator.description.moniker || x,
|
||||
address: x
|
||||
})
|
||||
})
|
||||
if(!local.value) local.value = {}
|
||||
local.value[selectChain.value] = newList
|
||||
const newList = [] as { name: string; address: string }[];
|
||||
selected.value.forEach((x) => {
|
||||
const validator = validators.value.find(
|
||||
(v) => consensusPubkeyToHexAddress(v.consensus_pubkey) === x
|
||||
);
|
||||
if (validator)
|
||||
newList.push({
|
||||
name: validator.description.moniker || x,
|
||||
address: x,
|
||||
});
|
||||
});
|
||||
if (!local.value) local.value = {};
|
||||
local.value[selectChain.value] = newList;
|
||||
|
||||
localStorage.setItem("uptime-validators", JSON.stringify(local.value))
|
||||
localStorage.setItem('uptime-validators', JSON.stringify(local.value));
|
||||
}
|
||||
|
||||
function changeChain() {
|
||||
validators.value = []
|
||||
const endpoint = dashboard.chains[selectChain.value].endpoints.rest?.at(0)?.address
|
||||
if(!endpoint) return
|
||||
validators.value = [];
|
||||
const endpoint =
|
||||
dashboard.chains[selectChain.value].endpoints.rest?.at(0)?.address;
|
||||
if (!endpoint) return;
|
||||
|
||||
const client = CosmosRestClient.newDefault(endpoint)
|
||||
client.getStakingValidators("BOND_STATUS_BONDED").then(x => {
|
||||
validators.value = x.validators
|
||||
})
|
||||
const client = CosmosRestClient.newDefault(endpoint);
|
||||
client.getStakingValidators('BOND_STATUS_BONDED').then((x) => {
|
||||
validators.value = x.validators;
|
||||
});
|
||||
|
||||
const vals = local.value[selectChain.value]
|
||||
if(vals) {
|
||||
selected.value = vals.map(x => x.address)
|
||||
const vals = local.value[selectChain.value];
|
||||
if (vals) {
|
||||
selected.value = vals.map((x) => x.address);
|
||||
} else {
|
||||
selected.value = []
|
||||
selected.value = [];
|
||||
}
|
||||
}
|
||||
|
||||
function color(v: string) {
|
||||
if(v) {
|
||||
const n = Number(v)
|
||||
if(n < 10) return " badge-success"
|
||||
if(n > 1000) return " badge-error"
|
||||
if(n > 0) return " badge-warning"
|
||||
if (v) {
|
||||
const n = Number(v);
|
||||
if (n < 10) return ' badge-success';
|
||||
if (n > 1000) return ' badge-error';
|
||||
if (n > 0) return ' badge-warning';
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="overflow-x-auto w-full">
|
||||
<div class="lg:!flex lg:!items-center lg:!justify-between bg-base-100 p-5">
|
||||
<div
|
||||
class="lg:!flex lg:!items-center lg:!justify-between bg-base-100 p-5"
|
||||
>
|
||||
<div class="min-w-0 flex-1">
|
||||
<h2 class="text-2xl font-bold leading-7 sm:!truncate sm:!text-3xl sm:!tracking-tight">{{ $t('uptime.my_validators') }}</h2>
|
||||
<div class="mt-1 flex flex-col sm:!mt-0 sm:!flex-row sm:!flex-wrap sm:!space-x-6">
|
||||
<h2
|
||||
class="text-2xl font-bold leading-7 sm:!truncate sm:!text-3xl sm:!tracking-tight"
|
||||
>
|
||||
{{ $t('uptime.my_validators') }}
|
||||
</h2>
|
||||
<div
|
||||
class="mt-1 flex flex-col sm:!mt-0 sm:!flex-row sm:!flex-wrap sm:!space-x-6"
|
||||
>
|
||||
<div class="mt-2 flex items-center text-sm text-gray-500">
|
||||
<svg class="mr-1.5 h-5 w-5 flex-shrink-0 text-gray-400" viewBox="0 0 20 20" fill="currentColor"
|
||||
aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M6 3.75A2.75 2.75 0 018.75 1h2.5A2.75 2.75 0 0114 3.75v.443c.572.055 1.14.122 1.706.2C17.053 4.582 18 5.75 18 7.07v3.469c0 1.126-.694 2.191-1.83 2.54-1.952.599-4.024.921-6.17.921s-4.219-.322-6.17-.921C2.694 12.73 2 11.665 2 10.539V7.07c0-1.321.947-2.489 2.294-2.676A41.047 41.047 0 016 4.193V3.75zm6.5 0v.325a41.622 41.622 0 00-5 0V3.75c0-.69.56-1.25 1.25-1.25h2.5c.69 0 1.25.56 1.25 1.25zM10 10a1 1 0 00-1 1v.01a1 1 0 001 1h.01a1 1 0 001-1V11a1 1 0 00-1-1H10z"
|
||||
clip-rule="evenodd" />
|
||||
<svg
|
||||
class="mr-1.5 h-5 w-5 flex-shrink-0 text-gray-400"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
d="M3 15.055v-.684c.126.053.255.1.39.142 2.092.642 4.313.987 6.61.987 2.297 0 4.518-.345 6.61-.987.135-.041.264-.089.39-.142v.684c0 1.347-.985 2.53-2.363 2.686a41.454 41.454 0 01-9.274 0C3.985 17.585 3 16.402 3 15.055z" />
|
||||
fill-rule="evenodd"
|
||||
d="M6 3.75A2.75 2.75 0 018.75 1h2.5A2.75 2.75 0 0114 3.75v.443c.572.055 1.14.122 1.706.2C17.053 4.582 18 5.75 18 7.07v3.469c0 1.126-.694 2.191-1.83 2.54-1.952.599-4.024.921-6.17.921s-4.219-.322-6.17-.921C2.694 12.73 2 11.665 2 10.539V7.07c0-1.321.947-2.489 2.294-2.676A41.047 41.047 0 016 4.193V3.75zm6.5 0v.325a41.622 41.622 0 00-5 0V3.75c0-.69.56-1.25 1.25-1.25h2.5c.69 0 1.25.56 1.25 1.25zM10 10a1 1 0 00-1 1v.01a1 1 0 001 1h.01a1 1 0 001-1V11a1 1 0 00-1-1H10z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
<path
|
||||
d="M3 15.055v-.684c.126.053.255.1.39.142 2.092.642 4.313.987 6.61.987 2.297 0 4.518-.345 6.61-.987.135-.041.264-.089.39-.142v.684c0 1.347-.985 2.53-2.363 2.686a41.454 41.454 0 01-9.274 0C3.985 17.585 3 16.402 3 15.055z"
|
||||
/>
|
||||
</svg>
|
||||
{{ $t('uptime.add_validators_monitor') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-5 flex lg:!ml-4 lg:!mt-0">
|
||||
|
||||
</div>
|
||||
<div class="mt-5 flex lg:!ml-4 lg:!mt-0"></div>
|
||||
</div>
|
||||
<table class="table table-compact w-full">
|
||||
<thead>
|
||||
@@ -161,64 +188,128 @@ function color(v: string) {
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(v, i) in list" class="hover">
|
||||
<td>{{ i+1 }}</td>
|
||||
<td class=" capitalize">{{ v.chainName }}</td>
|
||||
<td>{{ i + 1 }}</td>
|
||||
<td class="capitalize">{{ v.chainName }}</td>
|
||||
<td>{{ v.v.name }}</td>
|
||||
<td><span v-if="v.sigingInfo">{{ Number(v.sigingInfo.index_offset) - Number(v.sigingInfo.start_height) }}</span></td>
|
||||
<td>
|
||||
<div v-if="v.sigingInfo && !v.sigingInfo?.jailed_until.startsWith('1970')" class="text-xs flex flex-wrap">
|
||||
<div class="mt-1">{{ format.toLocaleDate(v.sigingInfo?.jailed_until) }}</div>
|
||||
<div class="badge">{{ format.toDay(v.sigingInfo.jailed_until, 'from') }}</div>
|
||||
<span v-if="v.sigingInfo">{{
|
||||
Number(v.sigingInfo.index_offset) -
|
||||
Number(v.sigingInfo.start_height)
|
||||
}}</span>
|
||||
</td>
|
||||
<td>
|
||||
<div
|
||||
v-if="
|
||||
v.sigingInfo && !v.sigingInfo?.jailed_until.startsWith('1970')
|
||||
"
|
||||
class="text-xs flex flex-wrap"
|
||||
>
|
||||
<div class="mt-1">
|
||||
{{ format.toLocaleDate(v.sigingInfo?.jailed_until) }}
|
||||
</div>
|
||||
<div class="badge">
|
||||
{{ format.toDay(v.sigingInfo.jailed_until, 'from') }}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class=" capitalize">{{ v.sigingInfo?.tombstoned }}</td>
|
||||
<td><span v-if="v.sigingInfo" class="badge " :class="color( v.sigingInfo?.missed_blocks_counter)">{{ v.sigingInfo?.missed_blocks_counter }}</span></td>
|
||||
<td class=""><RouterLink :to="`/${v.chainName}/uptime/#blocks`" class="btn btn-xs btn-primary">{{ $t('module.blocks') }}</RouterLink></td>
|
||||
<td class="capitalize">{{ v.sigingInfo?.tombstoned }}</td>
|
||||
<td>
|
||||
<span
|
||||
v-if="v.sigingInfo"
|
||||
class="badge"
|
||||
:class="color(v.sigingInfo?.missed_blocks_counter)"
|
||||
>{{ v.sigingInfo?.missed_blocks_counter }}</span
|
||||
>
|
||||
</td>
|
||||
<td class="">
|
||||
<RouterLink
|
||||
:to="`/${v.chainName}/uptime/#blocks`"
|
||||
class="btn btn-xs btn-primary"
|
||||
>{{ $t('module.blocks') }}</RouterLink
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="text-center">
|
||||
<label for="add-validator" class="btn btn-primary mt-5">{{ $t('uptime.add_validators') }}</label>
|
||||
<label for="add-validator" class="btn btn-primary mt-5">{{
|
||||
$t('uptime.add_validators')
|
||||
}}</label>
|
||||
</div>
|
||||
|
||||
<!-- Put this part before </body> tag -->
|
||||
<input type="checkbox" id="add-validator" class="modal-toggle" @change="initial" />
|
||||
<input
|
||||
type="checkbox"
|
||||
id="add-validator"
|
||||
class="modal-toggle"
|
||||
@change="initial"
|
||||
/>
|
||||
<div class="modal">
|
||||
<div class="modal-box relative">
|
||||
<label for="add-validator" class="btn btn-sm btn-circle absolute right-2 top-2">✕</label>
|
||||
<label
|
||||
for="add-validator"
|
||||
class="btn btn-sm btn-circle absolute right-2 top-2"
|
||||
>✕</label
|
||||
>
|
||||
<h3 class="text-lg font-bold">{{ $t('uptime.add_validators') }}</h3>
|
||||
<div class="form-control my-5 border-2">
|
||||
<div class="input-group input-group-md">
|
||||
<select v-model="selectChain" class="select select-bordered capitalize" @change="changeChain">
|
||||
<div class="form-control my-5 border-2">
|
||||
<div class="input-group input-group-md">
|
||||
<select
|
||||
v-model="selectChain"
|
||||
class="select select-bordered capitalize"
|
||||
@change="changeChain"
|
||||
>
|
||||
<option v-for="v in dashboard.chains" :value="v.chainName">
|
||||
{{ v.chainName }}
|
||||
</option>
|
||||
</select>
|
||||
<input v-model="keyword" type="text" class="input w-full" placeholder="keywords to filter validator">
|
||||
</div>
|
||||
<input
|
||||
v-model="keyword"
|
||||
type="text"
|
||||
class="input w-full"
|
||||
placeholder="keywords to filter validator"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="py-4 max-h-60 overflow-y-auto">
|
||||
|
||||
<table class="table table-compact w-full hover">
|
||||
<thead>
|
||||
<tr><th>{{ $t('account.validator') }}</th><th></th></tr>
|
||||
<tr>
|
||||
<th>{{ $t('account.validator') }}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(v, i) in filterValidators">
|
||||
<td><label :for="v.operator_address"><div class=" w-full">{{ i+1 }}. {{ v.description.moniker }}</div></label></td>
|
||||
<td><input :id="v.operator_address" v-model="selected" class="checkbox" type="checkbox" :value="consensusPubkeyToHexAddress(v.consensus_pubkey)"/></td>
|
||||
<td>
|
||||
<label :for="v.operator_address"
|
||||
><div class="w-full">
|
||||
{{ i + 1 }}. {{ v.description.moniker }}
|
||||
</div></label
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
:id="v.operator_address"
|
||||
v-model="selected"
|
||||
class="checkbox"
|
||||
type="checkbox"
|
||||
:value="consensusPubkeyToHexAddress(v.consensus_pubkey)"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="modal-action">
|
||||
<label class="btn btn-primary" @click="add">{{ $t('uptime.add') }}</label>
|
||||
<label class="btn btn-primary" @click="add">{{
|
||||
$t('uptime.add')
|
||||
}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-6"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ const keyword = ref('');
|
||||
const live = ref(true);
|
||||
const slashingParam = ref({} as SlashingParam);
|
||||
const signingInfo = ref({} as Record<string, SigningInfo>);
|
||||
const consumerValidators = ref([] as {moniker: string, base64: string}[]);
|
||||
const consumerValidators = ref([] as { moniker: string; base64: string }[]);
|
||||
|
||||
interface BlockColor {
|
||||
height: string;
|
||||
@@ -39,26 +39,32 @@ interface ValidatorUnit {
|
||||
}
|
||||
|
||||
function padding(blocks: BlockColor[] = []) {
|
||||
const raw = Array(50).fill({ height: "0", color: 'bg-secondary' } as BlockColor).concat(blocks)
|
||||
const raw = Array(50)
|
||||
.fill({ height: '0', color: 'bg-secondary' } as BlockColor)
|
||||
.concat(blocks);
|
||||
return raw.slice(raw.length - 50);
|
||||
}
|
||||
|
||||
const validatorSet = computed(() => {
|
||||
if (chainStore.isConsumerChain) {
|
||||
return consumerValidators.value.map((v) => {
|
||||
const b64 = valconsToBase64(v.moniker)
|
||||
const moniker = stakingStore.validators.find((x) => toBase64(fromHex(consensusPubkeyToHexAddress(x.consensus_pubkey))) === b64)?.description.moniker;
|
||||
const b64 = valconsToBase64(v.moniker);
|
||||
const moniker = stakingStore.validators.find(
|
||||
(x) =>
|
||||
toBase64(fromHex(consensusPubkeyToHexAddress(x.consensus_pubkey))) ===
|
||||
b64
|
||||
)?.description.moniker;
|
||||
return {
|
||||
moniker: moniker || v.moniker,
|
||||
base64: v.base64
|
||||
base64: v.base64,
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
return stakingStore.validators.map((v) => {
|
||||
const hex = consensusPubkeyToHexAddress(v.consensus_pubkey)
|
||||
const hex = consensusPubkeyToHexAddress(v.consensus_pubkey);
|
||||
return {
|
||||
moniker: v.description.moniker,
|
||||
base64: toBase64(fromHex(hex))
|
||||
base64: toBase64(fromHex(hex)),
|
||||
};
|
||||
});
|
||||
});
|
||||
@@ -66,34 +72,38 @@ const validatorSet = computed(() => {
|
||||
const blockColors = ref({} as Record<string, BlockColor[]>);
|
||||
|
||||
const grid = computed(() => {
|
||||
|
||||
const validators = keyword.value.length === 0 ? validatorSet.value :
|
||||
validatorSet.value.filter((v) => v.moniker.toLowerCase().includes(keyword.value.toLowerCase()));
|
||||
const validators =
|
||||
keyword.value.length === 0
|
||||
? validatorSet.value
|
||||
: validatorSet.value.filter((v) =>
|
||||
v.moniker.toLowerCase().includes(keyword.value.toLowerCase())
|
||||
);
|
||||
|
||||
const window = Number(slashingParam.value.signed_blocks_window || 0);
|
||||
return validators.map((v) => {
|
||||
const signing = signingInfo.value[v.base64];
|
||||
const uptime = signing && window > 0
|
||||
? (window - Number(signing.missed_blocks_counter)) / window
|
||||
: undefined
|
||||
const uptime =
|
||||
signing && window > 0
|
||||
? (window - Number(signing.missed_blocks_counter)) / window
|
||||
: undefined;
|
||||
return {
|
||||
moniker: v.moniker,
|
||||
base64: v.base64,
|
||||
blocks: padding(blockColors.value[v.base64] || []),
|
||||
uptime,
|
||||
missed_blocks_counter: signing?.missed_blocks_counter,
|
||||
signing
|
||||
signing,
|
||||
} as ValidatorUnit;
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
const preload = ref(false);
|
||||
baseStore.$subscribe((_, state) => {
|
||||
const newHeight = Number(state.latest?.block?.header?.height || 0)
|
||||
const newHeight = Number(state.latest?.block?.header?.height || 0);
|
||||
if (newHeight > latest.value) {
|
||||
latest.value = newHeight;
|
||||
// initialize if it's the first time
|
||||
if(!preload.value) {
|
||||
if (!preload.value) {
|
||||
preFill();
|
||||
preload.value = true;
|
||||
}
|
||||
@@ -101,19 +111,28 @@ baseStore.$subscribe((_, state) => {
|
||||
// reset the consumer validators
|
||||
if (newHeight > 0 && consumerValidators.value.length === 0) {
|
||||
const chain_id = state.latest.block.header.chain_id;
|
||||
Promise.resolve().then(async () =>{
|
||||
Promise.resolve().then(async () => {
|
||||
await stakingStore.getConsumerValidators(chain_id).then((x) => {
|
||||
x.validators.sort((a,b) => Number(b.power)-Number(a.power)).forEach(v => {
|
||||
const base64 = toBase64(fromHex(consensusPubkeyToHexAddress({"@type": "/cosmos.crypto.ed25519.PubKey", key: v.consumer_key.ed25519 })));
|
||||
const moniker = v.provider_address;
|
||||
consumerValidators.value.push({ moniker, base64});
|
||||
x.validators
|
||||
.sort((a, b) => Number(b.power) - Number(a.power))
|
||||
.forEach((v) => {
|
||||
const base64 = toBase64(
|
||||
fromHex(
|
||||
consensusPubkeyToHexAddress({
|
||||
'@type': '/cosmos.crypto.ed25519.PubKey',
|
||||
key: v.consumer_key.ed25519,
|
||||
})
|
||||
)
|
||||
);
|
||||
const moniker = v.provider_address;
|
||||
consumerValidators.value.push({ moniker, base64 });
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
if (Number(state.latest.block.header.height) % 7 === 0) updateTotalSigningInfo();
|
||||
|
||||
if (Number(state.latest.block.header.height) % 7 === 0)
|
||||
updateTotalSigningInfo();
|
||||
fillblock(state.latest);
|
||||
}
|
||||
});
|
||||
@@ -134,38 +153,47 @@ onMounted(() => {
|
||||
});
|
||||
|
||||
function preFill() {
|
||||
|
||||
if(latest.value > 50 && baseStore.recents.length >= 49 ) return
|
||||
if (latest.value > 50 && baseStore.recents.length >= 49) return;
|
||||
// preload 50 blocks if recent blocks are not enough
|
||||
let promise = Promise.resolve();
|
||||
for (let i = latest.value - baseStore.recents.length; i > latest.value - 50 && i > 1; i -= 1) {
|
||||
promise = promise.then(() =>
|
||||
new Promise((resolve) => {
|
||||
if (live.value) {
|
||||
// continue only if the page is living
|
||||
if (i > latest.value - 50)
|
||||
baseStore.fetchBlock(i).then((x) => {
|
||||
fillblock(x, 'start');
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
})
|
||||
for (
|
||||
let i = latest.value - baseStore.recents.length;
|
||||
i > latest.value - 50 && i > 1;
|
||||
i -= 1
|
||||
) {
|
||||
promise = promise.then(
|
||||
() =>
|
||||
new Promise((resolve) => {
|
||||
if (live.value) {
|
||||
// continue only if the page is living
|
||||
if (i > latest.value - 50)
|
||||
baseStore.fetchBlock(i).then((x) => {
|
||||
fillblock(x, 'start');
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
function fillblock(b: Block, direction: string = 'end') {
|
||||
validatorSet.value.forEach((v) => {
|
||||
const sig = b.block.last_commit?.signatures.find((s) => s.validator_address === v.base64)
|
||||
const sig = b.block.last_commit?.signatures.find(
|
||||
(s) => s.validator_address === v.base64
|
||||
);
|
||||
const block = blockColors.value[v.base64] || [];
|
||||
let color = {
|
||||
height: b.block.header.height,
|
||||
color: 'bg-red-500'
|
||||
}
|
||||
color: 'bg-red-500',
|
||||
};
|
||||
if (sig) {
|
||||
color = {
|
||||
height: b.block.header.height,
|
||||
color: sig.block_id_flag === 'BLOCK_ID_FLAG_COMMIT' ? 'bg-green-500' : 'bg-yellow-500'
|
||||
}
|
||||
color:
|
||||
sig.block_id_flag === 'BLOCK_ID_FLAG_COMMIT'
|
||||
? 'bg-green-500'
|
||||
: 'bg-yellow-500',
|
||||
};
|
||||
}
|
||||
if (direction === 'end') {
|
||||
block.push(color);
|
||||
@@ -194,24 +222,35 @@ const tab = ref('2');
|
||||
function changeTab(v: string) {
|
||||
tab.value = v;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="tabs tabs-boxed bg-transparent mb-4">
|
||||
<a class="tab text-gray-400 capitalize" :class="{ 'tab-active': tab === '3' }" @click="changeTab('3')">{{
|
||||
$t('uptime.overall') }}</a>
|
||||
<a class="tab text-gray-400 capitalize" :class="{ 'tab-active': tab === '2' }" @click="changeTab('2')">{{
|
||||
$t('module.blocks') }}</a>
|
||||
<a
|
||||
class="tab text-gray-400 capitalize"
|
||||
:class="{ 'tab-active': tab === '3' }"
|
||||
@click="changeTab('3')"
|
||||
>{{ $t('uptime.overall') }}</a
|
||||
>
|
||||
<a
|
||||
class="tab text-gray-400 capitalize"
|
||||
:class="{ 'tab-active': tab === '2' }"
|
||||
@click="changeTab('2')"
|
||||
>{{ $t('module.blocks') }}</a
|
||||
>
|
||||
<RouterLink :to="`/${chain}/uptime/customize`">
|
||||
<a class="tab text-gray-400 capitalize">{{ $t('uptime.customize') }}</a>
|
||||
</RouterLink>
|
||||
</div>
|
||||
<div class="bg-base-100 px-5 pt-5">
|
||||
<div class="flex items-center gap-x-4">
|
||||
<input type="text" v-model="keyword" placeholder="Keywords to filter validators"
|
||||
class="input input-sm w-full flex-1 border border-gray-200 dark:border-gray-600" />
|
||||
<input
|
||||
type="text"
|
||||
v-model="keyword"
|
||||
placeholder="Keywords to filter validators"
|
||||
class="input input-sm w-full flex-1 border border-gray-200 dark:border-gray-600"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-x-4 mt-4 -->
|
||||
@@ -220,13 +259,20 @@ function changeTab(v: string) {
|
||||
<div v-for="(unit, i) in grid" :key="i">
|
||||
<div class="flex justify-between py-0 w-[248px]">
|
||||
<label class="truncate text-sm">
|
||||
<span class="ml-1 text-black dark:text-white">{{ i + 1 }}.{{ unit.moniker }}</span>
|
||||
<span class="ml-1 text-black dark:text-white"
|
||||
>{{ i + 1 }}.{{ unit.moniker }}</span
|
||||
>
|
||||
</label>
|
||||
<div v-if="Number(unit?.missed_blocks_counter || 0) > 10"
|
||||
class="badge badge-sm bg-transparent border-0 text-red-500 font-bold">
|
||||
<div
|
||||
v-if="Number(unit?.missed_blocks_counter || 0) > 10"
|
||||
class="badge badge-sm bg-transparent border-0 text-red-500 font-bold"
|
||||
>
|
||||
{{ unit?.missed_blocks_counter }}
|
||||
</div>
|
||||
<div v-else class="badge badge-sm bg-transparent text-green-600 border-0 font-bold">
|
||||
<div
|
||||
v-else
|
||||
class="badge badge-sm bg-transparent text-green-600 border-0 font-bold"
|
||||
>
|
||||
{{ unit?.missed_blocks_counter }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -234,9 +280,10 @@ function changeTab(v: string) {
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-5 text-xs flex justify-center gap-2">
|
||||
<span class=" font-bold">{{ $t('uptime.legend') }}: </span>
|
||||
<span class="font-bold">{{ $t('uptime.legend') }}: </span>
|
||||
<span class="bg-green-500"> </span> {{ $t('uptime.committed') }}
|
||||
<span class="bg-yellow-500"> </span> {{ $t('uptime.precommitted') }}
|
||||
<span class="bg-yellow-500"> </span>
|
||||
{{ $t('uptime.precommitted') }}
|
||||
<span class="bg-red-500"> </span> {{ $t('uptime.missed') }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -255,32 +302,49 @@ function changeTab(v: string) {
|
||||
</thead>
|
||||
<tr v-for="(v, i) in grid" class="hover">
|
||||
<td>
|
||||
<div class="truncate max-w-sm">
|
||||
{{ i + 1 }}. {{ v.moniker }}
|
||||
</div>
|
||||
<div class="truncate max-w-sm">{{ i + 1 }}. {{ v.moniker }}</div>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span :class="v.uptime && v.uptime > 0.95 ? 'text-green-500' : 'text-red-500'
|
||||
">
|
||||
<div class="tooltip" :data-tip="`${v.missed_blocks_counter} missing blocks`">
|
||||
<span
|
||||
:class="
|
||||
v.uptime && v.uptime > 0.95
|
||||
? 'text-green-500'
|
||||
: 'text-red-500'
|
||||
"
|
||||
>
|
||||
<div
|
||||
class="tooltip"
|
||||
:data-tip="`${v.missed_blocks_counter} missing blocks`"
|
||||
>
|
||||
{{ format.percent(v.uptime) }}
|
||||
</div>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="v.signing && !v.signing.jailed_until.startsWith('1970')">
|
||||
<div class="tooltip" :data-tip="format.toDay(v.signing.jailed_until, 'long')">
|
||||
<span>{{ format.toDay(v.signing.jailed_until, 'from') }}</span>
|
||||
<span
|
||||
v-if="v.signing && !v.signing.jailed_until.startsWith('1970')"
|
||||
>
|
||||
<div
|
||||
class="tooltip"
|
||||
:data-tip="format.toDay(v.signing.jailed_until, 'long')"
|
||||
>
|
||||
<span>{{
|
||||
format.toDay(v.signing.jailed_until, 'from')
|
||||
}}</span>
|
||||
</div>
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-xs text-right">
|
||||
<span v-if="v.signing && v.signing.jailed_until.startsWith('1970')" class="text-right">{{
|
||||
format.percent(
|
||||
Number(v.signing.index_offset) /
|
||||
(latest - Number(v.signing.start_height))
|
||||
)
|
||||
}}</span>
|
||||
<span
|
||||
v-if="v.signing && v.signing.jailed_until.startsWith('1970')"
|
||||
class="text-right"
|
||||
>{{
|
||||
format.percent(
|
||||
Number(v.signing.index_offset) /
|
||||
(latest - Number(v.signing.start_height))
|
||||
)
|
||||
}}</span
|
||||
>
|
||||
{{ v.signing?.index_offset }}
|
||||
</td>
|
||||
<td class="text-right">{{ v.signing?.start_height }}</td>
|
||||
@@ -290,10 +354,12 @@ function changeTab(v: string) {
|
||||
<tr>
|
||||
<td colspan="2" class="text-right">
|
||||
{{ $t('uptime.minimum_uptime') }}:
|
||||
<span class="lowercase tooltip" :data-tip="`Window size: ${slashingParam.signed_blocks_window}`"><span
|
||||
class="ml-2 btn btn-error btn-xs">{{
|
||||
format.percent(slashingParam.min_signed_per_window)
|
||||
}}</span>
|
||||
<span
|
||||
class="lowercase tooltip"
|
||||
:data-tip="`Window size: ${slashingParam.signed_blocks_window}`"
|
||||
><span class="ml-2 btn btn-error btn-xs">{{
|
||||
format.percent(slashingParam.min_signed_per_window)
|
||||
}}</span>
|
||||
</span>
|
||||
</td>
|
||||
<td colspan="8"></td>
|
||||
|
||||
@@ -7,48 +7,65 @@ import { ref } from 'vue';
|
||||
|
||||
const props = defineProps(['chain']);
|
||||
const chainStore = useBlockchain();
|
||||
const baseStore = useBaseStore()
|
||||
const endpoint = ref(chainStore.current?.endpoints?.rest?.at(0)?.address)
|
||||
const baseStore = useBaseStore();
|
||||
const endpoint = ref(chainStore.current?.endpoints?.rest?.at(0)?.address);
|
||||
|
||||
const chainId = computed(() => baseStore.latest?.block?.header?.chain_id || "")
|
||||
const chainName = computed(() => chainStore?.current?.prettyName || "")
|
||||
const chainId = computed(() => baseStore.latest?.block?.header?.chain_id || '');
|
||||
const chainName = computed(() => chainStore?.current?.prettyName || '');
|
||||
const hdPath = computed(() => {
|
||||
return `m/44'/${ chainStore.current?.coinType }/0'/0/0`
|
||||
})
|
||||
return `m/44'/${chainStore.current?.coinType}/0'/0/0`;
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded shadow">
|
||||
<h2 class="card-title">{{ $t('widget.title') }}</h2>
|
||||
<div class="my-4 grid grid-flow-col auto-cols-max overflow-auto">
|
||||
<div class="my-4 grid grid-flow-col auto-cols-max overflow-auto">
|
||||
<div class="form-control">
|
||||
<div class="input-group">
|
||||
<span>{{ $t('widget.endpoint') }}</span>
|
||||
<select v-model="endpoint" class="select select-bordered w-fit">
|
||||
<option disabled selected>{{ $t('widget.select_endpoint') }}</option>
|
||||
<option v-for="v in chainStore.current?.endpoints.rest" :value="v.address">{{ v.address }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<span>{{ $t('widget.endpoint') }}</span>
|
||||
<select v-model="endpoint" class="select select-bordered w-fit">
|
||||
<option disabled selected>
|
||||
{{ $t('widget.select_endpoint') }}
|
||||
</option>
|
||||
<option
|
||||
v-for="v in chainStore.current?.endpoints.rest"
|
||||
:value="v.address"
|
||||
>
|
||||
{{ v.address }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="text-base">{{ $t('widget.text_1') }}</span>
|
||||
<div class="mockup-code bg-base-200 my-2">
|
||||
<pre data-prefix="1"><code class="text-gray-800 dark:invert"><script type="module" src="https://unpkg.com/ping-widget@latest/dist/ping-widget.js"></code></pre>
|
||||
</div>
|
||||
<div class="mockup-code bg-base-200 my-2">
|
||||
<pre
|
||||
data-prefix="1"
|
||||
><code class="text-gray-800 dark:invert"><script type="module" src="https://unpkg.com/ping-widget@latest/dist/ping-widget.js"></code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-base-100 my-5 px-4 pt-3 pb-4 rounded shadow">
|
||||
<h2 class="card-title">{{ $t('module.widget') }}</h2>
|
||||
<div class="mt-4">
|
||||
<span class="text-base"> 1. {{ $t('widget.text_2') }}</span>
|
||||
<div class="mockup-code bg-base-200 my-2">
|
||||
<pre data-prefix=">"><code class="text-green-400"><!-- This widget is optional. --> </code></pre>
|
||||
<pre data-prefix=">"><code class="text-gray-800 dark:invert"><ping-connect-wallet chain-id="{{ chainId }}" hd-path="{{ hdPath }}"/></code></pre>
|
||||
<pre
|
||||
data-prefix=">"
|
||||
><code class="text-green-400"><!-- This widget is optional. --> </code></pre>
|
||||
<pre
|
||||
data-prefix=">"
|
||||
><code class="text-gray-800 dark:invert"><ping-connect-wallet chain-id="{{ chainId }}" hd-path="{{ hdPath }}"/></code></pre>
|
||||
</div>
|
||||
|
||||
<span class="text-base"> 2. {{ $t('widget.text_3') }}</span>
|
||||
<div class="mockup-code bg-base-200 my-2">
|
||||
<pre data-prefix=">"><code class=" text-gray-800 dark:invert"><ping-token-convert chain-name="{{ chainName }}" endpoint="{{endpoint}}" hd-path="{{hdPath}}"/></code></pre>
|
||||
<pre data-prefix=">"><code class="text-gray-800 dark:invert"><label for="PingTokenConvert" class="btn btn-sm">Buy {{chainName.toUpperCase()}}</label></code></pre>
|
||||
<pre
|
||||
data-prefix=">"
|
||||
><code class=" text-gray-800 dark:invert"><ping-token-convert chain-name="{{ chainName }}" endpoint="{{endpoint}}" hd-path="{{hdPath}}"/></code></pre>
|
||||
<pre
|
||||
data-prefix=">"
|
||||
><code class="text-gray-800 dark:invert"><label for="PingTokenConvert" class="btn btn-sm">Buy {{chainName.toUpperCase()}}</label></code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+254
-111
@@ -15,12 +15,12 @@ import {
|
||||
import AdBanner from '@/components/ad/AdBanner.vue';
|
||||
|
||||
const dashboard = useDashboard();
|
||||
const chainStore = useBlockchain()
|
||||
const chainStore = useBlockchain();
|
||||
const format = useFormatter();
|
||||
const sourceAddress = ref(''); //
|
||||
const sourceHdPath = ref("m/44/118/0'/0/0"); //
|
||||
const selectedSource = ref({} as LocalKey); //
|
||||
const importStep = ref('step1')
|
||||
const importStep = ref('step1');
|
||||
|
||||
const conf = ref(
|
||||
JSON.parse(localStorage.getItem('imported-addresses') || '{}') as Record<
|
||||
@@ -31,8 +31,8 @@ const conf = ref(
|
||||
const balances = ref({} as Record<string, CoinWithPrice[]>);
|
||||
const delegations = ref({} as Record<string, Delegation[]>);
|
||||
|
||||
// initial loading queue
|
||||
// load balances
|
||||
// initial loading queue
|
||||
// load balances
|
||||
Object.values(conf.value).forEach((imported) => {
|
||||
let promise = Promise.resolve();
|
||||
for (let i = 0; i < imported.length; i++) {
|
||||
@@ -56,48 +56,53 @@ Object.values(conf.value).forEach((imported) => {
|
||||
|
||||
const accounts = computed(() => {
|
||||
let a = [] as {
|
||||
key: string,
|
||||
key: string;
|
||||
subaccounts: {
|
||||
account: AccountEntry;
|
||||
delegation: CoinWithPrice;
|
||||
balances: CoinWithPrice[];
|
||||
}[]
|
||||
}[];
|
||||
}[];
|
||||
Object.values(conf.value).forEach((x) => {
|
||||
const composition = x.map((entry) => {
|
||||
const d = delegations.value[entry.address];
|
||||
let delegation = {} as CoinWithPrice
|
||||
let delegation = {} as CoinWithPrice;
|
||||
if (d && d.length > 0) {
|
||||
d.forEach((b) => {
|
||||
delegation.amount = (Number(b.balance.amount) + Number(delegation.amount || 0)).toFixed()
|
||||
delegation.amount = (
|
||||
Number(b.balance.amount) + Number(delegation.amount || 0)
|
||||
).toFixed();
|
||||
delegation.denom = b.balance.denom;
|
||||
});
|
||||
delegation.value = format.tokenValueNumber(delegation)
|
||||
delegation.change24h = format.priceChanges(delegation.denom)
|
||||
delegation.value = format.tokenValueNumber(delegation);
|
||||
delegation.change24h = format.priceChanges(delegation.denom);
|
||||
}
|
||||
return {
|
||||
account: entry,
|
||||
delegation,
|
||||
balances: balances.value[entry.address]
|
||||
? balances.value[entry.address].map(x => {
|
||||
const value = format.tokenValueNumber(x)
|
||||
return {
|
||||
amount: x.amount,
|
||||
denom: x.denom,
|
||||
value,
|
||||
change24h: format.priceChanges(x.denom)
|
||||
}
|
||||
})
|
||||
: []
|
||||
}
|
||||
? balances.value[entry.address].map((x) => {
|
||||
const value = format.tokenValueNumber(x);
|
||||
return {
|
||||
amount: x.amount,
|
||||
denom: x.denom,
|
||||
value,
|
||||
change24h: format.priceChanges(x.denom),
|
||||
};
|
||||
})
|
||||
: [],
|
||||
};
|
||||
});
|
||||
if (x.at(0)) a.push({ key: x.at(0)?.address || " ", subaccounts: composition });
|
||||
if (x.at(0))
|
||||
a.push({ key: x.at(0)?.address || ' ', subaccounts: composition });
|
||||
});
|
||||
return a;
|
||||
});
|
||||
|
||||
const addresses = computed(() => {
|
||||
return accounts.value.flatMap(x => x.subaccounts.map(a => a.account.address))
|
||||
return accounts.value.flatMap((x) =>
|
||||
x.subaccounts.map((a) => a.account.address)
|
||||
);
|
||||
// const temp = [] as string[]
|
||||
// accounts.value.forEach((x) => x.accounts.forEach(a => {
|
||||
// temp.push(a.account.address)
|
||||
@@ -106,31 +111,35 @@ const addresses = computed(() => {
|
||||
});
|
||||
|
||||
const totalValue = computed(() => {
|
||||
return accounts.value.flatMap(x => x.subaccounts).reduce((s, e) => {
|
||||
s += e.delegation.value || 0
|
||||
e.balances.forEach(b => {
|
||||
s += b.value || 0
|
||||
})
|
||||
return s
|
||||
}, 0)
|
||||
})
|
||||
return accounts.value
|
||||
.flatMap((x) => x.subaccounts)
|
||||
.reduce((s, e) => {
|
||||
s += e.delegation.value || 0;
|
||||
e.balances.forEach((b) => {
|
||||
s += b.value || 0;
|
||||
});
|
||||
return s;
|
||||
}, 0);
|
||||
});
|
||||
|
||||
const totalChange = computed(() => {
|
||||
return accounts.value.flatMap(x => x.subaccounts).reduce((s, e) => {
|
||||
s += (e.delegation.change24h || 0) * (e.delegation.value || 0) / 100
|
||||
e.balances.forEach(b => {
|
||||
s += (b.change24h || 0) * (b.value || 0) / 100
|
||||
})
|
||||
return s
|
||||
}, 0)
|
||||
})
|
||||
return accounts.value
|
||||
.flatMap((x) => x.subaccounts)
|
||||
.reduce((s, e) => {
|
||||
s += ((e.delegation.change24h || 0) * (e.delegation.value || 0)) / 100;
|
||||
e.balances.forEach((b) => {
|
||||
s += ((b.change24h || 0) * (b.value || 0)) / 100;
|
||||
});
|
||||
return s;
|
||||
}, 0);
|
||||
});
|
||||
|
||||
// Adding Model Boxes
|
||||
const availableAccount = computed(() => {
|
||||
if (sourceAddress.value) {
|
||||
return scanCompatibleAccounts([{cosmosAddress: sourceAddress.value, hdPath: sourceHdPath.value }]).filter(
|
||||
(x) => !addresses.value.includes(x.address)
|
||||
);
|
||||
return scanCompatibleAccounts([
|
||||
{ cosmosAddress: sourceAddress.value, hdPath: sourceHdPath.value },
|
||||
]).filter((x) => !addresses.value.includes(x.address));
|
||||
}
|
||||
return [];
|
||||
});
|
||||
@@ -179,9 +188,12 @@ async function addAddress(acc: AccountEntry) {
|
||||
}
|
||||
|
||||
// load balances for an address
|
||||
async function loadBalances(chainName: string, endpoint: string, address: string) {
|
||||
|
||||
const endpointObj = chainStore.randomEndpoint(chainName)
|
||||
async function loadBalances(
|
||||
chainName: string,
|
||||
endpoint: string,
|
||||
address: string
|
||||
) {
|
||||
const endpointObj = chainStore.randomEndpoint(chainName);
|
||||
const client = CosmosRestClient.newDefault(endpointObj?.address || endpoint);
|
||||
await client.getBankBalances(address).then((res) => {
|
||||
balances.value[address] = res.balances.filter((x) => x.denom.length < 10);
|
||||
@@ -196,18 +208,31 @@ async function loadBalances(chainName: string, endpoint: string, address: string
|
||||
<div class="overflow-x-auto w-full rounded-md">
|
||||
<div class="flex flex-wrap justify-between bg-base-100 p-5">
|
||||
<div class="min-w-0">
|
||||
<h2 class="text-2xl font-bold leading-7 sm:!truncate sm:!text-3xl sm:!tracking-tight">
|
||||
<h2
|
||||
class="text-2xl font-bold leading-7 sm:!truncate sm:!text-3xl sm:!tracking-tight"
|
||||
>
|
||||
Accounts
|
||||
</h2>
|
||||
<div class="mt-1 flex flex-col sm:!mt-0 sm:!flex-row sm:!flex-wrap sm:!space-x-6">
|
||||
<div class="mt-2 items-center text-sm text-gray-500 hidden md:!flex">
|
||||
<svg class="mr-1.5 h-5 w-5 flex-shrink-0 text-gray-400" viewBox="0 0 20 20" fill="currentColor"
|
||||
aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M6 3.75A2.75 2.75 0 018.75 1h2.5A2.75 2.75 0 0114 3.75v.443c.572.055 1.14.122 1.706.2C17.053 4.582 18 5.75 18 7.07v3.469c0 1.126-.694 2.191-1.83 2.54-1.952.599-4.024.921-6.17.921s-4.219-.322-6.17-.921C2.694 12.73 2 11.665 2 10.539V7.07c0-1.321.947-2.489 2.294-2.676A41.047 41.047 0 016 4.193V3.75zm6.5 0v.325a41.622 41.622 0 00-5 0V3.75c0-.69.56-1.25 1.25-1.25h2.5c.69 0 1.25.56 1.25 1.25zM10 10a1 1 0 00-1 1v.01a1 1 0 001 1h.01a1 1 0 001-1V11a1 1 0 00-1-1H10z"
|
||||
clip-rule="evenodd" />
|
||||
<div
|
||||
class="mt-1 flex flex-col sm:!mt-0 sm:!flex-row sm:!flex-wrap sm:!space-x-6"
|
||||
>
|
||||
<div
|
||||
class="mt-2 items-center text-sm text-gray-500 hidden md:!flex"
|
||||
>
|
||||
<svg
|
||||
class="mr-1.5 h-5 w-5 flex-shrink-0 text-gray-400"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
d="M3 15.055v-.684c.126.053.255.1.39.142 2.092.642 4.313.987 6.61.987 2.297 0 4.518-.345 6.61-.987.135-.041.264-.089.39-.142v.684c0 1.347-.985 2.53-2.363 2.686a41.454 41.454 0 01-9.274 0C3.985 17.585 3 16.402 3 15.055z" />
|
||||
fill-rule="evenodd"
|
||||
d="M6 3.75A2.75 2.75 0 018.75 1h2.5A2.75 2.75 0 0114 3.75v.443c.572.055 1.14.122 1.706.2C17.053 4.582 18 5.75 18 7.07v3.469c0 1.126-.694 2.191-1.83 2.54-1.952.599-4.024.921-6.17.921s-4.219-.322-6.17-.921C2.694 12.73 2 11.665 2 10.539V7.07c0-1.321.947-2.489 2.294-2.676A41.047 41.047 0 016 4.193V3.75zm6.5 0v.325a41.622 41.622 0 00-5 0V3.75c0-.69.56-1.25 1.25-1.25h2.5c.69 0 1.25.56 1.25 1.25zM10 10a1 1 0 00-1 1v.01a1 1 0 001 1h.01a1 1 0 001-1V11a1 1 0 00-1-1H10z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
<path
|
||||
d="M3 15.055v-.684c.126.053.255.1.39.142 2.092.642 4.313.987 6.61.987 2.297 0 4.518-.345 6.61-.987.135-.041.264-.089.39-.142v.684c0 1.347-.985 2.53-2.363 2.686a41.454 41.454 0 01-9.274 0C3.985 17.585 3 16.402 3 15.055z"
|
||||
/>
|
||||
</svg>
|
||||
Manage all your assets in one page
|
||||
</div>
|
||||
@@ -215,55 +240,93 @@ async function loadBalances(chainName: string, endpoint: string, address: string
|
||||
</div>
|
||||
<div class="flex flex-col text-right">
|
||||
<span>Total Value</span>
|
||||
<span class="text-xl text-success font-bold">${{ format.formatNumber(totalValue, '0,0.[00]') }}</span>
|
||||
<span class="text-sm" :class="format.color(totalChange)">{{ format.formatNumber(totalChange, '+0,0.[00]')
|
||||
<span class="text-xl text-success font-bold"
|
||||
>${{ format.formatNumber(totalValue, '0,0.[00]') }}</span
|
||||
>
|
||||
<span class="text-sm" :class="format.color(totalChange)">{{
|
||||
format.formatNumber(totalChange, '+0,0.[00]')
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AdBanner id="account-banner-ad" unit="banner" width="970px" height="90px" />
|
||||
<AdBanner
|
||||
id="account-banner-ad"
|
||||
unit="banner"
|
||||
width="970px"
|
||||
height="90px"
|
||||
/>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<div v-for="{ key, subaccounts } in accounts" class="bg-base-100 rounded-md my-5 py-5">
|
||||
<div
|
||||
v-for="{ key, subaccounts } in accounts"
|
||||
class="bg-base-100 rounded-md my-5 py-5"
|
||||
>
|
||||
<div class="flex justify-self-center">
|
||||
<div class="mx-2 p-2">
|
||||
<svg :fill="chainStore.current?.themeColor || '#666CFF'" height="28px" width="28px" version="1.1" id="Capa_1"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 487.5 487.5"
|
||||
xml:space="preserve">
|
||||
<svg
|
||||
:fill="chainStore.current?.themeColor || '#666CFF'"
|
||||
height="28px"
|
||||
width="28px"
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 487.5 487.5"
|
||||
xml:space="preserve"
|
||||
>
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"></g>
|
||||
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g>
|
||||
<g
|
||||
id="SVGRepo_tracerCarrier"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
></g>
|
||||
<g id="SVGRepo_iconCarrier">
|
||||
<g>
|
||||
<g>
|
||||
<path
|
||||
d="M437,12.3C437,5.5,431.5,0,424.7,0H126.3C84.4,0,50.4,34.1,50.4,75.9v335.7c0,41.9,34.1,75.9,75.9,75.9h298.5 c6.8,0,12.3-5.5,12.3-12.3V139.6c0-6.8-5.5-12.3-12.3-12.3H126.3c-28.3,0-51.4-23.1-51.4-51.4S98,24.5,126.3,24.5h298.5 C431.5,24.5,437,19,437,12.3z M126.3,151.8h286.2V463H126.3c-28.3,0-51.4-23.1-51.4-51.4V131.7 C88.4,144.2,106.5,151.8,126.3,151.8z">
|
||||
</path>
|
||||
d="M437,12.3C437,5.5,431.5,0,424.7,0H126.3C84.4,0,50.4,34.1,50.4,75.9v335.7c0,41.9,34.1,75.9,75.9,75.9h298.5 c6.8,0,12.3-5.5,12.3-12.3V139.6c0-6.8-5.5-12.3-12.3-12.3H126.3c-28.3,0-51.4-23.1-51.4-51.4S98,24.5,126.3,24.5h298.5 C431.5,24.5,437,19,437,12.3z M126.3,151.8h286.2V463H126.3c-28.3,0-51.4-23.1-51.4-51.4V131.7 C88.4,144.2,106.5,151.8,126.3,151.8z"
|
||||
></path>
|
||||
<path
|
||||
d="M130.5,64.8c-6.8,0-12.3,5.5-12.3,12.3s5.5,12.3,12.3,12.3h280.1c6.8,0,12.3-5.5,12.3-12.3s-5.5-12.3-12.3-12.3H130.5z">
|
||||
</path>
|
||||
d="M130.5,64.8c-6.8,0-12.3,5.5-12.3,12.3s5.5,12.3,12.3,12.3h280.1c6.8,0,12.3-5.5,12.3-12.3s-5.5-12.3-12.3-12.3H130.5z"
|
||||
></path>
|
||||
<path
|
||||
d="M178,397.7c6.3,2.4,13.4-0.7,15.8-7.1l17.9-46.8h62.7c0.5,0,0.9-0.1,1.3-0.1l17.9,46.9c1.9,4.9,6.5,7.9,11.4,7.9 c1.5,0,2.9-0.3,4.4-0.8c6.3-2.4,9.5-9.5,7.1-15.8l-54-141.2c-3-7.9-10.4-13-18.8-13c-8.4,0-15.8,5.1-18.8,13l-54,141.2 C168.5,388.2,171.7,395.2,178,397.7z M243.7,260l22.7,59.3h-45.3L243.7,260z">
|
||||
</path>
|
||||
d="M178,397.7c6.3,2.4,13.4-0.7,15.8-7.1l17.9-46.8h62.7c0.5,0,0.9-0.1,1.3-0.1l17.9,46.9c1.9,4.9,6.5,7.9,11.4,7.9 c1.5,0,2.9-0.3,4.4-0.8c6.3-2.4,9.5-9.5,7.1-15.8l-54-141.2c-3-7.9-10.4-13-18.8-13c-8.4,0-15.8,5.1-18.8,13l-54,141.2 C168.5,388.2,171.7,395.2,178,397.7z M243.7,260l22.7,59.3h-45.3L243.7,260z"
|
||||
></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class=" max-w-md overflow-hidden"><div class="font-bold">{{ key }}</div></div>
|
||||
<div class="max-w-md overflow-hidden">
|
||||
<div class="font-bold">{{ key }}</div>
|
||||
</div>
|
||||
<div class="dropdown">
|
||||
<label tabindex="0" class=" cursor-pointer">{{ subaccounts.length }} addresses</label>
|
||||
<ul tabindex="0" class=" -left-14 dropdown-content menu p-2 shadow bg-base-200 rounded-box z-50">
|
||||
<label tabindex="0" class="cursor-pointer"
|
||||
>{{ subaccounts.length }} addresses</label
|
||||
>
|
||||
<ul
|
||||
tabindex="0"
|
||||
class="-left-14 dropdown-content menu p-2 shadow bg-base-200 rounded-box z-50"
|
||||
>
|
||||
<li v-for="x in subaccounts">
|
||||
<a>
|
||||
<img :src="x.account.logo" class="w-8 h-8 mr-2" />
|
||||
<span class="font-bold capitalize">{{ x.account.chainName }} <br>
|
||||
<span class="text-xs font-normal sm:w-16 sm:overflow-hidden">{{ x.account.address }}</span>
|
||||
</span>
|
||||
<label class=" btn btn-xs !btn-error" @click="removeAddress(x.account.address)">Remove</label>
|
||||
</a>
|
||||
</li>
|
||||
<a>
|
||||
<img :src="x.account.logo" class="w-8 h-8 mr-2" />
|
||||
<span class="font-bold capitalize"
|
||||
>{{ x.account.chainName }} <br />
|
||||
<span
|
||||
class="text-xs font-normal sm:w-16 sm:overflow-hidden"
|
||||
>{{ x.account.address }}</span
|
||||
>
|
||||
</span>
|
||||
<label
|
||||
class="btn btn-xs !btn-error"
|
||||
@click="removeAddress(x.account.address)"
|
||||
>Remove</label
|
||||
>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -273,16 +336,38 @@ async function loadBalances(chainName: string, endpoint: string, address: string
|
||||
<ul class="!menu w-full">
|
||||
<div v-for="x in subaccounts">
|
||||
<li v-if="x.delegation.amount">
|
||||
<RouterLink :to="`/${x.account.chainName}/account/${x.account.address}`">
|
||||
<RouterLink
|
||||
:to="`/${x.account.chainName}/account/${x.account.address}`"
|
||||
>
|
||||
<img :src="x.account.logo" class="w-6 h-6 mr-2" />
|
||||
<span class="font-bold">{{ format.formatToken(x.delegation, true, '0,0.[00]', 'all') }} <br><span
|
||||
class="text-xs" :class="format.color(x.delegation.change24h)">{{
|
||||
format.formatNumber(x.delegation.change24h, '+0.[00]') }}%</span></span>
|
||||
<span class="float-right text-right">${{ format.formatNumber(x.delegation.value, '0,0.[00]') }}<br><span
|
||||
class="text-xs" :class="format.color(x.delegation.change24h)">{{
|
||||
format.formatNumber((x.delegation.change24h || 0) * (x.delegation.value || 0) / 100, '+0,0.[00]')
|
||||
}}</span></span>
|
||||
</RouterLink>
|
||||
<span class="font-bold"
|
||||
>{{
|
||||
format.formatToken(x.delegation, true, '0,0.[00]', 'all')
|
||||
}}
|
||||
<br /><span
|
||||
class="text-xs"
|
||||
:class="format.color(x.delegation.change24h)"
|
||||
>{{
|
||||
format.formatNumber(x.delegation.change24h, '+0.[00]')
|
||||
}}%</span
|
||||
></span
|
||||
>
|
||||
<span class="float-right text-right"
|
||||
>${{ format.formatNumber(x.delegation.value, '0,0.[00]')
|
||||
}}<br /><span
|
||||
class="text-xs"
|
||||
:class="format.color(x.delegation.change24h)"
|
||||
>{{
|
||||
format.formatNumber(
|
||||
((x.delegation.change24h || 0) *
|
||||
(x.delegation.value || 0)) /
|
||||
100,
|
||||
'+0,0.[00]'
|
||||
)
|
||||
}}</span
|
||||
></span
|
||||
>
|
||||
</RouterLink>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
@@ -292,14 +377,30 @@ async function loadBalances(chainName: string, endpoint: string, address: string
|
||||
<ul class="!menu w-full">
|
||||
<div v-for="s in subaccounts">
|
||||
<li v-for="x in s.balances">
|
||||
<RouterLink :to="`/${s.account.chainName}/account/${s.account.address}`">
|
||||
<RouterLink
|
||||
:to="`/${s.account.chainName}/account/${s.account.address}`"
|
||||
>
|
||||
<img :src="s.account.logo" class="w-6 h-6 mr-2" />
|
||||
<span class="font-bold">{{ format.formatToken(x, true, '0,0.[00]', 'all') }} <br><span
|
||||
class="text-xs" :class="format.color(x.change24h)">{{ format.formatNumber(x.change24h, '+0.[00]')
|
||||
}}%</span></span>
|
||||
<span class="float-right text-right">${{ format.formatNumber(x.value, '0,0.[00]') }}<br><span
|
||||
class="text-xs" :class="format.color(x.change24h)">{{ format.formatNumber((x.change24h || 0) *
|
||||
(x.value || 0) / 100, '+0,0.[00]') }}</span></span>
|
||||
<span class="font-bold"
|
||||
>{{ format.formatToken(x, true, '0,0.[00]', 'all') }}
|
||||
<br /><span
|
||||
class="text-xs"
|
||||
:class="format.color(x.change24h)"
|
||||
>{{ format.formatNumber(x.change24h, '+0.[00]') }}%</span
|
||||
></span
|
||||
>
|
||||
<span class="float-right text-right"
|
||||
>${{ format.formatNumber(x.value, '0,0.[00]') }}<br /><span
|
||||
class="text-xs"
|
||||
:class="format.color(x.change24h)"
|
||||
>{{
|
||||
format.formatNumber(
|
||||
((x.change24h || 0) * (x.value || 0)) / 100,
|
||||
'+0,0.[00]'
|
||||
)
|
||||
}}</span
|
||||
></span
|
||||
>
|
||||
</RouterLink>
|
||||
</li>
|
||||
</div>
|
||||
@@ -307,14 +408,23 @@ async function loadBalances(chainName: string, endpoint: string, address: string
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=" text-center bg-base-100 rounded-md my-4 p-4">
|
||||
<a href="#address-modal"
|
||||
class="inline-flex items-center ml-3 rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50">
|
||||
<svg class="-ml-0.5 mr-1.5 h-5 w-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<div class="text-center bg-base-100 rounded-md my-4 p-4">
|
||||
<a
|
||||
href="#address-modal"
|
||||
class="inline-flex items-center ml-3 rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50"
|
||||
>
|
||||
<svg
|
||||
class="-ml-0.5 mr-1.5 h-5 w-5 text-gray-400"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
d="M12.232 4.232a2.5 2.5 0 013.536 3.536l-1.225 1.224a.75.75 0 001.061 1.06l1.224-1.224a4 4 0 00-5.656-5.656l-3 3a4 4 0 00.225 5.865.75.75 0 00.977-1.138 2.5 2.5 0 01-.142-3.667l3-3z" />
|
||||
d="M12.232 4.232a2.5 2.5 0 013.536 3.536l-1.225 1.224a.75.75 0 001.061 1.06l1.224-1.224a4 4 0 00-5.656-5.656l-3 3a4 4 0 00.225 5.865.75.75 0 00.977-1.138 2.5 2.5 0 01-.142-3.667l3-3z"
|
||||
/>
|
||||
<path
|
||||
d="M11.603 7.963a.75.75 0 00-.977 1.138 2.5 2.5 0 01.142 3.667l-3 3a2.5 2.5 0 01-3.536-3.536l1.225-1.224a.75.75 0 00-1.061-1.06l-1.224 1.224a4 4 0 105.656 5.656l3-3a4 4 0 00-.225-5.865z" />
|
||||
d="M11.603 7.963a.75.75 0 00-.977 1.138 2.5 2.5 0 01.142 3.667l-3 3a2.5 2.5 0 01-3.536-3.536l1.225-1.224a.75.75 0 00-1.061-1.06l-1.224 1.224a4 4 0 105.656 5.656l3-3a4 4 0 00-.225-5.865z"
|
||||
/>
|
||||
</svg>
|
||||
Import Address
|
||||
</a>
|
||||
@@ -323,15 +433,31 @@ async function loadBalances(chainName: string, endpoint: string, address: string
|
||||
<!-- Put this part before </body> tag -->
|
||||
<div class="modal" id="address-modal">
|
||||
<div class="modal-box">
|
||||
<a href="#" class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</a>
|
||||
<a
|
||||
href="#"
|
||||
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
|
||||
>✕</a
|
||||
>
|
||||
<h3 class="font-bold text-lg mb-2">Derive Account From Address</h3>
|
||||
<div>
|
||||
<label class="my-2">
|
||||
<input v-model="sourceAddress" class="input input-bordered w-full input-sm" placeholder="Input an address" @change="importStep = 'step2'" />
|
||||
<input v-model="sourceHdPath" class="input input-bordered w-full input-sm" placeholder="m/44/118/0'/0/0" />
|
||||
<input
|
||||
v-model="sourceAddress"
|
||||
class="input input-bordered w-full input-sm"
|
||||
placeholder="Input an address"
|
||||
@change="importStep = 'step2'"
|
||||
/>
|
||||
<input
|
||||
v-model="sourceHdPath"
|
||||
class="input input-bordered w-full input-sm"
|
||||
placeholder="m/44/118/0'/0/0"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div v-show="importStep === 'step2'" class="py-4 max-h-72 overflow-y-auto">
|
||||
<div
|
||||
v-show="importStep === 'step2'"
|
||||
class="py-4 max-h-72 overflow-y-auto"
|
||||
>
|
||||
<table class="table table-compact">
|
||||
<tr v-for="acc in availableAccount">
|
||||
<td>
|
||||
@@ -342,10 +468,19 @@ async function loadBalances(chainName: string, endpoint: string, address: string
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="tooltip" :class="acc.compatiable ? 'tooltip-success' : 'tooltip-error'
|
||||
" :data-tip="`Coin Type: ${acc.coinType}`">
|
||||
<div class="font-bold capitalize" :class="acc.compatiable ? 'text-green-500' : 'text-red-500'
|
||||
">
|
||||
<div
|
||||
class="tooltip"
|
||||
:class="
|
||||
acc.compatiable ? 'tooltip-success' : 'tooltip-error'
|
||||
"
|
||||
:data-tip="`Coin Type: ${acc.coinType}`"
|
||||
>
|
||||
<div
|
||||
class="font-bold capitalize"
|
||||
:class="
|
||||
acc.compatiable ? 'text-green-500' : 'text-red-500'
|
||||
"
|
||||
>
|
||||
{{ acc.chainName }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -356,7 +491,10 @@ async function loadBalances(chainName: string, endpoint: string, address: string
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span class="btn !bg-yes !border-yes btn-xs text-white" @click="addAddress(acc)">
|
||||
<span
|
||||
class="btn !bg-yes !border-yes btn-xs text-white"
|
||||
@click="addAddress(acc)"
|
||||
>
|
||||
<Icon icon="mdi:plus" />
|
||||
</span>
|
||||
</td>
|
||||
@@ -364,7 +502,12 @@ async function loadBalances(chainName: string, endpoint: string, address: string
|
||||
</table>
|
||||
</div>
|
||||
<div class="modal-action mt-2 mb-0">
|
||||
<a href="#" class="btn btn-primary btn-sm" @click="importStep = 'step1'">Close</a>
|
||||
<a
|
||||
href="#"
|
||||
class="btn btn-primary btn-sm"
|
||||
@click="importStep = 'step1'"
|
||||
>Close</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+101
-81
@@ -5,102 +5,122 @@ import { CosmosRestClient } from '@/libs/client';
|
||||
import { onMounted } from 'vue';
|
||||
import AdBanner from '@/components/ad/AdBanner.vue';
|
||||
|
||||
const error = ref("")
|
||||
const conf = ref("")
|
||||
const dashboard = useDashboard()
|
||||
const selected = ref({} as ChainConfig)
|
||||
const error = ref('');
|
||||
const conf = ref('');
|
||||
const dashboard = useDashboard();
|
||||
const selected = ref({} as ChainConfig);
|
||||
|
||||
onMounted(() => {
|
||||
const chainStore = useBlockchain()
|
||||
selected.value = chainStore.current || Object.values(dashboard.chains)[0]
|
||||
const chainStore = useBlockchain();
|
||||
selected.value = chainStore.current || Object.values(dashboard.chains)[0];
|
||||
debugger;
|
||||
initParamsForKeplr()
|
||||
})
|
||||
initParamsForKeplr();
|
||||
});
|
||||
async function initParamsForKeplr() {
|
||||
const chain = selected.value
|
||||
if(!chain.endpoints?.rest?.at(0)) throw new Error("Endpoint does not set");
|
||||
const client = CosmosRestClient.newDefault(chain.endpoints.rest?.at(0)?.address || "")
|
||||
const b = await client.getBaseBlockLatest()
|
||||
const chainid = b.block.header.chain_id
|
||||
const chain = selected.value;
|
||||
if (!chain.endpoints?.rest?.at(0)) throw new Error('Endpoint does not set');
|
||||
const client = CosmosRestClient.newDefault(
|
||||
chain.endpoints.rest?.at(0)?.address || ''
|
||||
);
|
||||
const b = await client.getBaseBlockLatest();
|
||||
const chainid = b.block.header.chain_id;
|
||||
|
||||
const gasPriceStep = chain.keplrPriceStep || {
|
||||
low: 0.01,
|
||||
average: 0.025,
|
||||
high: 0.03,
|
||||
}
|
||||
const coinDecimals = chain.assets[0].denom_units.find(x => x.denom === chain.assets[0].symbol.toLowerCase())?.exponent || 6
|
||||
conf.value = JSON.stringify({
|
||||
chainId: chainid,
|
||||
chainName: chain.chainName,
|
||||
rpc: chain.endpoints?.rpc?.at(0)?.address,
|
||||
rest: chain.endpoints?.rest?.at(0)?.address,
|
||||
bip44: {
|
||||
coinType: Number(chain.coinType),
|
||||
},
|
||||
const gasPriceStep = chain.keplrPriceStep || {
|
||||
low: 0.01,
|
||||
average: 0.025,
|
||||
high: 0.03,
|
||||
};
|
||||
const coinDecimals =
|
||||
chain.assets[0].denom_units.find(
|
||||
(x) => x.denom === chain.assets[0].symbol.toLowerCase()
|
||||
)?.exponent || 6;
|
||||
conf.value = JSON.stringify(
|
||||
{
|
||||
chainId: chainid,
|
||||
chainName: chain.chainName,
|
||||
rpc: chain.endpoints?.rpc?.at(0)?.address,
|
||||
rest: chain.endpoints?.rest?.at(0)?.address,
|
||||
bip44: {
|
||||
coinType: Number(chain.coinType),
|
||||
bech32Config: {
|
||||
bech32PrefixAccAddr: chain.bech32Prefix,
|
||||
bech32PrefixAccPub: `${chain.bech32Prefix}pub`,
|
||||
bech32PrefixValAddr: `${chain.bech32Prefix}valoper`,
|
||||
bech32PrefixValPub: `${chain.bech32Prefix}valoperpub`,
|
||||
bech32PrefixConsAddr: `${chain.bech32Prefix}valcons`,
|
||||
bech32PrefixConsPub: `${chain.bech32Prefix}valconspub`,
|
||||
},
|
||||
coinType: Number(chain.coinType),
|
||||
bech32Config: {
|
||||
bech32PrefixAccAddr: chain.bech32Prefix,
|
||||
bech32PrefixAccPub: `${chain.bech32Prefix}pub`,
|
||||
bech32PrefixValAddr: `${chain.bech32Prefix}valoper`,
|
||||
bech32PrefixValPub: `${chain.bech32Prefix}valoperpub`,
|
||||
bech32PrefixConsAddr: `${chain.bech32Prefix}valcons`,
|
||||
bech32PrefixConsPub: `${chain.bech32Prefix}valconspub`,
|
||||
},
|
||||
currencies: [
|
||||
{
|
||||
coinDenom: chain.assets[0].symbol,
|
||||
coinMinimalDenom: chain.assets[0].base,
|
||||
coinDecimals,
|
||||
coinGeckoId: chain.assets[0].coingecko_id || 'unknown',
|
||||
},
|
||||
currencies: [
|
||||
{
|
||||
coinDenom: chain.assets[0].symbol,
|
||||
coinMinimalDenom: chain.assets[0].base,
|
||||
coinDecimals,
|
||||
coinGeckoId: chain.assets[0].coingecko_id || 'unknown',
|
||||
},
|
||||
],
|
||||
feeCurrencies: [
|
||||
{
|
||||
coinDenom: chain.assets[0].symbol,
|
||||
coinMinimalDenom: chain.assets[0].base,
|
||||
coinDecimals,
|
||||
coinGeckoId: chain.assets[0].coingecko_id || 'unknown',
|
||||
gasPriceStep,
|
||||
},
|
||||
],
|
||||
gasPriceStep,
|
||||
stakeCurrency: {
|
||||
coinDenom: chain.assets[0].symbol,
|
||||
coinMinimalDenom: chain.assets[0].base,
|
||||
coinDecimals,
|
||||
coinGeckoId: chain.assets[0].coingecko_id || 'unknown',
|
||||
],
|
||||
feeCurrencies: [
|
||||
{
|
||||
coinDenom: chain.assets[0].symbol,
|
||||
coinMinimalDenom: chain.assets[0].base,
|
||||
coinDecimals,
|
||||
coinGeckoId: chain.assets[0].coingecko_id || 'unknown',
|
||||
gasPriceStep,
|
||||
},
|
||||
features: chain.keplrFeatures || [],
|
||||
}, null, '\t')
|
||||
],
|
||||
gasPriceStep,
|
||||
stakeCurrency: {
|
||||
coinDenom: chain.assets[0].symbol,
|
||||
coinMinimalDenom: chain.assets[0].base,
|
||||
coinDecimals,
|
||||
coinGeckoId: chain.assets[0].coingecko_id || 'unknown',
|
||||
},
|
||||
features: chain.keplrFeatures || [],
|
||||
},
|
||||
null,
|
||||
'\t'
|
||||
);
|
||||
}
|
||||
|
||||
function suggest() {
|
||||
// @ts-ignore
|
||||
if (window.keplr) {
|
||||
// @ts-ignore
|
||||
if (window.keplr) {
|
||||
// @ts-ignore
|
||||
window.keplr.experimentalSuggestChain(JSON.parse(conf.value)).catch(e => {
|
||||
error.value = e
|
||||
})
|
||||
}
|
||||
window.keplr.experimentalSuggestChain(JSON.parse(conf.value)).catch((e) => {
|
||||
error.value = e;
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-base-100 p-4 rounded text-center">
|
||||
<AdBanner id="keplr-banner-ad" unit="banner" width="970px" height="90px" />
|
||||
<div class="flex">
|
||||
<select v-model="selected" class="select select-bordered mx-5" @change="initParamsForKeplr">
|
||||
<option v-for="c in dashboard.chains" :value="c">
|
||||
{{ c.chainName }}
|
||||
</option>
|
||||
</select>
|
||||
<button class="btn !bg-yes !border-yes text-white px-10" @click="suggest">Add {{ selected.chainName }} TO Keplr Wallet</button>
|
||||
</div>
|
||||
<div class="text-main mt-5">
|
||||
<textarea v-model="conf" class="textarea textarea-bordered w-full" rows="15"></textarea>
|
||||
</div>
|
||||
<div class="mt-4 mb-4">
|
||||
If the chain is not offically support on Keplr, you can submit these parameters to enable Keplr.
|
||||
</div>
|
||||
<div class="bg-base-100 p-4 rounded text-center">
|
||||
<AdBanner id="keplr-banner-ad" unit="banner" width="970px" height="90px" />
|
||||
<div class="flex">
|
||||
<select
|
||||
v-model="selected"
|
||||
class="select select-bordered mx-5"
|
||||
@change="initParamsForKeplr"
|
||||
>
|
||||
<option v-for="c in dashboard.chains" :value="c">
|
||||
{{ c.chainName }}
|
||||
</option>
|
||||
</select>
|
||||
<button class="btn !bg-yes !border-yes text-white px-10" @click="suggest">
|
||||
Add {{ selected.chainName }} TO Keplr Wallet
|
||||
</button>
|
||||
</div>
|
||||
<div class="text-main mt-5">
|
||||
<textarea
|
||||
v-model="conf"
|
||||
class="textarea textarea-bordered w-full"
|
||||
rows="15"
|
||||
></textarea>
|
||||
</div>
|
||||
<div class="mt-4 mb-4">
|
||||
If the chain is not offically support on Keplr, you can submit these
|
||||
parameters to enable Keplr.
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
+185
-109
@@ -23,62 +23,76 @@ const balances = ref({} as Record<string, Coin[]>);
|
||||
const delegations = ref({} as Record<string, Delegation[]>);
|
||||
const tokenMeta = ref({} as Record<string, AccountEntry>);
|
||||
|
||||
const priceloading = ref(false)
|
||||
const currency = ref(localStorage.getItem('currency') || 'usd')
|
||||
const priceloading = ref(false);
|
||||
const currency = ref(localStorage.getItem('currency') || 'usd');
|
||||
|
||||
const prices = ref([] as {
|
||||
id: string,
|
||||
symbol: string,
|
||||
name: string,
|
||||
image: string,
|
||||
current_price: number,
|
||||
market_cap: number,
|
||||
market_cap_rank: number,
|
||||
fully_diluted_valuation: number,
|
||||
total_volume: number, high_24h: number,
|
||||
low_24h: number,
|
||||
price_change_24h: number, price_change_percentage_24h: number, market_cap_change_24h: number,
|
||||
market_cap_change_percentage_24h: number,
|
||||
circulating_supply: number, total_supply: number,
|
||||
max_supply: number,
|
||||
ath: number,
|
||||
ath_change_percentage: number,
|
||||
ath_date: string,
|
||||
atl: string,
|
||||
atl_change_percentage: number,
|
||||
atl_date: string, roi: string, last_updated: string,
|
||||
sparkline_in_7d: { prices: number[] }
|
||||
}[])
|
||||
const prices = ref(
|
||||
[] as {
|
||||
id: string;
|
||||
symbol: string;
|
||||
name: string;
|
||||
image: string;
|
||||
current_price: number;
|
||||
market_cap: number;
|
||||
market_cap_rank: number;
|
||||
fully_diluted_valuation: number;
|
||||
total_volume: number;
|
||||
high_24h: number;
|
||||
low_24h: number;
|
||||
price_change_24h: number;
|
||||
price_change_percentage_24h: number;
|
||||
market_cap_change_24h: number;
|
||||
market_cap_change_percentage_24h: number;
|
||||
circulating_supply: number;
|
||||
total_supply: number;
|
||||
max_supply: number;
|
||||
ath: number;
|
||||
ath_change_percentage: number;
|
||||
ath_date: string;
|
||||
atl: string;
|
||||
atl_change_percentage: number;
|
||||
atl_date: string;
|
||||
roi: string;
|
||||
last_updated: string;
|
||||
sparkline_in_7d: { prices: number[] };
|
||||
}[]
|
||||
);
|
||||
|
||||
const loading = ref(0)
|
||||
const loaded = ref(0)
|
||||
const loading = ref(0);
|
||||
const loaded = ref(0);
|
||||
watchEffect(() => {
|
||||
if (loading.value > 0 && loading.value === loaded.value) {
|
||||
if (!priceloading.value) {
|
||||
priceloading.value = true
|
||||
loadPrice()
|
||||
priceloading.value = true;
|
||||
loadPrice();
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
Object.values(conf.value).forEach((imported) => {
|
||||
if (imported)
|
||||
imported.forEach((x) => {
|
||||
if (x.endpoint && x.address) {
|
||||
loading.value += 1
|
||||
const endpoint = chainStore.randomEndpoint(x.chainName)
|
||||
const client = CosmosRestClient.newDefault(endpoint?.address || x.endpoint);
|
||||
client.getBankBalances(x.address).then((res) => {
|
||||
const bal = res.balances.filter((x) => x.denom.length < 10);
|
||||
if (bal) balances.value[x.address || ""] = bal;
|
||||
bal.forEach((b) => {
|
||||
tokenMeta.value[b.denom] = x;
|
||||
loading.value += 1;
|
||||
const endpoint = chainStore.randomEndpoint(x.chainName);
|
||||
const client = CosmosRestClient.newDefault(
|
||||
endpoint?.address || x.endpoint
|
||||
);
|
||||
client
|
||||
.getBankBalances(x.address)
|
||||
.then((res) => {
|
||||
const bal = res.balances.filter((x) => x.denom.length < 10);
|
||||
if (bal) balances.value[x.address || ''] = bal;
|
||||
bal.forEach((b) => {
|
||||
tokenMeta.value[b.denom] = x;
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
loaded.value += 1;
|
||||
});
|
||||
}).finally(() => {
|
||||
loaded.value += 1
|
||||
});
|
||||
client.getStakingDelegations(x.address).then((res) => {
|
||||
if (res && res.delegation_responses) delegations.value[x.address || ""] = res.delegation_responses;
|
||||
if (res && res.delegation_responses)
|
||||
delegations.value[x.address || ''] = res.delegation_responses;
|
||||
res.delegation_responses.forEach((del) => {
|
||||
tokenMeta.value[del.balance.denom] = x;
|
||||
});
|
||||
@@ -88,7 +102,7 @@ Object.values(conf.value).forEach((imported) => {
|
||||
});
|
||||
|
||||
const tokenQty = computed(() => {
|
||||
const values = {} as Record<string, { coinId: string, qty: number }>;
|
||||
const values = {} as Record<string, { coinId: string; qty: number }>;
|
||||
Object.values(balances.value).forEach((b) => {
|
||||
b.forEach((coin) => {
|
||||
const v = format.tokenDisplayNumber(coin);
|
||||
@@ -96,7 +110,11 @@ const tokenQty = computed(() => {
|
||||
if (values[coin.denom]) {
|
||||
values[coin.denom].qty += v;
|
||||
} else {
|
||||
values[coin.denom] = { qty: v, coinId: format.findGlobalAssetConfig(coin.denom)?.coingecko_id || "" };
|
||||
values[coin.denom] = {
|
||||
qty: v,
|
||||
coinId:
|
||||
format.findGlobalAssetConfig(coin.denom)?.coingecko_id || '',
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -108,7 +126,11 @@ const tokenQty = computed(() => {
|
||||
if (values[d.balance.denom]) {
|
||||
values[d.balance.denom].qty += v;
|
||||
} else {
|
||||
values[d.balance.denom] = { qty: v, coinId: format.findGlobalAssetConfig(d.balance.denom)?.coingecko_id || "" };
|
||||
values[d.balance.denom] = {
|
||||
qty: v,
|
||||
coinId:
|
||||
format.findGlobalAssetConfig(d.balance.denom)?.coingecko_id || '',
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -118,11 +140,11 @@ const tokenQty = computed(() => {
|
||||
|
||||
const tokenValues = computed(() => {
|
||||
const values = {} as Record<string, number>;
|
||||
Object.keys(tokenQty.value).forEach(k => {
|
||||
const x = tokenQty.value[k]
|
||||
const marketData = prices.value.find((p: any) => p.id === x.coinId)
|
||||
values[k] = marketData? x.qty * marketData.current_price : 0
|
||||
})
|
||||
Object.keys(tokenQty.value).forEach((k) => {
|
||||
const x = tokenQty.value[k];
|
||||
const marketData = prices.value.find((p: any) => p.id === x.coinId);
|
||||
values[k] = marketData ? x.qty * marketData.current_price : 0;
|
||||
});
|
||||
return values;
|
||||
});
|
||||
|
||||
@@ -151,81 +173,105 @@ const tokenList = computed(() => {
|
||||
});
|
||||
|
||||
function loadPrice() {
|
||||
localStorage.setItem('currency', currency.value)
|
||||
const ids = Object.values(tokenQty.value).map(x => x.coinId).join(',')
|
||||
get(`https://api.coingecko.com/api/v3/coins/markets?vs_currency=${currency.value}&ids=${ids}&order=market_cap_desc&per_page=100&page=1&sparkline=true&price_change_percentage=14d&locale=en`)
|
||||
.then(res => {
|
||||
prices.value = res
|
||||
})
|
||||
localStorage.setItem('currency', currency.value);
|
||||
const ids = Object.values(tokenQty.value)
|
||||
.map((x) => x.coinId)
|
||||
.join(',');
|
||||
get(
|
||||
`https://api.coingecko.com/api/v3/coins/markets?vs_currency=${currency.value}&ids=${ids}&order=market_cap_desc&per_page=100&page=1&sparkline=true&price_change_percentage=14d&locale=en`
|
||||
).then((res) => {
|
||||
prices.value = res;
|
||||
});
|
||||
}
|
||||
const totalChangeIn24 = computed(() => {
|
||||
return Object.values(tokenQty.value).map(x => {
|
||||
const marketData = prices.value.find((p: any) => p.id === x.coinId)
|
||||
if (marketData)
|
||||
return x.qty * (marketData.price_change_24h || 0)
|
||||
return 0
|
||||
}).reduce((s, c) => s + c, 0)
|
||||
})
|
||||
return Object.values(tokenQty.value)
|
||||
.map((x) => {
|
||||
const marketData = prices.value.find((p: any) => p.id === x.coinId);
|
||||
if (marketData) return x.qty * (marketData.price_change_24h || 0);
|
||||
return 0;
|
||||
})
|
||||
.reduce((s, c) => s + c, 0);
|
||||
});
|
||||
// Compute data for trend chart
|
||||
const changeData = computed(() => {
|
||||
const vals = Object.keys(tokenQty.value).map(denom => {
|
||||
const token = tokenQty.value[denom]
|
||||
const marketData: any = prices.value.find((x) => x.id === token.coinId)
|
||||
if (marketData) {
|
||||
return marketData.sparkline_in_7d?.price.map((p: number) => p * token.qty) as number[]
|
||||
}
|
||||
return []
|
||||
}).filter(x => x.length > 0)
|
||||
const vals = Object.keys(tokenQty.value)
|
||||
.map((denom) => {
|
||||
const token = tokenQty.value[denom];
|
||||
const marketData: any = prices.value.find((x) => x.id === token.coinId);
|
||||
if (marketData) {
|
||||
return marketData.sparkline_in_7d?.price.map(
|
||||
(p: number) => p * token.qty
|
||||
) as number[];
|
||||
}
|
||||
return [];
|
||||
})
|
||||
.filter((x) => x.length > 0);
|
||||
|
||||
const width = vals.at(0)?.length || 0
|
||||
const sum = new Array(width).fill(0)
|
||||
const width = vals.at(0)?.length || 0;
|
||||
const sum = new Array(width).fill(0);
|
||||
|
||||
for (let i = 0; i < width; i++) {
|
||||
for (let j = 0; j < vals.length; j++) {
|
||||
sum[i] += vals.at(j)?.at(i) || 0
|
||||
sum[i] += vals.at(j)?.at(i) || 0;
|
||||
}
|
||||
}
|
||||
|
||||
return [{ name: "value", data: sum }]
|
||||
})
|
||||
return [{ name: 'value', data: sum }];
|
||||
});
|
||||
|
||||
const baseStore = useBaseStore();
|
||||
const chartConfig = computed(() => {
|
||||
const theme = baseStore.theme;
|
||||
const labels = [] as any[]
|
||||
const time = new Date().getTime()
|
||||
for (let i = 0; i < 168; i++) { // only works for 14d
|
||||
labels.unshift(time - i * 2 * 60 * 60 * 1000)
|
||||
const labels = [] as any[];
|
||||
const time = new Date().getTime();
|
||||
for (let i = 0; i < 168; i++) {
|
||||
// only works for 14d
|
||||
labels.unshift(time - i * 2 * 60 * 60 * 1000);
|
||||
}
|
||||
return getMarketPriceChartConfig(theme, labels);
|
||||
});
|
||||
|
||||
const currencySign = computed(() => {
|
||||
switch(currency.value) {
|
||||
case 'usd': return '$'
|
||||
case 'cny': return '¥'
|
||||
case 'eur': return '€'
|
||||
case 'hkd': return 'HK$'
|
||||
case 'jpy': return '¥'
|
||||
case 'sdg': return 'S$'
|
||||
case 'krw': return '₩'
|
||||
case 'btc': return 'BTC'
|
||||
case 'eth': return 'ETH'
|
||||
switch (currency.value) {
|
||||
case 'usd':
|
||||
return '$';
|
||||
case 'cny':
|
||||
return '¥';
|
||||
case 'eur':
|
||||
return '€';
|
||||
case 'hkd':
|
||||
return 'HK$';
|
||||
case 'jpy':
|
||||
return '¥';
|
||||
case 'sdg':
|
||||
return 'S$';
|
||||
case 'krw':
|
||||
return '₩';
|
||||
case 'btc':
|
||||
return 'BTC';
|
||||
case 'eth':
|
||||
return 'ETH';
|
||||
}
|
||||
return '$'
|
||||
})
|
||||
return '$';
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div class="overflow-x-auto w-full rounded-md">
|
||||
|
||||
<div class="flex flex-wrap justify-between bg-base-100 p-5">
|
||||
<div class="min-w-0">
|
||||
<h2 class="text-2xl font-bold leading-7 sm:!truncate sm:!text-3xl sm:!tracking-tight">
|
||||
<h2
|
||||
class="text-2xl font-bold leading-7 sm:!truncate sm:!text-3xl sm:!tracking-tight"
|
||||
>
|
||||
Portfolio
|
||||
</h2>
|
||||
<div>
|
||||
<div class="flex items-center text-sm">
|
||||
Currency: <select v-model="currency" @change="loadPrice" class="ml-1 uppercase">
|
||||
Currency:
|
||||
<select
|
||||
v-model="currency"
|
||||
@change="loadPrice"
|
||||
class="ml-1 uppercase"
|
||||
>
|
||||
<option>usd</option>
|
||||
<option>cny</option>
|
||||
<option>eur</option>
|
||||
@@ -241,8 +287,16 @@ const currencySign = computed(() => {
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div>Total Value</div>
|
||||
<div class="text-success font-bold">{{ currencySign }} {{ format.formatNumber(totalValue, '0,0.[00]') }}</div>
|
||||
<div class="text-xs" :class="{ 'text-success': totalChangeIn24 > 0, 'text-error': totalChangeIn24 < 0 }">
|
||||
<div class="text-success font-bold">
|
||||
{{ currencySign }} {{ format.formatNumber(totalValue, '0,0.[00]') }}
|
||||
</div>
|
||||
<div
|
||||
class="text-xs"
|
||||
:class="{
|
||||
'text-success': totalChangeIn24 > 0,
|
||||
'text-error': totalChangeIn24 < 0,
|
||||
}"
|
||||
>
|
||||
{{ format.formatNumber(totalChangeIn24, '+0,0.[00]') }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -250,15 +304,33 @@ const currencySign = computed(() => {
|
||||
<div class="bg-base-100">
|
||||
<div v-if="tokenList" class="grid grid-cols-1 md:grid-cols-3">
|
||||
<div>
|
||||
<DonutChart height="280" :series="Object.values(tokenValues)"
|
||||
:labels="Object.keys(tokenValues).map(x => format.tokenDisplayDenom(x)?.toUpperCase())" />
|
||||
<DonutChart
|
||||
height="280"
|
||||
:series="Object.values(tokenValues)"
|
||||
:labels="
|
||||
Object.keys(tokenValues).map((x) =>
|
||||
format.tokenDisplayDenom(x)?.toUpperCase()
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div class="md:col-span-2">
|
||||
<ApexCharts type="area" height="280" :options="chartConfig" :series="changeData" />
|
||||
<ApexCharts
|
||||
type="area"
|
||||
height="280"
|
||||
:options="chartConfig"
|
||||
:series="changeData"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-x-auto mt-4">
|
||||
<AdBanner class="bg-base-200" id="portfolio-banner-ad" unit="banner" width="970px" height="90px" />
|
||||
<AdBanner
|
||||
class="bg-base-200"
|
||||
id="portfolio-banner-ad"
|
||||
unit="banner"
|
||||
width="970px"
|
||||
height="90px"
|
||||
/>
|
||||
<table class="table w-full">
|
||||
<thead class="bg-base-200">
|
||||
<tr>
|
||||
@@ -276,23 +348,27 @@ const currencySign = computed(() => {
|
||||
<img :src="x.logo" :alt="x.chainName" />
|
||||
</div>
|
||||
</div>
|
||||
<span class="uppercase font-bold text-lg">{{ format.tokenDisplayDenom(x.denom) }}</span> @
|
||||
<span class="capitalize ">{{ x.chainName }} </span>
|
||||
<span class="uppercase font-bold text-lg">{{
|
||||
format.tokenDisplayDenom(x.denom)
|
||||
}}</span>
|
||||
@
|
||||
<span class="capitalize">{{ x.chainName }} </span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-right">{{ currencySign }}{{ format.formatNumber(x.value, '0,0.[00]') }}</td>
|
||||
<td class="text-right">
|
||||
{{ currencySign }}{{ format.formatNumber(x.value, '0,0.[00]') }}
|
||||
</td>
|
||||
<td class="text-right">{{ format.percent(x.percentage) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="p-4 text-center" v-if="tokenList.length === 0">
|
||||
No Data
|
||||
</div>
|
||||
<div class="p-4 text-center" v-if="tokenList.length === 0">No Data</div>
|
||||
</div>
|
||||
<div class="text-center my-5 bg-base-200">
|
||||
<RouterLink to="./accounts" class="btn btn-link">Add More Asset</RouterLink>
|
||||
<RouterLink to="./accounts" class="btn btn-link"
|
||||
>Add More Asset</RouterLink
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -9,14 +9,19 @@ const qrcode = useQRCode(walletStore.currentAddress);
|
||||
<template>
|
||||
<div class="bg-base-100 p-4 rounded text-center">
|
||||
<div class="text-xl font-semibold text-center">Pay Me</div>
|
||||
<div v-if="walletStore.currentAddress" class="flex items-center justify-center mt-8 mb-4">
|
||||
<div
|
||||
v-if="walletStore.currentAddress"
|
||||
class="flex items-center justify-center mt-8 mb-4"
|
||||
>
|
||||
<img :src="qrcode" alt="QR Code" class="rounded-sm overflow-hidden" />
|
||||
</div>
|
||||
<div class="text-main">
|
||||
{{ walletStore.currentAddress }}
|
||||
</div>
|
||||
<div class="mt-4 mb-4">
|
||||
<button class="btn !bg-yes !border-yes text-white px-10">Go To Pay</button>
|
||||
<button class="btn !bg-yes !border-yes text-white px-10">
|
||||
Go To Pay
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
+198
-139
@@ -1,175 +1,234 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
import { suggestChain } from '@leapwallet/cosmos-snap-provider';
|
||||
import { useDashboard, type ChainConfig, useBlockchain, NetworkType } from '@/stores';
|
||||
import {
|
||||
useDashboard,
|
||||
type ChainConfig,
|
||||
useBlockchain,
|
||||
NetworkType,
|
||||
} from '@/stores';
|
||||
import { CosmosRestClient } from '@/libs/client';
|
||||
import { onMounted } from 'vue';
|
||||
import AdBanner from '@/components/ad/AdBanner.vue';
|
||||
|
||||
const error = ref("")
|
||||
const conf = ref("")
|
||||
const dashboard = useDashboard()
|
||||
const selected = ref({} as ChainConfig)
|
||||
const wallet = ref("keplr")
|
||||
const network = ref(NetworkType.Mainnet)
|
||||
const mainnet = ref([] as ChainConfig[])
|
||||
const testnet = ref([] as ChainConfig[])
|
||||
const error = ref('');
|
||||
const conf = ref('');
|
||||
const dashboard = useDashboard();
|
||||
const selected = ref({} as ChainConfig);
|
||||
const wallet = ref('keplr');
|
||||
const network = ref(NetworkType.Mainnet);
|
||||
const mainnet = ref([] as ChainConfig[]);
|
||||
const testnet = ref([] as ChainConfig[]);
|
||||
const chains = computed(() => {
|
||||
return network.value === NetworkType.Mainnet? mainnet.value : testnet.value
|
||||
})
|
||||
return network.value === NetworkType.Mainnet ? mainnet.value : testnet.value;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
const chainStore = useBlockchain()
|
||||
selected.value = chainStore.current || Object.values(dashboard.chains)[0]
|
||||
initParamsForKeplr()
|
||||
const chainStore = useBlockchain();
|
||||
selected.value = chainStore.current || Object.values(dashboard.chains)[0];
|
||||
initParamsForKeplr();
|
||||
|
||||
dashboard.loadLocalConfig(NetworkType.Mainnet).then((res) => {
|
||||
mainnet.value = Object.values<ChainConfig>(res)
|
||||
})
|
||||
dashboard.loadLocalConfig(NetworkType.Testnet).then((res) => {
|
||||
testnet.value = Object.values<ChainConfig>(res)
|
||||
})
|
||||
})
|
||||
dashboard.loadLocalConfig(NetworkType.Mainnet).then((res) => {
|
||||
mainnet.value = Object.values<ChainConfig>(res);
|
||||
});
|
||||
dashboard.loadLocalConfig(NetworkType.Testnet).then((res) => {
|
||||
testnet.value = Object.values<ChainConfig>(res);
|
||||
});
|
||||
});
|
||||
|
||||
function onchange() {
|
||||
wallet.value === "keplr" ? initParamsForKeplr() : initSnap()
|
||||
wallet.value === 'keplr' ? initParamsForKeplr() : initSnap();
|
||||
}
|
||||
|
||||
async function initParamsForKeplr() {
|
||||
const chain = selected.value
|
||||
if(!chain.endpoints?.rest?.at(0)) throw new Error("Endpoint does not set");
|
||||
const client = CosmosRestClient.newDefault(chain.endpoints.rest?.at(0)?.address || "")
|
||||
const b = await client.getBaseBlockLatest()
|
||||
const chainid = b.block.header.chain_id
|
||||
const chain = selected.value;
|
||||
if (!chain.endpoints?.rest?.at(0)) throw new Error('Endpoint does not set');
|
||||
const client = CosmosRestClient.newDefault(
|
||||
chain.endpoints.rest?.at(0)?.address || ''
|
||||
);
|
||||
const b = await client.getBaseBlockLatest();
|
||||
const chainid = b.block.header.chain_id;
|
||||
|
||||
const gasPriceStep = chain.keplrPriceStep || {
|
||||
low: 0.01,
|
||||
average: 0.025,
|
||||
high: 0.03,
|
||||
}
|
||||
const coinDecimals = chain.assets[0].denom_units.find(x => x.denom === chain.assets[0].symbol.toLowerCase())?.exponent || 6
|
||||
conf.value = JSON.stringify({
|
||||
chainId: chainid,
|
||||
chainName: chain.chainName,
|
||||
rpc: chain.endpoints?.rpc?.at(0)?.address,
|
||||
rest: chain.endpoints?.rest?.at(0)?.address,
|
||||
bip44: {
|
||||
coinType: Number(chain.coinType),
|
||||
},
|
||||
const gasPriceStep = chain.keplrPriceStep || {
|
||||
low: 0.01,
|
||||
average: 0.025,
|
||||
high: 0.03,
|
||||
};
|
||||
const coinDecimals =
|
||||
chain.assets[0].denom_units.find(
|
||||
(x) => x.denom === chain.assets[0].symbol.toLowerCase()
|
||||
)?.exponent || 6;
|
||||
conf.value = JSON.stringify(
|
||||
{
|
||||
chainId: chainid,
|
||||
chainName: chain.chainName,
|
||||
rpc: chain.endpoints?.rpc?.at(0)?.address,
|
||||
rest: chain.endpoints?.rest?.at(0)?.address,
|
||||
bip44: {
|
||||
coinType: Number(chain.coinType),
|
||||
bech32Config: {
|
||||
bech32PrefixAccAddr: chain.bech32Prefix,
|
||||
bech32PrefixAccPub: `${chain.bech32Prefix}pub`,
|
||||
bech32PrefixValAddr: `${chain.bech32Prefix}valoper`,
|
||||
bech32PrefixValPub: `${chain.bech32Prefix}valoperpub`,
|
||||
bech32PrefixConsAddr: `${chain.bech32Prefix}valcons`,
|
||||
bech32PrefixConsPub: `${chain.bech32Prefix}valconspub`,
|
||||
},
|
||||
coinType: Number(chain.coinType),
|
||||
bech32Config: {
|
||||
bech32PrefixAccAddr: chain.bech32Prefix,
|
||||
bech32PrefixAccPub: `${chain.bech32Prefix}pub`,
|
||||
bech32PrefixValAddr: `${chain.bech32Prefix}valoper`,
|
||||
bech32PrefixValPub: `${chain.bech32Prefix}valoperpub`,
|
||||
bech32PrefixConsAddr: `${chain.bech32Prefix}valcons`,
|
||||
bech32PrefixConsPub: `${chain.bech32Prefix}valconspub`,
|
||||
},
|
||||
currencies: [
|
||||
{
|
||||
coinDenom: chain.assets[0].symbol,
|
||||
coinMinimalDenom: chain.assets[0].base,
|
||||
coinDecimals,
|
||||
coinGeckoId: chain.assets[0].coingecko_id || 'unknown',
|
||||
},
|
||||
currencies: [
|
||||
{
|
||||
coinDenom: chain.assets[0].symbol,
|
||||
coinMinimalDenom: chain.assets[0].base,
|
||||
coinDecimals,
|
||||
coinGeckoId: chain.assets[0].coingecko_id || 'unknown',
|
||||
},
|
||||
],
|
||||
feeCurrencies: [
|
||||
{
|
||||
coinDenom: chain.assets[0].symbol,
|
||||
coinMinimalDenom: chain.assets[0].base,
|
||||
coinDecimals,
|
||||
coinGeckoId: chain.assets[0].coingecko_id || 'unknown',
|
||||
gasPriceStep,
|
||||
},
|
||||
],
|
||||
gasPriceStep,
|
||||
stakeCurrency: {
|
||||
coinDenom: chain.assets[0].symbol,
|
||||
coinMinimalDenom: chain.assets[0].base,
|
||||
coinDecimals,
|
||||
coinGeckoId: chain.assets[0].coingecko_id || 'unknown',
|
||||
],
|
||||
feeCurrencies: [
|
||||
{
|
||||
coinDenom: chain.assets[0].symbol,
|
||||
coinMinimalDenom: chain.assets[0].base,
|
||||
coinDecimals,
|
||||
coinGeckoId: chain.assets[0].coingecko_id || 'unknown',
|
||||
gasPriceStep,
|
||||
},
|
||||
features: chain.keplrFeatures || [],
|
||||
}, null, '\t')
|
||||
],
|
||||
gasPriceStep,
|
||||
stakeCurrency: {
|
||||
coinDenom: chain.assets[0].symbol,
|
||||
coinMinimalDenom: chain.assets[0].base,
|
||||
coinDecimals,
|
||||
coinGeckoId: chain.assets[0].coingecko_id || 'unknown',
|
||||
},
|
||||
features: chain.keplrFeatures || [],
|
||||
},
|
||||
null,
|
||||
'\t'
|
||||
);
|
||||
}
|
||||
|
||||
async function initSnap() {
|
||||
const chain = selected.value
|
||||
const [token] = chain.assets
|
||||
const chain = selected.value;
|
||||
const [token] = chain.assets;
|
||||
|
||||
if(!chain.endpoints?.rest?.at(0)) throw new Error("Endpoint does not set");
|
||||
const client = CosmosRestClient.newDefault(chain.endpoints.rest?.at(0)?.address || "")
|
||||
const b = await client.getBaseBlockLatest()
|
||||
const chainId = b.block.header.chain_id
|
||||
if (!chain.endpoints?.rest?.at(0)) throw new Error('Endpoint does not set');
|
||||
const client = CosmosRestClient.newDefault(
|
||||
chain.endpoints.rest?.at(0)?.address || ''
|
||||
);
|
||||
const b = await client.getBaseBlockLatest();
|
||||
const chainId = b.block.header.chain_id;
|
||||
|
||||
conf.value = JSON.stringify({
|
||||
chainId,
|
||||
chainName: chain.chainName,
|
||||
bech32Config: {
|
||||
bech32PrefixAccAddr: chain.bech32Prefix,
|
||||
conf.value = JSON.stringify(
|
||||
{
|
||||
chainId,
|
||||
chainName: chain.chainName,
|
||||
bech32Config: {
|
||||
bech32PrefixAccAddr: chain.bech32Prefix,
|
||||
},
|
||||
bip44: {
|
||||
coinType: Number(chain.coinType),
|
||||
},
|
||||
feeCurrencies: [
|
||||
{
|
||||
coinDenom: token.display,
|
||||
coinMinimalDenom: token.base,
|
||||
coinDecimals:
|
||||
token.denom_units.find((x) => x.denom === token.display)
|
||||
?.exponent || 6,
|
||||
coinGeckoId: token.coingecko_id,
|
||||
gasPriceStep: {
|
||||
low: 0.0625,
|
||||
average: 0.5,
|
||||
high: 62.5,
|
||||
},
|
||||
},
|
||||
bip44: {
|
||||
coinType: Number(chain.coinType),
|
||||
},
|
||||
feeCurrencies: [
|
||||
{
|
||||
coinDenom: token.display,
|
||||
coinMinimalDenom: token.base,
|
||||
coinDecimals: token.denom_units.find(x => x.denom === token.display)?.exponent || 6,
|
||||
coinGeckoId: token.coingecko_id,
|
||||
gasPriceStep: {
|
||||
low: 0.0625,
|
||||
average: 0.5,
|
||||
high: 62.5,
|
||||
},
|
||||
},
|
||||
],
|
||||
}, null, '\t')
|
||||
],
|
||||
},
|
||||
null,
|
||||
'\t'
|
||||
);
|
||||
}
|
||||
|
||||
function suggest() {
|
||||
if(wallet.value === "keplr") {
|
||||
// @ts-ignore
|
||||
if (window.keplr) {
|
||||
// @ts-ignore
|
||||
window.keplr.experimentalSuggestChain(JSON.parse(conf.value)).catch(e => {
|
||||
error.value = e
|
||||
})
|
||||
}
|
||||
} else {
|
||||
suggestChain(JSON.parse(conf.value));
|
||||
if (wallet.value === 'keplr') {
|
||||
// @ts-ignore
|
||||
if (window.keplr) {
|
||||
// @ts-ignore
|
||||
window.keplr
|
||||
.experimentalSuggestChain(JSON.parse(conf.value))
|
||||
.catch((e) => {
|
||||
error.value = e;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
suggestChain(JSON.parse(conf.value));
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-base-100 p-4 rounded text-center">
|
||||
<div class="flex text-center">
|
||||
<select v-model="network" class="select select-bordered">
|
||||
<option :value="NetworkType.Mainnet">Mainnet</option>
|
||||
<option :value="NetworkType.Testnet">Testnet</option>
|
||||
</select>
|
||||
<select v-model="selected" class="select select-bordered mx-5" @change="onchange">
|
||||
<option v-for="c in chains" :value="c">
|
||||
{{ c.chainName }}
|
||||
</option>
|
||||
</select>
|
||||
<label><input type="radio" v-model="wallet" value="keplr" class="radio radio-bordered" @change="onchange" /> Keplr</label>
|
||||
<label><input type="radio" v-model="wallet" value="metamask" class="radio radio-bordered ml-4" @change="onchange"/> Metamask</label>
|
||||
</div>
|
||||
<div class="text-main mt-5">
|
||||
<textarea v-model="conf" class="textarea textarea-bordered w-full" rows="15"></textarea>
|
||||
</div>
|
||||
<div class="mt-4 mb-4">
|
||||
|
||||
<button class="btn !bg-primary !border-primary text-white mr-2" @click="suggest">Suggest {{ selected.chainName }} TO {{ wallet }}</button>
|
||||
|
||||
<div class="mt-4">
|
||||
If the chain is not offically support on Keplr/Metamask Snap, you can submit these parameters to enable Keplr/Metamask Snap.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AdBanner id="suggest-banner-ad" unit="banner" width="970px" height="90px" />
|
||||
<div class="bg-base-100 p-4 rounded text-center">
|
||||
<div class="flex text-center">
|
||||
<select v-model="network" class="select select-bordered">
|
||||
<option :value="NetworkType.Mainnet">Mainnet</option>
|
||||
<option :value="NetworkType.Testnet">Testnet</option>
|
||||
</select>
|
||||
<select
|
||||
v-model="selected"
|
||||
class="select select-bordered mx-5"
|
||||
@change="onchange"
|
||||
>
|
||||
<option v-for="c in chains" :value="c">
|
||||
{{ c.chainName }}
|
||||
</option>
|
||||
</select>
|
||||
<label
|
||||
><input
|
||||
type="radio"
|
||||
v-model="wallet"
|
||||
value="keplr"
|
||||
class="radio radio-bordered"
|
||||
@change="onchange"
|
||||
/>
|
||||
Keplr</label
|
||||
>
|
||||
<label
|
||||
><input
|
||||
type="radio"
|
||||
v-model="wallet"
|
||||
value="metamask"
|
||||
class="radio radio-bordered ml-4"
|
||||
@change="onchange"
|
||||
/>
|
||||
Metamask</label
|
||||
>
|
||||
</div>
|
||||
<div class="text-main mt-5">
|
||||
<textarea
|
||||
v-model="conf"
|
||||
class="textarea textarea-bordered w-full"
|
||||
rows="15"
|
||||
></textarea>
|
||||
</div>
|
||||
<div class="mt-4 mb-4">
|
||||
<button
|
||||
class="btn !bg-primary !border-primary text-white mr-2"
|
||||
@click="suggest"
|
||||
>
|
||||
Suggest {{ selected.chainName }} TO {{ wallet }}
|
||||
</button>
|
||||
|
||||
<div class="mt-4">
|
||||
If the chain is not offically support on Keplr/Metamask Snap, you can
|
||||
submit these parameters to enable Keplr/Metamask Snap.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AdBanner
|
||||
id="suggest-banner-ad"
|
||||
unit="banner"
|
||||
width="970px"
|
||||
height="90px"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
+102
-80
@@ -5,100 +5,122 @@ import { CosmosRestClient } from '@/libs/client';
|
||||
import { onMounted } from 'vue';
|
||||
import AdBanner from '@/components/ad/AdBanner.vue';
|
||||
|
||||
const error = ref("")
|
||||
const conf = ref("")
|
||||
const dashboard = useDashboard()
|
||||
const selected = ref({} as ChainConfig)
|
||||
const error = ref('');
|
||||
const conf = ref('');
|
||||
const dashboard = useDashboard();
|
||||
const selected = ref({} as ChainConfig);
|
||||
|
||||
onMounted(() => {
|
||||
const chainStore = useBlockchain()
|
||||
selected.value = chainStore.current || Object.values(dashboard.chains)[0];
|
||||
})
|
||||
const chainStore = useBlockchain();
|
||||
selected.value = chainStore.current || Object.values(dashboard.chains)[0];
|
||||
});
|
||||
async function initParamsForKeplr() {
|
||||
const chain = selected.value
|
||||
if(!chain.endpoints?.rest?.at(0)) throw new Error("Endpoint does not set");
|
||||
const client = CosmosRestClient.newDefault(chain.endpoints.rest?.at(0)?.address || "")
|
||||
const b = await client.getBaseBlockLatest()
|
||||
const chainid = b.block.header.chain_id
|
||||
const chain = selected.value;
|
||||
if (!chain.endpoints?.rest?.at(0)) throw new Error('Endpoint does not set');
|
||||
const client = CosmosRestClient.newDefault(
|
||||
chain.endpoints.rest?.at(0)?.address || ''
|
||||
);
|
||||
const b = await client.getBaseBlockLatest();
|
||||
const chainid = b.block.header.chain_id;
|
||||
|
||||
const gasPriceStep = chain.keplrPriceStep || {
|
||||
low: 0.01,
|
||||
average: 0.025,
|
||||
high: 0.03,
|
||||
}
|
||||
const coinDecimals = chain.assets[0].denom_units.find(x => x.denom === chain.assets[0].symbol.toLowerCase())?.exponent || 6
|
||||
conf.value = JSON.stringify({
|
||||
chainId: chainid,
|
||||
chainName: chain.chainName,
|
||||
rpc: chain.endpoints?.rpc?.at(0)?.address,
|
||||
rest: chain.endpoints?.rest?.at(0)?.address,
|
||||
bip44: {
|
||||
coinType: Number(chain.coinType),
|
||||
},
|
||||
const gasPriceStep = chain.keplrPriceStep || {
|
||||
low: 0.01,
|
||||
average: 0.025,
|
||||
high: 0.03,
|
||||
};
|
||||
const coinDecimals =
|
||||
chain.assets[0].denom_units.find(
|
||||
(x) => x.denom === chain.assets[0].symbol.toLowerCase()
|
||||
)?.exponent || 6;
|
||||
conf.value = JSON.stringify(
|
||||
{
|
||||
chainId: chainid,
|
||||
chainName: chain.chainName,
|
||||
rpc: chain.endpoints?.rpc?.at(0)?.address,
|
||||
rest: chain.endpoints?.rest?.at(0)?.address,
|
||||
bip44: {
|
||||
coinType: Number(chain.coinType),
|
||||
bech32Config: {
|
||||
bech32PrefixAccAddr: chain.bech32Prefix,
|
||||
bech32PrefixAccPub: `${chain.bech32Prefix}pub`,
|
||||
bech32PrefixValAddr: `${chain.bech32Prefix}valoper`,
|
||||
bech32PrefixValPub: `${chain.bech32Prefix}valoperpub`,
|
||||
bech32PrefixConsAddr: `${chain.bech32Prefix}valcons`,
|
||||
bech32PrefixConsPub: `${chain.bech32Prefix}valconspub`,
|
||||
},
|
||||
coinType: Number(chain.coinType),
|
||||
bech32Config: {
|
||||
bech32PrefixAccAddr: chain.bech32Prefix,
|
||||
bech32PrefixAccPub: `${chain.bech32Prefix}pub`,
|
||||
bech32PrefixValAddr: `${chain.bech32Prefix}valoper`,
|
||||
bech32PrefixValPub: `${chain.bech32Prefix}valoperpub`,
|
||||
bech32PrefixConsAddr: `${chain.bech32Prefix}valcons`,
|
||||
bech32PrefixConsPub: `${chain.bech32Prefix}valconspub`,
|
||||
},
|
||||
currencies: [
|
||||
{
|
||||
coinDenom: chain.assets[0].symbol,
|
||||
coinMinimalDenom: chain.assets[0].base,
|
||||
coinDecimals,
|
||||
coinGeckoId: chain.assets[0].coingecko_id || 'unknown',
|
||||
},
|
||||
currencies: [
|
||||
{
|
||||
coinDenom: chain.assets[0].symbol,
|
||||
coinMinimalDenom: chain.assets[0].base,
|
||||
coinDecimals,
|
||||
coinGeckoId: chain.assets[0].coingecko_id || 'unknown',
|
||||
},
|
||||
],
|
||||
feeCurrencies: [
|
||||
{
|
||||
coinDenom: chain.assets[0].symbol,
|
||||
coinMinimalDenom: chain.assets[0].base,
|
||||
coinDecimals,
|
||||
coinGeckoId: chain.assets[0].coingecko_id || 'unknown',
|
||||
gasPriceStep,
|
||||
},
|
||||
],
|
||||
gasPriceStep,
|
||||
stakeCurrency: {
|
||||
coinDenom: chain.assets[0].symbol,
|
||||
coinMinimalDenom: chain.assets[0].base,
|
||||
coinDecimals,
|
||||
coinGeckoId: chain.assets[0].coingecko_id || 'unknown',
|
||||
],
|
||||
feeCurrencies: [
|
||||
{
|
||||
coinDenom: chain.assets[0].symbol,
|
||||
coinMinimalDenom: chain.assets[0].base,
|
||||
coinDecimals,
|
||||
coinGeckoId: chain.assets[0].coingecko_id || 'unknown',
|
||||
gasPriceStep,
|
||||
},
|
||||
features: chain.keplrFeatures || [],
|
||||
}, null, '\t')
|
||||
],
|
||||
gasPriceStep,
|
||||
stakeCurrency: {
|
||||
coinDenom: chain.assets[0].symbol,
|
||||
coinMinimalDenom: chain.assets[0].base,
|
||||
coinDecimals,
|
||||
coinGeckoId: chain.assets[0].coingecko_id || 'unknown',
|
||||
},
|
||||
features: chain.keplrFeatures || [],
|
||||
},
|
||||
null,
|
||||
'\t'
|
||||
);
|
||||
}
|
||||
|
||||
function suggest() {
|
||||
// @ts-ignore
|
||||
if (window.unisat) {
|
||||
// @ts-ignore
|
||||
if (window.unisat) {
|
||||
// @ts-ignore
|
||||
window.unisat.experimentalSuggestChain(JSON.parse(conf.value)).catch(e => {
|
||||
error.value = e
|
||||
})
|
||||
}
|
||||
window.unisat
|
||||
.experimentalSuggestChain(JSON.parse(conf.value))
|
||||
.catch((e) => {
|
||||
error.value = e;
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-base-100 p-4 rounded text-center">
|
||||
<AdBanner id="keplr-banner-ad" unit="banner" width="970px" height="90px" />
|
||||
<div class="flex">
|
||||
<select v-model="selected" class="select select-bordered mx-5" @change="initParamsForKeplr">
|
||||
<option v-for="c in dashboard.chains" :value="c">
|
||||
{{ c.chainName }}
|
||||
</option>
|
||||
</select>
|
||||
<button class="btn !bg-yes !border-yes text-white px-10" @click="suggest">Add {{ selected.chainName }} TO Unisat Wallet</button>
|
||||
</div>
|
||||
<div class="text-main mt-5">
|
||||
<textarea v-model="conf" class="textarea textarea-bordered w-full" rows="15"></textarea>
|
||||
</div>
|
||||
<div class="mt-4 mb-4">
|
||||
If the chain is not offically support on Keplr, you can submit these parameters to enable Keplr.
|
||||
</div>
|
||||
<div class="bg-base-100 p-4 rounded text-center">
|
||||
<AdBanner id="keplr-banner-ad" unit="banner" width="970px" height="90px" />
|
||||
<div class="flex">
|
||||
<select
|
||||
v-model="selected"
|
||||
class="select select-bordered mx-5"
|
||||
@change="initParamsForKeplr"
|
||||
>
|
||||
<option v-for="c in dashboard.chains" :value="c">
|
||||
{{ c.chainName }}
|
||||
</option>
|
||||
</select>
|
||||
<button class="btn !bg-yes !border-yes text-white px-10" @click="suggest">
|
||||
Add {{ selected.chainName }} TO Unisat Wallet
|
||||
</button>
|
||||
</div>
|
||||
<div class="text-main mt-5">
|
||||
<textarea
|
||||
v-model="conf"
|
||||
class="textarea textarea-bordered w-full"
|
||||
rows="15"
|
||||
></textarea>
|
||||
</div>
|
||||
<div class="mt-4 mb-4">
|
||||
If the chain is not offically support on Keplr, you can submit these
|
||||
parameters to enable Keplr.
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
+28
-27
@@ -1,49 +1,50 @@
|
||||
import { useDashboard } from "@/stores"
|
||||
import type { Coin } from "@/types"
|
||||
import { fromBech32, toBech32 } from "@cosmjs/encoding"
|
||||
import { useDashboard } from '@/stores';
|
||||
import type { Coin } from '@/types';
|
||||
import { fromBech32, toBech32 } from '@cosmjs/encoding';
|
||||
|
||||
export interface AccountEntry {
|
||||
chainName: string,
|
||||
logo: string,
|
||||
address: string,
|
||||
coinType: string,
|
||||
endpoint?: string,
|
||||
compatiable?: boolean,
|
||||
chainName: string;
|
||||
logo: string;
|
||||
address: string;
|
||||
coinType: string;
|
||||
endpoint?: string;
|
||||
compatiable?: boolean;
|
||||
}
|
||||
|
||||
export interface LocalKey {
|
||||
cosmosAddress: string, hdPath: string
|
||||
cosmosAddress: string;
|
||||
hdPath: string;
|
||||
}
|
||||
|
||||
export function scanLocalKeys() {
|
||||
const connected = [] as LocalKey[]
|
||||
const connected = [] as LocalKey[];
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
const key = localStorage.key(i)
|
||||
if (key?.startsWith("m/44")) {
|
||||
const wallet = JSON.parse(localStorage.getItem(key) || "")
|
||||
const key = localStorage.key(i);
|
||||
if (key?.startsWith('m/44')) {
|
||||
const wallet = JSON.parse(localStorage.getItem(key) || '');
|
||||
if (wallet) {
|
||||
connected.push(wallet)
|
||||
connected.push(wallet);
|
||||
}
|
||||
}
|
||||
}
|
||||
return connected
|
||||
return connected;
|
||||
}
|
||||
|
||||
export function scanCompatibleAccounts(keys: LocalKey[]) {
|
||||
const dashboard = useDashboard()
|
||||
const available = [] as AccountEntry[]
|
||||
keys.forEach(wallet => {
|
||||
Object.values(dashboard.chains).forEach(chain => {
|
||||
const { data } = fromBech32(wallet.cosmosAddress)
|
||||
const dashboard = useDashboard();
|
||||
const available = [] as AccountEntry[];
|
||||
keys.forEach((wallet) => {
|
||||
Object.values(dashboard.chains).forEach((chain) => {
|
||||
const { data } = fromBech32(wallet.cosmosAddress);
|
||||
available.push({
|
||||
chainName: chain.chainName,
|
||||
logo: chain.logo,
|
||||
address: toBech32(chain.bech32Prefix, data),
|
||||
coinType: chain.coinType,
|
||||
compatiable: wallet.hdPath.indexOf(chain.coinType) > 0,
|
||||
endpoint: chain.endpoints.rest?.at(0)?.address
|
||||
})
|
||||
})
|
||||
})
|
||||
return available
|
||||
}
|
||||
endpoint: chain.endpoints.rest?.at(0)?.address,
|
||||
});
|
||||
});
|
||||
});
|
||||
return available;
|
||||
}
|
||||
|
||||
+18
-16
@@ -3,23 +3,25 @@ import misc404 from '@/assets/images/pages/404.png';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="pt-10">
|
||||
<div class="text-center">
|
||||
<div class="text-8xl font-semibold text-main">{{ $t('pages.title_all') }}</div>
|
||||
<div class="text-xl font-bold my-2">{{ $t('pages.tag_all') }}</div>
|
||||
<div class="text-base">
|
||||
{{ $t('pages.description_all') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 👉 Image -->
|
||||
<div class="misc-avatar w-full text-center">
|
||||
<RouterLink to="/" class="btn no-underline btn-primary mt-4">
|
||||
{{ $t('pages.btn_index') }}
|
||||
</RouterLink>
|
||||
<img :src="misc404" alt="Coming Soon" class="mx-auto h-[400px] mt-10" />
|
||||
</div>
|
||||
<div class="pt-10">
|
||||
<div class="text-center">
|
||||
<div class="text-8xl font-semibold text-main">
|
||||
{{ $t('pages.title_all') }}
|
||||
</div>
|
||||
<div class="text-xl font-bold my-2">{{ $t('pages.tag_all') }}</div>
|
||||
<div class="text-base">
|
||||
{{ $t('pages.description_all') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 👉 Image -->
|
||||
<div class="misc-avatar w-full text-center">
|
||||
<RouterLink to="/" class="btn no-underline btn-primary mt-4">
|
||||
{{ $t('pages.btn_index') }}
|
||||
</RouterLink>
|
||||
<img :src="misc404" alt="Coming Soon" class="mx-auto h-[400px] mt-10" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<route lang="yaml">
|
||||
|
||||
+70
-31
@@ -19,8 +19,9 @@ const chains = computed(() => {
|
||||
const lowercaseKeywords = keywords.value.toLowerCase();
|
||||
|
||||
return Object.values(dashboard.chains).filter(
|
||||
(x: ChainConfig) => x.chainName.toLowerCase().indexOf(lowercaseKeywords) > -1
|
||||
|| x.prettyName.toLowerCase().indexOf(lowercaseKeywords) > -1
|
||||
(x: ChainConfig) =>
|
||||
x.chainName.toLowerCase().indexOf(lowercaseKeywords) > -1 ||
|
||||
x.prettyName.toLowerCase().indexOf(lowercaseKeywords) > -1
|
||||
);
|
||||
} else {
|
||||
return Object.values(dashboard.chains);
|
||||
@@ -28,36 +29,61 @@ const chains = computed(() => {
|
||||
});
|
||||
|
||||
const featured = computed(() => {
|
||||
const names = ["cosmos", "osmosis", "akash", "celestia", "evmos", "injective", "dydx", "noble"];
|
||||
const names = [
|
||||
'cosmos',
|
||||
'osmosis',
|
||||
'akash',
|
||||
'celestia',
|
||||
'evmos',
|
||||
'injective',
|
||||
'dydx',
|
||||
'noble',
|
||||
];
|
||||
return chains.value
|
||||
.filter(x => names.includes(x.chainName))
|
||||
.sort((a, b)=> (names.indexOf(a.chainName) - names.indexOf(b.chainName)))
|
||||
})
|
||||
|
||||
const chainStore = useBlockchain()
|
||||
.filter((x) => names.includes(x.chainName))
|
||||
.sort((a, b) => names.indexOf(a.chainName) - names.indexOf(b.chainName));
|
||||
});
|
||||
|
||||
const chainStore = useBlockchain();
|
||||
</script>
|
||||
<template>
|
||||
<div class="">
|
||||
<div class="flex md:!flex-row flex-col items-center justify-center mb-6 mt-14 gap-2">
|
||||
<div
|
||||
class="flex md:!flex-row flex-col items-center justify-center mb-6 mt-14 gap-2"
|
||||
>
|
||||
<div class="w-16 rounded-full">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
<svg
|
||||
version="1.0"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 150.000000 132.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<g transform="translate(0.000000,132.000000) scale(0.100000,-0.100000)"
|
||||
:fill="chainStore.current?.themeColor||'#666CFF'" class=" dark:invert" stroke="none">
|
||||
<path d="M500 1310 l-125 -5 -182 -315 c-100 -173 -182 -321 -182 -329 -1 -7
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
>
|
||||
<g
|
||||
transform="translate(0.000000,132.000000) scale(0.100000,-0.100000)"
|
||||
:fill="chainStore.current?.themeColor || '#666CFF'"
|
||||
class="dark:invert"
|
||||
stroke="none"
|
||||
>
|
||||
<path
|
||||
d="M500 1310 l-125 -5 -182 -315 c-100 -173 -182 -321 -182 -329 -1 -7
|
||||
81 -159 181 -337 l183 -324 372 0 371 0 186 325 c102 179 186 330 186 337 0 7
|
||||
-82 157 -182 335 l-183 323 -250 -2 c-137 -1 -306 -5 -375 -8z m588 -454 c61
|
||||
-106 112 -197 112 -201 0 -4 -50 -95 -111 -201 l-112 -194 -231 0 -231 0 -105
|
||||
181 c-58 100 -109 190 -114 200 -6 14 17 63 104 213 l112 196 232 0 231 0 113
|
||||
-194z"/>
|
||||
<path d="M591 1001 l-54 -6 -87 -150 -88 -150 176 -3 c97 -1 181 -1 187 2 9 3
|
||||
165 267 183 308 4 9 -233 7 -317 -1z"/>
|
||||
<path d="M872 824 l-90 -159 36 -66 c113 -201 147 -258 153 -251 8 8 179 302
|
||||
179 307 0 2 -37 68 -83 147 -46 78 -88 151 -94 162 -9 16 -24 -5 -101 -140z"/>
|
||||
<path d="M360 625 c0 -7 148 -263 172 -297 l19 -28 186 0 c101 0 183 3 181 8
|
||||
-1 4 -43 78 -93 165 l-90 157 -187 0 c-104 0 -188 -2 -188 -5z"/>
|
||||
-194z"
|
||||
/>
|
||||
<path
|
||||
d="M591 1001 l-54 -6 -87 -150 -88 -150 176 -3 c97 -1 181 -1 187 2 9 3
|
||||
165 267 183 308 4 9 -233 7 -317 -1z"
|
||||
/>
|
||||
<path
|
||||
d="M872 824 l-90 -159 36 -66 c113 -201 147 -258 153 -251 8 8 179 302
|
||||
179 307 0 2 -37 68 -83 147 -46 78 -88 151 -94 162 -9 16 -24 -5 -101 -140z"
|
||||
/>
|
||||
<path
|
||||
d="M360 625 c0 -7 148 -263 172 -297 l19 -28 186 0 c101 0 183 3 181 8
|
||||
-1 4 -43 78 -93 165 l-90 157 -187 0 c-104 0 -188 -2 -188 -5z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
@@ -77,14 +103,18 @@ const chainStore = useBlockchain()
|
||||
<progress class="progress progress-info w-80 h-1"></progress>
|
||||
</div>
|
||||
|
||||
<div v-if="featured.length>0" class="text-center text-base mt-6 text-primary">
|
||||
<h2 class="mb-6"> Featured Blockchains 🔥 </h2>
|
||||
<div
|
||||
v-if="featured.length > 0"
|
||||
class="text-center text-base mt-6 text-primary"
|
||||
>
|
||||
<h2 class="mb-6">Featured Blockchains 🔥</h2>
|
||||
</div>
|
||||
|
||||
<div v-if="featured.length>0"
|
||||
<div
|
||||
v-if="featured.length > 0"
|
||||
class="grid grid-cols-1 gap-4 mt-6 md:!grid-cols-3 lg:!grid-cols-4 2xl:!grid-cols-5"
|
||||
>
|
||||
<ChainSummary
|
||||
<ChainSummary
|
||||
v-for="(chain, index) in featured"
|
||||
:key="index"
|
||||
:name="chain.chainName"
|
||||
@@ -95,10 +125,18 @@ const chainStore = useBlockchain()
|
||||
<h2 class="mb-6">{{ $t('pages.description') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center rounded-lg bg-base-100 border border-gray-200 dark:border-gray-700 mt-10">
|
||||
<Icon icon="mdi:magnify" class="text-2xl text-gray-400 ml-3"/>
|
||||
<input :placeholder="$t('pages.search_placeholder')" class="px-4 h-10 bg-transparent flex-1 outline-none text-base" v-model="keywords" />
|
||||
<div class="px-4 text-base hidden md:!block">{{ chains.length }}/{{ dashboard.length }}</div>
|
||||
<div
|
||||
class="flex items-center rounded-lg bg-base-100 border border-gray-200 dark:border-gray-700 mt-10"
|
||||
>
|
||||
<Icon icon="mdi:magnify" class="text-2xl text-gray-400 ml-3" />
|
||||
<input
|
||||
:placeholder="$t('pages.search_placeholder')"
|
||||
class="px-4 h-10 bg-transparent flex-1 outline-none text-base"
|
||||
v-model="keywords"
|
||||
/>
|
||||
<div class="px-4 text-base hidden md:!block">
|
||||
{{ chains.length }}/{{ dashboard.length }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
@@ -114,7 +152,8 @@ const chainStore = useBlockchain()
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.logo path{
|
||||
.logo path {
|
||||
fill: #171d30;
|
||||
}
|
||||
</style>@/components/ad/ad
|
||||
</style>
|
||||
@/components/ad/ad
|
||||
|
||||
@@ -130,8 +130,8 @@
|
||||
"no_escrowed_assets": "No Escrowed Assets",
|
||||
"contract_states": "Contract States",
|
||||
"query_contract": "Query Contract",
|
||||
"suggested_messages":"Suggested Messages",
|
||||
"result":"Result",
|
||||
"suggested_messages": "Suggested Messages",
|
||||
"result": "Result",
|
||||
"tips_description_1": "This feature is available when deploying contracts via WELLDONE Code. For more information, please check the document at the link below."
|
||||
},
|
||||
"gov": {
|
||||
@@ -227,8 +227,8 @@
|
||||
"signer_addr": "Signer Address",
|
||||
"consensus_pub_key": "Consensus Public Key",
|
||||
"liquid_staking": "Liquid Staking",
|
||||
"liquid_staking_shares":"Liquid Shares",
|
||||
"validator_bond_share":"Validator Bonded"
|
||||
"liquid_staking_shares": "Liquid Shares",
|
||||
"validator_bond_share": "Validator Bonded"
|
||||
},
|
||||
"statesync": {
|
||||
"title": "What's State Sync?",
|
||||
|
||||
@@ -298,4 +298,4 @@
|
||||
"app_versions": "Versiones de Aplicación",
|
||||
"node_info": "Información del Nodo"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+284
-284
@@ -1,287 +1,287 @@
|
||||
{
|
||||
"module": {
|
||||
"dashboard": "Dasbor",
|
||||
"blocks": "Kumpulan Blok",
|
||||
"staking": "Staking",
|
||||
"governance": "Tata Kelola",
|
||||
"parameters": "Parameter",
|
||||
"uptime": "Pemantauan",
|
||||
"state-sync": "State Sync",
|
||||
"cosmwasm": "Cosmwasm",
|
||||
"widget": "Widgets",
|
||||
"ibc": "IBC",
|
||||
"consensus": "Konsensus",
|
||||
"supply": "Pasokan",
|
||||
"account": "Akun",
|
||||
"ecosystem": "Ekosistem",
|
||||
"favorite": "Favorit",
|
||||
"add_to_favorite": "Tambahkan ke favorit",
|
||||
"all_blockchains": "All Blockchain",
|
||||
"sponsors": "Sponsors",
|
||||
"links": "Tautan"
|
||||
"module": {
|
||||
"dashboard": "Dasbor",
|
||||
"blocks": "Kumpulan Blok",
|
||||
"staking": "Staking",
|
||||
"governance": "Tata Kelola",
|
||||
"parameters": "Parameter",
|
||||
"uptime": "Pemantauan",
|
||||
"state-sync": "State Sync",
|
||||
"cosmwasm": "Cosmwasm",
|
||||
"widget": "Widgets",
|
||||
"ibc": "IBC",
|
||||
"consensus": "Konsensus",
|
||||
"supply": "Pasokan",
|
||||
"account": "Akun",
|
||||
"ecosystem": "Ekosistem",
|
||||
"favorite": "Favorit",
|
||||
"add_to_favorite": "Tambahkan ke favorit",
|
||||
"all_blockchains": "All Blockchain",
|
||||
"sponsors": "Sponsors",
|
||||
"links": "Tautan"
|
||||
},
|
||||
"pages": {
|
||||
"title": "Ping Dashboard",
|
||||
"title_all": "404",
|
||||
"tag": "Beta",
|
||||
"tag_all": "Halaman Tidak Ditemukan",
|
||||
"slogan": "Ping Dashboard bukan hanya sebuah penjelajah tetapi juga sebuah dompet dan masih banyak lagi ... 🛠",
|
||||
"description": "Cosmos Ecosystem Blockchains 🚀",
|
||||
"search_placeholder": "Telusuri Rantai",
|
||||
"description_all": "Kami tidak dapat menemukan halaman yang anda cari.",
|
||||
"btn_index": "Kembali ke awal"
|
||||
},
|
||||
"account": {
|
||||
"type": "Tipe",
|
||||
"address": "Alamat",
|
||||
"acc": "Akun",
|
||||
"acc_num": "Nomor Akun",
|
||||
"sequence": "Urutan",
|
||||
"pub_key": "Kunci PUblik",
|
||||
"assets": "Aset",
|
||||
"btn_send": "Kirim",
|
||||
"btn_transfer": "Transfer",
|
||||
"total_value": "Nilai Total",
|
||||
"delegations": "Delegasi",
|
||||
"no_delegations": "Tidak Ada Delegasi",
|
||||
"btn_delegate": "Delegate",
|
||||
"btn_redelegate": "Redelegate",
|
||||
"btn_withdraw": "Withdraw",
|
||||
"btn_unbond": "Lepaskan ikatan",
|
||||
"validator": "Validator",
|
||||
"delegation": "Delegasi",
|
||||
"rewards": "Hadiah",
|
||||
"action": "Tindakan",
|
||||
"unbonding_delegations": "Delegasi yang tidak terikat",
|
||||
"creation_height": "Tingkat Teratas",
|
||||
"initial_balance": "Saldo Awal",
|
||||
"balance": "Saldo",
|
||||
"completion_time": "Waktu Penyelesaian",
|
||||
"transactions": "Transaksi",
|
||||
"no_transactions": "Tidak Ada Transaksi",
|
||||
"height": "Ketinggian",
|
||||
"hash": "Hash",
|
||||
"messages": "Pesan",
|
||||
"time": "Waktu",
|
||||
"error": "Akun tidak ada di dalam rantai"
|
||||
},
|
||||
"block": {
|
||||
"estimated_time": "Estimasi Waktu",
|
||||
"countdown_for_block": "Hitung Mundur Untuk Blok",
|
||||
"countdown_for_block_input": "Masukkan Nomor Blok Ke Target Baru",
|
||||
"btn_update": "Perbarui",
|
||||
"current_height": "Ketinggian Saat Ini",
|
||||
"remaining_blocks": "Blok Yang Tersisa",
|
||||
"average_block_time": "Rata-rata Waktu Blok",
|
||||
"block_header": "Blok Teratas",
|
||||
"last_commit": "Komit Terakhir",
|
||||
"recent": "Terkini",
|
||||
"future": "Masa Depan",
|
||||
"fees": "Biaya",
|
||||
"only_tx": "Hanya menampilkan txs di blok terbaru"
|
||||
},
|
||||
"consensus": {
|
||||
"monitor": "Memantau",
|
||||
"onboard_rate": "Penggabungan Nilai",
|
||||
"o": "O",
|
||||
"h": "H",
|
||||
"r": "R",
|
||||
"s": "S",
|
||||
"round": "Putaran",
|
||||
"step": "Langkah",
|
||||
"updated_at": "Diperbarui pada",
|
||||
"proposer_sign": "Proposer Ditandatangani",
|
||||
"proposer_not_sign": "Proposer Tidak Ditandatangani",
|
||||
"sign": "Ditandatangani",
|
||||
"not_sign": "Tidak Ditandatangani",
|
||||
"tips": "Tips",
|
||||
"tips_description_1": "Alat ini berguna bagi validator untuk memantau siapa saja yang berada di dalam penggabungan selama peningkatan",
|
||||
"tips_description_2": "Jika Anda ingin mengubah titik akhir rpc bawaan. pastikan bahwa `https` dan `CORS` diaktifkan pada server Anda."
|
||||
},
|
||||
"cosmwasm": {
|
||||
"title": "Cosmos Wasm Smart Contracts",
|
||||
"code_id": "Kode Id",
|
||||
"code_hash": "Kode Hash",
|
||||
"creator": "Pencipta",
|
||||
"permissions": "Perizinan",
|
||||
"btn_up_sc": "Unggah Smart Contract",
|
||||
"contract_list_code": "Daftar Kode Kontrak",
|
||||
"contract_list": "Daftar Kontrak",
|
||||
"btn_contract": "Kontrak",
|
||||
"btn_funds": "Dana",
|
||||
"btn_states": "Bagian",
|
||||
"btn_fees": "Biaya",
|
||||
"btn_query": "Query",
|
||||
"btn_execute": "Eksekusi",
|
||||
"instantiate_contract": "Kontrak Instansiasi",
|
||||
"contract_detail": "Rincian Kontrak",
|
||||
"contract_balances": "Saldo Kontrak",
|
||||
"no_escrowed_assets": "Tidak Ada Aset yang Dititipkan",
|
||||
"contract_states": "Bagian Kontrak",
|
||||
"query_contract": "Query Kontrak",
|
||||
"tips_description_1": "This feature is available when deploying contracts via WELLDONE Code. For more information, please check the document at the link below."
|
||||
},
|
||||
"gov": {
|
||||
"voting": "Voting",
|
||||
"passed": "Disetujui",
|
||||
"rejected": "Ditolak",
|
||||
"tally": "Perhitungan",
|
||||
"btn_vote": "Vote",
|
||||
"btn_deposit": "Deposit",
|
||||
"timeline": "Jadwal",
|
||||
"submit_at": "Dikirim pada",
|
||||
"deposited_at": "Didepositkan pada",
|
||||
"vote_start_from": "Voting dimulai dari",
|
||||
"vote_end": "Voting berakhir",
|
||||
"current_status": "Status Saat Ini",
|
||||
"proposal_statuses": {
|
||||
"PROPOSAL_STATUS_DEPOSIT_PERIOD": "Periode Deposit",
|
||||
"PROPOSAL_STATUS_VOTING_PERIOD": "Periode Pemungutan Suara",
|
||||
"PROPOSAL_STATUS_PASSED": "Disetujui",
|
||||
"PROPOSAL_STATUS_REJECTED": "Ditolak",
|
||||
"PROPOSAL_STATUS_FAILED": "Gagal"
|
||||
},
|
||||
"pages": {
|
||||
"title": "Ping Dashboard",
|
||||
"title_all": "404",
|
||||
"tag": "Beta",
|
||||
"tag_all": "Halaman Tidak Ditemukan",
|
||||
"slogan": "Ping Dashboard bukan hanya sebuah penjelajah tetapi juga sebuah dompet dan masih banyak lagi ... 🛠",
|
||||
"description": "Cosmos Ecosystem Blockchains 🚀",
|
||||
"search_placeholder": "Telusuri Rantai",
|
||||
"description_all": "Kami tidak dapat menemukan halaman yang anda cari.",
|
||||
"btn_index": "Kembali ke awal"
|
||||
},
|
||||
"account": {
|
||||
"type": "Tipe",
|
||||
"address": "Alamat",
|
||||
"acc": "Akun",
|
||||
"acc_num": "Nomor Akun",
|
||||
"sequence": "Urutan",
|
||||
"pub_key": "Kunci PUblik",
|
||||
"assets": "Aset",
|
||||
"btn_send": "Kirim",
|
||||
"btn_transfer": "Transfer",
|
||||
"total_value": "Nilai Total",
|
||||
"delegations": "Delegasi",
|
||||
"no_delegations": "Tidak Ada Delegasi",
|
||||
"btn_delegate": "Delegate",
|
||||
"btn_redelegate": "Redelegate",
|
||||
"btn_withdraw": "Withdraw",
|
||||
"btn_unbond": "Lepaskan ikatan",
|
||||
"validator": "Validator",
|
||||
"delegation": "Delegasi",
|
||||
"rewards": "Hadiah",
|
||||
"action": "Tindakan",
|
||||
"unbonding_delegations": "Delegasi yang tidak terikat",
|
||||
"creation_height": "Tingkat Teratas",
|
||||
"initial_balance": "Saldo Awal",
|
||||
"balance": "Saldo",
|
||||
"completion_time": "Waktu Penyelesaian",
|
||||
"transactions": "Transaksi",
|
||||
"no_transactions": "Tidak Ada Transaksi",
|
||||
"height": "Ketinggian",
|
||||
"hash": "Hash",
|
||||
"messages": "Pesan",
|
||||
"time": "Waktu",
|
||||
"error": "Akun tidak ada di dalam rantai"
|
||||
},
|
||||
"block": {
|
||||
"estimated_time": "Estimasi Waktu",
|
||||
"countdown_for_block": "Hitung Mundur Untuk Blok",
|
||||
"countdown_for_block_input": "Masukkan Nomor Blok Ke Target Baru",
|
||||
"btn_update": "Perbarui",
|
||||
"current_height": "Ketinggian Saat Ini",
|
||||
"remaining_blocks": "Blok Yang Tersisa",
|
||||
"average_block_time": "Rata-rata Waktu Blok",
|
||||
"block_header": "Blok Teratas",
|
||||
"last_commit": "Komit Terakhir",
|
||||
"recent": "Terkini",
|
||||
"future": "Masa Depan",
|
||||
"fees": "Biaya",
|
||||
"only_tx": "Hanya menampilkan txs di blok terbaru"
|
||||
},
|
||||
"consensus": {
|
||||
"monitor": "Memantau",
|
||||
"onboard_rate": "Penggabungan Nilai",
|
||||
"o": "O",
|
||||
"h": "H",
|
||||
"r": "R",
|
||||
"s": "S",
|
||||
"round": "Putaran",
|
||||
"step": "Langkah",
|
||||
"updated_at": "Diperbarui pada",
|
||||
"proposer_sign": "Proposer Ditandatangani",
|
||||
"proposer_not_sign": "Proposer Tidak Ditandatangani",
|
||||
"sign": "Ditandatangani",
|
||||
"not_sign": "Tidak Ditandatangani",
|
||||
"tips": "Tips",
|
||||
"tips_description_1": "Alat ini berguna bagi validator untuk memantau siapa saja yang berada di dalam penggabungan selama peningkatan",
|
||||
"tips_description_2": "Jika Anda ingin mengubah titik akhir rpc bawaan. pastikan bahwa `https` dan `CORS` diaktifkan pada server Anda."
|
||||
},
|
||||
"cosmwasm": {
|
||||
"title": "Cosmos Wasm Smart Contracts",
|
||||
"code_id": "Kode Id",
|
||||
"code_hash": "Kode Hash",
|
||||
"creator": "Pencipta",
|
||||
"permissions": "Perizinan",
|
||||
"btn_up_sc": "Unggah Smart Contract",
|
||||
"contract_list_code": "Daftar Kode Kontrak",
|
||||
"contract_list": "Daftar Kontrak",
|
||||
"btn_contract": "Kontrak",
|
||||
"btn_funds": "Dana",
|
||||
"btn_states": "Bagian",
|
||||
"btn_fees": "Biaya",
|
||||
"btn_query": "Query",
|
||||
"btn_execute": "Eksekusi",
|
||||
"instantiate_contract": "Kontrak Instansiasi",
|
||||
"contract_detail": "Rincian Kontrak",
|
||||
"contract_balances": "Saldo Kontrak",
|
||||
"no_escrowed_assets": "Tidak Ada Aset yang Dititipkan",
|
||||
"contract_states": "Bagian Kontrak",
|
||||
"query_contract": "Query Kontrak",
|
||||
"tips_description_1": "This feature is available when deploying contracts via WELLDONE Code. For more information, please check the document at the link below."
|
||||
},
|
||||
"gov": {
|
||||
"voting": "Voting",
|
||||
"passed": "Disetujui",
|
||||
"rejected": "Ditolak",
|
||||
"tally": "Perhitungan",
|
||||
"btn_vote": "Vote",
|
||||
"btn_deposit": "Deposit",
|
||||
"timeline": "Jadwal",
|
||||
"submit_at": "Dikirim pada",
|
||||
"deposited_at": "Didepositkan pada",
|
||||
"vote_start_from": "Voting dimulai dari",
|
||||
"vote_end": "Voting berakhir",
|
||||
"current_status": "Status Saat Ini",
|
||||
"proposal_statuses": {
|
||||
"PROPOSAL_STATUS_DEPOSIT_PERIOD": "Periode Deposit",
|
||||
"PROPOSAL_STATUS_VOTING_PERIOD": "Periode Pemungutan Suara",
|
||||
"PROPOSAL_STATUS_PASSED": "Disetujui",
|
||||
"PROPOSAL_STATUS_REJECTED": "Ditolak",
|
||||
"PROPOSAL_STATUS_FAILED": "Gagal"
|
||||
},
|
||||
"upgrade_plan": "Peningkatan Rencana",
|
||||
"votes": "Suara"
|
||||
},
|
||||
"ibc": {
|
||||
"title": "Koneksi IBC",
|
||||
"title_2": "Klien IBC",
|
||||
"registry": "Registri",
|
||||
"connection_id": "ID Koneksi",
|
||||
"btn_apply": "Ajukan",
|
||||
"trust_parameters": "Parameter Kepercayaan",
|
||||
"trust_level": "Tingkat Kepercayaan",
|
||||
"trusting_period": "Periode Kepercayaan",
|
||||
"unbonding_period": "Periode Pembatalan Tautan",
|
||||
"max_clock_drift": "Maksimum Selisih Jam",
|
||||
"frozen_height": "Ketinggian Terkunci",
|
||||
"latest_height": "Ketinggian Terbaru",
|
||||
"upgrade_parameters": "Parameter Pembaruan",
|
||||
"allow_update_after_expiry": "Izinkan pembaruan setelah kedaluwarsa",
|
||||
"allow_update_after_misbehaviour": "Izinkan pembaruan setelah perilaku yang tidak pantas",
|
||||
"upgrade_path": "Jalur Pembaruan",
|
||||
"channels": "Saluran",
|
||||
"txs": "Transaksi",
|
||||
"channel_id": "ID Saluran",
|
||||
"port_id": "ID Port",
|
||||
"state": "Status",
|
||||
"counterparty": "Pihak Lawan",
|
||||
"hops": "Hops",
|
||||
"version": "Versi",
|
||||
"ordering": "Pemesanan",
|
||||
"height": "Ketinggian",
|
||||
"txhash": "Hash Transaksi",
|
||||
"messages": "Pesan",
|
||||
"time": "Waktu",
|
||||
"btn_out": "Keluar",
|
||||
"btn_in": "Masuk"
|
||||
},
|
||||
"staking": {
|
||||
"inflation": "Inflasi",
|
||||
"unbonding_time": "Waktu Pembatalan Tautan",
|
||||
"double_sign_slashing": "Pemotongan Ganda Tanda Tangan",
|
||||
"downtime_slashing": "Pemotongan Downtime",
|
||||
"popular": "Populer",
|
||||
"active": "Aktif",
|
||||
"inactive": "Tidak Aktif",
|
||||
"rank": "Peringkat",
|
||||
"validator": "Validator",
|
||||
"voting_power": "Kekuatan Suara",
|
||||
"24h_changes": "Perubahan 24 Jam",
|
||||
"commission": "Komisi",
|
||||
"actions": "Tindakan",
|
||||
"jailed": "Dipenjara",
|
||||
"top": "Teratas",
|
||||
"description": "Mendelegasikan ke validator peringkat lebih rendah akan meningkatkan keamanan keseluruhan jaringan",
|
||||
"about_us": "Tentang Kami",
|
||||
"website": "Website",
|
||||
"contact": "Kontak",
|
||||
"validator_status": "Status Validator",
|
||||
"status": "Status",
|
||||
"total_bonded": "Total Jumlah Token Terikat",
|
||||
"self_bonded": "Total Token Terikat Sendiri",
|
||||
"min_self": "Minimum Delegasi Sendiri",
|
||||
"annual_profit": "Keuntungan Tahunan",
|
||||
"unbonding_height": "Ketinggian Pembatalan Tautan",
|
||||
"commissions_&_rewards": "Komisi & Imbalan",
|
||||
"commissions": "Komisi",
|
||||
"outstanding": "Total Imbalan",
|
||||
"addresses": "Kumpulan Alamat",
|
||||
"account_addr": "Alamat Akun",
|
||||
"operator_addr": "Alamat Operator",
|
||||
"hex_addr": "Alamat Hex",
|
||||
"signer_addr": "Alamat Penandatangan",
|
||||
"consensus_pub_key": "Kunci Publik Konsensus"
|
||||
},
|
||||
"statesync": {
|
||||
"title": "Apa itu State Sync?",
|
||||
"title_2": "Memulai Node Baru dari State Sync",
|
||||
"title_3": "Aktifkan Snapshot untuk State Sync",
|
||||
"description": "Rilis Tendermint Core 0.34 mencakup dukungan untuk state sync, yang memungkinkan node baru bergabung dengan jaringan dengan mengambil snapshot dari keadaan aplikasi pada ketinggian terkini daripada mengambil dan memainkan ulang semua blok historis. Hal ini dapat mengurangi waktu yang diperlukan untuk sinkronisasi dengan jaringan dari berhari-hari menjadi beberapa menit. Klik",
|
||||
"here": "Di sini",
|
||||
"version": "Versi",
|
||||
"for_more_info": "Untuk informasi lebih lanjut",
|
||||
"text_1": "Instalasi Binary",
|
||||
"text_1_1": "Kita perlu menginstal binary terlebih dahulu dan pastikan bahwa versinya sesuai dengan yang digunakan pada mainnet",
|
||||
"text_2": "Aktifkan State Sync",
|
||||
"text_2_1": "Kita dapat mengkonfigurasi Tendermint untuk menggunakan state sync di $DAEMON_HOME/config/config.toml",
|
||||
"text_3": "Mulai menjalankan daemon",
|
||||
"text_3_1": "Jika Anda mereset node, jalankan",
|
||||
"text_3_2": "sebelum Anda menjalankan daemon",
|
||||
"text_title_3": "Untuk membuat state sync berfungsi, kita dapat mengaktifkan snapshot di $DAEMON_HOME/config/app.toml"
|
||||
},
|
||||
"tx": {
|
||||
"title": "Rangkuman",
|
||||
"tx_hash": "Tx Hash",
|
||||
"gas": "Bahan bakar",
|
||||
"fee": "Biaya",
|
||||
"memo": "Memo",
|
||||
"no_messages": "Tidak ada pesan"
|
||||
},
|
||||
"uptime": {
|
||||
"overall": "Secara keseluruhan",
|
||||
"customize": "Sesuaikan",
|
||||
"only_consumer_set": "Hanya Pengatur Konsumen",
|
||||
"last_jailed_time": "Waktu Terakhir Dipenjara",
|
||||
"signed_precommits": "Precommits Tersign",
|
||||
"start_height": "Ketinggian Awal",
|
||||
"tombstoned": "Dalam keadaan mati",
|
||||
"minimum_uptime": "Waktu aktif minimum per jendela",
|
||||
"my_validators": "Validator Saya",
|
||||
"add_validators_monitor": "Tambahkan validator yang ingin Anda pantau",
|
||||
"no": "Tidak",
|
||||
"signed_blocks": "Blok Tersign",
|
||||
"missing_blocks": "Blok yang Hilang",
|
||||
"add_validators": "Tambahkan Validator",
|
||||
"add": "Tambahkan",
|
||||
"legend": "Legend",
|
||||
"committed": "Committed",
|
||||
"precommitted": "Precommitted",
|
||||
"missed": "Missed"
|
||||
},
|
||||
"widget": {
|
||||
"title": "Pengaturan Awal",
|
||||
"endpoint": "Titik Akhir",
|
||||
"select_endpoint": "Pilih Titik Akhir",
|
||||
"text_1": "Impor skrip global",
|
||||
"text_2": "Hubungkan Dompet",
|
||||
"text_3": "Konversi Osmosis"
|
||||
},
|
||||
"index": {
|
||||
"rank": "Peringkat",
|
||||
"price_calculator": "Kalkulator Harga",
|
||||
"close": "Tutup",
|
||||
"buy": "Beli",
|
||||
"active_proposals": "Proposal Aktif",
|
||||
"no_active_proposals": "Tidak ada proposal aktif",
|
||||
"more": "Selengkapnya",
|
||||
"reward": "Hadiah",
|
||||
"unbonding": "Pemisahan",
|
||||
"btn_withdraw_reward": "Tarik Hadiah",
|
||||
"btn_swap": "Tukar",
|
||||
"receive": "Terima",
|
||||
"app_versions": "Versi Aplikasi",
|
||||
"node_info": "Informasi Node"
|
||||
}
|
||||
"upgrade_plan": "Peningkatan Rencana",
|
||||
"votes": "Suara"
|
||||
},
|
||||
"ibc": {
|
||||
"title": "Koneksi IBC",
|
||||
"title_2": "Klien IBC",
|
||||
"registry": "Registri",
|
||||
"connection_id": "ID Koneksi",
|
||||
"btn_apply": "Ajukan",
|
||||
"trust_parameters": "Parameter Kepercayaan",
|
||||
"trust_level": "Tingkat Kepercayaan",
|
||||
"trusting_period": "Periode Kepercayaan",
|
||||
"unbonding_period": "Periode Pembatalan Tautan",
|
||||
"max_clock_drift": "Maksimum Selisih Jam",
|
||||
"frozen_height": "Ketinggian Terkunci",
|
||||
"latest_height": "Ketinggian Terbaru",
|
||||
"upgrade_parameters": "Parameter Pembaruan",
|
||||
"allow_update_after_expiry": "Izinkan pembaruan setelah kedaluwarsa",
|
||||
"allow_update_after_misbehaviour": "Izinkan pembaruan setelah perilaku yang tidak pantas",
|
||||
"upgrade_path": "Jalur Pembaruan",
|
||||
"channels": "Saluran",
|
||||
"txs": "Transaksi",
|
||||
"channel_id": "ID Saluran",
|
||||
"port_id": "ID Port",
|
||||
"state": "Status",
|
||||
"counterparty": "Pihak Lawan",
|
||||
"hops": "Hops",
|
||||
"version": "Versi",
|
||||
"ordering": "Pemesanan",
|
||||
"height": "Ketinggian",
|
||||
"txhash": "Hash Transaksi",
|
||||
"messages": "Pesan",
|
||||
"time": "Waktu",
|
||||
"btn_out": "Keluar",
|
||||
"btn_in": "Masuk"
|
||||
},
|
||||
"staking": {
|
||||
"inflation": "Inflasi",
|
||||
"unbonding_time": "Waktu Pembatalan Tautan",
|
||||
"double_sign_slashing": "Pemotongan Ganda Tanda Tangan",
|
||||
"downtime_slashing": "Pemotongan Downtime",
|
||||
"popular": "Populer",
|
||||
"active": "Aktif",
|
||||
"inactive": "Tidak Aktif",
|
||||
"rank": "Peringkat",
|
||||
"validator": "Validator",
|
||||
"voting_power": "Kekuatan Suara",
|
||||
"24h_changes": "Perubahan 24 Jam",
|
||||
"commission": "Komisi",
|
||||
"actions": "Tindakan",
|
||||
"jailed": "Dipenjara",
|
||||
"top": "Teratas",
|
||||
"description": "Mendelegasikan ke validator peringkat lebih rendah akan meningkatkan keamanan keseluruhan jaringan",
|
||||
"about_us": "Tentang Kami",
|
||||
"website": "Website",
|
||||
"contact": "Kontak",
|
||||
"validator_status": "Status Validator",
|
||||
"status": "Status",
|
||||
"total_bonded": "Total Jumlah Token Terikat",
|
||||
"self_bonded": "Total Token Terikat Sendiri",
|
||||
"min_self": "Minimum Delegasi Sendiri",
|
||||
"annual_profit": "Keuntungan Tahunan",
|
||||
"unbonding_height": "Ketinggian Pembatalan Tautan",
|
||||
"commissions_&_rewards": "Komisi & Imbalan",
|
||||
"commissions": "Komisi",
|
||||
"outstanding": "Total Imbalan",
|
||||
"addresses": "Kumpulan Alamat",
|
||||
"account_addr": "Alamat Akun",
|
||||
"operator_addr": "Alamat Operator",
|
||||
"hex_addr": "Alamat Hex",
|
||||
"signer_addr": "Alamat Penandatangan",
|
||||
"consensus_pub_key": "Kunci Publik Konsensus"
|
||||
},
|
||||
"statesync": {
|
||||
"title": "Apa itu State Sync?",
|
||||
"title_2": "Memulai Node Baru dari State Sync",
|
||||
"title_3": "Aktifkan Snapshot untuk State Sync",
|
||||
"description": "Rilis Tendermint Core 0.34 mencakup dukungan untuk state sync, yang memungkinkan node baru bergabung dengan jaringan dengan mengambil snapshot dari keadaan aplikasi pada ketinggian terkini daripada mengambil dan memainkan ulang semua blok historis. Hal ini dapat mengurangi waktu yang diperlukan untuk sinkronisasi dengan jaringan dari berhari-hari menjadi beberapa menit. Klik",
|
||||
"here": "Di sini",
|
||||
"version": "Versi",
|
||||
"for_more_info": "Untuk informasi lebih lanjut",
|
||||
"text_1": "Instalasi Binary",
|
||||
"text_1_1": "Kita perlu menginstal binary terlebih dahulu dan pastikan bahwa versinya sesuai dengan yang digunakan pada mainnet",
|
||||
"text_2": "Aktifkan State Sync",
|
||||
"text_2_1": "Kita dapat mengkonfigurasi Tendermint untuk menggunakan state sync di $DAEMON_HOME/config/config.toml",
|
||||
"text_3": "Mulai menjalankan daemon",
|
||||
"text_3_1": "Jika Anda mereset node, jalankan",
|
||||
"text_3_2": "sebelum Anda menjalankan daemon",
|
||||
"text_title_3": "Untuk membuat state sync berfungsi, kita dapat mengaktifkan snapshot di $DAEMON_HOME/config/app.toml"
|
||||
},
|
||||
"tx": {
|
||||
"title": "Rangkuman",
|
||||
"tx_hash": "Tx Hash",
|
||||
"gas": "Bahan bakar",
|
||||
"fee": "Biaya",
|
||||
"memo": "Memo",
|
||||
"no_messages": "Tidak ada pesan"
|
||||
},
|
||||
"uptime": {
|
||||
"overall": "Secara keseluruhan",
|
||||
"customize": "Sesuaikan",
|
||||
"only_consumer_set": "Hanya Pengatur Konsumen",
|
||||
"last_jailed_time": "Waktu Terakhir Dipenjara",
|
||||
"signed_precommits": "Precommits Tersign",
|
||||
"start_height": "Ketinggian Awal",
|
||||
"tombstoned": "Dalam keadaan mati",
|
||||
"minimum_uptime": "Waktu aktif minimum per jendela",
|
||||
"my_validators": "Validator Saya",
|
||||
"add_validators_monitor": "Tambahkan validator yang ingin Anda pantau",
|
||||
"no": "Tidak",
|
||||
"signed_blocks": "Blok Tersign",
|
||||
"missing_blocks": "Blok yang Hilang",
|
||||
"add_validators": "Tambahkan Validator",
|
||||
"add": "Tambahkan",
|
||||
"legend": "Legend",
|
||||
"committed": "Committed",
|
||||
"precommitted": "Precommitted",
|
||||
"missed": "Missed"
|
||||
},
|
||||
"widget": {
|
||||
"title": "Pengaturan Awal",
|
||||
"endpoint": "Titik Akhir",
|
||||
"select_endpoint": "Pilih Titik Akhir",
|
||||
"text_1": "Impor skrip global",
|
||||
"text_2": "Hubungkan Dompet",
|
||||
"text_3": "Konversi Osmosis"
|
||||
},
|
||||
"index": {
|
||||
"rank": "Peringkat",
|
||||
"price_calculator": "Kalkulator Harga",
|
||||
"close": "Tutup",
|
||||
"buy": "Beli",
|
||||
"active_proposals": "Proposal Aktif",
|
||||
"no_active_proposals": "Tidak ada proposal aktif",
|
||||
"more": "Selengkapnya",
|
||||
"reward": "Hadiah",
|
||||
"unbonding": "Pemisahan",
|
||||
"btn_withdraw_reward": "Tarik Hadiah",
|
||||
"btn_swap": "Tukar",
|
||||
"receive": "Terima",
|
||||
"app_versions": "Versi Aplikasi",
|
||||
"node_info": "Informasi Node"
|
||||
}
|
||||
}
|
||||
|
||||
+101
-101
@@ -189,113 +189,113 @@
|
||||
"btn_in": "入"
|
||||
},
|
||||
"staking": {
|
||||
"inflation": "インフレーション",
|
||||
"unbonding_time": "アンバンディング時間",
|
||||
"double_sign_slashing": "ダブルサインの罰金",
|
||||
"downtime_slashing": "ダウンタイムの罰金",
|
||||
"popular": "人気",
|
||||
"active": "アクティブ",
|
||||
"inactive": "非アクティブ",
|
||||
"rank": "ランク",
|
||||
"validator": "バリデーター",
|
||||
"voting_power": "投票権",
|
||||
"24h_changes": "24時間の変化",
|
||||
"commission": "手数料",
|
||||
"actions": "アクション",
|
||||
"jailed": "投獄中",
|
||||
"top": "トップ",
|
||||
"description": "低ランクのバリデーターに委任すると、ネットワーク全体のセキュリティが向上します。",
|
||||
"about_us": "私たちについて",
|
||||
"website": "ウェブサイト",
|
||||
"contact": "お問い合わせ",
|
||||
"validator_status": "バリデーターの状態",
|
||||
"status": "ステータス",
|
||||
"total_bonded": "総担保トークン",
|
||||
"self_bonded": "自己担保",
|
||||
"min_self": "最小自己委任",
|
||||
"annual_profit": "年次利益",
|
||||
"unbonding_height": "アンバンディング高さ",
|
||||
"commissions_&_rewards": "手数料と報酬",
|
||||
"commissions": "手数料",
|
||||
"outstanding": "未解決",
|
||||
"addresses": "アドレス",
|
||||
"account_addr": "アカウントアドレス",
|
||||
"operator_addr": "オペレーターアドレス",
|
||||
"hex_addr": "16進数アドレス",
|
||||
"signer_addr": "署名者アドレス",
|
||||
"consensus_pub_key": "コンセンサス公開鍵",
|
||||
"liquid_staking": "流動性ステーキング",
|
||||
"liquid_staking_shares": "流動性シェア",
|
||||
"validator_bond_share": "バリデーター担保シェア"
|
||||
},
|
||||
"inflation": "インフレーション",
|
||||
"unbonding_time": "アンバンディング時間",
|
||||
"double_sign_slashing": "ダブルサインの罰金",
|
||||
"downtime_slashing": "ダウンタイムの罰金",
|
||||
"popular": "人気",
|
||||
"active": "アクティブ",
|
||||
"inactive": "非アクティブ",
|
||||
"rank": "ランク",
|
||||
"validator": "バリデーター",
|
||||
"voting_power": "投票権",
|
||||
"24h_changes": "24時間の変化",
|
||||
"commission": "手数料",
|
||||
"actions": "アクション",
|
||||
"jailed": "投獄中",
|
||||
"top": "トップ",
|
||||
"description": "低ランクのバリデーターに委任すると、ネットワーク全体のセキュリティが向上します。",
|
||||
"about_us": "私たちについて",
|
||||
"website": "ウェブサイト",
|
||||
"contact": "お問い合わせ",
|
||||
"validator_status": "バリデーターの状態",
|
||||
"status": "ステータス",
|
||||
"total_bonded": "総担保トークン",
|
||||
"self_bonded": "自己担保",
|
||||
"min_self": "最小自己委任",
|
||||
"annual_profit": "年次利益",
|
||||
"unbonding_height": "アンバンディング高さ",
|
||||
"commissions_&_rewards": "手数料と報酬",
|
||||
"commissions": "手数料",
|
||||
"outstanding": "未解決",
|
||||
"addresses": "アドレス",
|
||||
"account_addr": "アカウントアドレス",
|
||||
"operator_addr": "オペレーターアドレス",
|
||||
"hex_addr": "16進数アドレス",
|
||||
"signer_addr": "署名者アドレス",
|
||||
"consensus_pub_key": "コンセンサス公開鍵",
|
||||
"liquid_staking": "流動性ステーキング",
|
||||
"liquid_staking_shares": "流動性シェア",
|
||||
"validator_bond_share": "バリデーター担保シェア"
|
||||
},
|
||||
"statesync": {
|
||||
"title": "ステートシンクとは?",
|
||||
"title_2": "ステートシンクから新しいノードを開始",
|
||||
"title_3": "ステートシンクのためにスナップショットを有効にする",
|
||||
"description": "Tendermint Core 0.34リリースはステートシンクをサポートしており、新しいノードが最近の高さでアプリケーションの状態のスナップショットを取得してネットワークに参加できるようにします。これにより、数日から数分に同期にかかる時間を短縮できます。クリック",
|
||||
"here": "こちら",
|
||||
"version": "バージョン",
|
||||
"for_more_info": "詳細情報は",
|
||||
"text_1": "バイナリをインストール",
|
||||
"text_1_1": "最初にバイナリをインストールし、メインネットで現在使用されているバージョンであることを確認する必要があります。",
|
||||
"text_2": "ステートシンクを有効にする",
|
||||
"text_2_1": "Tendermintを$DAEMON_HOME/config/config.tomlでステートシンクを使用するように構成できます。",
|
||||
"text_3": "デーモンを開始",
|
||||
"text_3_1": "ノードをリセットしている場合は、デーモンを開始する前に実行します。",
|
||||
"text_3_2": "",
|
||||
"text_title_3": "ステートシンクを機能させるために、$DAEMON_HOME/config/app.tomlでスナップショットを有効にできます。"
|
||||
},
|
||||
"title": "ステートシンクとは?",
|
||||
"title_2": "ステートシンクから新しいノードを開始",
|
||||
"title_3": "ステートシンクのためにスナップショットを有効にする",
|
||||
"description": "Tendermint Core 0.34リリースはステートシンクをサポートしており、新しいノードが最近の高さでアプリケーションの状態のスナップショットを取得してネットワークに参加できるようにします。これにより、数日から数分に同期にかかる時間を短縮できます。クリック",
|
||||
"here": "こちら",
|
||||
"version": "バージョン",
|
||||
"for_more_info": "詳細情報は",
|
||||
"text_1": "バイナリをインストール",
|
||||
"text_1_1": "最初にバイナリをインストールし、メインネットで現在使用されているバージョンであることを確認する必要があります。",
|
||||
"text_2": "ステートシンクを有効にする",
|
||||
"text_2_1": "Tendermintを$DAEMON_HOME/config/config.tomlでステートシンクを使用するように構成できます。",
|
||||
"text_3": "デーモンを開始",
|
||||
"text_3_1": "ノードをリセットしている場合は、デーモンを開始する前に実行します。",
|
||||
"text_3_2": "",
|
||||
"text_title_3": "ステートシンクを機能させるために、$DAEMON_HOME/config/app.tomlでスナップショットを有効にできます。"
|
||||
},
|
||||
"tx": {
|
||||
"title": "概要",
|
||||
"tx_hash": "取引ハッシュ",
|
||||
"gas": "ガス",
|
||||
"fee": "手数料",
|
||||
"memo": "メモ",
|
||||
"no_messages": "メッセージはありません"
|
||||
},
|
||||
"title": "概要",
|
||||
"tx_hash": "取引ハッシュ",
|
||||
"gas": "ガス",
|
||||
"fee": "手数料",
|
||||
"memo": "メモ",
|
||||
"no_messages": "メッセージはありません"
|
||||
},
|
||||
"uptime": {
|
||||
"overall": "全体",
|
||||
"customize": "カスタマイズ",
|
||||
"only_consumer_set": "消費者セットのみ",
|
||||
"last_jailed_time": "最終投獄時間",
|
||||
"signed_precommits": "署名済みプレコミット",
|
||||
"start_height": "開始高さ",
|
||||
"tombstoned": "トムストーン化",
|
||||
"minimum_uptime": "ウィンドウごとの最小稼働時間",
|
||||
"my_validators": "私のバリデーター",
|
||||
"add_validators_monitor": "監視したいバリデーターを追加",
|
||||
"no": "いいえ",
|
||||
"signed_blocks": "署名済みブロック",
|
||||
"missing_blocks": "欠落したブロック",
|
||||
"add_validators": "バリデーターを追加",
|
||||
"add": "追加",
|
||||
"legend": "凡例",
|
||||
"committed": "コミット済み",
|
||||
"precommitted": "プレコミット済み",
|
||||
"missed": "見逃し"
|
||||
"overall": "全体",
|
||||
"customize": "カスタマイズ",
|
||||
"only_consumer_set": "消費者セットのみ",
|
||||
"last_jailed_time": "最終投獄時間",
|
||||
"signed_precommits": "署名済みプレコミット",
|
||||
"start_height": "開始高さ",
|
||||
"tombstoned": "トムストーン化",
|
||||
"minimum_uptime": "ウィンドウごとの最小稼働時間",
|
||||
"my_validators": "私のバリデーター",
|
||||
"add_validators_monitor": "監視したいバリデーターを追加",
|
||||
"no": "いいえ",
|
||||
"signed_blocks": "署名済みブロック",
|
||||
"missing_blocks": "欠落したブロック",
|
||||
"add_validators": "バリデーターを追加",
|
||||
"add": "追加",
|
||||
"legend": "凡例",
|
||||
"committed": "コミット済み",
|
||||
"precommitted": "プレコミット済み",
|
||||
"missed": "見逃し"
|
||||
},
|
||||
"widget": {
|
||||
"title": "初期設定",
|
||||
"endpoint": "エンドポイント",
|
||||
"select_endpoint": "エンドポイントを選択",
|
||||
"text_1": "グローバルスクリプトをインポート",
|
||||
"text_2": "ウォレットに接続",
|
||||
"text_3": "オスモシス変換"
|
||||
"title": "初期設定",
|
||||
"endpoint": "エンドポイント",
|
||||
"select_endpoint": "エンドポイントを選択",
|
||||
"text_1": "グローバルスクリプトをインポート",
|
||||
"text_2": "ウォレットに接続",
|
||||
"text_3": "オスモシス変換"
|
||||
},
|
||||
"index": {
|
||||
"rank": "ランク",
|
||||
"price_calculator": "価格計算機",
|
||||
"close": "閉じる",
|
||||
"buy": "購入",
|
||||
"active_proposals": "アクティブな提案",
|
||||
"no_active_proposals": "アクティブな提案はありません",
|
||||
"more": "もっと",
|
||||
"reward": "報酬",
|
||||
"unbonding": "アンバンディング",
|
||||
"btn_withdraw_reward": "報酬を引き出す",
|
||||
"btn_swap": "スワップ",
|
||||
"receive": "受け取る",
|
||||
"app_versions": "アプリケーションバージョン",
|
||||
"node_info": "ノード情報"
|
||||
"rank": "ランク",
|
||||
"price_calculator": "価格計算機",
|
||||
"close": "閉じる",
|
||||
"buy": "購入",
|
||||
"active_proposals": "アクティブな提案",
|
||||
"no_active_proposals": "アクティブな提案はありません",
|
||||
"more": "もっと",
|
||||
"reward": "報酬",
|
||||
"unbonding": "アンバンディング",
|
||||
"btn_withdraw_reward": "報酬を引き出す",
|
||||
"btn_swap": "スワップ",
|
||||
"receive": "受け取る",
|
||||
"app_versions": "アプリケーションバージョン",
|
||||
"node_info": "ノード情報"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,44 +189,44 @@
|
||||
"btn_in": "입력"
|
||||
},
|
||||
"staking": {
|
||||
"inflation": "인플레이션",
|
||||
"unbonding_time": "언본딩 시간",
|
||||
"double_sign_slashing": "이중 서명 슬래싱",
|
||||
"downtime_slashing": "다운타임 슬래싱",
|
||||
"popular": "인기",
|
||||
"active": "활성",
|
||||
"inactive": "비활성",
|
||||
"rank": "순위",
|
||||
"validator": "검증인",
|
||||
"voting_power": "투표 권한",
|
||||
"24h_changes": "24시간 변화",
|
||||
"commission": "커미션",
|
||||
"actions": "작업",
|
||||
"jailed": "감금됨",
|
||||
"top": "상위",
|
||||
"description": "순위가 낮은 검증인에게 위임하면 네트워크의 전체 보안을 강화할 수 있습니다.",
|
||||
"about_us": "회사 소개",
|
||||
"website": "웹사이트",
|
||||
"contact": "연락처",
|
||||
"validator_status": "검증인 상태",
|
||||
"status": "상태",
|
||||
"total_bonded": "전체 본딩 토큰",
|
||||
"self_bonded": "셀프 본딩",
|
||||
"min_self": "최소 셀프 위임",
|
||||
"annual_profit": "연간 수익",
|
||||
"unbonding_height": "언본딩 높이",
|
||||
"commissions_&_rewards": "커미션 및 보상",
|
||||
"commissions": "커미션",
|
||||
"outstanding": "미지급",
|
||||
"addresses": "주소",
|
||||
"account_addr": "계정 주소",
|
||||
"operator_addr": "운영자 주소",
|
||||
"hex_addr": "Hex 주소",
|
||||
"signer_addr": "서명자 주소",
|
||||
"consensus_pub_key": "합의 공개 키",
|
||||
"liquid_staking": "유동 스테이킹",
|
||||
"liquid_staking_shares": "유동 지분",
|
||||
"validator_bond_share": "검증인 본딩 지분"
|
||||
"inflation": "인플레이션",
|
||||
"unbonding_time": "언본딩 시간",
|
||||
"double_sign_slashing": "이중 서명 슬래싱",
|
||||
"downtime_slashing": "다운타임 슬래싱",
|
||||
"popular": "인기",
|
||||
"active": "활성",
|
||||
"inactive": "비활성",
|
||||
"rank": "순위",
|
||||
"validator": "검증인",
|
||||
"voting_power": "투표 권한",
|
||||
"24h_changes": "24시간 변화",
|
||||
"commission": "커미션",
|
||||
"actions": "작업",
|
||||
"jailed": "감금됨",
|
||||
"top": "상위",
|
||||
"description": "순위가 낮은 검증인에게 위임하면 네트워크의 전체 보안을 강화할 수 있습니다.",
|
||||
"about_us": "회사 소개",
|
||||
"website": "웹사이트",
|
||||
"contact": "연락처",
|
||||
"validator_status": "검증인 상태",
|
||||
"status": "상태",
|
||||
"total_bonded": "전체 본딩 토큰",
|
||||
"self_bonded": "셀프 본딩",
|
||||
"min_self": "최소 셀프 위임",
|
||||
"annual_profit": "연간 수익",
|
||||
"unbonding_height": "언본딩 높이",
|
||||
"commissions_&_rewards": "커미션 및 보상",
|
||||
"commissions": "커미션",
|
||||
"outstanding": "미지급",
|
||||
"addresses": "주소",
|
||||
"account_addr": "계정 주소",
|
||||
"operator_addr": "운영자 주소",
|
||||
"hex_addr": "Hex 주소",
|
||||
"signer_addr": "서명자 주소",
|
||||
"consensus_pub_key": "합의 공개 키",
|
||||
"liquid_staking": "유동 스테이킹",
|
||||
"liquid_staking_shares": "유동 지분",
|
||||
"validator_bond_share": "검증인 본딩 지분"
|
||||
},
|
||||
"statesync": {
|
||||
"title": "상태 동기화란?",
|
||||
|
||||
+13
-13
@@ -1,9 +1,9 @@
|
||||
import { useBlockchain } from "@/stores";
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
import { useBlockchain } from '@/stores';
|
||||
import { createRouter, createWebHistory } from 'vue-router';
|
||||
// @ts-ignore
|
||||
import { setupLayouts } from "virtual:generated-layouts";
|
||||
import { setupLayouts } from 'virtual:generated-layouts';
|
||||
// @ts-ignore
|
||||
import routes from "~pages";
|
||||
import routes from '~pages';
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
@@ -12,15 +12,15 @@ const router = createRouter({
|
||||
|
||||
//update current blockchain
|
||||
router.beforeEach((to) => {
|
||||
const { chain } = to.params
|
||||
if(chain){
|
||||
const blockchain= useBlockchain()
|
||||
if(chain !== blockchain.chainName) {
|
||||
blockchain.setCurrent(chain.toString())
|
||||
}
|
||||
}
|
||||
})
|
||||
const { chain } = to.params;
|
||||
if (chain) {
|
||||
const blockchain = useBlockchain();
|
||||
if (chain !== blockchain.chainName) {
|
||||
blockchain.setCurrent(chain.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Docs: https://router.vuejs.org/guide/advanced/navigation-guards.html#global-before-guards
|
||||
|
||||
export default router;
|
||||
export default router;
|
||||
|
||||
@@ -10,7 +10,7 @@ export const useBankStore = defineStore('bankstore', {
|
||||
supply: {} as Coin,
|
||||
balances: {} as Record<string, Coin[]>,
|
||||
totalSupply: { supply: [] as Coin[] },
|
||||
ibcDenoms: {} as Record<string, DenomTrace>
|
||||
ibcDenoms: {} as Record<string, DenomTrace>,
|
||||
};
|
||||
},
|
||||
getters: {
|
||||
|
||||
+110
-109
@@ -8,118 +8,119 @@ import { fromBase64 } from '@cosmjs/encoding';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
export const useBaseStore = defineStore('baseStore', {
|
||||
state: () => {
|
||||
return {
|
||||
earlest: {} as Block,
|
||||
latest: {} as Block,
|
||||
recents: [] as Block[],
|
||||
theme: (window.localStorage.getItem('theme') || 'dark') as
|
||||
| 'light'
|
||||
| 'dark',
|
||||
connected: true,
|
||||
};
|
||||
state: () => {
|
||||
return {
|
||||
earlest: {} as Block,
|
||||
latest: {} as Block,
|
||||
recents: [] as Block[],
|
||||
theme: (window.localStorage.getItem('theme') || 'dark') as
|
||||
| 'light'
|
||||
| 'dark',
|
||||
connected: true,
|
||||
};
|
||||
},
|
||||
getters: {
|
||||
blocktime(): number {
|
||||
if (this.earlest && this.latest) {
|
||||
if (
|
||||
this.latest.block?.header?.height !==
|
||||
this.earlest.block?.header?.height
|
||||
) {
|
||||
const diff = dayjs(this.latest.block?.header?.time).diff(
|
||||
this.earlest.block?.header?.time
|
||||
);
|
||||
const blocks =
|
||||
Number(this.latest.block.header.height) -
|
||||
Number(this.earlest.block.header.height);
|
||||
return diff / blocks;
|
||||
}
|
||||
}
|
||||
return 6000;
|
||||
},
|
||||
getters: {
|
||||
blocktime(): number {
|
||||
if (this.earlest && this.latest) {
|
||||
if (
|
||||
this.latest.block?.header?.height !==
|
||||
this.earlest.block?.header?.height
|
||||
) {
|
||||
const diff = dayjs(this.latest.block?.header?.time).diff(
|
||||
this.earlest.block?.header?.time
|
||||
);
|
||||
const blocks = Number(this.latest.block.header.height) - Number(this.earlest.block.header.height)
|
||||
return diff / (blocks);
|
||||
}
|
||||
}
|
||||
return 6000;
|
||||
},
|
||||
blockchain() {
|
||||
return useBlockchain();
|
||||
},
|
||||
currentChainId(): string {
|
||||
return this.latest.block?.header.chain_id || '';
|
||||
},
|
||||
txsInRecents() {
|
||||
const txs = [] as {
|
||||
height: string;
|
||||
hash: string;
|
||||
tx: DecodedTxRaw;
|
||||
}[];
|
||||
this.recents.forEach((b) =>
|
||||
b.block?.data?.txs.forEach((tx: string) => {
|
||||
if (tx) {
|
||||
const raw = fromBase64(tx);
|
||||
try {
|
||||
txs.push({
|
||||
height: b.block.header.height,
|
||||
hash: hashTx(raw),
|
||||
tx: decodeTxRaw(raw),
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
return txs.sort((a, b) => {return Number(b.height) - Number(a.height)});
|
||||
},
|
||||
blockchain() {
|
||||
return useBlockchain();
|
||||
},
|
||||
actions: {
|
||||
async initial() {
|
||||
this.fetchLatest()
|
||||
},
|
||||
async clearRecentBlocks() {
|
||||
this.recents = [];
|
||||
},
|
||||
async fetchLatest() {
|
||||
try{
|
||||
this.latest = await this.blockchain.rpc?.getBaseBlockLatest();
|
||||
this.connected = true
|
||||
}catch(e) {
|
||||
this.connected = false
|
||||
currentChainId(): string {
|
||||
return this.latest.block?.header.chain_id || '';
|
||||
},
|
||||
txsInRecents() {
|
||||
const txs = [] as {
|
||||
height: string;
|
||||
hash: string;
|
||||
tx: DecodedTxRaw;
|
||||
}[];
|
||||
this.recents.forEach((b) =>
|
||||
b.block?.data?.txs.forEach((tx: string) => {
|
||||
if (tx) {
|
||||
const raw = fromBase64(tx);
|
||||
try {
|
||||
txs.push({
|
||||
height: b.block.header.height,
|
||||
hash: hashTx(raw),
|
||||
tx: decodeTxRaw(raw),
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
if (
|
||||
!this.earlest ||
|
||||
this.earlest?.block?.header?.chain_id !=
|
||||
this.latest?.block?.header?.chain_id
|
||||
) {
|
||||
//reset earlest and recents
|
||||
this.earlest = this.latest;
|
||||
this.recents = [];
|
||||
}
|
||||
//check if the block exists in recents
|
||||
if (
|
||||
this.recents.findIndex(
|
||||
(x) => x?.block_id?.hash === this.latest?.block_id?.hash
|
||||
) === -1
|
||||
) {
|
||||
if (this.recents.length >= 50) {
|
||||
this.recents.shift();
|
||||
}
|
||||
this.recents.push(this.latest);
|
||||
}
|
||||
return this.latest;
|
||||
},
|
||||
}
|
||||
})
|
||||
);
|
||||
return txs.sort((a, b) => {
|
||||
return Number(b.height) - Number(a.height);
|
||||
});
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
async initial() {
|
||||
this.fetchLatest();
|
||||
},
|
||||
async clearRecentBlocks() {
|
||||
this.recents = [];
|
||||
},
|
||||
async fetchLatest() {
|
||||
try {
|
||||
this.latest = await this.blockchain.rpc?.getBaseBlockLatest();
|
||||
this.connected = true;
|
||||
} catch (e) {
|
||||
this.connected = false;
|
||||
}
|
||||
if (
|
||||
!this.earlest ||
|
||||
this.earlest?.block?.header?.chain_id !=
|
||||
this.latest?.block?.header?.chain_id
|
||||
) {
|
||||
//reset earlest and recents
|
||||
this.earlest = this.latest;
|
||||
this.recents = [];
|
||||
}
|
||||
//check if the block exists in recents
|
||||
if (
|
||||
this.recents.findIndex(
|
||||
(x) => x?.block_id?.hash === this.latest?.block_id?.hash
|
||||
) === -1
|
||||
) {
|
||||
if (this.recents.length >= 50) {
|
||||
this.recents.shift();
|
||||
}
|
||||
this.recents.push(this.latest);
|
||||
}
|
||||
return this.latest;
|
||||
},
|
||||
|
||||
async fetchValidatorByHeight(height?: number, offset = 0) {
|
||||
return this.blockchain.rpc.getBaseValidatorsetAt(
|
||||
String(height),
|
||||
offset
|
||||
);
|
||||
},
|
||||
async fetchLatestValidators(offset = 0) {
|
||||
return this.blockchain.rpc.getBaseValidatorsetLatest(offset);
|
||||
},
|
||||
async fetchBlock(height?: number | string) {
|
||||
return this.blockchain.rpc.getBaseBlockAt(String(height));
|
||||
},
|
||||
async fetchAbciInfo() {
|
||||
return this.blockchain.rpc.getBaseNodeInfo();
|
||||
},
|
||||
// async fetchNodeInfo() {
|
||||
// return this.blockchain.rpc.no()
|
||||
// }
|
||||
async fetchValidatorByHeight(height?: number, offset = 0) {
|
||||
return this.blockchain.rpc.getBaseValidatorsetAt(String(height), offset);
|
||||
},
|
||||
async fetchLatestValidators(offset = 0) {
|
||||
return this.blockchain.rpc.getBaseValidatorsetLatest(offset);
|
||||
},
|
||||
async fetchBlock(height?: number | string) {
|
||||
return this.blockchain.rpc.getBaseBlockAt(String(height));
|
||||
},
|
||||
async fetchAbciInfo() {
|
||||
return this.blockchain.rpc.getBaseNodeInfo();
|
||||
},
|
||||
// async fetchNodeInfo() {
|
||||
// return this.blockchain.rpc.no()
|
||||
// }
|
||||
},
|
||||
});
|
||||
|
||||
+14
-13
@@ -20,7 +20,7 @@ import {
|
||||
useGovStore,
|
||||
useMintStore,
|
||||
useStakingStore,
|
||||
useWalletStore
|
||||
useWalletStore,
|
||||
} from '.';
|
||||
import { useBlockModule } from '@/modules/[chain]/block/block';
|
||||
import { DEFAULT } from '@/libs';
|
||||
@@ -42,10 +42,10 @@ export const useBlockchain = defineStore('blockchain', {
|
||||
},
|
||||
getters: {
|
||||
current(): ChainConfig | undefined {
|
||||
const chain = this.dashboard.chains[this.chainName]
|
||||
const chain = this.dashboard.chains[this.chainName];
|
||||
// update chain config with dynamic updated sdk version
|
||||
const sdkversion = localStorage.getItem(`sdk_version_${this.chainName}`)
|
||||
if(sdkversion && chain?.versions) {
|
||||
const sdkversion = localStorage.getItem(`sdk_version_${this.chainName}`);
|
||||
if (sdkversion && chain?.versions) {
|
||||
chain.versions.cosmosSdk = sdkversion;
|
||||
}
|
||||
return chain;
|
||||
@@ -72,7 +72,7 @@ export const useBlockchain = defineStore('blockchain', {
|
||||
if (this.current?.themeColor) {
|
||||
const { color } = hexToRgb(this.current?.themeColor);
|
||||
const { h, s, l } = rgbToHsl(color);
|
||||
const themeColor = h + ' ' + s + '% ' + l +'%';
|
||||
const themeColor = h + ' ' + s + '% ' + l + '%';
|
||||
document.body.style.setProperty('--p', `${themeColor}`);
|
||||
// document.body.style.setProperty('--p', `${this.current?.themeColor}`);
|
||||
} else {
|
||||
@@ -157,7 +157,7 @@ export const useBlockchain = defineStore('blockchain', {
|
||||
useDistributionStore().initial();
|
||||
},
|
||||
|
||||
randomEndpoint(chainName: string) : Endpoint | undefined {
|
||||
randomEndpoint(chainName: string): Endpoint | undefined {
|
||||
const end = localStorage.getItem(`endpoint-${chainName}`);
|
||||
if (end) {
|
||||
return JSON.parse(end);
|
||||
@@ -166,14 +166,14 @@ export const useBlockchain = defineStore('blockchain', {
|
||||
if (all) {
|
||||
const rn = Math.random();
|
||||
const endpoint = all[Math.floor(rn * all.length)];
|
||||
return endpoint
|
||||
return endpoint;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async randomSetupEndpoint() {
|
||||
const endpoint = this.randomEndpoint(this.chainName)
|
||||
if(endpoint) await this.setRestEndpoint(endpoint);
|
||||
const endpoint = this.randomEndpoint(this.chainName);
|
||||
if (endpoint) await this.setRestEndpoint(endpoint);
|
||||
},
|
||||
|
||||
async setRestEndpoint(endpoint: Endpoint) {
|
||||
@@ -187,14 +187,15 @@ export const useBlockchain = defineStore('blockchain', {
|
||||
},
|
||||
async setCurrent(name: string) {
|
||||
// Ensure chains are loaded due to asynchronous calls.
|
||||
if(this.dashboard.length === 0) {
|
||||
if (this.dashboard.length === 0) {
|
||||
await this.dashboard.initial();
|
||||
}
|
||||
|
||||
// Find the case-sensitive name for the chainName, else simply use the parameter-value.
|
||||
const caseSensitiveName =
|
||||
Object.keys(this.dashboard.chains).find((x) => x.toLowerCase() === name.toLowerCase())
|
||||
|| name;
|
||||
const caseSensitiveName =
|
||||
Object.keys(this.dashboard.chains).find(
|
||||
(x) => x.toLowerCase() === name.toLowerCase()
|
||||
) || name;
|
||||
|
||||
// Update chainName if needed
|
||||
if (caseSensitiveName !== this.chainName) {
|
||||
|
||||
+76
-62
@@ -62,7 +62,7 @@ export interface ChainConfig {
|
||||
coinType: string;
|
||||
assets: Asset[];
|
||||
themeColor?: string;
|
||||
features?: string[]
|
||||
features?: string[];
|
||||
endpoints: {
|
||||
rest?: Endpoint[];
|
||||
rpc?: Endpoint[];
|
||||
@@ -77,20 +77,20 @@ export interface ChainConfig {
|
||||
exponent: string;
|
||||
excludes?: string;
|
||||
providerChain: {
|
||||
api: Endpoint[]
|
||||
api: Endpoint[];
|
||||
};
|
||||
// keplr config
|
||||
keplrFeatures?: string[],
|
||||
keplrFeatures?: string[];
|
||||
keplrPriceStep?: {
|
||||
low: number,
|
||||
average: number,
|
||||
high: number,
|
||||
},
|
||||
low: number;
|
||||
average: number;
|
||||
high: number;
|
||||
};
|
||||
faucet?: {
|
||||
amount: string,
|
||||
ip_limit: number,
|
||||
address_limit: number,
|
||||
fees: string
|
||||
amount: string;
|
||||
ip_limit: number;
|
||||
address_limit: number;
|
||||
fees: string;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -101,8 +101,8 @@ export interface LocalConfig {
|
||||
api: string[] | Endpoint[];
|
||||
grpc: Endpoint[];
|
||||
provider_chain: {
|
||||
api: string[] | Endpoint[]
|
||||
}
|
||||
api: string[] | Endpoint[];
|
||||
};
|
||||
assets: {
|
||||
base: string;
|
||||
coingecko_id: string;
|
||||
@@ -120,16 +120,16 @@ export interface LocalConfig {
|
||||
registry_name?: string;
|
||||
features?: string[];
|
||||
keplr_price_step?: {
|
||||
low: number,
|
||||
average: number,
|
||||
high: number,
|
||||
},
|
||||
keplr_features: string[],
|
||||
low: number;
|
||||
average: number;
|
||||
high: number;
|
||||
};
|
||||
keplr_features: string[];
|
||||
faucet?: {
|
||||
amount: string,
|
||||
ip_limit: number,
|
||||
address_limit: number,
|
||||
fees: string
|
||||
amount: string;
|
||||
ip_limit: number;
|
||||
address_limit: number;
|
||||
fees: string;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ function apiConverter(api: any[]) {
|
||||
|
||||
export function fromLocal(lc: LocalConfig): ChainConfig {
|
||||
const conf = {} as ChainConfig;
|
||||
if(lc.assets && Array.isArray(lc.assets)) {
|
||||
if (lc.assets && Array.isArray(lc.assets)) {
|
||||
conf.assets = lc.assets.map((x) => ({
|
||||
name: x.base,
|
||||
base: x.base,
|
||||
@@ -167,10 +167,11 @@ export function fromLocal(lc: LocalConfig): ChainConfig {
|
||||
}));
|
||||
}
|
||||
conf.versions = {
|
||||
cosmosSdk: lc.sdk_version
|
||||
}
|
||||
cosmosSdk: lc.sdk_version,
|
||||
};
|
||||
conf.bech32Prefix = lc.addr_prefix;
|
||||
conf.bech32ConsensusPrefix = lc.consensus_prefix ?? lc.addr_prefix + 'valcons';
|
||||
conf.bech32ConsensusPrefix =
|
||||
lc.consensus_prefix ?? lc.addr_prefix + 'valcons';
|
||||
conf.chainName = lc.chain_name;
|
||||
conf.coinType = lc.coin_type;
|
||||
conf.prettyName = lc.registry_name || lc.chain_name;
|
||||
@@ -179,13 +180,15 @@ export function fromLocal(lc: LocalConfig): ChainConfig {
|
||||
rpc: apiConverter(lc.rpc),
|
||||
grpc: apiConverter(lc.grpc),
|
||||
};
|
||||
if(lc.provider_chain) {
|
||||
if (lc.provider_chain) {
|
||||
conf.providerChain = {
|
||||
api: apiConverter(lc.provider_chain.api)
|
||||
}
|
||||
api: apiConverter(lc.provider_chain.api),
|
||||
};
|
||||
}
|
||||
conf.features = lc.features
|
||||
conf.logo = lc.logo.startsWith('http') ? lc.logo : `https://ping.pub${lc.logo}`;
|
||||
conf.features = lc.features;
|
||||
conf.logo = lc.logo.startsWith('http')
|
||||
? lc.logo
|
||||
: `https://ping.pub${lc.logo}`;
|
||||
conf.keplrFeatures = lc.keplr_features;
|
||||
conf.keplrPriceStep = lc.keplr_price_step;
|
||||
conf.themeColor = lc.theme_color;
|
||||
@@ -277,8 +280,7 @@ export enum ConfigSource {
|
||||
export const useDashboard = defineStore('dashboard', {
|
||||
state: () => {
|
||||
const favMap = JSON.parse(
|
||||
localStorage.getItem('favoriteMap') ||
|
||||
'{"cosmos":true, "osmosis":true}'
|
||||
localStorage.getItem('favoriteMap') || '{"cosmos":true, "osmosis":true}'
|
||||
);
|
||||
return {
|
||||
status: LoadingStatus.Empty,
|
||||
@@ -287,7 +289,10 @@ export const useDashboard = defineStore('dashboard', {
|
||||
favoriteMap: favMap as Record<string, boolean>,
|
||||
chains: {} as Record<string, ChainConfig>,
|
||||
prices: {} as Record<string, any>,
|
||||
coingecko: {} as Record<string, {coinId: string, exponent: number, symbol: string}>,
|
||||
coingecko: {} as Record<
|
||||
string,
|
||||
{ coinId: string; exponent: number; symbol: string }
|
||||
>,
|
||||
};
|
||||
},
|
||||
getters: {
|
||||
@@ -301,28 +306,33 @@ export const useDashboard = defineStore('dashboard', {
|
||||
// await this.loadingFromRegistry()
|
||||
},
|
||||
loadingPrices() {
|
||||
const coinIds = [] as string[]
|
||||
const keys = Object.keys(this.chains) // load all blockchain
|
||||
const coinIds = [] as string[];
|
||||
const keys = Object.keys(this.chains); // load all blockchain
|
||||
// Object.keys(this.favoriteMap) //only load favorite once it has too many chains
|
||||
keys.forEach(k => {
|
||||
if(Array.isArray(this.chains[k]?.assets)) this.chains[k].assets.forEach(a => {
|
||||
if(a.coingecko_id !== undefined && a.coingecko_id.length > 0) {
|
||||
coinIds.push(a.coingecko_id)
|
||||
a.denom_units.forEach(u => {
|
||||
this.coingecko[u.denom] = {
|
||||
coinId: a.coingecko_id || '',
|
||||
exponent: u.exponent,
|
||||
symbol: a.symbol
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
keys.forEach((k) => {
|
||||
if (Array.isArray(this.chains[k]?.assets))
|
||||
this.chains[k].assets.forEach((a) => {
|
||||
if (a.coingecko_id !== undefined && a.coingecko_id.length > 0) {
|
||||
coinIds.push(a.coingecko_id);
|
||||
a.denom_units.forEach((u) => {
|
||||
this.coingecko[u.denom] = {
|
||||
coinId: a.coingecko_id || '',
|
||||
exponent: u.exponent,
|
||||
symbol: a.symbol,
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const currencies = ['usd, cny'] // usd,cny,eur,jpy,krw,sgd,hkd
|
||||
get(`https://api.coingecko.com/api/v3/simple/price?include_24hr_change=true&vs_currencies=${currencies.join(',')}&ids=${coinIds.join(",")}`).then(x => {
|
||||
this.prices = x
|
||||
})
|
||||
const currencies = ['usd, cny']; // usd,cny,eur,jpy,krw,sgd,hkd
|
||||
get(
|
||||
`https://api.coingecko.com/api/v3/simple/price?include_24hr_change=true&vs_currencies=${currencies.join(
|
||||
','
|
||||
)}&ids=${coinIds.join(',')}`
|
||||
).then((x) => {
|
||||
this.prices = x;
|
||||
});
|
||||
},
|
||||
async loadingFromRegistry() {
|
||||
if (this.status === LoadingStatus.Empty) {
|
||||
@@ -336,8 +346,8 @@ export const useDashboard = defineStore('dashboard', {
|
||||
}
|
||||
},
|
||||
async loadingFromLocal() {
|
||||
if(window.location.hostname.search("testnet") > -1) {
|
||||
this.networkType = NetworkType.Testnet
|
||||
if (window.location.hostname.search('testnet') > -1) {
|
||||
this.networkType = NetworkType.Testnet;
|
||||
}
|
||||
const source: Record<string, LocalConfig> =
|
||||
this.networkType === NetworkType.Mainnet
|
||||
@@ -350,7 +360,7 @@ export const useDashboard = defineStore('dashboard', {
|
||||
this.status = LoadingStatus.Loaded;
|
||||
},
|
||||
async loadLocalConfig(network: NetworkType) {
|
||||
const config: Record<string, ChainConfig> = {}
|
||||
const config: Record<string, ChainConfig> = {};
|
||||
const source: Record<string, LocalConfig> =
|
||||
network === NetworkType.Mainnet
|
||||
? import.meta.glob('../../chains/mainnet/*.json', { eager: true })
|
||||
@@ -358,23 +368,27 @@ export const useDashboard = defineStore('dashboard', {
|
||||
Object.values<LocalConfig>(source).forEach((x: LocalConfig) => {
|
||||
config[x.chain_name] = fromLocal(x);
|
||||
});
|
||||
return config
|
||||
return config;
|
||||
},
|
||||
setupDefault() {
|
||||
if (this.length > 0) {
|
||||
const blockchain = useBlockchain();
|
||||
const keys = Object.keys(this.favoriteMap)
|
||||
const keys = Object.keys(this.favoriteMap);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
if (!blockchain.chainName && this.chains[keys[i]] && this.favoriteMap[keys[i]]) {
|
||||
if (
|
||||
!blockchain.chainName &&
|
||||
this.chains[keys[i]] &&
|
||||
this.favoriteMap[keys[i]]
|
||||
) {
|
||||
blockchain.setCurrent(keys[i]);
|
||||
break
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!blockchain.chainName) {
|
||||
const [first] = Object.keys(this.chains);
|
||||
blockchain.setCurrent(first);
|
||||
}
|
||||
this.loadingPrices()
|
||||
this.loadingPrices();
|
||||
}
|
||||
},
|
||||
setConfigSource(newSource: ConfigSource) {
|
||||
|
||||
+81
-68
@@ -13,7 +13,7 @@ import { consensusPubkeyToHexAddress, get } from '@/libs';
|
||||
import { useBankStore } from './useBankStore';
|
||||
import type { Coin, DenomTrace } from '@/types';
|
||||
import { useDashboard } from './useDashboard';
|
||||
import type { Asset } from '@ping-pub/chain-registry-client/dist/types'
|
||||
import type { Asset } from '@ping-pub/chain-registry-client/dist/types';
|
||||
|
||||
dayjs.extend(localeData);
|
||||
dayjs.extend(duration);
|
||||
@@ -72,33 +72,35 @@ export const useFormatter = defineStore('formatter', {
|
||||
return trace;
|
||||
},
|
||||
async fetchDenomMetadata(denom: string) {
|
||||
if(this.loading.includes(denom)) return
|
||||
this.loading.push(denom)
|
||||
const asset = await get(`https://metadata.ping.pub/metadata/${denom}`) as Asset
|
||||
this.ibcMetadata[denom] = asset
|
||||
if (this.loading.includes(denom)) return;
|
||||
this.loading.push(denom);
|
||||
const asset = (await get(
|
||||
`https://metadata.ping.pub/metadata/${denom}`
|
||||
)) as Asset;
|
||||
this.ibcMetadata[denom] = asset;
|
||||
},
|
||||
priceInfo(denom: string) {
|
||||
const id = this.dashboard.coingecko[denom]?.coinId || "";
|
||||
const id = this.dashboard.coingecko[denom]?.coinId || '';
|
||||
const prices = this.dashboard.prices[id];
|
||||
return prices;
|
||||
},
|
||||
color(change?: number) {
|
||||
if(!change) return ""
|
||||
if (!change) return '';
|
||||
switch (true) {
|
||||
case change > 0:
|
||||
return "text-success"
|
||||
return 'text-success';
|
||||
case change < 0:
|
||||
return "text-error"
|
||||
return 'text-error';
|
||||
default:
|
||||
return ""
|
||||
return '';
|
||||
}
|
||||
},
|
||||
priceColor(denom: string, currency = "usd") {
|
||||
const change = this.priceChanges(denom, currency)
|
||||
return this.color(change)
|
||||
priceColor(denom: string, currency = 'usd') {
|
||||
const change = this.priceChanges(denom, currency);
|
||||
return this.color(change);
|
||||
},
|
||||
price(denom: string, currency = "usd") {
|
||||
if(!denom || denom.length < 2) return 0
|
||||
price(denom: string, currency = 'usd') {
|
||||
if (!denom || denom.length < 2) return 0;
|
||||
const info = this.priceInfo(denom);
|
||||
return info ? info[currency] || 0 : 0;
|
||||
},
|
||||
@@ -107,39 +109,45 @@ export const useFormatter = defineStore('formatter', {
|
||||
return info ? info[`${currency}_24h_change`] || 0 : 0;
|
||||
},
|
||||
showChanges(v?: number) {
|
||||
return v!==0 ? numeral(v).format("+0,0"): ""
|
||||
return v !== 0 ? numeral(v).format('+0,0') : '';
|
||||
},
|
||||
tokenValue(token?: Coin) {
|
||||
if(token) {
|
||||
return numeral(this.tokenValueNumber(token)).format("0,0.[00]")
|
||||
if (token) {
|
||||
return numeral(this.tokenValueNumber(token)).format('0,0.[00]');
|
||||
}
|
||||
return ""
|
||||
return '';
|
||||
},
|
||||
specialDenom(denom: string) {
|
||||
switch(true) {
|
||||
case denom.startsWith('u'): return 6
|
||||
case denom.startsWith("a"): return 18
|
||||
case denom==='inj': return 18
|
||||
switch (true) {
|
||||
case denom.startsWith('u'):
|
||||
return 6;
|
||||
case denom.startsWith('a'):
|
||||
return 18;
|
||||
case denom === 'inj':
|
||||
return 18;
|
||||
}
|
||||
return this.exponentForDenom(denom)
|
||||
return this.exponentForDenom(denom);
|
||||
},
|
||||
tokenAmountNumber(token?: Coin) {
|
||||
if(!token || !token.denom) return 0
|
||||
if (!token || !token.denom) return 0;
|
||||
|
||||
// find the symbol
|
||||
const symbol = this.dashboard.coingecko[token.denom]?.symbol || token.denom
|
||||
const symbol =
|
||||
this.dashboard.coingecko[token.denom]?.symbol || token.denom;
|
||||
// convert denomination to symbol
|
||||
const exponent = this.dashboard.coingecko[symbol?.toLowerCase()]?.exponent || this.specialDenom(token.denom);
|
||||
const exponent =
|
||||
this.dashboard.coingecko[symbol?.toLowerCase()]?.exponent ||
|
||||
this.specialDenom(token.denom);
|
||||
// caculate amount of symbol
|
||||
const amount = Number(token.amount) / (10 ** exponent)
|
||||
return amount
|
||||
const amount = Number(token.amount) / 10 ** exponent;
|
||||
return amount;
|
||||
},
|
||||
tokenValueNumber(token?: Coin) {
|
||||
if(!token || !token.denom) return 0
|
||||
if (!token || !token.denom) return 0;
|
||||
|
||||
const amount = this.tokenAmountNumber(token)
|
||||
const value = amount * this.price(token.denom)
|
||||
return value
|
||||
const amount = this.tokenAmountNumber(token);
|
||||
const value = amount * this.price(token.denom);
|
||||
return value;
|
||||
},
|
||||
formatTokenAmount(token: { denom: string; amount: string }) {
|
||||
return this.formatToken(token, false);
|
||||
@@ -147,20 +155,20 @@ export const useFormatter = defineStore('formatter', {
|
||||
formatToken2(token: { denom: string; amount: string }, withDenom = true) {
|
||||
return this.formatToken(token, true, '0,0.[00]');
|
||||
},
|
||||
|
||||
|
||||
findGlobalAssetConfig(denom: string) {
|
||||
const chains = Object.values(this.dashboard.chains)
|
||||
for ( let i =0; i < chains.length; i++ ) {
|
||||
const assets = chains[i].assets
|
||||
const conf = assets.find(a => a.base === denom)
|
||||
if(conf) {
|
||||
return conf
|
||||
const chains = Object.values(this.dashboard.chains);
|
||||
for (let i = 0; i < chains.length; i++) {
|
||||
const assets = chains[i].assets;
|
||||
const conf = assets.find((a) => a.base === denom);
|
||||
if (conf) {
|
||||
return conf;
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
return undefined;
|
||||
},
|
||||
exponentForDenom(denom: string) {
|
||||
const asset: Asset | undefined = this.findGlobalAssetConfig(denom)
|
||||
const asset: Asset | undefined = this.findGlobalAssetConfig(denom);
|
||||
let exponent = 0;
|
||||
if (asset) {
|
||||
// find the max exponent for display
|
||||
@@ -177,17 +185,16 @@ export const useFormatter = defineStore('formatter', {
|
||||
if (denom) {
|
||||
let asset: Asset | undefined;
|
||||
if (denom && denom.startsWith('ibc/')) {
|
||||
const ibcDenom = denom.replace('ibc/', '')
|
||||
asset = this.ibcMetadata[ibcDenom];
|
||||
if(!asset) {
|
||||
const ibcDenom = denom.replace('ibc/', '');
|
||||
asset = this.ibcMetadata[ibcDenom];
|
||||
if (!asset) {
|
||||
// update ibc metadata if not exits in local cache
|
||||
this.fetchDenomMetadata(ibcDenom)
|
||||
this.fetchDenomMetadata(ibcDenom);
|
||||
} else {
|
||||
console.log("ibc metadata", asset)
|
||||
console.log('ibc metadata', asset);
|
||||
}
|
||||
|
||||
} else {
|
||||
asset = this.findGlobalAssetConfig(denom)
|
||||
asset = this.findGlobalAssetConfig(denom);
|
||||
}
|
||||
|
||||
if (asset) {
|
||||
@@ -197,7 +204,7 @@ export const useFormatter = defineStore('formatter', {
|
||||
if (x.exponent >= unit.exponent) {
|
||||
unit = x;
|
||||
}
|
||||
});
|
||||
});
|
||||
return unit.denom;
|
||||
}
|
||||
return denom;
|
||||
@@ -211,15 +218,18 @@ export const useFormatter = defineStore('formatter', {
|
||||
let amount = Number(token.amount);
|
||||
let denom = token.denom;
|
||||
|
||||
let conf = mode === 'local'? this.blockchain.current?.assets?.find(
|
||||
// @ts-ignore
|
||||
(x) => x.base === token.denom || x.base.denom === token.denom
|
||||
): this.findGlobalAssetConfig(token.denom)
|
||||
let conf =
|
||||
mode === 'local'
|
||||
? this.blockchain.current?.assets?.find(
|
||||
// @ts-ignore
|
||||
(x) => x.base === token.denom || x.base.denom === token.denom
|
||||
)
|
||||
: this.findGlobalAssetConfig(token.denom);
|
||||
|
||||
if (denom && denom.startsWith('ibc/')) {
|
||||
conf = this.ibcMetadata[denom.replace('ibc/', '')];
|
||||
if (!conf) {
|
||||
this.fetchDenomMetadata(denom.replace('ibc/', ''))
|
||||
this.fetchDenomMetadata(denom.replace('ibc/', ''));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,15 +259,18 @@ export const useFormatter = defineStore('formatter', {
|
||||
let amount = Number(token.amount);
|
||||
let denom = token.denom;
|
||||
|
||||
let conf = mode === 'local'? this.blockchain.current?.assets?.find(
|
||||
// @ts-ignore
|
||||
(x) => x.base === token.denom || x.base.denom === token.denom
|
||||
): this.findGlobalAssetConfig(token.denom)
|
||||
let conf =
|
||||
mode === 'local'
|
||||
? this.blockchain.current?.assets?.find(
|
||||
// @ts-ignore
|
||||
(x) => x.base === token.denom || x.base.denom === token.denom
|
||||
)
|
||||
: this.findGlobalAssetConfig(token.denom);
|
||||
|
||||
if (denom && denom.startsWith('ibc/')) {
|
||||
conf = this.ibcMetadata[denom.replace('ibc/', '')];
|
||||
if (!conf) {
|
||||
this.fetchDenomMetadata(denom.replace('ibc/', ''))
|
||||
this.fetchDenomMetadata(denom.replace('ibc/', ''));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,11 +287,11 @@ export const useFormatter = defineStore('formatter', {
|
||||
denom = unit.denom.toUpperCase();
|
||||
}
|
||||
}
|
||||
if(amount < 0.000001) {
|
||||
if (amount < 0.000001) {
|
||||
return `0 ${denom.substring(0, 10)}`;
|
||||
}
|
||||
if(amount < 0.01) {
|
||||
fmt = '0.[000000]'
|
||||
if (amount < 0.01) {
|
||||
fmt = '0.[000000]';
|
||||
}
|
||||
return `${numeral(amount).format(fmt)} ${
|
||||
withDenom ? denom.substring(0, 10) : ''
|
||||
@@ -338,17 +351,17 @@ export const useFormatter = defineStore('formatter', {
|
||||
return decimal ? numeral(decimal).format('0.[00]%') : '-';
|
||||
},
|
||||
formatNumber(input?: number, fmt = '0.[00]') {
|
||||
if(!input) return ""
|
||||
return numeral(input).format(fmt)
|
||||
if (!input) return '';
|
||||
return numeral(input).format(fmt);
|
||||
},
|
||||
numberAndSign(input: number, fmt = '+0,0') {
|
||||
return numeral(input).format(fmt);
|
||||
},
|
||||
toLocaleDate(time?: string | number | Date) {
|
||||
if(!time) return ""
|
||||
return new Date(time).toLocaleString(navigator.language)
|
||||
if (!time) return '';
|
||||
return new Date(time).toLocaleString(navigator.language);
|
||||
},
|
||||
toDay(time?: string | number| Date, format = 'long') {
|
||||
toDay(time?: string | number | Date, format = 'long') {
|
||||
if (!time) return '';
|
||||
if (format === 'long') {
|
||||
return dayjs(time).format('YYYY-MM-DD HH:mm');
|
||||
|
||||
@@ -39,10 +39,10 @@ export const useGovStore = defineStore('govStore', {
|
||||
);
|
||||
|
||||
//filter spam proposals
|
||||
if(proposals?.proposals) {
|
||||
if (proposals?.proposals) {
|
||||
proposals.proposals = proposals.proposals.filter((item) => {
|
||||
const title = item.content?.title || item.title || ""
|
||||
return title.toLowerCase().indexOf("airdrop")===-1
|
||||
const title = item.content?.title || item.title || '';
|
||||
return title.toLowerCase().indexOf('airdrop') === -1;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -58,17 +58,18 @@ export const useGovStore = defineStore('govStore', {
|
||||
this.walletstore.currentAddress
|
||||
)
|
||||
.then((res) => {
|
||||
item.voterStatus = res?.vote?.option || 'VOTE_OPTION_NO_WITH_VETO'
|
||||
item.voterStatus =
|
||||
res?.vote?.option || 'VOTE_OPTION_NO_WITH_VETO';
|
||||
// 'No With Veto';
|
||||
})
|
||||
.catch((reject) => {
|
||||
item.voterStatus = 'VOTE_OPTION_NO_WITH_VETO'
|
||||
item.voterStatus = 'VOTE_OPTION_NO_WITH_VETO';
|
||||
});
|
||||
} catch (error) {
|
||||
item.voterStatus = 'VOTE_OPTION_NO_WITH_VETO'
|
||||
item.voterStatus = 'VOTE_OPTION_NO_WITH_VETO';
|
||||
}
|
||||
} else {
|
||||
item.voterStatus = 'VOTE_OPTION_NO_WITH_VETO'
|
||||
item.voterStatus = 'VOTE_OPTION_NO_WITH_VETO';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -194,8 +194,11 @@ export const useParamStore = defineStore('paramstore', {
|
||||
async handleAbciInfo() {
|
||||
const res = await this.fetchAbciInfo();
|
||||
|
||||
localStorage.setItem(`sdk_version_${this.blockchain.chainName}`, res.application_version?.cosmos_sdk_version);
|
||||
|
||||
localStorage.setItem(
|
||||
`sdk_version_${this.blockchain.chainName}`,
|
||||
res.application_version?.cosmos_sdk_version
|
||||
);
|
||||
|
||||
this.appVersion.items = Object.entries(res.application_version).map(
|
||||
([key, value]) => ({ subtitle: key, value: value })
|
||||
);
|
||||
|
||||
@@ -4,8 +4,18 @@ import { useBlockchain } from './useBlockchain';
|
||||
import { get } from '@/libs/http';
|
||||
import type { StakingParam, StakingPool, Validator } from '@/types';
|
||||
import { CosmosRestClient } from '@/libs/client';
|
||||
import { consensusPubkeyToHexAddress, pubKeyToValcons, valconsToBase64 } from '@/libs';
|
||||
import { toHex, fromBase64, toBase64, fromHex, fromBech32 } from '@cosmjs/encoding';
|
||||
import {
|
||||
consensusPubkeyToHexAddress,
|
||||
pubKeyToValcons,
|
||||
valconsToBase64,
|
||||
} from '@/libs';
|
||||
import {
|
||||
toHex,
|
||||
fromBase64,
|
||||
toBase64,
|
||||
fromHex,
|
||||
fromBech32,
|
||||
} from '@cosmjs/encoding';
|
||||
import { useBaseStore } from './useBaseStore';
|
||||
|
||||
export const useStakingStore = defineStore('stakingStore', {
|
||||
@@ -67,7 +77,7 @@ export const useStakingStore = defineStore('stakingStore', {
|
||||
},
|
||||
async fetchInacitveValdiators() {
|
||||
return this.fetchValidators('BOND_STATUS_UNBONDED');
|
||||
},
|
||||
},
|
||||
async fetchUnbondingValdiators() {
|
||||
return this.fetchValidators('BOND_STATUS_UNBONDING');
|
||||
},
|
||||
@@ -83,84 +93,112 @@ export const useStakingStore = defineStore('stakingStore', {
|
||||
delegatorAddr
|
||||
);
|
||||
},
|
||||
async fetchKeyRotation(chain_id: string, validatorAddr: string ) : Promise<string> {
|
||||
if(this.blockchain.isConsumerChain) {
|
||||
if(this.blockchain.current?.providerChain.api && this.blockchain.current.providerChain.api.length > 0) {
|
||||
const signatures = useBaseStore().latest?.block?.last_commit.signatures
|
||||
if(signatures) {
|
||||
async fetchKeyRotation(
|
||||
chain_id: string,
|
||||
validatorAddr: string
|
||||
): Promise<string> {
|
||||
if (this.blockchain.isConsumerChain) {
|
||||
if (
|
||||
this.blockchain.current?.providerChain.api &&
|
||||
this.blockchain.current.providerChain.api.length > 0
|
||||
) {
|
||||
const signatures =
|
||||
useBaseStore().latest?.block?.last_commit.signatures;
|
||||
if (signatures) {
|
||||
// console.log(signatures)
|
||||
const key = toBase64(fromHex(valconsToBase64(validatorAddr)))
|
||||
const exists = signatures.findIndex((x) => x.validator_address === key)
|
||||
if(exists < 0) {
|
||||
const key = toBase64(fromHex(valconsToBase64(validatorAddr)));
|
||||
const exists = signatures.findIndex(
|
||||
(x) => x.validator_address === key
|
||||
);
|
||||
if (exists < 0) {
|
||||
const client = CosmosRestClient.newDefault(
|
||||
this.blockchain.current.providerChain.api[0].address
|
||||
);
|
||||
|
||||
const client = CosmosRestClient.newDefault(this.blockchain.current.providerChain.api[0].address)
|
||||
|
||||
client.getInterchainSecurityProviderOptedInValidators(chain_id).then((res) => {
|
||||
console.log(res)
|
||||
})
|
||||
const res = await client.getInterchainSecurityValidatorRotatedKey(chain_id, validatorAddr);
|
||||
if(res.consumer_address) {
|
||||
this.keyRotation[validatorAddr] = res.consumer_address
|
||||
localStorage.setItem(`key-rotation-${chain_id}`, JSON.stringify(this.keyRotation))
|
||||
client
|
||||
.getInterchainSecurityProviderOptedInValidators(chain_id)
|
||||
.then((res) => {
|
||||
console.log(res);
|
||||
});
|
||||
const res = await client.getInterchainSecurityValidatorRotatedKey(
|
||||
chain_id,
|
||||
validatorAddr
|
||||
);
|
||||
if (res.consumer_address) {
|
||||
this.keyRotation[validatorAddr] = res.consumer_address;
|
||||
localStorage.setItem(
|
||||
`key-rotation-${chain_id}`,
|
||||
JSON.stringify(this.keyRotation)
|
||||
);
|
||||
}
|
||||
return res.consumer_address
|
||||
return res.consumer_address;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
return '';
|
||||
},
|
||||
|
||||
async loadKeyRotationFromLocalstorage(chain_id: string) {
|
||||
const keyRotation = localStorage.getItem(`key-rotation-${chain_id}`)
|
||||
this.keyRotation = keyRotation ? JSON.parse(keyRotation) : {}
|
||||
const keyRotation = localStorage.getItem(`key-rotation-${chain_id}`);
|
||||
this.keyRotation = keyRotation ? JSON.parse(keyRotation) : {};
|
||||
},
|
||||
|
||||
findRotatedHexAddress(key: {
|
||||
"@type": string;
|
||||
key: string;
|
||||
}) {
|
||||
|
||||
const prefix = "cosmos"
|
||||
const conskey = pubKeyToValcons(key, prefix)
|
||||
const rotated = this.keyRotation[conskey]
|
||||
if(rotated) {
|
||||
return valconsToBase64(rotated)
|
||||
findRotatedHexAddress(key: { '@type': string; key: string }) {
|
||||
const prefix = 'cosmos';
|
||||
const conskey = pubKeyToValcons(key, prefix);
|
||||
const rotated = this.keyRotation[conskey];
|
||||
if (rotated) {
|
||||
return valconsToBase64(rotated);
|
||||
}
|
||||
return consensusPubkeyToHexAddress(key)
|
||||
|
||||
return consensusPubkeyToHexAddress(key);
|
||||
},
|
||||
async getConsumerValidators(chain_id: string) {
|
||||
if(this.blockchain.current?.providerChain?.api && this.blockchain.current.providerChain.api.length > 0) {
|
||||
const client = CosmosRestClient.newDefault(this.blockchain.current.providerChain.api[0].address)
|
||||
await client.getStakingValidators('BOND_STATUS_BONDED', 500).then((res) => { this.validators = res.validators });
|
||||
const id_map = {"neutron": "0", "stride": "1"} as Record<string, string>;
|
||||
const consumer_id = Object.keys(id_map).find((k) => chain_id.startsWith(k)) || 0;
|
||||
return client.getInterchainSecurityConsumerValidators(id_map[consumer_id])
|
||||
if (
|
||||
this.blockchain.current?.providerChain?.api &&
|
||||
this.blockchain.current.providerChain.api.length > 0
|
||||
) {
|
||||
const client = CosmosRestClient.newDefault(
|
||||
this.blockchain.current.providerChain.api[0].address
|
||||
);
|
||||
await client
|
||||
.getStakingValidators('BOND_STATUS_BONDED', 500)
|
||||
.then((res) => {
|
||||
this.validators = res.validators;
|
||||
});
|
||||
const id_map = { neutron: '0', stride: '1' } as Record<string, string>;
|
||||
const consumer_id =
|
||||
Object.keys(id_map).find((k) => chain_id.startsWith(k)) || 0;
|
||||
return client.getInterchainSecurityConsumerValidators(
|
||||
id_map[consumer_id]
|
||||
);
|
||||
} else {
|
||||
return { validators: [] }
|
||||
return { validators: [] };
|
||||
}
|
||||
},
|
||||
async fetchAllKeyRotation(chain_id: string) {
|
||||
let vs = []
|
||||
for(const val of this.validators) {
|
||||
const { prefix } = fromBech32(val.operator_address)
|
||||
const cons = pubKeyToValcons(val.consensus_pubkey, prefix.replace('valoper','valcons'))
|
||||
vs.push(cons)
|
||||
let vs = [];
|
||||
for (const val of this.validators) {
|
||||
const { prefix } = fromBech32(val.operator_address);
|
||||
const cons = pubKeyToValcons(
|
||||
val.consensus_pubkey,
|
||||
prefix.replace('valoper', 'valcons')
|
||||
);
|
||||
vs.push(cons);
|
||||
}
|
||||
},
|
||||
async fetchValidators(status: string, limit = 300) {
|
||||
return this.blockchain.rpc?.getStakingValidators(status, limit).then((res) => {
|
||||
const vals = res.validators.sort(
|
||||
(a, b) => Number(b.delegator_shares) - Number(a.delegator_shares)
|
||||
);
|
||||
if (status === 'BOND_STATUS_BONDED') {
|
||||
this.validators = vals;
|
||||
}
|
||||
return vals;
|
||||
});
|
||||
return this.blockchain.rpc
|
||||
?.getStakingValidators(status, limit)
|
||||
.then((res) => {
|
||||
const vals = res.validators.sort(
|
||||
(a, b) => Number(b.delegator_shares) - Number(a.delegator_shares)
|
||||
);
|
||||
if (status === 'BOND_STATUS_BONDED') {
|
||||
this.validators = vals;
|
||||
}
|
||||
return vals;
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
|
||||
+36
-28
@@ -1,68 +1,76 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { useWalletStore } from './useWalletStore';
|
||||
import { useBlockchain } from './useBlockchain';
|
||||
import router from '@/router'
|
||||
import router from '@/router';
|
||||
|
||||
let CALLBACK: any = null
|
||||
let CALLBACK: any = null;
|
||||
|
||||
export const useTxDialog = defineStore('txDialogStore', {
|
||||
state: () => {
|
||||
return {
|
||||
sender: "",
|
||||
type: "send",
|
||||
endpoint: "",
|
||||
params: "",
|
||||
sender: '',
|
||||
type: 'send',
|
||||
endpoint: '',
|
||||
params: '',
|
||||
};
|
||||
},
|
||||
getters: {
|
||||
walletAddress() {
|
||||
return useWalletStore().currentAddress
|
||||
return useWalletStore().currentAddress;
|
||||
},
|
||||
currentEndpoint() {
|
||||
return useBlockchain().endpoint?.address
|
||||
return useBlockchain().endpoint?.address;
|
||||
},
|
||||
blockchain() {
|
||||
return useBlockchain()
|
||||
return useBlockchain();
|
||||
},
|
||||
hdPaths() {
|
||||
return useBlockchain().defaultHDPath
|
||||
return useBlockchain().defaultHDPath;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setParams(param: any) {
|
||||
this.params = JSON.stringify(param)
|
||||
this.params = JSON.stringify(param);
|
||||
},
|
||||
openWithArgument(type: string, sender: string, endpoint: string, param: any) {
|
||||
openWithArgument(
|
||||
type: string,
|
||||
sender: string,
|
||||
endpoint: string,
|
||||
param: any
|
||||
) {
|
||||
this.type = type;
|
||||
this.sender = sender;
|
||||
this.endpoint = endpoint;
|
||||
this.params = JSON.stringify(param)
|
||||
this.params = JSON.stringify(param);
|
||||
},
|
||||
open(type: string, param: any, callback?: Function) {
|
||||
this.type = type;
|
||||
this.sender = this.walletAddress;
|
||||
this.endpoint = this.currentEndpoint || "";
|
||||
this.params = JSON.stringify(param)
|
||||
if(callback) {
|
||||
CALLBACK = callback
|
||||
}else {
|
||||
CALLBACK = undefined
|
||||
this.endpoint = this.currentEndpoint || '';
|
||||
this.params = JSON.stringify(param);
|
||||
if (callback) {
|
||||
CALLBACK = callback;
|
||||
} else {
|
||||
CALLBACK = undefined;
|
||||
}
|
||||
},
|
||||
view(tx: {
|
||||
detail: {
|
||||
eventType: string,
|
||||
hash: string
|
||||
}
|
||||
eventType: string;
|
||||
hash: string;
|
||||
};
|
||||
}) {
|
||||
console.log(tx.detail)
|
||||
if (tx.detail && tx.detail.hash) router.push({ path: `/${this.blockchain.chainName}/tx/${tx.detail.hash}` })
|
||||
console.log(tx.detail);
|
||||
if (tx.detail && tx.detail.hash)
|
||||
router.push({
|
||||
path: `/${this.blockchain.chainName}/tx/${tx.detail.hash}`,
|
||||
});
|
||||
},
|
||||
confirmed(tx: any) {
|
||||
console.log("confirmed:", tx)
|
||||
if(CALLBACK) {
|
||||
CALLBACK()
|
||||
console.log('confirmed:', tx);
|
||||
if (CALLBACK) {
|
||||
CALLBACK();
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ import type {
|
||||
WalletConnected,
|
||||
} from '@/types';
|
||||
import { useStakingStore } from './useStakingStore';
|
||||
import router from '@/router'
|
||||
import router from '@/router';
|
||||
|
||||
export const useWalletStore = defineStore('walletStore', {
|
||||
state: () => {
|
||||
@@ -17,8 +17,8 @@ export const useWalletStore = defineStore('walletStore', {
|
||||
balances: [] as Coin[],
|
||||
delegations: [] as Delegation[],
|
||||
unbonding: [] as UnbondingResponses[],
|
||||
rewards: {total: [], rewards: []} as DelegatorRewards,
|
||||
wallet: {} as WalletConnected
|
||||
rewards: { total: [], rewards: [] } as DelegatorRewards,
|
||||
wallet: {} as WalletConnected,
|
||||
};
|
||||
},
|
||||
getters: {
|
||||
@@ -27,11 +27,11 @@ export const useWalletStore = defineStore('walletStore', {
|
||||
},
|
||||
connectedWallet() {
|
||||
// @ts-ignore
|
||||
if(this.wallet.cosmosAddress) return this.wallet
|
||||
if (this.wallet.cosmosAddress) return this.wallet;
|
||||
const chainStore = useBlockchain();
|
||||
const key = chainStore.defaultHDPath;
|
||||
const connected = JSON.parse(localStorage.getItem(key) || '{}');
|
||||
return connected
|
||||
return connected;
|
||||
},
|
||||
balanceOfStakingToken(): Coin {
|
||||
const stakingStore = useStakingStore();
|
||||
@@ -77,15 +77,14 @@ export const useWalletStore = defineStore('walletStore', {
|
||||
return toBech32(chainStore.current?.bech32Prefix || prefix, data);
|
||||
},
|
||||
shortAddress() {
|
||||
const address: string = this.currentAddress
|
||||
if(address.length > 4) {
|
||||
return `${address.substring(address.length -4)}`
|
||||
const address: string = this.currentAddress;
|
||||
if (address.length > 4) {
|
||||
return `${address.substring(address.length - 4)}`;
|
||||
}
|
||||
return ""
|
||||
}
|
||||
return '';
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
|
||||
async loadMyAsset() {
|
||||
if (!this.currentAddress) return;
|
||||
this.blockchain.rpc.getBankBalances(this.currentAddress).then((x) => {
|
||||
@@ -122,17 +121,17 @@ export const useWalletStore = defineStore('walletStore', {
|
||||
const chainStore = useBlockchain();
|
||||
const key = chainStore.defaultHDPath;
|
||||
localStorage.removeItem(key);
|
||||
this.$reset()
|
||||
this.$reset();
|
||||
},
|
||||
setConnectedWallet(value: WalletConnected) {
|
||||
if(value) this.wallet = value
|
||||
if (value) this.wallet = value;
|
||||
},
|
||||
suggestChain() {
|
||||
if (window.location.pathname === '/SIDE-Testnet') {
|
||||
router.push({path: '/wallet/unisat'})
|
||||
router.push({ path: '/wallet/unisat' });
|
||||
} else {
|
||||
router.push({path: '/wallet/keplr'})
|
||||
router.push({ path: '/wallet/keplr' });
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user