feat: format code

This commit is contained in:
Alisa | Side.one 2023-05-06 22:56:04 +08:00
parent b17d6fd6bf
commit 5c3118116f
85 changed files with 4308 additions and 3261 deletions

6
.idea/vcs.xml generated
View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

106
.idea/workspace.xml generated
View File

@ -1,106 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="863a12bf-d142-4126-a636-76cbfb7ec337" name="Changes" comment="" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="MacroExpansionManager">
<option name="directoryName" value="scny7heo" />
</component>
<component name="MarkdownSettingsMigration">
<option name="stateVersion" value="1" />
</component>
<component name="MavenImportPreferences">
<option name="generalSettings">
<MavenGeneralSettings>
<option name="mavenHome" value="$APPLICATION_HOME_DIR$/plugins/maven/lib/maven3" />
</MavenGeneralSettings>
</option>
</component>
<component name="ProjectId" id="2MyxeMOFrRrG7rSZlmfjzrCBu0L" />
<component name="ProjectViewState">
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;RunOnceActivity.OpenProjectViewOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;/Users/ping/workspace/dashboard&quot;
}
}</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="SvnConfiguration">
<configuration>$USER_HOME$/.subversion</configuration>
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="863a12bf-d142-4126-a636-76cbfb7ec337" name="Changes" comment="" />
<created>1678753718354</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1678753718354</updated>
</task>
<servers />
</component>
<component name="Vcs.Log.History.Properties">
<option name="COLUMN_ID_ORDER">
<list>
<option value="Default.Root" />
<option value="Default.Author" />
<option value="Default.Date" />
<option value="Default.Subject" />
<option value="Space.CommitStatus" />
</list>
</option>
</component>
<component name="Vcs.Log.Tabs.Properties">
<option name="TAB_STATES">
<map>
<entry key="9c0ade11-225d-412a-bada-509908951784">
<value>
<State>
<option name="SHOW_ONLY_AFFECTED_CHANGES" value="true" />
<option name="FILTERS">
<map>
<entry key="branch">
<value>
<list>
<option value="HEAD" />
</list>
</value>
</entry>
<entry key="roots">
<value>
<list>
<option value="$PROJECT_DIR$" />
</list>
</value>
</entry>
</map>
</option>
</State>
</value>
</entry>
<entry key="MAIN">
<value>
<State />
</value>
</entry>
</map>
</option>
<option name="OPEN_GENERIC_TABS">
<map>
<entry key="9c0ade11-225d-412a-bada-509908951784" value="TOOL_WINDOW" />
</map>
</option>
</component>
</project>

View File

@ -1,26 +1,34 @@
<script setup lang="ts"> <script setup lang="ts">
import { useTheme } from 'vuetify' import { useTheme } from 'vuetify';
import { useThemeConfig } from '@/plugins/vuetify/@core/composable/useThemeConfig' import { useThemeConfig } from '@/plugins/vuetify/@core/composable/useThemeConfig';
import { hexToRgb } from '@/plugins/vuetify/@layouts/utils' import { hexToRgb } from '@/plugins/vuetify/@layouts/utils';
import { themeChange } from 'theme-change' import { themeChange } from 'theme-change';
import { onMounted } from 'vue' import { onMounted } from 'vue';
const { syncInitialLoaderTheme, syncVuetifyThemeWithTheme: syncConfigThemeWithVuetifyTheme, isAppRtl } = useThemeConfig() const {
syncInitialLoaderTheme,
syncVuetifyThemeWithTheme: syncConfigThemeWithVuetifyTheme,
isAppRtl,
} = useThemeConfig();
const { global } = useTheme() const { global } = useTheme();
// Sync current theme with initial loader theme // Sync current theme with initial loader theme
syncInitialLoaderTheme() syncInitialLoaderTheme();
syncConfigThemeWithVuetifyTheme() syncConfigThemeWithVuetifyTheme();
onMounted(() => { onMounted(() => {
themeChange(false) themeChange(false);
}) });
</script> </script>
<template> <template>
<VLocaleProvider :rtl="isAppRtl"> <VLocaleProvider :rtl="isAppRtl">
<!-- This is required to set the background color of active nav link based on currently active global theme's primary --> <!-- This is required to set the background color of active nav link based on currently active global theme's primary -->
<VApp :style="`--v-global-theme-primary: ${hexToRgb(global.current.value.colors.primary)}`"> <VApp
:style="`--v-global-theme-primary: ${hexToRgb(
global.current.value.colors.primary
)}`"
>
<RouterView /> <RouterView />
</VApp> </VApp>
</VLocaleProvider> </VLocaleProvider>

View File

@ -1,26 +1,26 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import { useFormatter } from "@/stores"; import { useFormatter } from '@/stores';
const props = defineProps({ const props = defineProps({
cardItem: { cardItem: {
type: Object as PropType<{ title: string; items: Array<any> }>, type: Object as PropType<{ title: string; items: Array<any> }>,
}, },
}); });
const formatter = useFormatter() const formatter = useFormatter();
function calculateValue(value: any) { function calculateValue(value: any) {
if (Array.isArray(value)) { if (Array.isArray(value)) {
return (value[0] && value[0].amount)|| '-' return (value[0] && value[0].amount) || '-';
} }
const newValue = Number(value) const newValue = Number(value);
if(`${newValue}` === 'NaN' || typeof(value) === 'boolean'){ if (`${newValue}` === 'NaN' || typeof value === 'boolean') {
return value return value;
} }
if (newValue < 1 && newValue > 0) { if (newValue < 1 && newValue > 0) {
return formatter.formatDecimalToPercent(value) return formatter.formatDecimalToPercent(value);
} }
return newValue return newValue;
} }
</script> </script>
<template> <template>

View File

@ -2,18 +2,21 @@
import { controlledComputed } from '@vueuse/shared'; import { controlledComputed } from '@vueuse/shared';
interface Props { interface Props {
title: string title: string;
color?: string color?: string;
icon: string icon: string;
stats: number stats: number;
change?: number change?: number;
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
color: 'primary', color: 'primary',
}) });
const isPositive = controlledComputed(() => props.change, () => Math.sign(props.change||0) === 1) const isPositive = controlledComputed(
() => props.change,
() => Math.sign(props.change || 0) === 1
);
</script> </script>
<template> <template>
@ -26,16 +29,13 @@ const isPositive = controlledComputed(() => props.change, () => Math.sign(props.
variant="tonal" variant="tonal"
class="me-4" class="me-4"
> >
<VIcon <VIcon :icon="props.icon" size="24" />
:icon="props.icon"
size="24"
/>
</VAvatar> </VAvatar>
<div class="d-flex flex-column"> <div class="d-flex flex-column">
<div class="d-flex align-center flex-wrap"> <div class="d-flex align-center flex-wrap">
<h6 class="text-h6"> <h6 class="text-h6">
{{ (props.stats) }} {{ props.stats }}
</h6> </h6>
<div <div
v-if="props.change" v-if="props.change"

View File

@ -21,7 +21,13 @@ const isPositive = controlledComputed(
<template> <template>
<VCard class="h-full flex-col content-between"> <VCard class="h-full flex-col content-between">
<VCardText class="d-flex align-center justify-between"> <VCardText class="d-flex align-center justify-between">
<VAvatar v-if="props.icon" rounded size="38" variant="tonal" :color="props.color"> <VAvatar
v-if="props.icon"
rounded
size="38"
variant="tonal"
:color="props.color"
>
<VIcon :icon="props.icon" size="24" /> <VIcon :icon="props.icon" size="24" />
</VAvatar> </VAvatar>

View File

@ -1,19 +1,22 @@
<script setup lang="ts"> <script setup lang="ts">
interface Props { interface Props {
title: string title: string;
subtitle: string subtitle: string;
stats: string stats: string;
change: number change: number;
image: string image: string;
imgWidth: number imgWidth: number;
color?: string color?: string;
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
color: 'primary', color: 'primary',
}) });
const isPositive = controlledComputed(() => props.change, () => Math.sign(props.change) === 1) const isPositive = controlledComputed(
() => props.change,
() => Math.sign(props.change) === 1
);
</script> </script>
<template> <template>
@ -47,11 +50,7 @@ const isPositive = controlledComputed(() => props.change, () => Math.sign(props.
<VSpacer /> <VSpacer />
<div class="illustrator-img"> <div class="illustrator-img">
<VImg <VImg v-if="props.image" :src="props.image" :width="props.imgWidth" />
v-if="props.image"
:src="props.image"
:width="props.imgWidth"
/>
</div> </div>
</VCard> </VCard>
</template> </template>

View File

@ -3,10 +3,15 @@ import VueCountdown from '@chenfengyuan/vue-countdown';
const props = defineProps({ const props = defineProps({
time: { type: Number }, time: { type: Number },
}) });
</script> </script>
<template> <template>
<vue-countdown v-if="time" :time="time > 0? time: 0" v-slot="{ days, hours, minutes, seconds }"> <vue-countdown
Time Remaining{{ days }} days, {{ hours }} hours, {{ minutes }} minutes, {{ seconds }} seconds. v-if="time"
:time="time > 0 ? time : 0"
v-slot="{ days, hours, minutes, seconds }"
>
Time Remaining{{ days }} days, {{ hours }} hours, {{ minutes }} minutes,
{{ seconds }} seconds.
</vue-countdown> </vue-countdown>
</template> </template>

View File

@ -15,17 +15,28 @@ const props = defineProps({
}); });
const total = computed(() => props.pool?.bonded_tokens); const total = computed(() => props.pool?.bonded_tokens);
const format = useFormatter(); const format = useFormatter();
const yes = computed(() => format.calculatePercent(props.tally?.yes, total.value)); const yes = computed(() =>
const no = computed(() => format.calculatePercent(props.tally?.no, total.value)); format.calculatePercent(props.tally?.yes, total.value)
const abstain = computed(() => format.calculatePercent(props.tally?.abstain, total.value)); );
const veto = computed(() => format.calculatePercent(props.tally?.no_with_veto, total.value)); const no = computed(() =>
format.calculatePercent(props.tally?.no, total.value)
);
const abstain = computed(() =>
format.calculatePercent(props.tally?.abstain, total.value)
);
const veto = computed(() =>
format.calculatePercent(props.tally?.no_with_veto, total.value)
);
</script> </script>
<template> <template>
<div class="progress rounded-full h-1 text-xs flex items-center"> <div class="progress rounded-full h-1 text-xs flex items-center">
<div class="h-1 bg-yes" :style="`width: ${yes}`"></div> <div class="h-1 bg-yes" :style="`width: ${yes}`"></div>
<div class="h-1 bg-no" :style="`width: ${no}`"></div> <div class="h-1 bg-no" :style="`width: ${no}`"></div>
<div class="h-1" :style="`width: ${veto}; background-color: #B71C1C;`"></div> <div
class="h-1"
:style="`width: ${veto}; background-color: #B71C1C;`"
></div>
<div class="h-1 bg-secondary" :style="`width: ${abstain}`"></div> <div class="h-1 bg-secondary" :style="`width: ${abstain}`"></div>
</div> </div>
</template> </template>

View File

@ -14,7 +14,7 @@ const {
next: getNextThemeName, next: getNextThemeName,
index: currentThemeIndex, index: currentThemeIndex,
} = useCycleList( } = useCycleList(
props.themes.map(t => t.name), props.themes.map((t) => t.name),
{ initialValue: theme.value } { initialValue: theme.value }
); );
@ -24,18 +24,20 @@ const changeTheme = () => {
const changeMode = (val: 'dark' | 'light' | 'system') => { const changeMode = (val: 'dark' | 'light' | 'system') => {
let value = val; let value = val;
if (theme.value === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches) { if (
theme.value === 'system' &&
window.matchMedia('(prefers-color-scheme: dark)').matches
) {
value = 'dark'; value = 'dark';
} }
if (value === 'dark') { if (value === 'dark') {
document.documentElement.classList.add('dark'); document.documentElement.classList.add('dark');
document.documentElement.classList.remove('light'); document.documentElement.classList.remove('light');
} else { } else {
document.documentElement.classList.add('light'); document.documentElement.classList.add('light');
document.documentElement.classList.remove('dark'); document.documentElement.classList.remove('dark');
} }
document.documentElement.setAttribute("data-theme", value); document.documentElement.setAttribute('data-theme', value);
}; };
// Update icon if theme is changed from other sources // Update icon if theme is changed from other sources
watch(theme, (val: 'dark' | 'light' | 'system') => { watch(theme, (val: 'dark' | 'light' | 'system') => {
@ -52,10 +54,7 @@ onMounted(() => {
<template> <template>
<div class="tooltip tooltip-bottom delay-1000" :data-tip="currentThemeName"> <div class="tooltip tooltip-bottom delay-1000" :data-tip="currentThemeName">
<button <button class="btn btn-ghost btn-circle btn-sm mx-1" @click="changeTheme">
class="btn btn-ghost btn-circle btn-sm mx-1"
@click="changeTheme"
>
<Icon :icon="props.themes[currentThemeIndex].icon" class="text-2xl" /> <Icon :icon="props.themes[currentThemeIndex].icon" class="text-2xl" />
</button> </button>
</div> </div>

View File

@ -1,32 +1,37 @@
<script lang=ts setup> <script lang="ts" setup>
import type { Commit } from "@/types"; import type { Commit } from '@/types';
const props = defineProps({ const props = defineProps({
blocks: { type: Array as PropType<Commit[]> }, blocks: { type: Array as PropType<Commit[]> },
validator: { type: String }, validator: { type: String },
}) });
const bars = computed(() => { const bars = computed(() => {
const uptime = Array(50).fill({height:0, color: 'bg-secondary'}) const uptime = Array(50).fill({ height: 0, color: 'bg-secondary' });
props.blocks.forEach(element => { props.blocks.forEach((element) => {
const has = element.signatures?.findIndex(sig => sig.validator_address === props.validator ) const has = element.signatures?.findIndex(
(sig) => sig.validator_address === props.validator
);
// console.log(has, props.validato, element) // console.log(has, props.validato, element)
uptime.push({ uptime.push({
height: element.height, height: element.height,
color: has > -1 ? 'bg-success' : 'bg-error' color: has > -1 ? 'bg-success' : 'bg-error',
}) });
uptime.shift() uptime.shift();
});
return uptime;
}); });
return uptime
})
</script> </script>
<template> <template>
<div class="d-flex justify-evenly"> <div class="d-flex justify-evenly">
<span v-for="(b,i) in bars" <span v-for="(b, i) in bars" :key="i" :class="b.color" style="width: 1.5%"
:key="i" >&nbsp;
:class="b.color" <v-tooltip
style="width:1.5%">&nbsp; v-if="Number(b.height) > 0"
<v-tooltip v-if="Number(b.height) > 0" activator="parent" location="top">{{ b.height }}</v-tooltip> activator="parent"
location="top"
>{{ b.height }}</v-tooltip
>
</span> </span>
</div> </div>
</template> </template>

View File

@ -1,44 +1,61 @@
<script setup lang="ts"> <script setup lang="ts">
import VueApexCharts from 'vue3-apexcharts' import VueApexCharts from 'vue3-apexcharts';
import { useTheme } from 'vuetify' import { useTheme } from 'vuetify';
import { hexToRgb } from '@/plugins/vuetify/@layouts/utils' import { hexToRgb } from '@/plugins/vuetify/@layouts/utils';
import { computed, type PropType } from 'vue'; import { computed, type PropType } from 'vue';
import { useFormatter } from '@/stores'; import { useFormatter } from '@/stores';
import type { CommissionRate } from '@/types' import type { CommissionRate } from '@/types';
const props = defineProps({ 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)
let change = computed(() => Number(props.commission?.commission_rates.max_change_rate || 0) * 100)
let max = computed(() => Number(props.commission?.commission_rates.max_rate || 1) * 100)
let rate = computed(
() => Number(props.commission?.commission_rates.rate || 0) * 100
);
let change = computed(
() => Number(props.commission?.commission_rates.max_change_rate || 0) * 100
);
let max = computed(
() => Number(props.commission?.commission_rates.max_rate || 1) * 100
);
// const rate = 15 // props.commision?.commissionRates.rate // const rate = 15 // props.commision?.commissionRates.rate
// const change = 15 // const change = 15
// const max = 20 // const max = 20
const left = rate const left = rate;
const right = computed(() => max.value - rate.value) const right = computed(() => max.value - rate.value);
const s1 = computed(() => left.value > change.value ? left.value - change.value : 0 ) const s1 = computed(() =>
const s2 = computed(() => left.value > change.value ? change.value: left.value) left.value > change.value ? left.value - change.value : 0
const s3 = 2 );
const s4 = computed(() => right.value > change.value? change.value: right.value) const s2 = computed(() =>
const s5 = computed(() => right.value > change.value? right.value - change.value: 0) left.value > change.value ? change.value : left.value
);
const s3 = 2;
const s4 = computed(() =>
right.value > change.value ? change.value : right.value
);
const s5 = computed(() =>
right.value > change.value ? right.value - change.value : 0
);
const series = computed(() => [s1.value, s2.value, s3, s4.value, s5.value]) const series = computed(() => [s1.value, s2.value, s3, s4.value, s5.value]);
const vuetifyTheme = useTheme() const vuetifyTheme = useTheme();
const format = useFormatter() const format = useFormatter();
const chartConfig = computed(() => { const chartConfig = computed(() => {
const themeColors = vuetifyTheme.current.value.colors const themeColors = vuetifyTheme.current.value.colors;
const variableTheme = vuetifyTheme.current.value.variables const variableTheme = vuetifyTheme.current.value.variables;
const secondaryText = `rgba(${hexToRgb(String(themeColors['on-background']))},${variableTheme['medium-emphasis-opacity']})` const secondaryText = `rgba(${hexToRgb(
const primaryText = `rgba(${hexToRgb(String(themeColors['on-background']))},${variableTheme['high-emphasis-opacity']})` String(themeColors['on-background'])
)},${variableTheme['medium-emphasis-opacity']})`;
const primaryText = `rgba(${hexToRgb(String(themeColors['on-background']))},${
variableTheme['high-emphasis-opacity']
})`;
return { return {
chart: { chart: {
@ -54,8 +71,18 @@ const chartConfig = computed(() => {
legend: { show: false }, legend: { show: false },
tooltip: { enabled: false }, tooltip: { enabled: false },
dataLabels: { enabled: false }, dataLabels: { enabled: false },
stroke: { width: 3, lineCap: 'round', colors: ['rgba(var(--v-theme-surface), 1)'] }, stroke: {
labels: ['Available', 'Daily Change', 'Commission Rate', 'Daily Change', 'Available'], width: 3,
lineCap: 'round',
colors: ['rgba(var(--v-theme-surface), 1)'],
},
labels: [
'Available',
'Daily Change',
'Commission Rate',
'Daily Change',
'Available',
],
states: { states: {
hover: { hover: {
filter: { type: 'none' }, filter: { type: 'none' },
@ -104,13 +131,18 @@ const chartConfig = computed(() => {
}, },
}, },
], ],
} };
}) });
</script> </script>
<template> <template>
<VCard title="Commission Rate" :subtitle="`Updated at ${format.toDay(props.commission?.update_time, 'short')}`"> <VCard
title="Commission Rate"
:subtitle="`Updated at ${format.toDay(
props.commission?.update_time,
'short'
)}`"
>
<VCardText> <VCardText>
<VueApexCharts <VueApexCharts
type="donut" type="donut"

View File

@ -1,15 +1,15 @@
<script lang="ts" setup> <script lang="ts" setup>
import VueApexCharts from 'vue3-apexcharts' import VueApexCharts from 'vue3-apexcharts';
import { useTheme } from 'vuetify' import { useTheme } from 'vuetify';
import { getDonutChartConfig } from './apexChartConfig' import { getDonutChartConfig } from './apexChartConfig';
const props = defineProps(["series", "labels"]) const props = defineProps(['series', 'labels']);
const vuetifyTheme = useTheme()
const expenseRationChartConfig = computed(() => getDonutChartConfig(vuetifyTheme.current.value, props.labels))
const vuetifyTheme = useTheme();
const expenseRationChartConfig = computed(() =>
getDonutChartConfig(vuetifyTheme.current.value, props.labels)
);
</script> </script>
<template> <template>

View File

@ -1,26 +1,37 @@
<script lang="ts" setup> <script lang="ts" setup>
import VueApexCharts from 'vue3-apexcharts' import VueApexCharts from 'vue3-apexcharts';
import { useTheme } from 'vuetify' import { useTheme } from 'vuetify';
import { getAreaChartSplineConfig, getMarketPriceChartConfig } from './apexChartConfig' import {
getAreaChartSplineConfig,
getMarketPriceChartConfig,
} from './apexChartConfig';
import { useIndexModule } from '@/modules/[chain]/indexStore'; import { useIndexModule } from '@/modules/[chain]/indexStore';
import { computed, ref } from '@vue/reactivity'; import { computed, ref } from '@vue/reactivity';
const store = useIndexModule() const store = useIndexModule();
const vuetifyTheme = useTheme() const vuetifyTheme = useTheme();
const chartConfig = computed(() => { const chartConfig = computed(() => {
const labels = store.marketData.prices.map(x => x[0]) const labels = store.marketData.prices.map((x) => x[0]);
return getMarketPriceChartConfig(vuetifyTheme.current.value, labels) return getMarketPriceChartConfig(vuetifyTheme.current.value, labels);
}) });
const kind = ref('price') const kind = ref('price');
const series = computed(() => { const series = computed(() => {
return [{ return [
{
name: 'Price', name: 'Price',
data: kind.value ==='price'?store.marketData.prices.map(x => x[1]) : store.marketData.total_volumes.map(x => x[1])}] data:
}) kind.value === 'price'
? store.marketData.prices.map((x) => x[1])
: store.marketData.total_volumes.map((x) => x[1]),
},
];
});
</script> </script>
<template> <template>
<VTabs v-model="kind" align-tabs="end"><VTab value="price">Price</VTab><VTab value="volume">Volume</VTab></VTabs> <VTabs v-model="kind" align-tabs="end"
><VTab value="price">Price</VTab><VTab value="volume">Volume</VTab></VTabs
>
<VueApexCharts <VueApexCharts
type="area" type="area"
height="261" height="261"

View File

@ -1,20 +1,38 @@
import type { ThemeInstance } from 'vuetify' import type { ThemeInstance } from 'vuetify';
import { hexToRgb } from '@/plugins/vuetify/@layouts/utils' import { hexToRgb } from '@/plugins/vuetify/@layouts/utils';
import numeral from 'numeral' import numeral from 'numeral';
// 👉 Colors variables // 👉 Colors variables
const colorVariables = (themeColors: ThemeInstance['themes']['value']['colors']) => { const colorVariables = (
const themeSecondaryTextColor = `rgba(${hexToRgb(themeColors.colors['on-surface'])},${themeColors.variables['medium-emphasis-opacity']})` themeColors: ThemeInstance['themes']['value']['colors']
const themeDisabledTextColor = `rgba(${hexToRgb(themeColors.colors['on-surface'])},${themeColors.variables['disabled-opacity']})` ) => {
const themeBorderColor = `rgba(${hexToRgb(String(themeColors.variables['border-color']))},${themeColors.variables['border-opacity']})` const themeSecondaryTextColor = `rgba(${hexToRgb(
const themePrimaryTextColor = `rgba(${hexToRgb(themeColors.colors['on-surface'])},${themeColors.variables['high-emphasis-opacity']})` themeColors.colors['on-surface']
)},${themeColors.variables['medium-emphasis-opacity']})`;
const themeDisabledTextColor = `rgba(${hexToRgb(
themeColors.colors['on-surface']
)},${themeColors.variables['disabled-opacity']})`;
const themeBorderColor = `rgba(${hexToRgb(
String(themeColors.variables['border-color'])
)},${themeColors.variables['border-opacity']})`;
const themePrimaryTextColor = `rgba(${hexToRgb(
themeColors.colors['on-surface']
)},${themeColors.variables['high-emphasis-opacity']})`;
return { themeSecondaryTextColor, themeDisabledTextColor, themeBorderColor, themePrimaryTextColor } return {
} themeSecondaryTextColor,
themeDisabledTextColor,
themeBorderColor,
themePrimaryTextColor,
};
};
/// Price Chart config /// Price Chart config
export const getMarketPriceChartConfig = (themeColors: ThemeInstance['themes']['value']['colors'], categories: string[]) => { export const getMarketPriceChartConfig = (
themeColors: ThemeInstance['themes']['value']['colors'],
const { themeSecondaryTextColor, themeBorderColor, themeDisabledTextColor } = colorVariables(themeColors) categories: string[]
) => {
const { themeSecondaryTextColor, themeBorderColor, themeDisabledTextColor } =
colorVariables(themeColors);
return { return {
chart: { chart: {
@ -25,7 +43,7 @@ export const getMarketPriceChartConfig = (themeColors: ThemeInstance['themes']['
}, },
tooltip: { tooltip: {
theme: 'dark', theme: 'dark',
shared: false shared: false,
}, },
dataLabels: { enabled: false }, dataLabels: { enabled: false },
stroke: { stroke: {
@ -64,9 +82,9 @@ export const getMarketPriceChartConfig = (themeColors: ThemeInstance['themes']['
labels: { labels: {
style: { colors: themeDisabledTextColor }, style: { colors: themeDisabledTextColor },
formatter: function (value: string) { formatter: function (value: string) {
const pattern = (Number(value) > 0.01 ? '0.0[0]a': '0.00[000]') const pattern = Number(value) > 0.01 ? '0.0[0]a' : '0.00[000]';
return numeral(value).format(pattern); return numeral(value).format(pattern);
} },
}, },
}, },
xaxis: { xaxis: {
@ -82,19 +100,22 @@ export const getMarketPriceChartConfig = (themeColors: ThemeInstance['themes']['
}, },
categories, categories,
}, },
} };
} };
/// default config /// default config
export const getScatterChartConfig = (themeColors: ThemeInstance['themes']['value']['colors']) => { export const getScatterChartConfig = (
themeColors: ThemeInstance['themes']['value']['colors']
) => {
const scatterColors = { const scatterColors = {
series1: '#ff9f43', series1: '#ff9f43',
series2: '#7367f0', series2: '#7367f0',
series3: '#28c76f', series3: '#28c76f',
} };
const { themeSecondaryTextColor, themeBorderColor, themeDisabledTextColor } = colorVariables(themeColors) const { themeSecondaryTextColor, themeBorderColor, themeDisabledTextColor } =
colorVariables(themeColors);
return { return {
chart: { chart: {
@ -116,7 +137,11 @@ export const getScatterChartConfig = (themeColors: ThemeInstance['themes']['valu
horizontal: 10, horizontal: 10,
}, },
}, },
colors: [scatterColors.series1, scatterColors.series2, scatterColors.series3], colors: [
scatterColors.series1,
scatterColors.series2,
scatterColors.series3,
],
grid: { grid: {
borderColor: themeBorderColor, borderColor: themeBorderColor,
xaxis: { xaxis: {
@ -141,10 +166,13 @@ export const getScatterChartConfig = (themeColors: ThemeInstance['themes']['valu
formatter: (val: string) => parseFloat(val).toFixed(1), formatter: (val: string) => parseFloat(val).toFixed(1),
}, },
}, },
} };
} };
export const getLineChartSimpleConfig = (themeColors: ThemeInstance['themes']['value']['colors']) => { export const getLineChartSimpleConfig = (
const { themeBorderColor, themeDisabledTextColor } = colorVariables(themeColors) themeColors: ThemeInstance['themes']['value']['colors']
) => {
const { themeBorderColor, themeDisabledTextColor } =
colorVariables(themeColors);
return { return {
chart: { chart: {
@ -174,7 +202,7 @@ export const getLineChartSimpleConfig = (themeColors: ThemeInstance['themes']['v
custom(data: any) { custom(data: any) {
return `<div class='bar-chart pa-2'> return `<div class='bar-chart pa-2'>
<span>${data.series[data.seriesIndex][data.dataPointIndex]}%</span> <span>${data.series[data.seriesIndex][data.dataPointIndex]}%</span>
</div>` </div>`;
}, },
}, },
yaxis: { yaxis: {
@ -210,11 +238,14 @@ export const getLineChartSimpleConfig = (themeColors: ThemeInstance['themes']['v
'21/12', '21/12',
], ],
}, },
} };
} };
export const getBarChartConfig = (themeColors: ThemeInstance['themes']['value']['colors']) => { export const getBarChartConfig = (
const { themeBorderColor, themeDisabledTextColor } = colorVariables(themeColors) themeColors: ThemeInstance['themes']['value']['colors']
) => {
const { themeBorderColor, themeDisabledTextColor } =
colorVariables(themeColors);
return { return {
chart: { chart: {
@ -248,21 +279,32 @@ export const getBarChartConfig = (themeColors: ThemeInstance['themes']['value'][
xaxis: { xaxis: {
axisBorder: { show: false }, axisBorder: { show: false },
axisTicks: { color: themeBorderColor }, axisTicks: { color: themeBorderColor },
categories: ['MON, 11', 'THU, 14', 'FRI, 15', 'MON, 18', 'WED, 20', 'FRI, 21', 'MON, 23'], categories: [
'MON, 11',
'THU, 14',
'FRI, 15',
'MON, 18',
'WED, 20',
'FRI, 21',
'MON, 23',
],
labels: { labels: {
style: { colors: themeDisabledTextColor }, style: { colors: themeDisabledTextColor },
}, },
}, },
} };
} };
export const getCandlestickChartConfig = (themeColors: ThemeInstance['themes']['value']['colors']) => { export const getCandlestickChartConfig = (
themeColors: ThemeInstance['themes']['value']['colors']
) => {
const candlestickColors = { const candlestickColors = {
series1: '#28c76f', series1: '#28c76f',
series2: '#ea5455', series2: '#ea5455',
} };
const { themeBorderColor, themeDisabledTextColor } = colorVariables(themeColors) const { themeBorderColor, themeDisabledTextColor } =
colorVariables(themeColors);
return { return {
chart: { chart: {
@ -305,18 +347,21 @@ export const getCandlestickChartConfig = (themeColors: ThemeInstance['themes']['
style: { colors: themeDisabledTextColor }, style: { colors: themeDisabledTextColor },
}, },
}, },
} };
} };
export const getRadialBarChartConfig = (themeColors: ThemeInstance['themes']['value']['colors']) => { export const getRadialBarChartConfig = (
themeColors: ThemeInstance['themes']['value']['colors']
) => {
const radialBarColors = { const radialBarColors = {
series1: '#fdd835', series1: '#fdd835',
series2: '#32baff', series2: '#32baff',
series3: '#00d4bd', series3: '#00d4bd',
series4: '#7367f0', series4: '#7367f0',
series5: '#FFA1A1', series5: '#FFA1A1',
} };
const { themeSecondaryTextColor, themePrimaryTextColor } = colorVariables(themeColors) const { themeSecondaryTextColor, themePrimaryTextColor } =
colorVariables(themeColors);
return { return {
stroke: { lineCap: 'round' }, stroke: { lineCap: 'round' },
@ -335,7 +380,11 @@ export const getRadialBarChartConfig = (themeColors: ThemeInstance['themes']['va
horizontal: 10, horizontal: 10,
}, },
}, },
colors: [radialBarColors.series1, radialBarColors.series2, radialBarColors.series4], colors: [
radialBarColors.series1,
radialBarColors.series2,
radialBarColors.series4,
],
plotOptions: { plotOptions: {
radialBar: { radialBar: {
hollow: { size: '30%' }, hollow: { size: '30%' },
@ -359,16 +408,16 @@ export const getRadialBarChartConfig = (themeColors: ThemeInstance['themes']['va
color: themePrimaryTextColor, color: themePrimaryTextColor,
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
formatter(w: { globals: { seriesTotals: any[]; series: string | any[] } }) { formatter(w: {
const totalValue globals: { seriesTotals: any[]; series: string | any[] };
= w.globals.seriesTotals.reduce((a: number, b: number) => { }) {
return a + b const totalValue =
}, 0) / w.globals.series.length w.globals.seriesTotals.reduce((a: number, b: number) => {
return a + b;
}, 0) / w.globals.series.length;
if (totalValue % 1 === 0) if (totalValue % 1 === 0) return `${totalValue}%`;
return `${totalValue}%` else return `${totalValue.toFixed(2)}%`;
else
return `${totalValue.toFixed(2)}%`
}, },
}, },
}, },
@ -380,24 +429,33 @@ export const getRadialBarChartConfig = (themeColors: ThemeInstance['themes']['va
bottom: -30, bottom: -30,
}, },
}, },
} };
} };
export const getDonutChartConfig = (themeColors: ThemeInstance['themes']['value']['colors'], labels: string[]) => { export const getDonutChartConfig = (
themeColors: ThemeInstance['themes']['value']['colors'],
labels: string[]
) => {
const donutColors = { const donutColors = {
series1: '#fdd835', series1: '#fdd835',
series2: '#00d4bd', series2: '#00d4bd',
series3: '#826bf8', series3: '#826bf8',
series4: '#32baff', series4: '#32baff',
series5: '#ffa1a1', series5: '#ffa1a1',
} };
const { themeSecondaryTextColor, themePrimaryTextColor } = colorVariables(themeColors) const { themeSecondaryTextColor, themePrimaryTextColor } =
colorVariables(themeColors);
return { return {
stroke: { width: 0 }, stroke: { width: 0 },
labels, labels,
colors: [donutColors.series1, donutColors.series5, donutColors.series3, donutColors.series2], colors: [
donutColors.series1,
donutColors.series5,
donutColors.series3,
donutColors.series2,
],
dataLabels: { dataLabels: {
enabled: true, enabled: true,
formatter: (val: string) => `${parseInt(val, 10)}%`, formatter: (val: string) => `${parseInt(val, 10)}%`,
@ -474,17 +532,20 @@ export const getDonutChartConfig = (themeColors: ThemeInstance['themes']['value'
}, },
}, },
], ],
} };
} };
export const getAreaChartSplineConfig = (themeColors: ThemeInstance['themes']['value']['colors']) => { export const getAreaChartSplineConfig = (
themeColors: ThemeInstance['themes']['value']['colors']
) => {
const areaColors = { const areaColors = {
series3: '#e0cffe', series3: '#e0cffe',
series2: '#b992fe', series2: '#b992fe',
series1: '#ab7efd', series1: '#ab7efd',
} };
const { themeSecondaryTextColor, themeBorderColor, themeDisabledTextColor } = colorVariables(themeColors) const { themeSecondaryTextColor, themeBorderColor, themeDisabledTextColor } =
colorVariables(themeColors);
return { return {
chart: { chart: {
@ -555,17 +616,20 @@ export const getAreaChartSplineConfig = (themeColors: ThemeInstance['themes']['v
'19/12', '19/12',
], ],
}, },
} };
} };
export const getColumnChartConfig = (themeColors: ThemeInstance['themes']['value']['colors']) => { export const getColumnChartConfig = (
themeColors: ThemeInstance['themes']['value']['colors']
) => {
const columnColors = { const columnColors = {
series1: '#826af9', series1: '#826af9',
series2: '#d2b0ff', series2: '#d2b0ff',
bg: '#f8d3ff', bg: '#f8d3ff',
} };
const { themeSecondaryTextColor, themeBorderColor, themeDisabledTextColor } = colorVariables(themeColors) const { themeSecondaryTextColor, themeBorderColor, themeDisabledTextColor } =
colorVariables(themeColors);
return { return {
chart: { chart: {
@ -602,7 +666,13 @@ export const getColumnChartConfig = (themeColors: ThemeInstance['themes']['value
colors: { colors: {
backgroundBarRadius: 10, backgroundBarRadius: 10,
backgroundBarColors: [columnColors.bg, columnColors.bg, columnColors.bg, columnColors.bg, columnColors.bg], backgroundBarColors: [
columnColors.bg,
columnColors.bg,
columnColors.bg,
columnColors.bg,
columnColors.bg,
],
}, },
}, },
}, },
@ -621,7 +691,17 @@ export const getColumnChartConfig = (themeColors: ThemeInstance['themes']['value
axisBorder: { show: false }, axisBorder: { show: false },
axisTicks: { color: themeBorderColor }, axisTicks: { color: themeBorderColor },
categories: ['7/12', '8/12', '9/12', '10/12', '11/12', '12/12', '13/12', '14/12', '15/12'], categories: [
'7/12',
'8/12',
'9/12',
'10/12',
'11/12',
'12/12',
'13/12',
'14/12',
'15/12',
],
crosshairs: { crosshairs: {
stroke: { color: themeBorderColor }, stroke: { color: themeBorderColor },
}, },
@ -641,11 +721,14 @@ export const getColumnChartConfig = (themeColors: ThemeInstance['themes']['value
}, },
}, },
], ],
} };
} };
export const getHeatMapChartConfig = (themeColors: ThemeInstance['themes']['value']['colors']) => { export const getHeatMapChartConfig = (
const { themeSecondaryTextColor, themeDisabledTextColor } = colorVariables(themeColors) themeColors: ThemeInstance['themes']['value']['colors']
) => {
const { themeSecondaryTextColor, themeDisabledTextColor } =
colorVariables(themeColors);
return { return {
chart: { chart: {
@ -700,16 +783,19 @@ export const getHeatMapChartConfig = (themeColors: ThemeInstance['themes']['valu
axisTicks: { show: false }, axisTicks: { show: false },
axisBorder: { show: false }, axisBorder: { show: false },
}, },
} };
} };
export const getRadarChartConfig = (themeColors: ThemeInstance['themes']['value']['colors']) => { export const getRadarChartConfig = (
themeColors: ThemeInstance['themes']['value']['colors']
) => {
const radarColors = { const radarColors = {
series1: '#9b88fa', series1: '#9b88fa',
series2: '#ffa1a1', series2: '#ffa1a1',
} };
const { themeSecondaryTextColor, themeBorderColor, themeDisabledTextColor } = colorVariables(themeColors) const { themeSecondaryTextColor, themeBorderColor, themeDisabledTextColor } =
colorVariables(themeColors);
return { return {
chart: { chart: {
@ -759,7 +845,16 @@ export const getRadarChartConfig = (themeColors: ThemeInstance['themes']['value'
}, },
yaxis: { show: false }, yaxis: { show: false },
xaxis: { xaxis: {
categories: ['Battery', 'Brand', 'Camera', 'Memory', 'Storage', 'Display', 'OS', 'Price'], categories: [
'Battery',
'Brand',
'Camera',
'Memory',
'Storage',
'Display',
'OS',
'Price',
],
labels: { labels: {
style: { style: {
colors: [ colors: [
@ -775,5 +870,5 @@ export const getRadarChartConfig = (themeColors: ThemeInstance['themes']['value'
}, },
}, },
}, },
} };
} };

View File

@ -3,8 +3,7 @@ import { fromBase64, toBase64 } from '@cosmjs/encoding';
const props = defineProps({ const props = defineProps({
value: { type: Array<Uint8Array> }, value: { type: Array<Uint8Array> },
}) });
</script> </script>
<template> <template>
<div> <div>

View File

@ -4,18 +4,17 @@ import type { Coin } from '@/types';
const props = defineProps({ const props = defineProps({
value: { type: Array<Coin> }, value: { type: Array<Coin> },
}) });
const format = useFormatter()
const format = useFormatter();
</script> </script>
<template> <template>
<div> <div>
{{ format.formatTokens(value, true, "0,0.[000000]") }} {{ format.formatTokens(value, true, '0,0.[000000]') }}
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
export default { export default {
name: 'ArrayCoinElement' name: 'ArrayCoinElement',
} };
</script> </script>

View File

@ -3,7 +3,7 @@ import { toBase64 } from '@cosmjs/encoding';
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import { computed } from '@vue/reactivity'; import { computed } from '@vue/reactivity';
import DynamicComponentVue from './DynamicComponent.vue'; import DynamicComponentVue from './DynamicComponent.vue';
import {select} from './index' import { select } from './index';
import ArrayBytesElement from './ArrayBytesElement.vue'; import ArrayBytesElement from './ArrayBytesElement.vue';
import ArrayObjectElement from './ArrayObjectElement.vue'; import ArrayObjectElement from './ArrayObjectElement.vue';
import TextElement from './TextElement.vue'; import TextElement from './TextElement.vue';
@ -11,23 +11,22 @@ import ArrayCoinElement from './ArrayCoinElement.vue';
const props = defineProps({ const props = defineProps({
value: { type: Array<Object> }, value: { type: Array<Object> },
}) });
function selectByElement() { function selectByElement() {
if (props.value && props.value.length > 0) { if (props.value && props.value.length > 0) {
const [first] = props.value const [first] = props.value;
switch (true) { switch (true) {
case first instanceof Uint8Array: case first instanceof Uint8Array:
return ArrayBytesElement return ArrayBytesElement;
case Object.keys(first).includes('denom'): case Object.keys(first).includes('denom'):
return ArrayCoinElement return ArrayCoinElement;
default: default:
return ArrayObjectElement return ArrayObjectElement;
} }
} }
return TextElement return TextElement;
} }
</script> </script>
<template> <template>
<Component :is="selectByElement()" :value="props.value"></Component> <Component :is="selectByElement()" :value="props.value"></Component>

View File

@ -6,33 +6,44 @@ const props = defineProps({
value: { type: null as any }, value: { type: null as any },
thead: { thead: {
type: Boolean, type: Boolean,
default: true default: true,
} },
}) });
const header = computed(() => { const header = computed(() => {
if (props.value && props.value.length > 0) { if (props.value && props.value.length > 0) {
return Object.keys(props.value[0]) return Object.keys(props.value[0]);
} }
return [] return [];
}) });
</script> </script>
<template> <template>
<VTable v-if="header.length > 0" density="compact" height="300px" fixed-header hover> <VTable
v-if="header.length > 0"
density="compact"
height="300px"
fixed-header
hover
>
<thead v-if="thead"> <thead v-if="thead">
<tr> <tr>
<th v-for="(item, index) in header" :key="index" class="text-left text-capitalize">{{ item }}</th> <th
v-for="(item, index) in header"
:key="index"
class="text-left text-capitalize"
>
{{ item }}
</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="(item, index) in value" :key="index"> <tr v-for="(item, index) in value" :key="index">
<td v-for="(el, key) in header" :key="key"> <DynamicComponent :value="item[el]" /></td> <td v-for="(el, key) in header" :key="key">
<DynamicComponent :value="item[el]" />
</td>
</tr> </tr>
</tbody> </tbody>
</VTable> </VTable>
<div v-else class="h-[300px]"> <div v-else class="h-[300px]"></div>
</div>
</template> </template>

View File

@ -1,8 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { select } from './index' import { select } from './index';
const props = defineProps(["value", "direct"]); const props = defineProps(['value', 'direct']);
</script> </script>
<template> <template>
<Component :is="select(value, direct)" :value="value"></Component> <Component :is="select(value, direct)" :value="value"></Component>

View File

@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
const props = defineProps(["value"]); const props = defineProps(['value']);
</script> </script>
<template> <template>
<span>{{ Number(props.value) }}</span> <span>{{ Number(props.value) }}</span>

View File

@ -1,19 +1,25 @@
<script lang="ts" setup> <script lang="ts" setup>
import DynamicComponent from './DynamicComponent.vue'; import DynamicComponent from './DynamicComponent.vue';
import {select} from './index' import { select } from './index';
const props = defineProps(["value"]);
const props = defineProps(['value']);
</script> </script>
<template> <template>
<div class="overflow-x-auto"> <div class="overflow-x-auto">
<table class="table w-full text-sm"> <table class="table w-full text-sm">
<tbody> <tbody>
<tr v-for="(v, k) of value"> <tr v-for="(v, k) of value">
<td class="text-capitalize" style="max-width: 200px;">{{ k }}</td> <td class="text-capitalize" style="max-width: 200px">{{ k }}</td>
<td> <td>
<div class="overflow-hidden w-auto whitespace-normal" style="max-width: 1000px;"> <div
<Component v-if="v" :is="select(v, 'horizontal')" :value="v"></Component> class="overflow-hidden w-auto whitespace-normal"
style="max-width: 1000px"
>
<Component
v-if="v"
:is="select(v, 'horizontal')"
:value="v"
></Component>
</div> </div>
</td> </td>
</tr> </tr>

View File

@ -1,17 +1,19 @@
<script lang="ts" setup> <script lang="ts" setup>
import DynamicComponent from './DynamicComponent.vue'; import DynamicComponent from './DynamicComponent.vue';
import {select} from './index' import { select } from './index';
const props = defineProps(["value"]); const props = defineProps(['value']);
const tab = ref("") const tab = ref('');
</script> </script>
<template> <template>
<div> <div>
<VTabs v-model="tab"> <VTabs v-model="tab">
<VTab v-for="(v, k) of value" :value="k">{{ k }}</VTab> <VTab v-for="(v, k) of value" :value="k">{{ k }}</VTab>
</VTabs> </VTabs>
<VWindow v-model="tab" style="min-height: 25px;"> <VWindow v-model="tab" style="min-height: 25px">
<VWindowItem v-for="(v, k) of value" :value="k"><DynamicComponent :value="v"/></VWindowItem> <VWindowItem v-for="(v, k) of value" :value="k"
><DynamicComponent :value="v"
/></VWindowItem>
</VWindow> </VWindow>
</div> </div>
</template> </template>

View File

@ -2,16 +2,23 @@
import { useFormatter } from '@/stores'; import { useFormatter } from '@/stores';
import MdEditor from 'md-editor-v3'; import MdEditor from 'md-editor-v3';
const props = defineProps(["value"]); const props = defineProps(['value']);
const format = useFormatter() const format = useFormatter();
function isMD() { function isMD() {
if(props.value && (props.value.indexOf("\n") > -1 || props.value.indexOf("\\n") > -1)){ if (
return true props.value &&
(props.value.indexOf('\n') > -1 || props.value.indexOf('\\n') > -1)
) {
return true;
} }
return false return false;
} }
</script> </script>
<template> <template>
<MdEditor v-if="isMD()" :model-value="format.multiLine(value)" previewOnly></MdEditor> <MdEditor
v-if="isMD()"
:model-value="format.multiLine(value)"
previewOnly
></MdEditor>
<span v-else>{{ value }}</span> <span v-else>{{ value }}</span>
</template> </template>

View File

@ -1,32 +1,48 @@
<script lang="ts" setup> <script lang="ts" setup>
import { fromBase64, toBase64 } from '@cosmjs/encoding'; import { fromBase64, toBase64 } from '@cosmjs/encoding';
import { decodeTxRaw } from '@cosmjs/proto-signing' import { decodeTxRaw } from '@cosmjs/proto-signing';
import { computed } from '@vue/reactivity'; import { computed } from '@vue/reactivity';
import { hashTx } from '@/libs' import { hashTx } from '@/libs';
import { useBlockchain, useFormatter } from '@/stores'; import { useBlockchain, useFormatter } from '@/stores';
const props = defineProps({ const props = defineProps({
value: { type: Array<string> }, value: { type: Array<string> },
}); });
const txs = computed(() => { const txs = computed(() => {
return props.value?.map(x => ({ hash: hashTx(fromBase64(x)) , tx: decodeTxRaw(fromBase64(x)) })) || [] return (
}) props.value?.map((x) => ({
hash: hashTx(fromBase64(x)),
const format = useFormatter() tx: decodeTxRaw(fromBase64(x)),
const chain = useBlockchain() })) || []
);
});
const format = useFormatter();
const chain = useBlockchain();
</script> </script>
<template> <template>
<VTable density="compact" v-if="txs.length > 0"> <VTable density="compact" v-if="txs.length > 0">
<thead> <thead>
<tr> <tr>
<th>Hash</th><th>Msgs</th><th>Memo</th> <th>Hash</th>
<th>Msgs</th>
<th>Memo</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="item in txs"> <tr v-for="item in txs">
<td><RouterLink :to="`/${chain.chainName}/tx/${item.hash}`">{{ item.hash }}</RouterLink></td> <td>
<td>{{ format.messages(item.tx.body.messages.map(x => ({"@type": x.typeUrl}))) }}</td> <RouterLink :to="`/${chain.chainName}/tx/${item.hash}`">{{
item.hash
}}</RouterLink>
</td>
<td>
{{
format.messages(
item.tx.body.messages.map((x) => ({ '@type': x.typeUrl }))
)
}}
</td>
<td>{{ item.tx.body.memo }}</td> <td>{{ item.tx.body.memo }}</td>
</tr> </tr>
</tbody> </tbody>

View File

@ -2,15 +2,17 @@
import { toBase64, toHex } from '@cosmjs/encoding'; import { toBase64, toHex } from '@cosmjs/encoding';
import { computed } from '@vue/reactivity'; import { computed } from '@vue/reactivity';
const props = defineProps(["value"]); const props = defineProps(['value']);
const format = ref('base64') const format = ref('base64');
const text = computed(() => { const text = computed(() => {
return format.value === 'hex'? toHex(props.value) : toBase64(props.value) return format.value === 'hex' ? toHex(props.value) : toBase64(props.value);
}) });
function change() { function change() {
format.value = format.value === 'hex'? 'base64': 'hex' format.value = format.value === 'hex' ? 'base64' : 'hex';
} }
</script> </script>
<template> <template>
<span>{{ text }} <VIcon size="12" icon="mdi-cached" @click="change()"/></span> <span
>{{ text }} <VIcon size="12" icon="mdi-cached" @click="change()"
/></span>
</template> </template>

View File

@ -1,24 +1,24 @@
import ObjectElement from './ObjectElement.vue' import ObjectElement from './ObjectElement.vue';
import TextElement from './TextElement.vue' import TextElement from './TextElement.vue';
import ArrayElement from './ArrayElement.vue' import ArrayElement from './ArrayElement.vue';
import UInt8Array from './UInt8Array.vue' import UInt8Array from './UInt8Array.vue';
import NumberElement from './NumberElement.vue' import NumberElement from './NumberElement.vue';
import TxsElement from './TxsElement.vue' import TxsElement from './TxsElement.vue';
import ObjectHorizontalElement from './ObjectHorizontalElement.vue' import ObjectHorizontalElement from './ObjectHorizontalElement.vue';
import Long from 'long' import Long from 'long';
export function select(v: any, direct?: string) { export function select(v: any, direct?: string) {
// if(k === 'txs' && v) { // if(k === 'txs' && v) {
// return TxsElement // return TxsElement
// } else { // } else {
const type = typeof v const type = typeof v;
switch (type) { switch (type) {
case 'object': case 'object':
return selectObject(v, direct) return selectObject(v, direct);
case 'number': case 'number':
return NumberElement return NumberElement;
default: default:
return TextElement return TextElement;
} }
// } // }
} }
@ -26,14 +26,14 @@ export function select(v: any, direct?: string) {
function selectObject(v: Object, direct?: string) { function selectObject(v: Object, direct?: string) {
switch (true) { switch (true) {
case v instanceof Long: case v instanceof Long:
return NumberElement return NumberElement;
case v instanceof Uint8Array: case v instanceof Uint8Array:
return UInt8Array return UInt8Array;
case Array.isArray(v): case Array.isArray(v):
return ArrayElement return ArrayElement;
case direct === 'horizontal': case direct === 'horizontal':
return ObjectHorizontalElement return ObjectHorizontalElement;
default: default:
return ObjectElement return ObjectElement;
} }
} }

View File

@ -1,5 +1,10 @@
<template> <template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor"> <svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
fill="currentColor"
>
<path <path
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z" d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
/> />

View File

@ -1,5 +1,10 @@
<template> <template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor"> <svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="17"
fill="currentColor"
>
<path <path
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z" d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
/> />

View File

@ -1,5 +1,10 @@
<template> <template>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor"> <svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="20"
fill="currentColor"
>
<path <path
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z" d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
/> />

View File

@ -1,5 +1,10 @@
<template> <template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor"> <svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
fill="currentColor"
>
<path <path
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z" d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
/> />

View File

@ -1,17 +1,18 @@
<script lang="ts"> <script lang="ts">
import { useSkins } from '@core/composable/useSkins' import { useSkins } from '@core/composable/useSkins';
export default defineComponent({ export default defineComponent({
setup() { setup() {
const routerView = resolveComponent('router-view') const routerView = resolveComponent('router-view');
const { injectSkinClasses } = useSkins() const { injectSkinClasses } = useSkins();
// This will inject classes in body tag for accurate styling // This will inject classes in body tag for accurate styling
injectSkinClasses() injectSkinClasses();
return () => h('div', { class: 'layout-wrapper layout-blank' }, h(routerView)) return () =>
h('div', { class: 'layout-wrapper layout-blank' }, h(routerView));
}, },
}) });
</script> </script>
<style> <style>

View File

@ -4,13 +4,18 @@ import { computed } from '@vue/reactivity';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
const chain = useBlockchain() const chain = useBlockchain();
const route = useRoute() const route = useRoute();
const i18n = useI18n() const i18n = useI18n();
/// To display human readable module name, we have to set the prefix("module.") + route name to the key in i18n. /// To display human readable module name, we have to set the prefix("module.") + route name to the key in i18n.
/// such as `module.chain` = 'Dashboard' /// such as `module.chain` = 'Dashboard'
const moduleName = computed(() => i18n.t(`module.${route.name?.toString()||''}`)) const moduleName = computed(() =>
const items = computed(() => [{title: String(chain.name).toUpperCase(), href: `/${chain.name}`}, moduleName.value]) i18n.t(`module.${route.name?.toString() || ''}`)
);
const items = computed(() => [
{ title: String(chain.name).toUpperCase(), href: `/${chain.name}` },
moduleName.value,
]);
</script> </script>
<template> <template>
<div class="d-flex flex-rows align-center"> <div class="d-flex flex-rows align-center">

View File

@ -1,14 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { useBlockchain, useBaseStore } from '@/stores'; import { useBlockchain, useBaseStore } from '@/stores';
const chainStore = useBlockchain() const chainStore = useBlockchain();
const baseStore = useBaseStore() const baseStore = useBaseStore();
chainStore.initial() chainStore.initial();
chainStore.$subscribe((m, s) => { chainStore.$subscribe((m, s) => {
if (!Array.isArray(m.events) && m.events.key === 'endpoint') { if (!Array.isArray(m.events) && m.events.key === 'endpoint') {
chainStore.initial() chainStore.initial();
} }
}) });
</script> </script>
<template> <template>
@ -23,28 +22,36 @@ chainStore.$subscribe((m, s) => {
color="success" color="success"
class="mr-2" class="mr-2"
> >
<VAvatar <VAvatar class="cursor-pointer" color="primary" variant="tonal">
class="cursor-pointer"
color="primary"
variant="tonal"
>
<VImg :src="chainStore.logo" /> <VImg :src="chainStore.logo" />
<!-- SECTION Menu --> <!-- SECTION Menu -->
<VMenu <VMenu activator="parent" location="bottom start" offset="14px">
activator="parent"
location="bottom start"
offset="14px"
>
<VList> <VList>
<!-- 👉 Rest --> <!-- 👉 Rest -->
<VListSubheader v-if="chainStore.current?.endpoints?.rest" title="Rest Endpoint" /> <VListSubheader
<VListItem v-for="i in chainStore.current?.endpoints?.rest" link @click="chainStore.setRestEndpoint(i)"> v-if="chainStore.current?.endpoints?.rest"
<VListItemTitle>{{ i.provider }} <VIcon v-if="i.address === chainStore.endpoint?.address" icon="mdi-check" color="success" /></VListItemTitle> title="Rest Endpoint"
/>
<VListItem
v-for="i in chainStore.current?.endpoints?.rest"
link
@click="chainStore.setRestEndpoint(i)"
>
<VListItemTitle
>{{ i.provider }}
<VIcon
v-if="i.address === chainStore.endpoint?.address"
icon="mdi-check"
color="success"
/></VListItemTitle>
<VListItemSubtitle>{{ i.address }}</VListItemSubtitle> <VListItemSubtitle>{{ i.address }}</VListItemSubtitle>
</VListItem> </VListItem>
<VListSubheader v-if="chainStore.current?.endpoints?.grpc" title="gRPC Endpoint" /> <VListSubheader
v-if="chainStore.current?.endpoints?.grpc"
title="gRPC Endpoint"
/>
<VListItem v-for="i in chainStore.current?.endpoints?.grpc" link> <VListItem v-for="i in chainStore.current?.endpoints?.grpc" link>
<VListItemTitle>{{ i.provider }}</VListItemTitle> <VListItemTitle>{{ i.provider }}</VListItemTitle>
<VListItemSubtitle>{{ i.address }}</VListItemSubtitle> <VListItemSubtitle>{{ i.address }}</VListItemSubtitle>
@ -55,7 +62,11 @@ chainStore.$subscribe((m, s) => {
</VAvatar> </VAvatar>
</VBadge> </VBadge>
</template> </template>
<VListItemTitle>{{ baseStore.latest.block?.header?.chain_id || chainStore.chainName || '' }}</VListItemTitle> <VListItemTitle>{{
<VListItemSubtitle> {{ chainStore.connErr|| chainStore.endpoint.address }}</VListItemSubtitle> baseStore.latest.block?.header?.chain_id || chainStore.chainName || ''
}}</VListItemTitle>
<VListItemSubtitle>
{{ chainStore.connErr || chainStore.endpoint.address }}</VListItemSubtitle
>
</VListItem> </VListItem>
</template> </template>

View File

@ -1,9 +1,9 @@
<script lang="ts" setup> <script lang="ts" setup>
import NavBarI18n from '@core/components/I18n.vue' import NavBarI18n from '@core/components/I18n.vue';
import { useThemeConfig } from '@core/composable/useThemeConfig' import { useThemeConfig } from '@core/composable/useThemeConfig';
import type { I18nLanguage } from '@layouts/types' import type { I18nLanguage } from '@layouts/types';
const { isAppRtl } = useThemeConfig() const { isAppRtl } = useThemeConfig();
const i18nCompLanguages: I18nLanguage[] = [ const i18nCompLanguages: I18nLanguage[] = [
{ {
@ -18,17 +18,14 @@ const i18nCompLanguages: I18nLanguage[] = [
label: 'Arabic', label: 'Arabic',
i18nLang: 'ar', i18nLang: 'ar',
}, },
] ];
const handleLangChange = (lang: string) => { const handleLangChange = (lang: string) => {
isAppRtl.value = lang === 'ar' isAppRtl.value = lang === 'ar';
localStorage.setItem('lang', lang) localStorage.setItem('lang', lang);
} };
</script> </script>
<template> <template>
<NavBarI18n <NavBarI18n :languages="i18nCompLanguages" @change="handleLangChange" />
:languages="i18nCompLanguages"
@change="handleLangChange"
/>
</template> </template>

View File

@ -1,12 +1,12 @@
<script lang="ts" setup> <script lang="ts" setup>
import Notifications from '@core/components/Notifications.vue' import Notifications from '@core/components/Notifications.vue';
import type { Notification } from '@layouts/types' import type { Notification } from '@layouts/types';
// Images // Images
import avatar3 from '@images/avatars/avatar-3.png' import avatar3 from '@images/avatars/avatar-3.png';
import avatar4 from '@images/avatars/avatar-4.png' import avatar4 from '@images/avatars/avatar-4.png';
import avatar5 from '@images/avatars/avatar-5.png' import avatar5 from '@images/avatars/avatar-5.png';
import paypal from '@images/svg/paypal.svg' import paypal from '@images/svg/paypal.svg';
const notifications = ref<Notification[]>([ const notifications = ref<Notification[]>([
{ {
@ -50,32 +50,29 @@ const notifications = ref<Notification[]>([
time: '19 Mar', time: '19 Mar',
isRead: false, isRead: false,
}, },
]) ]);
const removeNotification = (notificationId: number) => { const removeNotification = (notificationId: number) => {
notifications.value.forEach((item, index) => { notifications.value.forEach((item, index) => {
if (notificationId === item.id) if (notificationId === item.id) notifications.value.splice(index, 1);
notifications.value.splice(index, 1) });
}) };
}
const markRead = (notificationId: number[]) => { const markRead = (notificationId: number[]) => {
notifications.value.forEach(item => { notifications.value.forEach((item) => {
notificationId.forEach(id => { notificationId.forEach((id) => {
if (id === item.id) if (id === item.id) item.isRead = true;
item.isRead = true });
}) });
}) };
}
const markUnRead = (notificationId: number[]) => { const markUnRead = (notificationId: number[]) => {
notifications.value.forEach(item => { notifications.value.forEach((item) => {
notificationId.forEach(id => { notificationId.forEach((id) => {
if (id === item.id) if (id === item.id) item.isRead = false;
item.isRead = false });
}) });
}) };
}
</script> </script>
<template> <template>

View File

@ -36,7 +36,7 @@ const shortcuts = [
subtitle: 'FAQs & Articles', subtitle: 'FAQs & Articles',
to: { name: 'pages-help-center' }, to: { name: 'pages-help-center' },
}, },
] ];
</script> </script>
<template> <template>

View File

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import type { ThemeSwitcherTheme } from '@layouts/types' import type { ThemeSwitcherTheme } from '@layouts/types';
import NewThemeSwitcher from '@/components/ThemeSwitcher.vue' import NewThemeSwitcher from '@/components/ThemeSwitcher.vue';
const themes: ThemeSwitcherTheme[] = [ const themes: ThemeSwitcherTheme[] = [
{ {
name: 'system', name: 'system',
@ -14,7 +14,7 @@ const themes: ThemeSwitcherTheme[] = [
name: 'dark', name: 'dark',
icon: 'mdi-weather-night', icon: 'mdi-weather-night',
}, },
] ];
</script> </script>
<template> <template>

View File

@ -6,21 +6,29 @@
Made With Made With
<img src="../../assets/images/heart.svg" /> <img src="../../assets/images/heart.svg" />
By By
<a class="link link-info no-underline" <a
class="link link-info no-underline"
href="https://ping.pub" href="https://ping.pub"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
>Ping.pub</a> >Ping.pub</a
>
</div> </div>
<div class="grid-flow-col gap-4 md:place-self-center md:justify-self-end hidden md:grid"> <div
<a class="link link-info no-underline" class="grid-flow-col gap-4 md:place-self-center md:justify-self-end hidden md:grid"
>
<a
class="link link-info no-underline"
href="https://github.com/ping-pub/explorer/blob/master/LICENSE" href="https://github.com/ping-pub/explorer/blob/master/LICENSE"
target="noopener noreferrer" target="noopener noreferrer"
>License</a> >License</a
<a class="link link-info no-underline" >
<a
class="link link-info no-underline"
href="https://github.com/ping-pub/explorer" href="https://github.com/ping-pub/explorer"
target="noopener noreferrer" target="noopener noreferrer"
>Github</a> >Github</a
>
</div> </div>
</footer> </footer>
</template> </template>

View File

@ -1,65 +1,79 @@
import {fromBase64, fromBech32, fromHex, toBase64, toBech32, toHex} from '@cosmjs/encoding' import {
import { Ripemd160, sha256 } from '@cosmjs/crypto' fromBase64,
fromBech32,
fromHex,
toBase64,
toBech32,
toHex,
} from '@cosmjs/encoding';
import { Ripemd160, sha256 } from '@cosmjs/crypto';
export function decodeAddress(address: string) { export function decodeAddress(address: string) {
return fromBech32(address) return fromBech32(address);
} }
export function valoperToPrefix(valoper?: string) { export function valoperToPrefix(valoper?: string) {
if(!valoper) return '' if (!valoper) return '';
const prefixIndex = valoper.indexOf('valoper') const prefixIndex = valoper.indexOf('valoper');
if (prefixIndex === -1) return null if (prefixIndex === -1) return null;
return valoper.slice(0, prefixIndex) return valoper.slice(0, prefixIndex);
} }
export function operatorAddressToAccount(operAddress?: string) { export function operatorAddressToAccount(operAddress?: string) {
if(!operAddress) return '' if (!operAddress) return '';
const { prefix, data } = fromBech32(operAddress) const { prefix, data } = fromBech32(operAddress);
if (prefix === 'iva') { // handle special cases if (prefix === 'iva') {
return toBech32('iaa', data) // handle special cases
return toBech32('iaa', data);
} }
if (prefix === 'crocncl') { // handle special cases if (prefix === 'crocncl') {
return toBech32('cro', data) // handle special cases
return toBech32('cro', data);
} }
return toBech32(prefix.replace('valoper', ''), data) return toBech32(prefix.replace('valoper', ''), data);
} }
export function consensusPubkeyToHexAddress(consensusPubkey?: {"@type": string, key: string}) { export function consensusPubkeyToHexAddress(consensusPubkey?: {
if(!consensusPubkey) return "" '@type': string;
let raw = "" key: string;
}) {
if (!consensusPubkey) return '';
let raw = '';
if (consensusPubkey['@type'] === '/cosmos.crypto.ed25519.PubKey') { if (consensusPubkey['@type'] === '/cosmos.crypto.ed25519.PubKey') {
const pubkey = fromBase64(consensusPubkey.key) const pubkey = fromBase64(consensusPubkey.key);
if(pubkey) return toHex(sha256(pubkey)).slice(0, 40).toUpperCase() if (pubkey) return toHex(sha256(pubkey)).slice(0, 40).toUpperCase();
} }
if (consensusPubkey['@type'] === '/cosmos.crypto.secp256k1.PubKey') { if (consensusPubkey['@type'] === '/cosmos.crypto.secp256k1.PubKey') {
const pubkey = fromBase64(consensusPubkey.key) const pubkey = fromBase64(consensusPubkey.key);
if(pubkey) return toHex(new Ripemd160().update(sha256(pubkey)).digest()) if (pubkey) return toHex(new Ripemd160().update(sha256(pubkey)).digest());
} }
return raw return raw;
} }
export function pubKeyToValcons(consensusPubkey: {"@type": string, key: string}, prefix: string) { export function pubKeyToValcons(
consensusPubkey: { '@type': string; key: string },
prefix: string
) {
if (consensusPubkey && consensusPubkey.key) { if (consensusPubkey && consensusPubkey.key) {
const pubkey = fromBase64(consensusPubkey.key) const pubkey = fromBase64(consensusPubkey.key);
if (pubkey) { if (pubkey) {
const addressData = sha256(pubkey).slice(0, 20) const addressData = sha256(pubkey).slice(0, 20);
return toBech32(`${prefix}valcons`, addressData) return toBech32(`${prefix}valcons`, addressData);
} }
} }
return '' return '';
} }
export function valconsToBase64(address: string) { export function valconsToBase64(address: string) {
if(address) return toHex(fromBech32(address).data).toUpperCase() if (address) return toHex(fromBech32(address).data).toUpperCase();
return '' return '';
} }
export function toETHAddress(cosmosAddress: string) { export function toETHAddress(cosmosAddress: string) {
return `0x${toHex(fromBech32(cosmosAddress).data)}` return `0x${toHex(fromBech32(cosmosAddress).data)}`;
} }
export function addressEnCode(prefix: string, pubkey: Uint8Array) { export function addressEnCode(prefix: string, pubkey: Uint8Array) {
return toBech32(prefix, pubkey) return toBech32(prefix, pubkey);
} }

View File

@ -1,77 +1,203 @@
import { type RequestRegistry, type Registry, adapter, withCustomAdapter } from "./registry"; import {
type RequestRegistry,
type Registry,
adapter,
withCustomAdapter,
} from './registry';
export const DEFAULT: RequestRegistry = { export const DEFAULT: RequestRegistry = {
auth_params: { url: "/cosmos/auth/v1beta1/params", adapter }, auth_params: { url: '/cosmos/auth/v1beta1/params', adapter },
auth_accounts: { url: "/cosmos/auth/v1beta1/accounts", adapter }, auth_accounts: { url: '/cosmos/auth/v1beta1/accounts', adapter },
auth_account_address: { url: "/cosmos/auth/v1beta1/accounts/{address}", adapter }, auth_account_address: {
bank_params: { url: "/cosmos/bank/v1beta1/params", adapter }, url: '/cosmos/auth/v1beta1/accounts/{address}',
bank_balances_address: { url: "/cosmos/bank/v1beta1/balances/{address}", adapter }, adapter,
bank_denoms_metadata: { url: "/cosmos/bank/v1beta1/denoms_metadata", adapter }, },
bank_supply: { url: "/cosmos/bank/v1beta1/supply", adapter }, bank_params: { url: '/cosmos/bank/v1beta1/params', adapter },
bank_supply_by_denom: { url: "/cosmos/bank/v1beta1/supply/{denom}", adapter }, bank_balances_address: {
distribution_params: { url: "/cosmos/distribution/v1beta1/params", adapter }, url: '/cosmos/bank/v1beta1/balances/{address}',
distribution_community_pool: { url: "/cosmos/distribution/v1beta1/community_pool", adapter }, adapter,
distribution_validator_commission: { url: "/cosmos/distribution/v1beta1/validators/{validator_address}/commission", adapter }, },
distribution_validator_outstanding_rewards: { url: "/cosmos/distribution/v1beta1/validators/{validator_address}/outstanding_rewards", adapter }, bank_denoms_metadata: {
distribution_validator_slashes: { url: "/cosmos/distribution/v1beta1/validators/{validator_address}/slashes", adapter }, url: '/cosmos/bank/v1beta1/denoms_metadata',
distribution_delegator_rewards: { url: "/cosmos/distribution/v1beta1/delegators/{delegator_addr}/rewards", adapter }, adapter,
slashing_params: { url: "/cosmos/slashing/v1beta1/params", adapter }, },
slashing_signing_info: { url: "/cosmos/slashing/v1beta1/signing_infos", adapter }, bank_supply: { url: '/cosmos/bank/v1beta1/supply', adapter },
gov_params_voting: { url: "/cosmos/gov/v1beta1/params/voting", adapter }, bank_supply_by_denom: { url: '/cosmos/bank/v1beta1/supply/{denom}', adapter },
gov_params_tally: { url: "/cosmos/gov/v1beta1/params/tallying", adapter }, distribution_params: { url: '/cosmos/distribution/v1beta1/params', adapter },
gov_params_deposit: { url: "/cosmos/gov/v1beta1/params/deposit", adapter }, distribution_community_pool: {
gov_proposals: { url: "/cosmos/gov/v1beta1/proposals", adapter }, url: '/cosmos/distribution/v1beta1/community_pool',
gov_proposals_proposal_id: { url: "/cosmos/gov/v1beta1/proposals/{proposal_id}", adapter }, adapter,
gov_proposals_deposits: { url: "/cosmos/gov/v1beta1/proposals/{proposal_id}/deposits", adapter }, },
gov_proposals_tally: { url: "/cosmos/gov/v1beta1/proposals/{proposal_id}/tally", adapter }, distribution_validator_commission: {
gov_proposals_votes: { url: "/cosmos/gov/v1beta1/proposals/{proposal_id}/votes?pagination.key={next_key}", adapter }, url: '/cosmos/distribution/v1beta1/validators/{validator_address}/commission',
gov_proposals_votes_voter: { url: "/cosmos/gov/v1beta1/proposals/{proposal_id}/votes/{voter}", adapter }, adapter,
staking_deletations: { url: "/cosmos/staking/v1beta1/delegations/{delegator_addr}", adapter }, },
staking_delegator_redelegations: { url: "/cosmos/staking/v1beta1/delegators/{delegator_addr}/redelegations", adapter }, distribution_validator_outstanding_rewards: {
staking_delegator_unbonding_delegations: { url: "/cosmos/staking/v1beta1/delegators/{delegator_addr}/unbonding_delegations", adapter }, url: '/cosmos/distribution/v1beta1/validators/{validator_address}/outstanding_rewards',
staking_delegator_validators: { url: "/cosmos/staking/v1beta1/delegators/{delegator_addr}/validators", adapter }, adapter,
staking_params: { url: "/cosmos/staking/v1beta1/params", adapter }, },
staking_pool: { url: "/cosmos/staking/v1beta1/pool", adapter }, distribution_validator_slashes: {
staking_validators: { url: "/cosmos/staking/v1beta1/validators?pagination.limit={limit}&status={status}", adapter }, url: '/cosmos/distribution/v1beta1/validators/{validator_address}/slashes',
staking_validators_address: { url: "/cosmos/staking/v1beta1/validators/{validator_addr}", adapter }, adapter,
staking_validators_delegations: { url: "/cosmos/staking/v1beta1/validators/{validator_addr}/delegations", adapter }, },
staking_validators_delegations_delegator: { url: "/cosmos/staking/v1beta1/validators/{validator_addr}/delegations/{delegator_addr}", adapter }, distribution_delegator_rewards: {
staking_validators_delegations_unbonding_delegations: { url: "/cosmos/staking/v1beta1/validators/{validator_addr}/delegations/{delegator_addr}/unbonding_delegation", adapter }, url: '/cosmos/distribution/v1beta1/delegators/{delegator_addr}/rewards',
base_tendermint_abci_query: { url: "/cosmos/base/tendermint/v1beta1/abci_query", adapter }, adapter,
base_tendermint_block_latest: { url: "/cosmos/base/tendermint/v1beta1/blocks/latest", adapter }, },
base_tendermint_block_height: { url: "/cosmos/base/tendermint/v1beta1/blocks/{height}", adapter }, slashing_params: { url: '/cosmos/slashing/v1beta1/params', adapter },
base_tendermint_node_info: { url: "/cosmos/base/tendermint/v1beta1/node_info", adapter }, slashing_signing_info: {
base_tendermint_validatorsets_latest: { url: "/cosmos/base/tendermint/v1beta1/validatorsets/latest", adapter }, url: '/cosmos/slashing/v1beta1/signing_infos',
base_tendermint_validatorsets_height: { url: "/cosmos/base/tendermint/v1beta1/validatorsets/{height}", adapter }, adapter,
tx_txs: { url: "/cosmos/tx/v1beta1/txs", adapter }, },
tx_txs_block: { url: "/cosmos/tx/v1beta1/txs/block/{height}", adapter }, gov_params_voting: { url: '/cosmos/gov/v1beta1/params/voting', adapter },
tx_hash: { url: "/cosmos/tx/v1beta1/txs/{hash}", adapter }, gov_params_tally: { url: '/cosmos/gov/v1beta1/params/tallying', adapter },
gov_params_deposit: { url: '/cosmos/gov/v1beta1/params/deposit', adapter },
gov_proposals: { url: '/cosmos/gov/v1beta1/proposals', adapter },
gov_proposals_proposal_id: {
url: '/cosmos/gov/v1beta1/proposals/{proposal_id}',
adapter,
},
gov_proposals_deposits: {
url: '/cosmos/gov/v1beta1/proposals/{proposal_id}/deposits',
adapter,
},
gov_proposals_tally: {
url: '/cosmos/gov/v1beta1/proposals/{proposal_id}/tally',
adapter,
},
gov_proposals_votes: {
url: '/cosmos/gov/v1beta1/proposals/{proposal_id}/votes?pagination.key={next_key}',
adapter,
},
gov_proposals_votes_voter: {
url: '/cosmos/gov/v1beta1/proposals/{proposal_id}/votes/{voter}',
adapter,
},
staking_deletations: {
url: '/cosmos/staking/v1beta1/delegations/{delegator_addr}',
adapter,
},
staking_delegator_redelegations: {
url: '/cosmos/staking/v1beta1/delegators/{delegator_addr}/redelegations',
adapter,
},
staking_delegator_unbonding_delegations: {
url: '/cosmos/staking/v1beta1/delegators/{delegator_addr}/unbonding_delegations',
adapter,
},
staking_delegator_validators: {
url: '/cosmos/staking/v1beta1/delegators/{delegator_addr}/validators',
adapter,
},
staking_params: { url: '/cosmos/staking/v1beta1/params', adapter },
staking_pool: { url: '/cosmos/staking/v1beta1/pool', adapter },
staking_validators: {
url: '/cosmos/staking/v1beta1/validators?pagination.limit={limit}&status={status}',
adapter,
},
staking_validators_address: {
url: '/cosmos/staking/v1beta1/validators/{validator_addr}',
adapter,
},
staking_validators_delegations: {
url: '/cosmos/staking/v1beta1/validators/{validator_addr}/delegations',
adapter,
},
staking_validators_delegations_delegator: {
url: '/cosmos/staking/v1beta1/validators/{validator_addr}/delegations/{delegator_addr}',
adapter,
},
staking_validators_delegations_unbonding_delegations: {
url: '/cosmos/staking/v1beta1/validators/{validator_addr}/delegations/{delegator_addr}/unbonding_delegation',
adapter,
},
base_tendermint_abci_query: {
url: '/cosmos/base/tendermint/v1beta1/abci_query',
adapter,
},
base_tendermint_block_latest: {
url: '/cosmos/base/tendermint/v1beta1/blocks/latest',
adapter,
},
base_tendermint_block_height: {
url: '/cosmos/base/tendermint/v1beta1/blocks/{height}',
adapter,
},
base_tendermint_node_info: {
url: '/cosmos/base/tendermint/v1beta1/node_info',
adapter,
},
base_tendermint_validatorsets_latest: {
url: '/cosmos/base/tendermint/v1beta1/validatorsets/latest',
adapter,
},
base_tendermint_validatorsets_height: {
url: '/cosmos/base/tendermint/v1beta1/validatorsets/{height}',
adapter,
},
tx_txs: { url: '/cosmos/tx/v1beta1/txs', adapter },
tx_txs_block: { url: '/cosmos/tx/v1beta1/txs/block/{height}', adapter },
tx_hash: { url: '/cosmos/tx/v1beta1/txs/{hash}', adapter },
mint_inflation: { url: "/cosmos/mint/v1beta1/inflation", adapter }, mint_inflation: { url: '/cosmos/mint/v1beta1/inflation', adapter },
mint_params: { url: "/cosmos/mint/v1beta1/params", adapter }, mint_params: { url: '/cosmos/mint/v1beta1/params', adapter },
mint_annual_provisions: { url: "/cosmos/mint/v1beta1/annual_provisions", adapter }, mint_annual_provisions: {
url: '/cosmos/mint/v1beta1/annual_provisions',
adapter,
},
// ibc // ibc
ibc_app_ica_controller_params: { url: "/ibc/apps/interchain_accounts/controller/v1/params", adapter }, ibc_app_ica_controller_params: {
ibc_app_ica_host_params: { url: "/ibc/apps/interchain_accounts/host/v1/params", adapter }, url: '/ibc/apps/interchain_accounts/controller/v1/params',
ibc_app_transfer_escrow_address: { url: "/ibc/apps/transfer/v1/channels/{channel_id}/ports/{port_id}/escrow_address", adapter }, adapter,
ibc_app_transfer_denom_traces: { url: "/ibc/apps/transfer/v1/denom_traces", adapter }, },
ibc_app_transfer_denom_traces_hash: { url: "/ibc/apps/transfer/v1/denom_traces/{hash}", adapter }, ibc_app_ica_host_params: {
ibc_core_channel_channels: { url: "/ibc/core/channel/v1/channels", adapter }, url: '/ibc/apps/interchain_accounts/host/v1/params',
ibc_core_channel_channels_next_sequence: { url: "/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/next_sequence", adapter }, adapter,
ibc_core_channel_channels_acknowledgements: { url: "/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_acknowledgements", adapter }, },
ibc_core_channel_connections_channels: { url: "/ibc/core/channel/v1/connections/{connection_id}/channels", adapter }, ibc_app_transfer_escrow_address: {
ibc_core_connection_connections: { url: "/ibc/core/connection/v1/connections", adapter }, url: '/ibc/apps/transfer/v1/channels/{channel_id}/ports/{port_id}/escrow_address',
ibc_core_connection_connections_connection_id: { url: "/ibc/core/connection/v1/connections/{connection_id}", adapter }, adapter,
ibc_core_connection_connections_connection_id_client_state: { url: "/ibc/core/connection/v1/connections/{connection_id}/client_state", adapter } },
ibc_app_transfer_denom_traces: {
url: '/ibc/apps/transfer/v1/denom_traces',
adapter,
},
ibc_app_transfer_denom_traces_hash: {
url: '/ibc/apps/transfer/v1/denom_traces/{hash}',
adapter,
},
ibc_core_channel_channels: { url: '/ibc/core/channel/v1/channels', adapter },
ibc_core_channel_channels_next_sequence: {
url: '/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/next_sequence',
adapter,
},
ibc_core_channel_channels_acknowledgements: {
url: '/ibc/core/channel/v1/channels/{channel_id}/ports/{port_id}/packet_acknowledgements',
adapter,
},
ibc_core_channel_connections_channels: {
url: '/ibc/core/channel/v1/connections/{connection_id}/channels',
adapter,
},
ibc_core_connection_connections: {
url: '/ibc/core/connection/v1/connections',
adapter,
},
ibc_core_connection_connections_connection_id: {
url: '/ibc/core/connection/v1/connections/{connection_id}',
adapter,
},
ibc_core_connection_connections_connection_id_client_state: {
url: '/ibc/core/connection/v1/connections/{connection_id}/client_state',
adapter,
},
}; };
export const VERSION_REGISTRY: Registry = { export const VERSION_REGISTRY: Registry = {
"0.46.1": DEFAULT '0.46.1': DEFAULT,
} };
export const NAME_REGISTRY: Registry = { export const NAME_REGISTRY: Registry = {
"evmos": withCustomAdapter(DEFAULT, {}) evmos: withCustomAdapter(DEFAULT, {}),
} };

View File

@ -1,205 +1,268 @@
import { fetchData } from '@/libs'; import { fetchData } from '@/libs';
import { DEFAULT } from '@/libs' import { DEFAULT } from '@/libs';
import { adapter, withCustomAdapter, type Request, type RequestRegistry, type Registry, type AbstractRegistry } from './registry'; import {
adapter,
withCustomAdapter,
type Request,
type RequestRegistry,
type Registry,
type AbstractRegistry,
} from './registry';
export class BaseRestClient<R extends AbstractRegistry> { export class BaseRestClient<R extends AbstractRegistry> {
endpoint: string; endpoint: string;
registry: R; registry: R;
constructor(endpoint: string, registry: R) { constructor(endpoint: string, registry: R) {
this.endpoint = endpoint this.endpoint = endpoint;
this.registry = registry this.registry = registry;
} }
async request<T>(request: Request<T>, args: Record<string, any>, query="") { async request<T>(request: Request<T>, args: Record<string, any>, query = '') {
let url = `${this.endpoint}${request.url}${query}` let url = `${this.endpoint}${request.url}${query}`;
Object.keys(args).forEach(k => { Object.keys(args).forEach((k) => {
url = url.replace(`{${k}}`, args[k] || "") url = url.replace(`{${k}}`, args[k] || '');
}) });
return fetchData<T>(url, adapter) return fetchData<T>(url, adapter);
} }
} }
export class CosmosRestClient extends BaseRestClient<RequestRegistry> { export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
// Auth Module // Auth Module
async getAuthAccounts() { async getAuthAccounts() {
return this.request(this.registry.auth_accounts, {}) return this.request(this.registry.auth_accounts, {});
} }
async getAuthAccount(address: string) { async getAuthAccount(address: string) {
return this.request(this.registry.auth_account_address, {address}) return this.request(this.registry.auth_account_address, { address });
} }
// Bank Module // Bank Module
async getBankParams() { async getBankParams() {
return this.request(this.registry.bank_params, {}) return this.request(this.registry.bank_params, {});
} }
async getBankBalances(address: string) { async getBankBalances(address: string) {
return this.request(this.registry.bank_balances_address, {address}) return this.request(this.registry.bank_balances_address, { address });
} }
async getBankDenomMetadata() { async getBankDenomMetadata() {
return this.request(this.registry.bank_denoms_metadata, {}) return this.request(this.registry.bank_denoms_metadata, {});
} }
async getBankSupply() { async getBankSupply() {
return this.request(this.registry.bank_supply, {}) return this.request(this.registry.bank_supply, {});
} }
async getBankSupplyByDenom(denom: string) { async getBankSupplyByDenom(denom: string) {
return this.request(this.registry.bank_supply_by_denom, {denom}) return this.request(this.registry.bank_supply_by_denom, { denom });
} }
// Distribution Module // Distribution Module
async getDistributionParams() { async getDistributionParams() {
return this.request(this.registry.distribution_params, {}) return this.request(this.registry.distribution_params, {});
} }
async getDistributionCommunityPool() { async getDistributionCommunityPool() {
return this.request(this.registry.distribution_community_pool, {}) return this.request(this.registry.distribution_community_pool, {});
} }
async getDistributionDelegatorRewards(delegator_addr: string) { async getDistributionDelegatorRewards(delegator_addr: string) {
return this.request(this.registry.distribution_delegator_rewards, {delegator_addr}) return this.request(this.registry.distribution_delegator_rewards, {
delegator_addr,
});
} }
async getDistributionValidatorCommission(validator_address: string) { async getDistributionValidatorCommission(validator_address: string) {
return this.request(this.registry.distribution_validator_commission, {validator_address}) return this.request(this.registry.distribution_validator_commission, {
validator_address,
});
} }
async getDistributionValidatorOutstandingRewards(validator_address: string) { async getDistributionValidatorOutstandingRewards(validator_address: string) {
return this.request(this.registry.distribution_validator_outstanding_rewards, {validator_address}) return this.request(
this.registry.distribution_validator_outstanding_rewards,
{ validator_address }
);
} }
async getDistributionValidatorSlashes(validator_address: string) { async getDistributionValidatorSlashes(validator_address: string) {
return this.request(this.registry.distribution_validator_slashes, {validator_address}) return this.request(this.registry.distribution_validator_slashes, {
validator_address,
});
} }
// Slashing // Slashing
async getSlashingParams() { async getSlashingParams() {
return this.request(this.registry.slashing_params, {}) return this.request(this.registry.slashing_params, {});
} }
async getSlashingSigningInfos() { async getSlashingSigningInfos() {
const query = "?pagination.limit=300" const query = '?pagination.limit=300';
return this.request(this.registry.slashing_signing_info, {}, query) return this.request(this.registry.slashing_signing_info, {}, query);
} }
// Gov // Gov
async getGovParamsVoting() { async getGovParamsVoting() {
return this.request(this.registry.gov_params_voting, {}) return this.request(this.registry.gov_params_voting, {});
} }
async getGovParamsDeposit() { async getGovParamsDeposit() {
return this.request(this.registry.gov_params_deposit, {}) return this.request(this.registry.gov_params_deposit, {});
} }
async getGovParamsTally() { async getGovParamsTally() {
return this.request(this.registry.gov_params_tally, {}) return this.request(this.registry.gov_params_tally, {});
} }
async getGovProposals(status: string, limit = 50) { async getGovProposals(status: string, limit = 50) {
const query = "?proposal_status={status}&pagination.limit={limit}&pagination.reverse=true&pagination.key=" const query =
return this.request(this.registry.gov_proposals, {status, limit}, query) '?proposal_status={status}&pagination.limit={limit}&pagination.reverse=true&pagination.key=';
return this.request(this.registry.gov_proposals, { status, limit }, query);
} }
async getGovProposal(proposal_id: string) { async getGovProposal(proposal_id: string) {
return this.request(this.registry.gov_proposals_proposal_id, {proposal_id}) return this.request(this.registry.gov_proposals_proposal_id, {
proposal_id,
});
} }
async getGovProposalDeposits(proposal_id: string) { async getGovProposalDeposits(proposal_id: string) {
return this.request(this.registry.gov_proposals_deposits, {proposal_id}) return this.request(this.registry.gov_proposals_deposits, { proposal_id });
} }
async getGovProposalTally(proposal_id: string) { async getGovProposalTally(proposal_id: string) {
return this.request(this.registry.gov_proposals_tally, {proposal_id}) return this.request(this.registry.gov_proposals_tally, { proposal_id });
} }
async getGovProposalVotes(proposal_id: string, next_key?: string) { async getGovProposalVotes(proposal_id: string, next_key?: string) {
return this.request(this.registry.gov_proposals_votes, {proposal_id, next_key}) return this.request(this.registry.gov_proposals_votes, {
proposal_id,
next_key,
});
} }
async getGovProposalVotesVoter(proposal_id: string, voter: string) { async getGovProposalVotesVoter(proposal_id: string, voter: string) {
return this.request(this.registry.gov_proposals_votes_voter, {proposal_id, voter}) return this.request(this.registry.gov_proposals_votes_voter, {
proposal_id,
voter,
});
} }
// staking // staking
async getStakingDelegations(delegator_addr: string) { async getStakingDelegations(delegator_addr: string) {
return this.request(this.registry.staking_deletations, {delegator_addr}) return this.request(this.registry.staking_deletations, { delegator_addr });
} }
async getStakingDelegatorRedelegations(delegator_addr: string) { async getStakingDelegatorRedelegations(delegator_addr: string) {
return this.request(this.registry.staking_delegator_redelegations, {delegator_addr}) return this.request(this.registry.staking_delegator_redelegations, {
delegator_addr,
});
} }
async getStakingDelegatorUnbonding(delegator_addr: string) { async getStakingDelegatorUnbonding(delegator_addr: string) {
return this.request(this.registry.staking_delegator_unbonding_delegations, {delegator_addr}) return this.request(this.registry.staking_delegator_unbonding_delegations, {
delegator_addr,
});
} }
async getStakingDelegatorValidators(delegator_addr: string) { async getStakingDelegatorValidators(delegator_addr: string) {
return this.request(this.registry.staking_delegator_validators, {delegator_addr}) return this.request(this.registry.staking_delegator_validators, {
delegator_addr,
});
} }
async getStakingParams() { async getStakingParams() {
return this.request(this.registry.staking_params, {}) return this.request(this.registry.staking_params, {});
} }
async getStakingPool() { async getStakingPool() {
return this.request(this.registry.staking_pool, {}) return this.request(this.registry.staking_pool, {});
} }
async getStakingValidators(status: string, limit = 200) { async getStakingValidators(status: string, limit = 200) {
return this.request(this.registry.staking_validators, {status, limit}) return this.request(this.registry.staking_validators, { status, limit });
} }
async getStakingValidator(validator_addr: string) { async getStakingValidator(validator_addr: string) {
return this.request(this.registry.staking_validators_address, {validator_addr}) return this.request(this.registry.staking_validators_address, {
validator_addr,
});
} }
async getStakingValidatorsDelegations(validator_addr: string) { async getStakingValidatorsDelegations(validator_addr: string) {
return this.request(this.registry.staking_validators_delegations, {validator_addr}) return this.request(this.registry.staking_validators_delegations, {
validator_addr,
});
} }
async getStakingValidatorsDelegationsDelegator(validator_addr: string, delegator_addr: string) { async getStakingValidatorsDelegationsDelegator(
return this.request(this.registry.staking_validators_delegations_delegator, {validator_addr, delegator_addr}) validator_addr: string,
delegator_addr: string
) {
return this.request(
this.registry.staking_validators_delegations_delegator,
{ validator_addr, delegator_addr }
);
} }
async getStakingValidatorsDelegationsUnbonding(validator_addr: string, delegator_addr: string) { async getStakingValidatorsDelegationsUnbonding(
return this.request(this.registry.staking_validators_delegations_unbonding_delegations, {validator_addr, delegator_addr}) validator_addr: string,
delegator_addr: string
) {
return this.request(
this.registry.staking_validators_delegations_unbonding_delegations,
{ validator_addr, delegator_addr }
);
} }
//tendermint //tendermint
async getBaseAbciQuery() { async getBaseAbciQuery() {
return this.request(this.registry.base_tendermint_abci_query, {}) return this.request(this.registry.base_tendermint_abci_query, {});
} }
async getBaseBlockLatest() { async getBaseBlockLatest() {
return this.request(this.registry.base_tendermint_block_latest, {}) return this.request(this.registry.base_tendermint_block_latest, {});
} }
async getBaseBlockAt(height: string | number) { async getBaseBlockAt(height: string | number) {
return this.request(this.registry.base_tendermint_block_height, {height}) return this.request(this.registry.base_tendermint_block_height, { height });
} }
async getBaseNodeInfo() { async getBaseNodeInfo() {
return this.request(this.registry.base_tendermint_node_info, {}) return this.request(this.registry.base_tendermint_node_info, {});
} }
async getBaseValidatorsetAt(height: string | number) { async getBaseValidatorsetAt(height: string | number) {
return this.request(this.registry.base_tendermint_validatorsets_height, {height}) return this.request(this.registry.base_tendermint_validatorsets_height, {
height,
});
} }
async getBaseValidatorsetLatest() { async getBaseValidatorsetLatest() {
return this.request(this.registry.base_tendermint_validatorsets_latest, {}) return this.request(this.registry.base_tendermint_validatorsets_latest, {});
} }
// tx // tx
async getTxsBySender(sender: string) { async getTxsBySender(sender: string) {
const query = `?pagination.reverse=true&events=message.sender='${sender}'` const query = `?pagination.reverse=true&events=message.sender='${sender}'`;
return this.request(this.registry.tx_txs, {}, query) return this.request(this.registry.tx_txs, {}, query);
} }
async getTxsAt(height: string | number) { async getTxsAt(height: string | number) {
return this.request(this.registry.tx_txs_block, {height}) return this.request(this.registry.tx_txs_block, { height });
} }
async getTx(hash: string) { async getTx(hash: string) {
return this.request(this.registry.tx_hash, {hash}) return this.request(this.registry.tx_hash, { hash });
} }
// mint // mint
async getMintParam() { async getMintParam() {
return this.request(this.registry.mint_params, {}) return this.request(this.registry.mint_params, {});
} }
async getMintInflation() { async getMintInflation() {
return this.request(this.registry.mint_inflation, {}) return this.request(this.registry.mint_inflation, {});
} }
async getMintAnnualProvisions() { async getMintAnnualProvisions() {
return this.request(this.registry.mint_annual_provisions, {}) return this.request(this.registry.mint_annual_provisions, {});
} }
// ibc // ibc
async getIBCAppTransferDenom(hash: string) { async getIBCAppTransferDenom(hash: string) {
return this.request(this.registry.ibc_app_transfer_denom_traces_hash, {hash}) return this.request(this.registry.ibc_app_transfer_denom_traces_hash, {
hash,
});
} }
async getIBCConnections() { async getIBCConnections() {
return this.request(this.registry.ibc_core_connection_connections, {}) return this.request(this.registry.ibc_core_connection_connections, {});
} }
async getIBCConnectionsById(connection_id: string) { async getIBCConnectionsById(connection_id: string) {
return this.request(this.registry.ibc_core_connection_connections_connection_id, {connection_id}) return this.request(
this.registry.ibc_core_connection_connections_connection_id,
{ connection_id }
);
} }
async getIBCConnectionsClientState(connection_id: string) { async getIBCConnectionsClientState(connection_id: string) {
return this.request(this.registry.ibc_core_connection_connections_connection_id_client_state, {connection_id}) return this.request(
this.registry.ibc_core_connection_connections_connection_id_client_state,
{ connection_id }
);
} }
async getIBCConnectionsChannels(connection_id: string) { async getIBCConnectionsChannels(connection_id: string) {
return this.request(this.registry.ibc_core_channel_connections_channels, {connection_id}) return this.request(this.registry.ibc_core_channel_connections_channels, {
connection_id,
});
} }
async getIBCChannels() { async getIBCChannels() {
return this.request(this.registry.ibc_core_channel_channels, {}) return this.request(this.registry.ibc_core_channel_channels, {});
} }
async getIBCChannelAcknowledgements(channel_id: string, port_id: string) { async getIBCChannelAcknowledgements(channel_id: string, port_id: string) {
return this.request(this.registry.ibc_core_channel_channels_acknowledgements, {channel_id, port_id}) return this.request(
this.registry.ibc_core_channel_channels_acknowledgements,
{ channel_id, port_id }
);
} }
async getIBCChannelNextSequence(channel_id: string, port_id: string) { async getIBCChannelNextSequence(channel_id: string, port_id: string) {
return this.request(this.registry.ibc_core_channel_channels_next_sequence, {channel_id, port_id}) return this.request(this.registry.ibc_core_channel_channels_next_sequence, {
channel_id,
port_id,
});
} }
} }

View File

@ -1,6 +1,9 @@
import fetch from 'cross-fetch' import fetch from 'cross-fetch';
export async function fetchData<T>(url: string, adapter: (source: any) => T): Promise<T> { export async function fetchData<T>(
url: string,
adapter: (source: any) => T
): Promise<T> {
const response = await fetch(url); const response = await fetch(url);
if (!response.ok) { if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`); throw new Error(`HTTP error: ${response.status}`);
@ -27,7 +30,7 @@ try {
// */ // */
export async function get(url: string) { export async function get(url: string) {
return (await fetch(url)).json() return (await fetch(url)).json();
} }
export async function post(url: string, data: any) { export async function post(url: string, data: any) {
@ -42,7 +45,7 @@ export async function post(url: string, data: any) {
'Accept-Encoding': 'gzip, deflate, br', 'Accept-Encoding': 'gzip, deflate, br',
}, },
body: JSON.stringify(data), // body data type must match "Content-Type" header body: JSON.stringify(data), // body data type must match "Content-Type" header
}) });
// const response = axios.post((config ? config.api : this.config.api) + url, data) // const response = axios.post((config ? config.api : this.config.api) + url, data)
return response.json() // parses JSON response into native JavaScript objects return response.json(); // parses JSON response into native JavaScript objects
} }

View File

@ -1,4 +1,4 @@
export * from './address' export * from './address';
export * from './http' export * from './http';
export * from './misc' export * from './misc';
export * from './api' export * from './api';

View File

@ -1,16 +1,16 @@
import { sha256 } from "@cosmjs/crypto"; import { sha256 } from '@cosmjs/crypto';
import { toHex } from "@cosmjs/encoding"; import { toHex } from '@cosmjs/encoding';
export function newPageRequest(param: { export function newPageRequest(param: {
key?: Uint8Array, key?: Uint8Array;
limit?: number, limit?: number;
offset?: number, offset?: number;
countTotal?: boolean, countTotal?: boolean;
reverse?: boolean reverse?: boolean;
}) { }) {
return param return param;
} }
export function hashTx(raw: Uint8Array) { export function hashTx(raw: Uint8Array) {
return toHex(sha256(raw)).toUpperCase() return toHex(sha256(raw)).toUpperCase();
} }

View File

@ -1,24 +1,60 @@
import type { AuthAccount, Block, ClientStateWithProof, Coin, ConnectionWithProof, DenomTrace, NodeInfo, PaginabledAccounts, PaginatedIBCChannels, PaginatedIBCConnections, PaginatedTendermintValidator,} from "@/types"; import type {
import type { BankParams, PaginatedBalances, PaginatedDenomMetadata, PaginatedSupply } from "@/types/bank"; AuthAccount,
import type { DistributionParams, PaginatedSlashes } from "@/types/distribution"; Block,
import type { GovParams, GovProposal, GovVote, PaginatedProposalDeposit, PaginatedProposalVotes, PaginatedProposals, Tally } from "@/types/gov"; ClientStateWithProof,
import type { PaginatedSigningInfo } from "@/types/slashing"; Coin,
import type { Delegation, PaginatedDelegations, PaginatedRedelegations, PaginatedUnbonding, PaginatedValdiators, StakingParam, StakingPool, Validator } from "@/types/staking"; ConnectionWithProof,
import type { PaginatedTxs, Tx, TxResponse } from "@/types/tx"; DenomTrace,
NodeInfo,
PaginabledAccounts,
PaginatedIBCChannels,
PaginatedIBCConnections,
PaginatedTendermintValidator,
} from '@/types';
import type {
BankParams,
PaginatedBalances,
PaginatedDenomMetadata,
PaginatedSupply,
} from '@/types/bank';
import type {
DistributionParams,
PaginatedSlashes,
} from '@/types/distribution';
import type {
GovParams,
GovProposal,
GovVote,
PaginatedProposalDeposit,
PaginatedProposalVotes,
PaginatedProposals,
Tally,
} from '@/types/gov';
import type { PaginatedSigningInfo } from '@/types/slashing';
import type {
Delegation,
PaginatedDelegations,
PaginatedRedelegations,
PaginatedUnbonding,
PaginatedValdiators,
StakingParam,
StakingPool,
Validator,
} from '@/types/staking';
import type { PaginatedTxs, Tx, TxResponse } from '@/types/tx';
export interface Request<T> { export interface Request<T> {
url: string, url: string;
adapter: (source: any) => T adapter: (source: any) => T;
} }
export interface AbstractRegistry { export interface AbstractRegistry {
[key: string]: Request<any> [key: string]: Request<any>;
} }
// use snake style, since the all return object use snake style. // use snake style, since the all return object use snake style.
export interface RequestRegistry extends AbstractRegistry { export interface RequestRegistry extends AbstractRegistry {
auth_params: Request<any> auth_params: Request<any>;
auth_accounts: Request<PaginabledAccounts>; auth_accounts: Request<PaginabledAccounts>;
auth_account_address: Request<{ account: AuthAccount }>; auth_account_address: Request<{ account: AuthAccount }>;
@ -29,8 +65,12 @@ export interface RequestRegistry extends AbstractRegistry {
bank_supply_by_denom: Request<{ amount: Coin }>; bank_supply_by_denom: Request<{ amount: Coin }>;
distribution_params: Request<DistributionParams>; distribution_params: Request<DistributionParams>;
distribution_validator_commission: Request<{commission?: {commission?: Coin[]}}>; distribution_validator_commission: Request<{
distribution_validator_outstanding_rewards: Request<{rewards?: {rewards?: Coin[]}}>; commission?: { commission?: Coin[] };
}>;
distribution_validator_outstanding_rewards: Request<{
rewards?: { rewards?: Coin[] };
}>;
distribution_validator_slashes: Request<PaginatedSlashes>; distribution_validator_slashes: Request<PaginatedSlashes>;
distribution_community_pool: Request<{ pool: Coin[] }>; distribution_community_pool: Request<{ pool: Coin[] }>;
distribution_delegator_rewards: Request<any>; distribution_delegator_rewards: Request<any>;
@ -38,11 +78,11 @@ export interface RequestRegistry extends AbstractRegistry {
mint_inflation: Request<{ inflation: string }>; mint_inflation: Request<{ inflation: string }>;
mint_params: Request<{ mint_params: Request<{
params: { params: {
mint_denom: string, mint_denom: string;
blocks_per_year: string blocks_per_year: string;
} };
}>; }>;
mint_annual_provisions: Request<{annual_provisions: string}> mint_annual_provisions: Request<{ annual_provisions: string }>;
slashing_params: Request<any>; slashing_params: Request<any>;
slashing_signing_info: Request<PaginatedSigningInfo>; slashing_signing_info: Request<PaginatedSigningInfo>;
@ -66,7 +106,9 @@ export interface RequestRegistry extends AbstractRegistry {
staking_validators: Request<PaginatedValdiators>; staking_validators: Request<PaginatedValdiators>;
staking_validators_address: Request<{ validator: Validator }>; staking_validators_address: Request<{ validator: Validator }>;
staking_validators_delegations: Request<PaginatedDelegations>; staking_validators_delegations: Request<PaginatedDelegations>;
staking_validators_delegations_delegator: Request<{delegation_response: Delegation}>; staking_validators_delegations_delegator: Request<{
delegation_response: Delegation;
}>;
staking_validators_delegations_unbonding_delegations: Request<PaginatedUnbonding>; staking_validators_delegations_unbonding_delegations: Request<PaginatedUnbonding>;
base_tendermint_abci_query: Request<any>; base_tendermint_abci_query: Request<any>;
@ -78,45 +120,50 @@ export interface RequestRegistry extends AbstractRegistry {
tx_txs: Request<PaginatedTxs>; tx_txs: Request<PaginatedTxs>;
tx_txs_block: Request<Tx>; tx_txs_block: Request<Tx>;
tx_hash: Request<{tx: Tx, tx_response: TxResponse}>; tx_hash: Request<{ tx: Tx; tx_response: TxResponse }>;
ibc_app_ica_controller_params: Request<any>; ibc_app_ica_controller_params: Request<any>;
ibc_app_ica_host_params: Request<any> ibc_app_ica_host_params: Request<any>;
ibc_app_transfer_escrow_address: Request<any>; ibc_app_transfer_escrow_address: Request<any>;
ibc_app_transfer_denom_traces: Request<any>; ibc_app_transfer_denom_traces: Request<any>;
ibc_app_transfer_denom_traces_hash: Request<{ ibc_app_transfer_denom_traces_hash: Request<{
denom_trace: DenomTrace denom_trace: DenomTrace;
}>; }>;
ibc_core_channel_channels: Request<PaginatedIBCChannels>; ibc_core_channel_channels: Request<PaginatedIBCChannels>;
ibc_core_channel_channels_next_sequence: Request<{ ibc_core_channel_channels_next_sequence: Request<{
next_sequence_receive: string, next_sequence_receive: string;
proof: string, proof: string;
proof_height: { proof_height: {
revision_number: string, revision_number: string;
revision_height: string revision_height: string;
} };
}>; }>;
ibc_core_channel_channels_acknowledgements: Request<any>; ibc_core_channel_channels_acknowledgements: Request<any>;
ibc_core_channel_connections_channels: Request<PaginatedIBCChannels>; ibc_core_channel_connections_channels: Request<PaginatedIBCChannels>;
ibc_core_connection_connections: Request<PaginatedIBCConnections>; ibc_core_connection_connections: Request<PaginatedIBCConnections>;
ibc_core_connection_connections_connection_id: Request<ConnectionWithProof>; ibc_core_connection_connections_connection_id: Request<ConnectionWithProof>;
ibc_core_connection_connections_connection_id_client_state: Request<ClientStateWithProof>; ibc_core_connection_connections_connection_id_client_state: Request<ClientStateWithProof>;
} }
export function adapter<T>(source: any): T { export function adapter<T>(source: any): T {
return source return source;
} }
export interface Registry { export interface Registry {
[key: string]: RequestRegistry; [key: string]: RequestRegistry;
} }
export function withCustomAdapter<T extends RequestRegistry>(target: T, source?: Partial<T>): T { export function withCustomAdapter<T extends RequestRegistry>(
target: T,
source?: Partial<T>
): T {
return source ? Object.assign({}, target, source) : target; return source ? Object.assign({}, target, source) : target;
} }
export function findConfigByName(name: string, registry: Registry): RequestRegistry { export function findConfigByName(
name: string,
registry: Registry
): RequestRegistry {
const url = registry[name]; const url = registry[name];
if (!url) { if (!url) {
throw new Error(`Unsupported version or name: ${name}`); throw new Error(`Unsupported version or name: ${name}`);
@ -125,7 +172,10 @@ export function findConfigByName(name: string, registry: Registry): RequestRegis
return url; return url;
} }
export function findConfigByVersion(version: string, registry: Registry): RequestRegistry { export function findConfigByVersion(
version: string,
registry: Registry
): RequestRegistry {
let closestVersion: string | null = null; let closestVersion: string | null = null;
for (const key in registry) { for (const key in registry) {

View File

@ -1,110 +1,157 @@
export function getLocalObject(name: string) { export function getLocalObject(name: string) {
const text = localStorage.getItem(name) const text = localStorage.getItem(name);
if (text) { if (text) {
return JSON.parse(text) return JSON.parse(text);
} }
return null return null;
} }
export function getLocalChains() { export function getLocalChains() {
return 'osmosis' return 'osmosis';
} }
export const percent = (num: number) => { export const percent = (num: number) => {
return parseFloat((num * 100).toFixed(2)) return parseFloat((num * 100).toFixed(2));
} };
const COUNT_ABBRS = ['', 'K', 'M', 'B', 't', 'q', 's', 'S', 'o', 'n', 'd', 'U', 'D', 'T', 'Qt', 'Qd', 'Sd', 'St'] const COUNT_ABBRS = [
'',
'K',
'M',
'B',
't',
'q',
's',
'S',
'o',
'n',
'd',
'U',
'D',
'T',
'Qt',
'Qd',
'Sd',
'St',
];
export function formatNumber(count: number, withAbbr = false, decimals = 2) { export function formatNumber(count: number, withAbbr = false, decimals = 2) {
const i = count === 0 ? count : Math.floor(Math.log(count) / Math.log(1000)) const i = count === 0 ? count : Math.floor(Math.log(count) / Math.log(1000));
let result: any = parseFloat((count / (1000 ** i)).toFixed(decimals)) let result: any = parseFloat((count / 1000 ** i).toFixed(decimals));
if (withAbbr && COUNT_ABBRS[i]) { if (withAbbr && COUNT_ABBRS[i]) {
result += `${COUNT_ABBRS[i]}` result += `${COUNT_ABBRS[i]}`;
} }
return result return result;
} }
export function formatTokenAmount(assets: any ,tokenAmount: any, decimals = 2, tokenDenom = 'uatom', format = true) { export function formatTokenAmount(
const denom = tokenDenom?.denom_trace ? tokenDenom?.denom_trace?.base_denom : tokenDenom assets: any,
let amount = 0 tokenAmount: any,
const asset = assets.find((a:any) => (a.base === denom)) decimals = 2,
let exp = asset? asset.exponent: String(denom).startsWith('gravity') ? 18 : 6 tokenDenom = 'uatom',
const config = Object.values(getLocalChains()) format = true
) {
const denom = tokenDenom?.denom_trace
? tokenDenom?.denom_trace?.base_denom
: tokenDenom;
let amount = 0;
const asset = assets.find((a: any) => a.base === denom);
let exp = asset
? asset.exponent
: String(denom).startsWith('gravity')
? 18
: 6;
const config = Object.values(getLocalChains());
amount = Number(Number(tokenAmount)) / (10 ** exp) amount = Number(Number(tokenAmount)) / 10 ** exp;
if (amount > 10) { if (amount > 10) {
if (format) { return numberWithCommas(parseFloat(amount.toFixed(decimals))) } if (format) {
return parseFloat(amount.toFixed(decimals)) return numberWithCommas(parseFloat(amount.toFixed(decimals)));
} }
return parseFloat(amount.toFixed(exp)) return parseFloat(amount.toFixed(decimals));
}
return parseFloat(amount.toFixed(exp));
} }
export function numberWithCommas(x: any) { export function numberWithCommas(x: any) {
const parts = x.toString().split('.') const parts = x.toString().split('.');
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',') parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return parts.join('.') return parts.join('.');
} }
export function tokenFormatter(tokens: any, denoms = {}, decimal = 2) { export function tokenFormatter(tokens: any, denoms = {}, decimal = 2) {
if (Array.isArray(tokens)) { if (Array.isArray(tokens)) {
return tokens.map(t => formatToken(t, denoms, decimal)).join(', ') return tokens.map((t) => formatToken(t, denoms, decimal)).join(', ');
} }
return formatToken(tokens, denoms, 2) return formatToken(tokens, denoms, 2);
} }
export function formatToken(token:any, IBCDenom = {}, decimals = 2, withDenom = true) { export function formatToken(
token: any,
IBCDenom = {},
decimals = 2,
withDenom = true
) {
if (token) { if (token) {
const denom = IBCDenom[token.denom] || token.denom const denom = IBCDenom[token.denom] || token.denom;
if (withDenom) { if (withDenom) {
return `${formatTokenAmount(token.amount, decimals, denom)} ${formatTokenDenom(denom)}` return `${formatTokenAmount(
token.amount,
decimals,
denom
)} ${formatTokenDenom(denom)}`;
} }
return formatTokenAmount(token.amount, decimals, denom) return formatTokenAmount(token.amount, decimals, denom);
} }
return token return token;
} }
export function formatTokenDenom(tokenDenom: any) { export function formatTokenDenom(tokenDenom: any) {
if (tokenDenom && tokenDenom.code === undefined) { if (tokenDenom && tokenDenom.code === undefined) {
let denom = tokenDenom.denom_trace ? tokenDenom.denom_trace.base_denom : tokenDenom let denom = tokenDenom.denom_trace
const chains = getLocalChains() ? tokenDenom.denom_trace.base_denom
const selected = localStorage.getItem('selected_chain') : tokenDenom;
const selChain = chains[selected] const chains = getLocalChains();
const nativeAsset = selChain.assets.find(a => (a.base === denom)) const selected = localStorage.getItem('selected_chain');
const selChain = chains[selected];
const nativeAsset = selChain.assets.find((a) => a.base === denom);
if (nativeAsset) { if (nativeAsset) {
denom = nativeAsset.symbol denom = nativeAsset.symbol;
} else { } else {
const config = Object.values(chains) const config = Object.values(chains);
config.forEach(x => { config.forEach((x) => {
if (x.assets) { if (x.assets) {
const asset = x.assets.find(a => (a.base === denom)) const asset = x.assets.find((a) => a.base === denom);
if (asset) denom = asset.symbol if (asset) denom = asset.symbol;
} }
}) });
} }
return denom.length > 10 ? `${denom.substring(0, 7).toUpperCase()}..${denom.substring(denom.length - 3)}` : denom.toUpperCase() return denom.length > 10
? `${denom.substring(0, 7).toUpperCase()}..${denom.substring(
denom.length - 3
)}`
: denom.toUpperCase();
} }
return '' return '';
} }
export function isToken(value: string) { export function isToken(value: string) {
let is = false let is = false;
if (Array.isArray(value)) { if (Array.isArray(value)) {
is = value.findIndex(x => Object.keys(x).includes('denom')) > -1 is = value.findIndex((x) => Object.keys(x).includes('denom')) > -1;
} else { } else {
is = Object.keys(value).includes('denom') is = Object.keys(value).includes('denom');
} }
return is return is;
} }
export function isStringArray(value: any) { export function isStringArray(value: any) {
let is = false let is = false;
if (Array.isArray(value)) { if (Array.isArray(value)) {
is = value.findIndex(x => typeof x === 'string') > -1 is = value.findIndex((x) => typeof x === 'string') > -1;
} }
return is return is;
} }
export function isHexAddress(v: any) { export function isHexAddress(v: any) {
// const re = /^[A-Z\d]{40}$/ // const re = /^[A-Z\d]{40}$/
// return re.test(v) // return re.test(v)
return v.length === 28 return v.length === 28;
} }

View File

@ -1,81 +1,86 @@
<script lang="ts" setup> <script lang="ts" setup>
import { useBlockchain, useFormatter, useStakingStore } from '@/stores'; import { useBlockchain, useFormatter, useStakingStore } from '@/stores';
import DynamicComponent from '@/components/dynamic/DynamicComponent.vue'; import DynamicComponent from '@/components/dynamic/DynamicComponent.vue';
import DonutChart from '@/components/charts/DonutChart.vue' import DonutChart from '@/components/charts/DonutChart.vue';
import { computed, ref } from '@vue/reactivity'; import { computed, ref } from '@vue/reactivity';
import 'vue-json-pretty/lib/styles.css'; import 'vue-json-pretty/lib/styles.css';
import type { AuthAccount, Delegation, TxResponse, DelegatorRewards, UnbondingResponses } from '@/types'; import type {
AuthAccount,
Delegation,
TxResponse,
DelegatorRewards,
UnbondingResponses,
} from '@/types';
import type { Coin } from '@cosmjs/amino'; import type { Coin } from '@cosmjs/amino';
const props = defineProps(['address', 'chain']) const props = defineProps(['address', 'chain']);
const blockchain = useBlockchain() const blockchain = useBlockchain();
const stakingStore = useStakingStore() const stakingStore = useStakingStore();
const format = useFormatter() const format = useFormatter();
const account = ref({} as AuthAccount) const account = ref({} as AuthAccount);
const txs = ref({} as TxResponse[]) const txs = ref({} as TxResponse[]);
const delegations = ref([] as Delegation[]) const delegations = ref([] as Delegation[]);
const rewards = ref({} as DelegatorRewards) const rewards = ref({} as DelegatorRewards);
const balances = ref([] as Coin[]) const balances = ref([] as Coin[]);
const unbonding = ref([] as UnbondingResponses[]) const unbonding = ref([] as UnbondingResponses[]);
const unbondingTotal = ref(0) const unbondingTotal = ref(0);
const chart = {} const chart = {};
const totalAmountByCategory = computed(() => { const totalAmountByCategory = computed(() => {
let sumDel = 0; let sumDel = 0;
delegations.value?.forEach(x => { delegations.value?.forEach((x) => {
sumDel += Number(x.balance.amount) sumDel += Number(x.balance.amount);
}) });
let sumRew = 0 let sumRew = 0;
rewards.value?.total?.forEach(x => { rewards.value?.total?.forEach((x) => {
sumRew += Number(x.amount) sumRew += Number(x.amount);
}) });
let sumBal = 0 let sumBal = 0;
balances.value?.forEach(x => { balances.value?.forEach((x) => {
sumBal += Number(x.amount) sumBal += Number(x.amount);
}) });
let sumUn = 0 let sumUn = 0;
unbonding.value?.forEach(x => { unbonding.value?.forEach((x) => {
x.entries?.forEach(y => { x.entries?.forEach((y) => {
sumUn += Number(y.balance) sumUn += Number(y.balance);
}) });
}) });
return [sumBal, sumDel, sumRew, sumUn] return [sumBal, sumDel, sumRew, sumUn];
}) });
const labels = ['Balance', 'Delegation', 'Reward', 'Unbonding'] const labels = ['Balance', 'Delegation', 'Reward', 'Unbonding'];
const totalAmount = computed(() => { const totalAmount = computed(() => {
return totalAmountByCategory.value.reduce((p, c)=> c + p, 0) return totalAmountByCategory.value.reduce((p, c) => c + p, 0);
}) });
function loadAccount(address: string) { function loadAccount(address: string) {
blockchain.rpc.getAuthAccount(address).then(x => { blockchain.rpc.getAuthAccount(address).then((x) => {
account.value = x.account account.value = x.account;
}) });
blockchain.rpc.getTxsBySender(address).then(x => { blockchain.rpc.getTxsBySender(address).then((x) => {
txs.value = x.tx_responses txs.value = x.tx_responses;
}) });
blockchain.rpc.getDistributionDelegatorRewards(address).then(x => { blockchain.rpc.getDistributionDelegatorRewards(address).then((x) => {
rewards.value = x rewards.value = x;
}) });
blockchain.rpc.getStakingDelegations(address).then(x => { blockchain.rpc.getStakingDelegations(address).then((x) => {
delegations.value = x.delegation_responses delegations.value = x.delegation_responses;
}) });
blockchain.rpc.getBankBalances(address).then(x => { blockchain.rpc.getBankBalances(address).then((x) => {
balances.value = x.balances balances.value = x.balances;
}) });
blockchain.rpc.getStakingDelegatorUnbonding(address).then(x => { blockchain.rpc.getStakingDelegatorUnbonding(address).then((x) => {
unbonding.value = x.unbonding_responses unbonding.value = x.unbonding_responses;
x.unbonding_responses?.forEach(y => { x.unbonding_responses?.forEach((y) => {
y.entries.forEach(z => { y.entries.forEach((z) => {
unbondingTotal.value += Number(z.balance) unbondingTotal.value += Number(z.balance);
}) });
}) });
}) });
} }
loadAccount(props.address) loadAccount(props.address);
</script> </script>
<template> <template>
<div v-if="account"> <div v-if="account">
@ -83,12 +88,7 @@ loadAccount(props.address)
<VList> <VList>
<VListItem> <VListItem>
<template #prepend> <template #prepend>
<VAvatar <VAvatar rounded variant="tonal" size="45" color="primary">
rounded
variant="tonal"
size="45"
color="primary"
>
<VIcon icon="mdi-qrcode" /> <VIcon icon="mdi-qrcode" />
</VAvatar> </VAvatar>
</template> </template>
@ -114,12 +114,7 @@ loadAccount(props.address)
<VList class="card-list"> <VList class="card-list">
<VListItem v-for="v in balances"> <VListItem v-for="v in balances">
<template #prepend> <template #prepend>
<VAvatar <VAvatar rounded variant="tonal" size="35" color="info">
rounded
variant="tonal"
size="35"
color="info"
>
<VIcon icon="mdi-account-cash" size="20" /> <VIcon icon="mdi-account-cash" size="20" />
</VAvatar> </VAvatar>
</template> </template>
@ -130,17 +125,14 @@ loadAccount(props.address)
${{ 0 }} ${{ 0 }}
</VListItemSubtitle> </VListItemSubtitle>
<template #append> <template #append>
<VChip color="primary">{{ format.calculatePercent(v.amount, totalAmount) }}</VChip> <VChip color="primary">{{
format.calculatePercent(v.amount, totalAmount)
}}</VChip>
</template> </template>
</VListItem> </VListItem>
<VListItem v-for="v in delegations"> <VListItem v-for="v in delegations">
<template #prepend> <template #prepend>
<VAvatar <VAvatar rounded variant="tonal" size="35" color="warning">
rounded
variant="tonal"
size="35"
color="warning"
>
<VIcon icon="mdi-user-clock" size="20" /> <VIcon icon="mdi-user-clock" size="20" />
</VAvatar> </VAvatar>
</template> </template>
@ -152,17 +144,14 @@ loadAccount(props.address)
${{ 0 }} ${{ 0 }}
</VListItemSubtitle> </VListItemSubtitle>
<template #append> <template #append>
<VChip color="primary">{{ format.calculatePercent(v.balance.amount, totalAmount) }}</VChip> <VChip color="primary">{{
format.calculatePercent(v.balance.amount, totalAmount)
}}</VChip>
</template> </template>
</VListItem> </VListItem>
<VListItem v-for="v in rewards.total"> <VListItem v-for="v in rewards.total">
<template #prepend> <template #prepend>
<VAvatar <VAvatar rounded variant="tonal" size="35" color="success">
rounded
variant="tonal"
size="35"
color="success"
>
<VIcon icon="mdi-account-arrow-up" size="20" /> <VIcon icon="mdi-account-arrow-up" size="20" />
</VAvatar> </VAvatar>
</template> </template>
@ -174,30 +163,34 @@ loadAccount(props.address)
${{ 0 }} ${{ 0 }}
</VListItemSubtitle> </VListItemSubtitle>
<template #append> <template #append>
<VChip color="primary">{{ format.calculatePercent(v.amount, totalAmount) }}</VChip> <VChip color="primary">{{
format.calculatePercent(v.amount, totalAmount)
}}</VChip>
</template> </template>
</VListItem> </VListItem>
<VListItem> <VListItem>
<template #prepend> <template #prepend>
<VAvatar <VAvatar rounded variant="tonal" size="35" color="error">
rounded
variant="tonal"
size="35"
color="error"
>
<VIcon icon="mdi-account-arrow-right" size="20" /> <VIcon icon="mdi-account-arrow-right" size="20" />
</VAvatar> </VAvatar>
</template> </template>
<VListItemTitle class="text-sm font-weight-semibold"> <VListItemTitle class="text-sm font-weight-semibold">
{{ format.formatToken({amount: String(unbondingTotal), denom: stakingStore.params.bond_denom}) }} {{
format.formatToken({
amount: String(unbondingTotal),
denom: stakingStore.params.bond_denom,
})
}}
</VListItemTitle> </VListItemTitle>
<VListItemSubtitle class="text-xs"> <VListItemSubtitle class="text-xs">
${{ 0 }} ${{ 0 }}
</VListItemSubtitle> </VListItemSubtitle>
<template #append> <template #append>
<VChip color="primary">{{ format.calculatePercent(unbondingTotal, totalAmount) }}</VChip> <VChip color="primary">{{
format.calculatePercent(unbondingTotal, totalAmount)
}}</VChip>
</template> </template>
</VListItem> </VListItem>
</VList> </VList>
@ -213,16 +206,35 @@ loadAccount(props.address)
<VCardTitle>Delegations</VCardTitle> <VCardTitle>Delegations</VCardTitle>
<VTable> <VTable>
<thead> <thead>
<tr><th>Validator</th><th>Delegation</th><th>Rewards</th><th>Action</th></tr> <tr>
<th>Validator</th>
<th>Delegation</th>
<th>Rewards</th>
<th>Action</th>
</tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="v in delegations"> <tr v-for="v in delegations">
<td class="text-caption text-primary"><RouterLink :to="`/${chain}/staking/${v.delegation.validator_address}`">{{ format.validatorFromBech32(v.delegation.validator_address) }}</RouterLink></td> <td class="text-caption text-primary">
<td>{{ format.formatToken(v.balance, true, "0,0.[00]") }} </td> <RouterLink
<td>{{ format.formatTokens(rewards?.rewards?.find(x => x.validator_address ===v.delegation.validator_address)?.reward) }} </td> :to="`/${chain}/staking/${v.delegation.validator_address}`"
<td> >{{
action format.validatorFromBech32(v.delegation.validator_address)
}}</RouterLink
>
</td> </td>
<td>{{ format.formatToken(v.balance, true, '0,0.[00]') }}</td>
<td>
{{
format.formatTokens(
rewards?.rewards?.find(
(x) =>
x.validator_address === v.delegation.validator_address
)?.reward
)
}}
</td>
<td>action</td>
</tr> </tr>
</tbody> </tbody>
</VTable> </VTable>
@ -233,18 +245,52 @@ loadAccount(props.address)
<VCardTitle>Unbonding Delegations</VCardTitle> <VCardTitle>Unbonding Delegations</VCardTitle>
<VTable> <VTable>
<thead> <thead>
<tr><th>Creation Height</th><th>Initial Balance</th><th>Balance</th><th>Completion Time</th></tr> <tr>
<th>Creation Height</th>
<th>Initial Balance</th>
<th>Balance</th>
<th>Completion Time</th>
</tr>
</thead> </thead>
<tbody> <tbody>
<div v-for="v in unbonding"> <div v-for="v in unbonding">
<tr> <tr>
<td class="text-caption text-primary"><RouterLink :to="`/${chain}/staking/${v.validator_address}`">{{ format.validatorFromBech32(v.validator_address) }}</RouterLink></td> <td class="text-caption text-primary">
<RouterLink
:to="`/${chain}/staking/${v.validator_address}`"
>{{
format.validatorFromBech32(v.validator_address)
}}</RouterLink
>
</td>
</tr> </tr>
<tr v-for="entry in v.entries"> <tr v-for="entry in v.entries">
<td>{{ entry.creation_height }}</td> <td>{{ entry.creation_height }}</td>
<td>{{ format.formatToken({ amount: entry.initial_balance, denom: stakingStore.params.bond_denom }, true, "0,0.[00]") }}</td> <td>
<td>{{ format.formatToken({ amount: entry.balance, denom: stakingStore.params.bond_denom }, true, "0,0.[00]") }}</td> {{
<td>{{ format.toDay(entry.completion_time, "to") }} </td> format.formatToken(
{
amount: entry.initial_balance,
denom: stakingStore.params.bond_denom,
},
true,
'0,0.[00]'
)
}}
</td>
<td>
{{
format.formatToken(
{
amount: entry.balance,
denom: stakingStore.params.bond_denom,
},
true,
'0,0.[00]'
)
}}
</td>
<td>{{ format.toDay(entry.completion_time, 'to') }}</td>
</tr> </tr>
</div> </div>
</tbody> </tbody>
@ -256,17 +302,33 @@ loadAccount(props.address)
<VCardTitle>Transactions</VCardTitle> <VCardTitle>Transactions</VCardTitle>
<VTable> <VTable>
<thead> <thead>
<tr><th>Height</th><th>Hash</th><th>Messages</th><th>Time</th></tr> <tr>
<th>Height</th>
<th>Hash</th>
<th>Messages</th>
<th>Time</th>
</tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="v in txs"> <tr v-for="v in txs">
<td class="text-sm text-primary"><RouterLink :to="`/${chain}/block/${v.height}`">{{ v.height }}</RouterLink></td> <td class="text-sm text-primary">
<td class="text-truncate" style="max-width: 200px;">{{ v.txhash }} </td> <RouterLink :to="`/${chain}/block/${v.height}`">{{
v.height
}}</RouterLink>
</td>
<td class="text-truncate" style="max-width: 200px">
{{ v.txhash }}
</td>
<td> <td>
{{ format.messages(v.tx.body.messages) }} {{ format.messages(v.tx.body.messages) }}
<VIcon v-if="v.code === 0" icon="mdi-check" color="success"></VIcon> <VIcon
<VIcon v-else icon="mdi-multiply" color="error"></VIcon> </td> v-if="v.code === 0"
<td>{{ format.toDay(v.timestamp, "from") }}</td> icon="mdi-check"
color="success"
></VIcon>
<VIcon v-else icon="mdi-multiply" color="error"></VIcon>
</td>
<td>{{ format.toDay(v.timestamp, 'from') }}</td>
</tr> </tr>
</tbody> </tbody>
</VTable> </VTable>
@ -279,9 +341,7 @@ loadAccount(props.address)
</VCardItem> </VCardItem>
</VCard> </VCard>
</div> </div>
<div v-else> <div v-else>Account does not exists on chain</div>
Account does not exists on chain
</div>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.card-list { .card-list {

View File

@ -21,7 +21,6 @@ onBeforeRouteUpdate(async (to, from, next) => {
next(); next();
} }
}); });
</script> </script>
<template> <template>
<div> <div>
@ -29,12 +28,16 @@ onBeforeRouteUpdate(async (to, from, next) => {
<h2 class="card-title flex flex-row justify-between"> <h2 class="card-title flex flex-row justify-between">
<p class="">#{{ store.current.block?.header?.height }}</p> <p class="">#{{ store.current.block?.header?.height }}</p>
<div class="" v-if="props.height"> <div class="" v-if="props.height">
<RouterLink :to="`/${store.blockchain.chainName}/block/${height - 1}`" <RouterLink
class="btn btn-primary btn-sm p-1 text-2xl mr-2"> :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" /> <Icon icon="mdi-arrow-left" class="w-full h-full" />
</RouterLink> </RouterLink>
<RouterLink :to="`/${store.blockchain.chainName}/block/${height + 1}`" <RouterLink
class="btn btn-primary btn-sm p-1 text-2xl"> :to="`/${store.blockchain.chainName}/block/${height + 1}`"
class="btn btn-primary btn-sm p-1 text-2xl"
>
<Icon icon="mdi-arrow-right" /> <Icon icon="mdi-arrow-right" />
</RouterLink> </RouterLink>
</div> </div>
@ -45,25 +48,17 @@ onBeforeRouteUpdate(async (to, from, next) => {
</div> </div>
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow"> <div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
<h2 class="card-title flex flex-row justify-between"> <h2 class="card-title flex flex-row justify-between">Block Header</h2>
Block Header
</h2>
<DynamicComponent :value="store.current.block?.header" /> <DynamicComponent :value="store.current.block?.header" />
</div> </div>
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow"> <div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
<h2 class="card-title flex flex-row justify-between"> <h2 class="card-title flex flex-row justify-between">Transactions</h2>
Transactions
</h2>
<TxsElement :value="store.current.block?.data?.txs" /> <TxsElement :value="store.current.block?.data?.txs" />
</div> </div>
<div class="bg-base-100 px-4 pt-3 pb-4 rounded shadow"> <div class="bg-base-100 px-4 pt-3 pb-4 rounded shadow">
<h2 class="card-title flex flex-row justify-between"> <h2 class="card-title flex flex-row justify-between">Last Commit</h2>
Last Commit
</h2>
<DynamicComponent :value="store.current.block?.last_commit" /> <DynamicComponent :value="store.current.block?.last_commit" />
</div> </div>
</div> </div>

View File

@ -1,61 +1,64 @@
import { defineStore } from "pinia"; import { defineStore } from 'pinia';
import { decodeTxRaw, type DecodedTxRaw } from '@cosmjs/proto-signing' import { decodeTxRaw, type DecodedTxRaw } from '@cosmjs/proto-signing';
import { useBlockchain } from "@/stores"; import { useBlockchain } from '@/stores';
import { hashTx } from "@/libs"; import { hashTx } from '@/libs';
import type { Block } from "@/types"; import type { Block } from '@/types';
export const useBlockModule = defineStore('blockModule', { export const useBlockModule = defineStore('blockModule', {
state: () => { state: () => {
return { return {
latest: {} as Block, latest: {} as Block,
current: {} as Block, current: {} as Block,
recents: [] as Block[] recents: [] as Block[],
} };
}, },
getters: { getters: {
blockchain() { blockchain() {
return useBlockchain() return useBlockchain();
}, },
blocktime() { blocktime() {
if(this.recents.length<2) return 6000 if (this.recents.length < 2) return 6000;
return 6000 // todo later return 6000; // todo later
}, },
txsInRecents() { txsInRecents() {
const txs = [] as {hash:string, tx: DecodedTxRaw}[] const txs = [] as { hash: string; tx: DecodedTxRaw }[];
this.recents.forEach((x) => x.block?.data?.txs.forEach((tx:Uint8Array) => txs.push({ this.recents.forEach((x) =>
x.block?.data?.txs.forEach((tx: Uint8Array) =>
txs.push({
hash: hashTx(tx), hash: hashTx(tx),
tx :decodeTxRaw(tx) tx: decodeTxRaw(tx),
}))) })
return txs )
} );
return txs;
},
}, },
actions: { actions: {
initial() { initial() {
this.clearRecentBlocks() this.clearRecentBlocks();
this.autoFetch() this.autoFetch();
}, },
async clearRecentBlocks() { async clearRecentBlocks() {
this.recents = [] this.recents = [];
}, },
autoFetch() { autoFetch() {
this.fetchLatest().then(x => { this.fetchLatest().then((x) => {
const timer = this.autoFetch const timer = this.autoFetch;
this.latest = x; this.latest = x;
// if(this.recents.length >= 50) this.recents.pop() // if(this.recents.length >= 50) this.recents.pop()
// this.recents.push(x) // this.recents.push(x)
// setTimeout(timer, 6000) // setTimeout(timer, 6000)
}) });
}, },
async fetchLatest() { async fetchLatest() {
this.latest = await this.blockchain.rpc.getBaseBlockLatest() this.latest = await this.blockchain.rpc.getBaseBlockLatest();
if(this.recents.length >= 50) this.recents.shift() if (this.recents.length >= 50) this.recents.shift();
this.recents.push(this.latest) this.recents.push(this.latest);
return this.latest return this.latest;
}, },
async fetchBlock(height: string) { async fetchBlock(height: string) {
this.current = await this.blockchain.rpc.getBaseBlockAt(height) this.current = await this.blockchain.rpc.getBaseBlockAt(height);
return this.current return this.current;
}, },
} },
}) });

View File

@ -31,7 +31,7 @@ const format = useFormatter();
<table class="table w-full"> <table class="table w-full">
<thead> <thead>
<tr> <tr>
<th style="position: relative;">Height</th> <th style="position: relative">Height</th>
<th>Hash</th> <th>Hash</th>
<th>Proposer</th> <th>Proposer</th>
<th>Txs</th> <th>Txs</th>
@ -57,11 +57,14 @@ const format = useFormatter();
</table> </table>
</div> </div>
<div v-show="tab === 'transactions'" class="bg-base-100 rounded overflow-x-auto"> <div
v-show="tab === 'transactions'"
class="bg-base-100 rounded overflow-x-auto"
>
<table class="table w-full"> <table class="table w-full">
<thead> <thead>
<tr> <tr>
<th style="position: relative;">Hash</th> <th style="position: relative">Hash</th>
<th>Messages</th> <th>Messages</th>
<th>Fees</th> <th>Fees</th>
</tr> </tr>
@ -73,7 +76,7 @@ const format = useFormatter();
item.hash item.hash
}}</RouterLink> }}</RouterLink>
</td> </td>
<td>{{ format.messages(item.tx.body.messages) }}</td> <td>{{ format.messages(item.tx.body.messages as any) }}</td>
<td>{{ format.formatTokens(item.tx.authInfo.fee?.amount) }}</td> <td>{{ format.formatTokens(item.tx.authInfo.fee?.amount) }}</td>
</tr> </tr>
</tbody> </tbody>

View File

@ -1,16 +1,27 @@
import { BaseRestClient } from "@/libs/client"; import { BaseRestClient } from '@/libs/client';
import { adapter, type AbstractRegistry, type Request } from "@/libs/registry"; import { adapter, type AbstractRegistry, type Request } from '@/libs/registry';
import { defineStore } from "pinia"; import { defineStore } from 'pinia';
import type { CodeInfo, ContractInfo, PaginabledCodeInfos, PaginabledContractHistory, PaginabledContracts, PaginabledContractStates, WasmParam } from "./types"; import type {
import { toBase64 } from "@cosmjs/encoding"; CodeInfo,
import { useBlockchain } from "@/stores"; ContractInfo,
PaginabledCodeInfos,
PaginabledContractHistory,
PaginabledContracts,
PaginabledContractStates,
WasmParam,
} from './types';
import { toBase64 } from '@cosmjs/encoding';
import { useBlockchain } from '@/stores';
export interface WasmRequestRegistry extends AbstractRegistry { export interface WasmRequestRegistry extends AbstractRegistry {
cosmwasm_code: Request<PaginabledCodeInfos>; cosmwasm_code: Request<PaginabledCodeInfos>;
cosmwasm_code_id: Request<CodeInfo>; cosmwasm_code_id: Request<CodeInfo>;
cosmwasm_code_id_contracts: Request<PaginabledContracts>; cosmwasm_code_id_contracts: Request<PaginabledContracts>;
cosmwasm_param: Request<WasmParam>; cosmwasm_param: Request<WasmParam>;
cosmwasm_contract_address: Request<{address: string, contract_info: ContractInfo}>; cosmwasm_contract_address: Request<{
address: string;
contract_info: ContractInfo;
}>;
cosmwasm_contract_address_history: Request<PaginabledContractHistory>; cosmwasm_contract_address_history: Request<PaginabledContractHistory>;
cosmwasm_contract_address_raw_query_data: Request<any>; cosmwasm_contract_address_raw_query_data: Request<any>;
cosmwasm_contract_address_smart_query_data: Request<any>; cosmwasm_contract_address_smart_query_data: Request<any>;
@ -18,58 +29,85 @@ export interface WasmRequestRegistry extends AbstractRegistry {
} }
export const DEFAULT: WasmRequestRegistry = { export const DEFAULT: WasmRequestRegistry = {
cosmwasm_code: { url: "/cosmwasm/wasm/v1/code", adapter }, cosmwasm_code: { url: '/cosmwasm/wasm/v1/code', adapter },
cosmwasm_code_id: { url: "/cosmwasm/wasm/v1/code/{code_id}", 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_code_id_contracts: {
cosmwasm_param: { url: "/cosmwasm/wasm/v1/codes/params", adapter }, url: '/cosmwasm/wasm/v1/code/{code_id}/contracts',
cosmwasm_contract_address: { url: "/cosmwasm/wasm/v1/contract/{address}", adapter }, 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_param: { url: '/cosmwasm/wasm/v1/codes/params', adapter },
cosmwasm_contract_address_smart_query_data: { url: "/cosmwasm/wasm/v1/contract/{address}/smart/{query_data}", adapter }, cosmwasm_contract_address: {
cosmwasm_contract_address_state: { url: "/cosmwasm/wasm/v1/contract/{address}/state", adapter } 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,
},
};
class WasmRestClient extends BaseRestClient<WasmRequestRegistry> { class WasmRestClient extends BaseRestClient<WasmRequestRegistry> {
getWasmCodeList() { getWasmCodeList() {
return this.request(this.registry.cosmwasm_code, {}) return this.request(this.registry.cosmwasm_code, {});
} }
getWasmCodeById(code_id: string) { getWasmCodeById(code_id: string) {
return this.request(this.registry.cosmwasm_code, {code_id}) // `code_id` is a param in above url return this.request(this.registry.cosmwasm_code, { code_id }); // `code_id` is a param in above url
} }
getWasmCodeContracts(code_id: string) { getWasmCodeContracts(code_id: string) {
return this.request(this.registry.cosmwasm_code_id_contracts, {code_id}) return this.request(this.registry.cosmwasm_code_id_contracts, { code_id });
} }
getWasmParams() { getWasmParams() {
return this.request(this.registry.cosmwasm_param, {}) return this.request(this.registry.cosmwasm_param, {});
} }
getWasmContracts(address: string) { getWasmContracts(address: string) {
return this.request(this.registry.cosmwasm_contract_address, {address}) return this.request(this.registry.cosmwasm_contract_address, { address });
} }
getWasmContractHistory(address: string) { getWasmContractHistory(address: string) {
return this.request(this.registry.cosmwasm_contract_address_history, {address}) return this.request(this.registry.cosmwasm_contract_address_history, {
address,
});
} }
getWasmContractRawQuery(address: string, query: string) { getWasmContractRawQuery(address: string, query: string) {
const query_data = toBase64(new TextEncoder().encode(query)) const query_data = toBase64(new TextEncoder().encode(query));
return this.request(this.registry.cosmwasm_contract_address_raw_query_data, {address, query_data}) return this.request(
this.registry.cosmwasm_contract_address_raw_query_data,
{ address, query_data }
);
} }
getWasmContractSmartQuery(address: string, query: string) { getWasmContractSmartQuery(address: string, query: string) {
const query_data = toBase64(new TextEncoder().encode(query)) const query_data = toBase64(new TextEncoder().encode(query));
return this.request(this.registry.cosmwasm_contract_address_smart_query_data, {address, query_data}) return this.request(
this.registry.cosmwasm_contract_address_smart_query_data,
{ address, query_data }
);
} }
getWasmContractStates(address: string) { getWasmContractStates(address: string) {
return this.request(this.registry.cosmwasm_contract_address_state, {address}) return this.request(this.registry.cosmwasm_contract_address_state, {
address,
});
} }
} }
export const useWasmStore = defineStore('module-wasm', { export const useWasmStore = defineStore('module-wasm', {
state: () => { state: () => {
return {} return {};
}, },
getters: { getters: {
wasmClient() { wasmClient() {
const blockchain = useBlockchain() const blockchain = useBlockchain();
return new WasmRestClient(blockchain.endpoint.address, DEFAULT) return new WasmRestClient(blockchain.endpoint.address, DEFAULT);
} },
} },
}) });

View File

@ -1,67 +1,75 @@
<script lang="ts" setup> <script lang="ts" setup>
import { fromHex } from "@cosmjs/encoding"; import { fromHex } from '@cosmjs/encoding';
import { useWasmStore } from '../WasmStore'; import { useWasmStore } from '../WasmStore';
import { ref } from 'vue'; import { ref } from 'vue';
import type { ContractInfo, PaginabledContractStates, PaginabledContracts } from '../types'; import type {
ContractInfo,
PaginabledContractStates,
PaginabledContracts,
} from '../types';
import DynamicComponent from '@/components/dynamic/DynamicComponent.vue'; import DynamicComponent from '@/components/dynamic/DynamicComponent.vue';
import type CustomRadiosVue from '@/plugins/vuetify/@core/components/CustomRadios.vue'; import type CustomRadiosVue from '@/plugins/vuetify/@core/components/CustomRadios.vue';
import type { CustomInputContent } from '@/plugins/vuetify/@core/types'; import type { CustomInputContent } from '@/plugins/vuetify/@core/types';
import { useFormatter } from "@/stores"; import { useFormatter } from '@/stores';
const props = defineProps(['code_id', 'chain', ]) const props = defineProps(['code_id', 'chain']);
const response = ref({} as PaginabledContracts) const response = ref({} as PaginabledContracts);
const wasmStore = useWasmStore() const wasmStore = useWasmStore();
wasmStore.wasmClient.getWasmCodeContracts(props.code_id).then(x =>{ wasmStore.wasmClient.getWasmCodeContracts(props.code_id).then((x) => {
response.value = x response.value = x;
}) });
const format = useFormatter() const format = useFormatter();
const infoDialog = ref(false) const infoDialog = ref(false);
const stateDialog = ref(false) const stateDialog = ref(false);
const queryDialog = ref(false) const queryDialog = ref(false);
const info = ref({} as ContractInfo) const info = ref({} as ContractInfo);
const state = ref( {} as PaginabledContractStates) const state = ref({} as PaginabledContractStates);
const selected = ref("") const selected = ref('');
function showInfo(address: string) { function showInfo(address: string) {
wasmStore.wasmClient.getWasmContracts(address).then(x => { wasmStore.wasmClient.getWasmContracts(address).then((x) => {
info.value = x.contract_info info.value = x.contract_info;
infoDialog.value = true infoDialog.value = true;
}) });
} }
function showState(address: string) { function showState(address: string) {
wasmStore.wasmClient.getWasmContractStates(address).then(x => { wasmStore.wasmClient.getWasmContractStates(address).then((x) => {
state.value = x state.value = x;
stateDialog.value = true stateDialog.value = true;
}) });
} }
function showQuery(address: string) { function showQuery(address: string) {
queryDialog.value = true queryDialog.value = true;
selected.value = address selected.value = address;
query.value = "" query.value = '';
result.value = "" result.value = '';
} }
function queryContract() { function queryContract() {
try { try {
if (selectedRadio.value === 'raw') { if (selectedRadio.value === 'raw') {
wasmStore.wasmClient.getWasmContractRawQuery(selected.value, query.value).then(x => { wasmStore.wasmClient
result.value = JSON.stringify(x) .getWasmContractRawQuery(selected.value, query.value)
}).catch(err => { .then((x) => {
result.value = JSON.stringify(err) result.value = JSON.stringify(x);
}) })
.catch((err) => {
result.value = JSON.stringify(err);
});
} else { } else {
wasmStore.wasmClient.getWasmContractSmartQuery(selected.value, query.value).then(x => { wasmStore.wasmClient
result.value = JSON.stringify(x) .getWasmContractSmartQuery(selected.value, query.value)
}).catch(err => { .then((x) => {
result.value = JSON.stringify(err) result.value = JSON.stringify(x);
}) })
.catch((err) => {
result.value = JSON.stringify(err);
});
} }
} catch (err) { } catch (err) {
result.value = JSON.stringify(err) // not works for now result.value = JSON.stringify(err); // not works for now
} }
// TODO, show error in the result. // TODO, show error in the result.
} }
@ -77,12 +85,11 @@ const radioContent: CustomInputContent[] = [
desc: 'Return structure result if possible', desc: 'Return structure result if possible',
value: 'smart', value: 'smart',
}, },
] ];
const selectedRadio = ref('raw')
const query = ref("")
const result = ref("")
const selectedRadio = ref('raw');
const query = ref('');
const result = ref('');
</script> </script>
<template> <template>
<div> <div>
@ -90,14 +97,21 @@ const result = ref("")
<VCardTitle>Contract List of Code: {{ props.code_id }}</VCardTitle> <VCardTitle>Contract List of Code: {{ props.code_id }}</VCardTitle>
<VTable> <VTable>
<thead> <thead>
<tr><th>Contract List</th><th>Actions</th></tr> <tr>
<th>Contract List</th>
<th>Actions</th>
</tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="v in response.contracts"> <tr v-for="v in response.contracts">
<td>{{ v }}</td><td> <td>{{ v }}</td>
<td>
<VBtn size="small" @click="showInfo(v)">contract</VBtn> <VBtn size="small" @click="showInfo(v)">contract</VBtn>
<VBtn size="small" @click="showState(v)" class="ml-2">States</VBtn> <VBtn size="small" @click="showState(v)" class="ml-2"
<VBtn size="small" @click="showQuery(v)" class="ml-2">Query</VBtn></td> >States</VBtn
>
<VBtn size="small" @click="showQuery(v)" class="ml-2">Query</VBtn>
</td>
</tr> </tr>
</tbody> </tbody>
</VTable> </VTable>
@ -109,7 +123,9 @@ const result = ref("")
<DynamicComponent :value="info" /> <DynamicComponent :value="info" />
</v-card-text> </v-card-text>
<v-card-actions> <v-card-actions>
<v-btn color="primary" block @click="infoDialog = false">Close Dialog</v-btn> <v-btn color="primary" block @click="infoDialog = false"
>Close Dialog</v-btn
>
</v-card-actions> </v-card-actions>
</v-card> </v-card>
</v-dialog> </v-dialog>
@ -127,7 +143,9 @@ const result = ref("")
</VListItem> </VListItem>
</VList> </VList>
<v-card-actions> <v-card-actions>
<v-btn color="primary" block @click="stateDialog = false">Close Dialog</v-btn> <v-btn color="primary" block @click="stateDialog = false"
>Close Dialog</v-btn
>
</v-card-actions> </v-card-actions>
</v-card> </v-card>
</v-dialog> </v-dialog>
@ -145,7 +163,9 @@ const result = ref("")
<VTextarea v-model="result" label="Result" /> <VTextarea v-model="result" label="Result" />
</v-card-text> </v-card-text>
<v-card-actions> <v-card-actions>
<v-btn color="primary" @click="queryDialog = false">Close Dialog</v-btn> <v-btn color="primary" @click="queryDialog = false"
>Close Dialog</v-btn
>
<v-btn color="success" @click="queryContract()">Query Contract</v-btn> <v-btn color="success" @click="queryContract()">Query Contract</v-btn>
</v-card-actions> </v-card-actions>
</v-card> </v-card>

View File

@ -4,14 +4,14 @@ import { useWasmStore } from './WasmStore';
import { ref } from 'vue'; import { ref } from 'vue';
import type { PaginabledCodeInfos } from './types'; import type { PaginabledCodeInfos } from './types';
const props = defineProps(['chain']) const props = defineProps(['chain']);
const codes = ref({} as PaginabledCodeInfos) const codes = ref({} as PaginabledCodeInfos);
const wasmStore = useWasmStore() const wasmStore = useWasmStore();
wasmStore.wasmClient.getWasmCodeList().then(x =>{ wasmStore.wasmClient.getWasmCodeList().then((x) => {
codes.value = x codes.value = x;
}) });
</script> </script>
<template> <template>
<div> <div>
@ -19,17 +19,31 @@ wasmStore.wasmClient.getWasmCodeList().then(x =>{
<VCardTitle>Cosmos Wasm Smart Contracts</VCardTitle> <VCardTitle>Cosmos Wasm Smart Contracts</VCardTitle>
<VTable> <VTable>
<thead> <thead>
<tr><th>Code Id</th><th>Code Hash</th><th>Creator</th><th>Permissions</th></tr> <tr>
<th>Code Id</th>
<th>Code Hash</th>
<th>Creator</th>
<th>Permissions</th>
</tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="v in codes.code_infos"> <tr v-for="v in codes.code_infos">
<td>{{ v.code_id }}</td> <td>{{ v.code_id }}</td>
<td><RouterLink :to="`/${props.chain}/cosmwasm/${v.code_id}/contracts`"><div class="text-truncate" style="max-width: 200px;">{{ v.data_hash }}</div></RouterLink></td> <td>
<RouterLink
:to="`/${props.chain}/cosmwasm/${v.code_id}/contracts`"
><div class="text-truncate" style="max-width: 200px">
{{ v.data_hash }}
</div></RouterLink
>
</td>
<td>{{ v.creator }}</td> <td>{{ v.creator }}</td>
<td> <td>
{{ v.instantiate_permission?.permission }} {{ v.instantiate_permission?.permission }}
<span>{{ v.instantiate_permission?.address }} {{ v.instantiate_permission.addresses.join(", ") }}</span> <span
>{{ v.instantiate_permission?.address }}
{{ v.instantiate_permission.addresses.join(', ') }}</span
>
</td> </td>
</tr> </tr>
</tbody> </tbody>

View File

@ -1,69 +1,69 @@
import type { PaginatedResponse } from "@/types" import type { PaginatedResponse } from '@/types';
export interface CodeInfo { export interface CodeInfo {
code_id: string, code_id: string;
creator: string, creator: string;
data_hash: string, data_hash: string;
instantiate_permission: { instantiate_permission: {
permission: string, permission: string;
address: string, address: string;
addresses: string[] addresses: string[];
} };
} }
export interface ContractInfo { export interface ContractInfo {
code_id: string, code_id: string;
creator: string, creator: string;
admin: string, admin: string;
label: string, label: string;
created: { created: {
block_height: string, block_height: string;
tx_index: string tx_index: string;
}, };
ibc_port_id: string, ibc_port_id: string;
extension: { extension: {
type_url: string, type_url: string;
value: string value: string;
} };
} }
export interface WasmParam { export interface WasmParam {
params: { params: {
code_upload_access: { code_upload_access: {
permission: string, permission: string;
address: string, address: string;
addresses: string[] addresses: string[];
}, };
instantiate_default_permission: string instantiate_default_permission: string;
} };
} }
export interface HistoryEntry { export interface HistoryEntry {
operation: string, operation: string;
code_id: string, code_id: string;
updated: { updated: {
block_height: string, block_height: string;
tx_index: string tx_index: string;
}, };
msg: string msg: string;
} }
export interface Models { export interface Models {
key: string, key: string;
value: string value: string;
} }
export interface PaginabledContractHistory extends PaginatedResponse { export interface PaginabledContractHistory extends PaginatedResponse {
entries: HistoryEntry[] entries: HistoryEntry[];
} }
export interface PaginabledContractStates extends PaginatedResponse { export interface PaginabledContractStates extends PaginatedResponse {
models: Models[] models: Models[];
} }
export interface PaginabledCodeInfos extends PaginatedResponse { export interface PaginabledCodeInfos extends PaginatedResponse {
code_infos: CodeInfo[] code_infos: CodeInfo[];
} }
export interface PaginabledContracts extends PaginatedResponse { export interface PaginabledContracts extends PaginatedResponse {
contracts: string[] contracts: string[];
} }

View File

@ -6,8 +6,6 @@ import { ref , reactive} from 'vue';
import Countdown from '@/components/Countdown.vue'; import Countdown from '@/components/Countdown.vue';
import { computed } from '@vue/reactivity'; import { computed } from '@vue/reactivity';
const props = defineProps(["proposal_id", "chain"]); const props = defineProps(["proposal_id", "chain"]);
const proposal = ref({} as GovProposal) const proposal = ref({} as GovProposal)
const format = useFormatter() const format = useFormatter()
@ -147,7 +145,6 @@ const processList = computed(()=>{
{name: 'Abstain', value : abstain.value, class: 'bg-warning' } {name: 'Abstain', value : abstain.value, class: 'bg-warning' }
] ]
}) })
</script> </script>
<template> <template>

View File

@ -6,10 +6,10 @@ const tab = ref('2');
const store = useGovStore(); const store = useGovStore();
onMounted(() => { onMounted(() => {
store.fetchProposals('2').then(x => { store.fetchProposals('2').then((x) => {
if (x.proposals.length === 0) { if (x.proposals.length === 0) {
tab.value = "3" tab.value = '3';
store.fetchProposals('3') store.fetchProposals('3');
} }
}); });
}); });

View File

@ -1,61 +1,84 @@
<script lang="ts" setup> <script lang="ts" setup>
import DynamicComponent from '@/components/dynamic/DynamicComponent.vue'; import DynamicComponent from '@/components/dynamic/DynamicComponent.vue';
import { useBaseStore, useBlockchain, useFormatter } from '@/stores'; import { useBaseStore, useBlockchain, useFormatter } from '@/stores';
import type { ClientStateWithProof, Connection, ClientState, Channel } from '@/types'; import type {
ClientStateWithProof,
Connection,
ClientState,
Channel,
} from '@/types';
import { onMounted } from 'vue'; import { onMounted } from 'vue';
import { ref } from 'vue'; import { ref } from 'vue';
const props = defineProps(['chain', 'connection_id']) const props = defineProps(['chain', 'connection_id']);
const chainStore = useBlockchain() const chainStore = useBlockchain();
const baseStore = useBaseStore() const baseStore = useBaseStore();
const conn = ref({} as Connection) const conn = ref({} as Connection);
const clientState = ref({} as {client_id: string, client_state: ClientState}) const clientState = ref({} as { client_id: string; client_state: ClientState });
const channels = ref([] as Channel[]) const channels = ref([] as Channel[]);
onMounted(() => { onMounted(() => {
if (props.connection_id) { if (props.connection_id) {
chainStore.rpc.getIBCConnectionsById(props.connection_id).then(x => { chainStore.rpc.getIBCConnectionsById(props.connection_id).then((x) => {
conn.value = x.connection conn.value = x.connection;
}) });
chainStore.rpc.getIBCConnectionsClientState(props.connection_id).then(x => { chainStore.rpc
clientState.value = x.identified_client_state .getIBCConnectionsClientState(props.connection_id)
}) .then((x) => {
chainStore.rpc.getIBCConnectionsChannels(props.connection_id).then(x => { clientState.value = x.identified_client_state;
channels.value = x.channels });
}) chainStore.rpc.getIBCConnectionsChannels(props.connection_id).then((x) => {
channels.value = x.channels;
});
} }
}) });
function loadChannel(channel: string, port: string) { function loadChannel(channel: string, port: string) {
chainStore.rpc.getIBCChannelNextSequence(channel, port).then(x => { chainStore.rpc.getIBCChannelNextSequence(channel, port).then((x) => {
console.log(x) console.log(x);
}) });
} }
function color(v: string) { function color(v: string) {
if(v && v.indexOf("_OPEN") > -1) { if (v && v.indexOf('_OPEN') > -1) {
return "success" return 'success';
} }
return "warning" return 'warning';
} }
</script> </script>
<template> <template>
<div> <div>
<div class="px-4 pt-3 pb-4 bg-base-100 rounded mb-4 shadow py-24 sm:py-32"> <div class="px-4 pt-3 pb-4 bg-base-100 rounded mb-4 shadow py-24 sm:py-32">
<div class="mx-auto max-w-7xl px-6 lg:px-8"> <div class="mx-auto max-w-7xl px-6 lg:px-8">
<dl class="grid grid-cols-1 gap-x-8 gap-y-16 text-center lg:grid-cols-3"> <dl
class="grid grid-cols-1 gap-x-8 gap-y-16 text-center lg:grid-cols-3"
>
<div class="mx-auto flex max-w-xs flex-col gap-y-4"> <div class="mx-auto flex max-w-xs flex-col gap-y-4">
<dt class="text-base leading-7 text-gray-600">{{ conn.client_id }} {{props.connection_id}}</dt> <dt class="text-base leading-7 text-gray-600">
<dd class="order-first text-3xl font-semibold tracking-tight text-gray-900 sm:text-5xl">{{ baseStore.latest?.block?.header?.chain_id }}</dd> {{ conn.client_id }} {{ props.connection_id }}
</dt>
<dd
class="order-first text-3xl font-semibold tracking-tight text-gray-900 sm:text-5xl"
>
{{ baseStore.latest?.block?.header?.chain_id }}
</dd>
</div> </div>
<div class="mx-auto flex max-w-xs flex-col gap-y-4"> <div class="mx-auto flex max-w-xs flex-col gap-y-4">
<dt class="text-base leading-7 text-gray-600">{{ conn.state }}</dt> <dt class="text-base leading-7 text-gray-600">{{ conn.state }}</dt>
<dd class="order-first text-3xl font-semibold tracking-tight text-gray-900 sm:text-5xl"> &lt;&gt;<VProgressLinear class="w-100" color="success" /></dd> <dd
class="order-first text-3xl font-semibold tracking-tight text-gray-900 sm:text-5xl"
>
&lt;&gt;<VProgressLinear class="w-100" color="success" />
</dd>
</div> </div>
<div class="mx-auto flex max-w-xs flex-col gap-y-4"> <div class="mx-auto flex max-w-xs flex-col gap-y-4">
<dt class="text-base leading-7 text-gray-600">{{ conn.counterparty?.connection_id }} {{ clientState.client_id }}</dt> <dt class="text-base leading-7 text-gray-600">
<dd class="order-first text-3xl font-semibold tracking-tight text-gray-900 sm:text-5xl">{{clientState.client_state?.chain_id}}</dd> {{ conn.counterparty?.connection_id }} {{ clientState.client_id }}
</dt>
<dd
class="order-first text-3xl font-semibold tracking-tight text-gray-900 sm:text-5xl"
>
{{ clientState.client_state?.chain_id }}
</dd>
</div> </div>
</dl> </dl>
</div> </div>
@ -64,16 +87,22 @@ function color(v: string) {
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow"> <div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
<h2 class="card-title">IBC Client State</h2> <h2 class="card-title">IBC Client State</h2>
<div class="text-sm"> <div class="text-sm">
<br>update after expiry: {{ clientState.client_state?.allow_update_after_expiry }} <br />update after expiry:
<br>allow_update_after_misbehaviour: {{ clientState.client_state?.allow_update_after_misbehaviour }} {{ clientState.client_state?.allow_update_after_expiry }}
<br>trust_level: {{ clientState.client_state?.trust_level?.numerator }}/{{ clientState.client_state?.trust_level?.denominator }} <br />allow_update_after_misbehaviour:
<br>trusting_period: {{ clientState.client_state?.trusting_period }} {{ clientState.client_state?.allow_update_after_misbehaviour }}
<br>unbonding_period: {{ clientState.client_state?.unbonding_period }} <br />trust_level:
<br>frozen_height: {{ clientState.client_state?.frozen_height }} {{ clientState.client_state?.trust_level?.numerator }}/{{
<br>latest_height: {{ clientState.client_state?.latest_height }} clientState.client_state?.trust_level?.denominator
<br>type: {{ clientState.client_state?.['@type'] }} }}
<br>upgrade_path: {{ clientState.client_state?.upgrade_path }} <br />trusting_period: {{ clientState.client_state?.trusting_period }}
<br> {{ clientState.client_state?.max_clock_drift }} <br />unbonding_period:
{{ clientState.client_state?.unbonding_period }} <br />frozen_height:
{{ clientState.client_state?.frozen_height }} <br />latest_height:
{{ clientState.client_state?.latest_height }} <br />type:
{{ clientState.client_state?.['@type'] }} <br />upgrade_path:
{{ clientState.client_state?.upgrade_path }} <br />
{{ clientState.client_state?.max_clock_drift }}
</div> </div>
</div> </div>
@ -82,27 +111,36 @@ function color(v: string) {
<div class="overflow-x-auto"></div> <div class="overflow-x-auto"></div>
<table class="table w-full mt-4"> <table class="table w-full mt-4">
<thead> <thead>
<tr><th style="position: relative;">Channel Id</th> <tr>
<th style="position: relative">Channel Id</th>
<th>Port Id</th> <th>Port Id</th>
<th>Counterparty</th> <th>Counterparty</th>
<th>Hops</th> <th>Hops</th>
<th>Version</th> <th>Version</th>
<th>Ordering</th> <th>Ordering</th>
<th>State</th></tr> <th>State</th>
</tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="v in channels"> <tr v-for="v in channels">
<td><a href="#" @click="loadChannel(v.channel_id, v.port_id)">{{ v.channel_id }}</a></td> <td>
<a href="#" @click="loadChannel(v.channel_id, v.port_id)">{{
v.channel_id
}}</a>
</td>
<td>{{ v.port_id }}</td> <td>{{ v.port_id }}</td>
<td>{{ v.counterparty?.port_id }}/{{ v.counterparty?.channel_id }}</td> <td>
<td>{{ v.connection_hops.join(", ") }} </td> {{ v.counterparty?.port_id }}/{{ v.counterparty?.channel_id }}
</td>
<td>{{ v.connection_hops.join(', ') }}</td>
<td>{{ v.version }}</td> <td>{{ v.version }}</td>
<td>{{ v.ordering }}</td> <td>{{ v.ordering }}</td>
<td><VChip :color="color(v.state)">{{ v.state }}</VChip></td> <td>
<VChip :color="color(v.state)">{{ v.state }}</VChip>
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
</div>
</template> </template>

View File

@ -4,22 +4,21 @@ import type { Connection } from '@/types';
import { onMounted } from 'vue'; import { onMounted } from 'vue';
import { ref } from 'vue'; import { ref } from 'vue';
const props = defineProps(['chain']) const props = defineProps(['chain']);
const chainStore = useBlockchain() const chainStore = useBlockchain();
const list = ref([] as Connection[]) const list = ref([] as Connection[]);
onMounted(() => { onMounted(() => {
chainStore.rpc.getIBCConnections().then(x => { chainStore.rpc.getIBCConnections().then((x) => {
list.value = x.connections list.value = x.connections;
}) });
}) });
function color(v: string) { function color(v: string) {
if(v && v.indexOf("_OPEN") > -1) { if (v && v.indexOf('_OPEN') > -1) {
return "success" return 'success';
} }
return "warning" return 'warning';
} }
</script> </script>
<template> <template>
<div> <div>
@ -28,14 +27,29 @@ function color(v: string) {
<div class="overflow-x-auto mt-4"> <div class="overflow-x-auto mt-4">
<table class="table w-full"> <table class="table w-full">
<thead> <thead>
<tr><th style="position: relative;">Connection Id</th><th>Connection</th><th>Delay Period</th><th>State</th></tr> <tr>
<th style="position: relative">Connection Id</th>
<th>Connection</th>
<th>Delay Period</th>
<th>State</th>
</tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="v in list"> <tr v-for="v in list">
<td><RouterLink :to="`/${chain}/ibc/${v.id}`">{{ v.id }}</RouterLink></td> <td>
<td>{{ v.client_id }} {{ v.id }} <br> {{v.counterparty.client_id }} {{ v.counterparty.connection_id }} </td> <RouterLink :to="`/${chain}/ibc/${v.id}`">{{
v.id
}}</RouterLink>
</td>
<td>
{{ v.client_id }} {{ v.id }} <br />
{{ v.counterparty.client_id }}
{{ v.counterparty.connection_id }}
</td>
<td>{{ v.delay_period }}</td> <td>{{ v.delay_period }}</td>
<td><VChip :color="color(v.state)">{{ v.state }}</VChip></td> <td>
<VChip :color="color(v.state)">{{ v.state }}</VChip>
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@ -25,12 +25,18 @@ const format = useFormatter();
const ticker = computed(() => store.coinInfo.tickers[store.tickerIndex]); const ticker = computed(() => store.coinInfo.tickers[store.tickerIndex]);
blockchain.$subscribe((m, s) => { blockchain.$subscribe((m, s) => {
if (!Array.isArray(m.events) && ['chainName', 'endpoint'].includes(m.events.key)) { if (
!Array.isArray(m.events) &&
['chainName', 'endpoint'].includes(m.events.key)
) {
store.loadDashboard(); store.loadDashboard();
} }
}); });
function shortName(name: string, id: string) { function shortName(name: string, id: string) {
return name.toLowerCase().startsWith('ibc/') || name.toLowerCase().startsWith('0x') ? id : name; return name.toLowerCase().startsWith('ibc/') ||
name.toLowerCase().startsWith('0x')
? id
: name;
} }
</script> </script>
@ -47,27 +53,51 @@ function shortName(name: string, id: string) {
</VCardTitle> </VCardTitle>
<VCardSubtitle> <VCardSubtitle>
Rank: Rank:
<VChip color="error" size="x-small">#{{ coinInfo.market_cap_rank }}</VChip> <VChip color="error" size="x-small"
>#{{ coinInfo.market_cap_rank }}</VChip
>
</VCardSubtitle> </VCardSubtitle>
</VCardItem> </VCardItem>
<VDivider /> <VDivider />
<VCardItem> <VCardItem>
<VBtn variant="text" size="small" :href="store.homepage" prependIcon="mdi-web"> <VBtn
variant="text"
size="small"
:href="store.homepage"
prependIcon="mdi-web"
>
Website Website
</VBtn> </VBtn>
<VBtn variant="text" size="small" :href="store.twitter" prependIcon="mdi-twitter"> <VBtn
variant="text"
size="small"
:href="store.twitter"
prependIcon="mdi-twitter"
>
Twitter Twitter
</VBtn> </VBtn>
<VBtn variant="text" size="small" :href="store.telegram" prependIcon="mdi-telegram"> <VBtn
variant="text"
size="small"
:href="store.telegram"
prependIcon="mdi-telegram"
>
Telegram Telegram
</VBtn> </VBtn>
<VBtn variant="text" size="small" :href="store.github" prependIcon="mdi-github"> <VBtn
variant="text"
size="small"
:href="store.github"
prependIcon="mdi-github"
>
Github Github
</VBtn> </VBtn>
</VCardItem> </VCardItem>
<VCardItem> <VCardItem>
<!-- SECTION upgrade plan banner --> <!-- SECTION upgrade plan banner -->
<div class="plan-upgrade-banner d-flex bg-light-secondary rounded align-center pa-3"> <div
class="plan-upgrade-banner d-flex bg-light-secondary rounded align-center pa-3"
>
<h3 class="plan-details me-3" :class="store.priceColor"> <h3 class="plan-details me-3" :class="store.priceColor">
{{ store.priceChange }} {{ store.priceChange }}
<small>%</small> <small>%</small>
@ -102,7 +132,10 @@ function shortName(name: string, id: string) {
}} }}
</VListItemSubtitle> </VListItemSubtitle>
<template #append> <template #append>
<span class="ml-3" :class="`text-${store.tickerColor(item.trust_score)}`"> <span
class="ml-3"
:class="`text-${store.tickerColor(item.trust_score)}`"
>
{{ item.converted_last.usd }} {{ item.converted_last.usd }}
</span> </span>
</template> </template>
@ -119,9 +152,13 @@ function shortName(name: string, id: string) {
<span class="text-h5">{{ ticker.converted_last.usd }}</span> <span class="text-h5">{{ ticker.converted_last.usd }}</span>
</div> </div>
</div> </div>
<!-- !SECTION -->
<VSpacer /> <VSpacer />
<VBtn block :color="store.trustColor" class="mt-3" :href="ticker.trade_url"> <VBtn
block
:color="store.trustColor"
class="mt-3"
:href="ticker.trade_url"
>
Buy {{ coinInfo.symbol || '' }} Buy {{ coinInfo.symbol || '' }}
</VBtn> </VBtn>
</VCardItem> </VCardItem>
@ -134,10 +171,15 @@ function shortName(name: string, id: string) {
</VRow> </VRow>
<VDivider /> <VDivider />
<VCardText style="max-height: 250px; overflow: auto"> <VCardText style="max-height: 250px; overflow: auto">
<MdEditor :model-value="coinInfo.description?.en" previewOnly></MdEditor> <MdEditor
:model-value="coinInfo.description?.en"
previewOnly
></MdEditor>
</VCardText> </VCardText>
<VCardItem> <VCardItem>
<VChip v-for="tag in coinInfo.categories" size="x-small" class="mr-2">{{ tag }}</VChip> <VChip v-for="tag in coinInfo.categories" size="x-small" class="mr-2">{{
tag
}}</VChip>
</VCardItem> </VCardItem>
</VCard> </VCard>
@ -154,10 +196,14 @@ function shortName(name: string, id: string) {
<VCardItem> <VCardItem>
<ProposalListItem :proposals="store?.proposals" /> <ProposalListItem :proposals="store?.proposals" />
</VCardItem> </VCardItem>
<VCardText v-if="store.proposals.length === 0">No active proposals</VCardText> <VCardText v-if="store.proposals.length === 0"
>No active proposals</VCardText
>
</VCard> </VCard>
<VBtn block color="secondary" variant="outlined" class="mt-5">Connect Wallet</VBtn> <VBtn block color="secondary" variant="outlined" class="mt-5"
>Connect Wallet</VBtn
>
</div> </div>
</template> </template>

View File

@ -1,19 +1,26 @@
import { useBlockchain, useCoingecko, useBaseStore, useBankStore, useFormatter, useGovStore } from "@/stores"; import {
import { useDistributionStore } from "@/stores/useDistributionStore"; useBlockchain,
import { useMintStore } from "@/stores/useMintStore"; useCoingecko,
import { useStakingStore } from "@/stores/useStakingStore"; useBaseStore,
import type { GovProposal, PaginatedProposals, Tally } from "@/types"; useBankStore,
import numeral from "numeral"; useFormatter,
import { defineStore } from "pinia"; useGovStore,
} from '@/stores';
import { useDistributionStore } from '@/stores/useDistributionStore';
import { useMintStore } from '@/stores/useMintStore';
import { useStakingStore } from '@/stores/useStakingStore';
import type { GovProposal, PaginatedProposals, Tally } from '@/types';
import numeral from 'numeral';
import { defineStore } from 'pinia';
function colorMap(color: string) { function colorMap(color: string) {
switch (color) { switch (color) {
case 'yellow': case 'yellow':
return 'warning' return 'warning';
case 'green': case 'green':
return 'success' return 'success';
default: default:
return 'secondary' return 'secondary';
} }
} }
@ -26,7 +33,7 @@ export const useIndexModule = defineStore('module-index', {
name: '', name: '',
symbol: '', symbol: '',
description: { description: {
en: '' en: '',
}, },
categories: [] as string[], categories: [] as string[],
market_cap_rank: 0, market_cap_rank: 0,
@ -34,99 +41,101 @@ export const useIndexModule = defineStore('module-index', {
twitter_screen_name: '', twitter_screen_name: '',
homepage: [] as string[], homepage: [] as string[],
repos_url: { repos_url: {
github: [] github: [],
}, },
telegram_channel_identifier: '' telegram_channel_identifier: '',
}, },
market_data: { market_data: {
price_change_percentage_24h: 0 price_change_percentage_24h: 0,
}, },
tickers: [] as { tickers: [] as {
market: { market: {
name: string, name: string;
identifier: string, identifier: string;
}, };
coin_id: string, coin_id: string;
target_coin_id: string, target_coin_id: string;
trust_score: string, trust_score: string;
trade_url: string, trade_url: string;
converted_last: { converted_last: {
btc: number, btc: number;
eth: number, eth: number;
usd: number, usd: number;
}, };
base: string, base: string;
target: string, target: string;
}[] }[],
}, },
marketData: { marketData: {
market_caps: [], market_caps: [],
prices: [] as number[], prices: [] as number[],
total_volumes: [] as number[], total_volumes: [] as number[],
}, },
communityPool: [] as {amount: string, denom: string}[], communityPool: [] as { amount: string; denom: string }[],
proposals: {} as PaginatedProposals, proposals: {} as PaginatedProposals,
tally: {} as Record<string, Tally> tally: {} as Record<string, Tally>,
} };
}, },
getters: { getters: {
blockchain() { blockchain() {
const chain = useBlockchain() const chain = useBlockchain();
return chain.current return chain.current;
}, },
coingecko() { coingecko() {
return useCoingecko() return useCoingecko();
}, },
bankStore() { bankStore() {
return useBankStore() return useBankStore();
}, },
twitter(): string { twitter(): string {
return `https://twitter.com/${this.coinInfo.links.twitter_screen_name}` return `https://twitter.com/${this.coinInfo.links.twitter_screen_name}`;
}, },
homepage(): string { homepage(): string {
const [page1, page2, page3] = this.coinInfo.links?.homepage const [page1, page2, page3] = this.coinInfo.links?.homepage;
return page1 || page2 || page3 return page1 || page2 || page3;
}, },
github(): string { github(): string {
const [page1, page2, page3] = this.coinInfo.links?.repos_url?.github const [page1, page2, page3] = this.coinInfo.links?.repos_url?.github;
return page1 || page2 || page3 return page1 || page2 || page3;
}, },
telegram(): string { telegram(): string {
return `https://t.me/${this.coinInfo.links.telegram_channel_identifier}` return `https://t.me/${this.coinInfo.links.telegram_channel_identifier}`;
}, },
priceChange(): string { priceChange(): string {
const change = this.coinInfo.market_data?.price_change_percentage_24h || 0 const change =
return numeral(change).format('+0.[00]') this.coinInfo.market_data?.price_change_percentage_24h || 0;
return numeral(change).format('+0.[00]');
}, },
priceColor(): string { priceColor(): string {
const change = this.coinInfo.market_data?.price_change_percentage_24h || 0 const change =
this.coinInfo.market_data?.price_change_percentage_24h || 0;
switch (true) { switch (true) {
case change > 0: case change > 0:
return 'text-success' return 'text-success';
case change < 0: case change < 0:
return 'text-error' return 'text-error';
default: default:
return '' return '';
} }
}, },
trustColor(): string { trustColor(): string {
const change = this.coinInfo.tickers[this.tickerIndex]?.trust_score const change = this.coinInfo.tickers[this.tickerIndex]?.trust_score;
return colorMap(change) return colorMap(change);
}, },
pool() { pool() {
const staking = useStakingStore() const staking = useStakingStore();
return staking.pool return staking.pool;
}, },
stats() { stats() {
const base = useBaseStore() const base = useBaseStore();
const bank = useBankStore() const bank = useBankStore();
const staking = useStakingStore() const staking = useStakingStore();
const mintStore = useMintStore() const mintStore = useMintStore();
const formatter = useFormatter() const formatter = useFormatter();
return [ return [
{ {
@ -154,7 +163,10 @@ export const useIndexModule = defineStore('module-index', {
title: 'Bonded Tokens', title: 'Bonded Tokens',
color: 'warning', color: 'warning',
icon: 'mdi-lock', icon: 'mdi-lock',
stats: formatter.formatTokenAmount({amount: this.pool.bonded_tokens, denom: staking.params.bond_denom }), stats: formatter.formatTokenAmount({
amount: this.pool.bonded_tokens,
denom: staking.params.bond_denom,
}),
change: 0, change: 0,
}, },
{ {
@ -168,45 +180,55 @@ export const useIndexModule = defineStore('module-index', {
title: 'Community Pool', title: 'Community Pool',
color: 'primary', color: 'primary',
icon: 'mdi-bank', icon: 'mdi-bank',
stats: formatter.formatTokens(this.communityPool?.filter(x => x.denom === staking.params.bond_denom)), stats: formatter.formatTokens(
this.communityPool?.filter(
(x) => x.denom === staking.params.bond_denom
)
),
change: 0, change: 0,
}, },
] ];
}, },
}, },
actions: { actions: {
async loadDashboard() { async loadDashboard() {
this.$reset() this.$reset();
this.initCoingecko() this.initCoingecko();
useMintStore().fetchInflation() useMintStore().fetchInflation();
useDistributionStore().fetchCommunityPool().then(x => { useDistributionStore()
this.communityPool = x.pool.filter(t => t.denom.length < 10).map(t => ({ .fetchCommunityPool()
.then((x) => {
this.communityPool = x.pool
.filter((t) => t.denom.length < 10)
.map((t) => ({
amount: String(parseInt(t.amount)), amount: String(parseInt(t.amount)),
denom: t.denom denom: t.denom,
})) }));
}) });
const gov = useGovStore() const gov = useGovStore();
gov.fetchProposals("2").then(x => { gov.fetchProposals('2').then((x) => {
this.proposals = x this.proposals = x;
}) });
}, },
tickerColor(color: string) { tickerColor(color: string) {
return colorMap(color) return colorMap(color);
}, },
initCoingecko() { initCoingecko() {
this.tickerIndex = 0 this.tickerIndex = 0;
const [firstAsset] = this.blockchain?.assets || [] const [firstAsset] = this.blockchain?.assets || [];
if (firstAsset && firstAsset.coingecko_id) { if (firstAsset && firstAsset.coingecko_id) {
this.coingecko.getCoinInfo(firstAsset.coingecko_id).then(x => { this.coingecko.getCoinInfo(firstAsset.coingecko_id).then((x) => {
this.coinInfo = x this.coinInfo = x;
}) });
this.coingecko.getMarketChart(this.days, firstAsset.coingecko_id).then(x => { this.coingecko
this.marketData = x .getMarketChart(this.days, firstAsset.coingecko_id)
}) .then((x) => {
this.marketData = x;
});
} }
}, },
selectTicker(i: number) { selectTicker(i: number) {
this.tickerIndex = i this.tickerIndex = i;
} },
} },
}) });

View File

@ -1,15 +1,14 @@
<script lang="ts" setup> <script lang="ts" setup>
import { useParamStore } from '@/stores'; import { useParamStore } from '@/stores';
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue';
import CardParameter from '@/components/CardParameter.vue' import CardParameter from '@/components/CardParameter.vue';
import ArrayObjectElement from '@/components/dynamic/ArrayObjectElement.vue'; import ArrayObjectElement from '@/components/dynamic/ArrayObjectElement.vue';
const store = useParamStore() const store = useParamStore();
const chain = ref(store.chain) const chain = ref(store.chain);
onMounted(() => { onMounted(() => {
// fetch the data // fetch the data
store.initial() store.initial();
}) });
</script> </script>
<template> <template>
<div class="overflow-hidden"> <div class="overflow-hidden">
@ -50,7 +49,6 @@ onMounted(() => {
<div class="text-base mb-3 text-main">{{ store.nodeVersion?.title }}</div> <div class="text-base mb-3 text-main">{{ store.nodeVersion?.title }}</div>
<ArrayObjectElement :value="store.nodeVersion?.items" :thead="false" /> <ArrayObjectElement :value="store.nodeVersion?.items" :thead="false" />
</div> </div>
</div> </div>
</template> </template>

View File

@ -1,104 +1,134 @@
<script setup lang="ts"> <script setup lang="ts">
import { useBankStore, useBlockchain, useFormatter, useMintStore, useStakingStore } from '@/stores'; import {
useBankStore,
useBlockchain,
useFormatter,
useMintStore,
useStakingStore,
} from '@/stores';
import { onMounted, computed, ref } from 'vue'; import { onMounted, computed, ref } from 'vue';
import ValidatorCommissionRate from '@/components/ValidatorCommissionRate.vue' import ValidatorCommissionRate from '@/components/ValidatorCommissionRate.vue';
import { consensusPubkeyToHexAddress, operatorAddressToAccount, pubKeyToValcons, valoperToPrefix } from '@/libs'; import {
consensusPubkeyToHexAddress,
operatorAddressToAccount,
pubKeyToValcons,
valoperToPrefix,
} from '@/libs';
import type { Coin, Delegation, PaginatedTxs, Validator } from '@/types'; import type { Coin, Delegation, PaginatedTxs, Validator } from '@/types';
const props = defineProps(['validator', 'chain']) const props = defineProps(['validator', 'chain']);
const staking = useStakingStore() const staking = useStakingStore();
const blockchain = useBlockchain() const blockchain = useBlockchain();
const format = useFormatter() const format = useFormatter();
const validator: string = props.validator const validator: string = props.validator;
const v = ref({} as Validator) const v = ref({} as Validator);
const cache = JSON.parse(localStorage.getItem('avatars')||'{}') const cache = JSON.parse(localStorage.getItem('avatars') || '{}');
const avatars = ref( cache || {} ) const avatars = ref(cache || {});
const identity = ref("") const identity = ref('');
const rewards = ref([] as Coin[]|undefined) const rewards = ref([] as Coin[] | undefined);
const commission = ref([] as Coin[]|undefined) const commission = ref([] as Coin[] | undefined);
const addresses = ref({} as { const addresses = ref(
account: string {} as {
operAddress: string account: string;
hex: string operAddress: string;
valCons: string, hex: string;
}) valCons: string;
const selfBonded = ref({} as Delegation)
addresses.value.account = operatorAddressToAccount(validator)
// load self bond
staking.fetchValidatorDelegation(validator, addresses.value.account).then(x => {
if(x) {
selfBonded.value = x.delegation_response
} }
}) );
const selfBonded = ref({} as Delegation);
const txs = ref({} as PaginatedTxs) addresses.value.account = operatorAddressToAccount(validator);
// load self bond
staking
.fetchValidatorDelegation(validator, addresses.value.account)
.then((x) => {
if (x) {
selfBonded.value = x.delegation_response;
}
});
blockchain.rpc.getTxsBySender(addresses.value.account).then(x => { const txs = ref({} as PaginatedTxs);
console.log("txs", x)
txs.value = x blockchain.rpc.getTxsBySender(addresses.value.account).then((x) => {
}) console.log('txs', x);
txs.value = x;
});
const apr = computed(() => { const apr = computed(() => {
const rate = v.value.commission?.commission_rates.rate || 0 const rate = v.value.commission?.commission_rates.rate || 0;
const inflation = useMintStore().inflation const inflation = useMintStore().inflation;
if (Number(inflation)) { if (Number(inflation)) {
return format.percent((1 - Number(rate)) * Number(inflation)) return format.percent((1 - Number(rate)) * Number(inflation));
} }
return "-" return '-';
}) });
const selfRate = computed(() => { const selfRate = computed(() => {
if (selfBonded.value.balance?.amount) { if (selfBonded.value.balance?.amount) {
return format.calculatePercent(selfBonded.value.balance.amount, v.value.tokens) return format.calculatePercent(
selfBonded.value.balance.amount,
v.value.tokens
);
} }
return "-" return '-';
}) });
onMounted(() => { onMounted(() => {
if (validator) { if (validator) {
staking.fetchValidator(validator).then(res => { staking.fetchValidator(validator).then((res) => {
v.value = res.validator v.value = res.validator;
identity.value = res.validator?.description?.identity || '' identity.value = res.validator?.description?.identity || '';
if (identity.value && !avatars.value[identity.value]) { if (identity.value && !avatars.value[identity.value]) {
console.log(identity.value, avatars) console.log(identity.value, avatars);
staking.keybase(identity.value).then(d => { staking.keybase(identity.value).then((d) => {
if (Array.isArray(d.them) && d.them.length > 0) { if (Array.isArray(d.them) && d.them.length > 0) {
const uri = String(d.them[0]?.pictures?.primary?.url).replace("https://s3.amazonaws.com/keybase_processed_uploads/", "") const uri = String(d.them[0]?.pictures?.primary?.url).replace(
'https://s3.amazonaws.com/keybase_processed_uploads/',
''
);
if (uri) { if (uri) {
avatars.value[identity.value] = uri avatars.value[identity.value] = uri;
localStorage.setItem('avatars', JSON.stringify(avatars.value)) localStorage.setItem('avatars', JSON.stringify(avatars.value));
} }
} }
}) });
} }
const prefix = valoperToPrefix(v.value.operator_address) || '<Invalid>' const prefix = valoperToPrefix(v.value.operator_address) || '<Invalid>';
addresses.value.hex = consensusPubkeyToHexAddress(v.value.consensus_pubkey) addresses.value.hex = consensusPubkeyToHexAddress(
addresses.value.valCons = pubKeyToValcons(v.value.consensus_pubkey, prefix) v.value.consensus_pubkey
}) );
blockchain.rpc.getDistributionValidatorOutstandingRewards(validator).then(res => { addresses.value.valCons = pubKeyToValcons(
rewards.value = res.rewards?.rewards?.sort((a, b) => Number(b.amount) - Number(a.amount)) v.value.consensus_pubkey,
res.rewards?.rewards?.forEach(x => { prefix
if(x.denom.startsWith("ibc/")) { );
format.fetchDenomTrace(x.denom) });
blockchain.rpc
.getDistributionValidatorOutstandingRewards(validator)
.then((res) => {
rewards.value = res.rewards?.rewards?.sort(
(a, b) => Number(b.amount) - Number(a.amount)
);
res.rewards?.rewards?.forEach((x) => {
if (x.denom.startsWith('ibc/')) {
format.fetchDenomTrace(x.denom);
} }
}) });
}) });
blockchain.rpc.getDistributionValidatorCommission(validator).then(res => { blockchain.rpc.getDistributionValidatorCommission(validator).then((res) => {
commission.value = res.commission?.commission?.sort((a, b) => Number(b.amount) - Number(a.amount)) commission.value = res.commission?.commission?.sort(
res.commission?.commission?.forEach(x => { (a, b) => Number(b.amount) - Number(a.amount)
if(x.denom.startsWith("ibc/")) { );
format.fetchDenomTrace(x.denom) res.commission?.commission?.forEach((x) => {
if (x.denom.startsWith('ibc/')) {
format.fetchDenomTrace(x.denom);
} }
}) });
}) });
} }
});
})
</script> </script>
<template> <template>
<div> <div>
@ -107,67 +137,109 @@ onMounted(()=> {
<VRow> <VRow>
<VCol cols="12" md="6"> <VCol cols="12" md="6">
<div class="d-flex pl-2"> <div class="d-flex pl-2">
<VAvatar icon="mdi-help-circle-outline" :image="avatars[identity]" size="90" rounded variant="outlined" color="secondary"/> <VAvatar
icon="mdi-help-circle-outline"
:image="avatars[identity]"
size="90"
rounded
variant="outlined"
color="secondary"
/>
<div class="mx-2"> <div class="mx-2">
<h4>{{ v.description?.moniker }}</h4> <h4>{{ v.description?.moniker }}</h4>
<div class="text-sm mb-2">{{ v.description?.identity || '-'}}</div> <div class="text-sm mb-2">
{{ v.description?.identity || '-' }}
</div>
<VBtn>Delegate</VBtn> <VBtn>Delegate</VBtn>
</div> </div>
</div> </div>
<VSpacer /> <VSpacer />
<VCardText> <VCardText>
<p class="text-md"> <p class="text-md">About Us</p>
About Us
</p>
<VList class="card-list"> <VList class="card-list">
<VListItem prepend-icon="mdi-web"> <VListItem prepend-icon="mdi-web">
<span>Website: </span><span> {{ v.description?.website || '-' }}</span> <span>Website: </span
><span> {{ v.description?.website || '-' }}</span>
</VListItem> </VListItem>
<VListItem prepend-icon="mdi-email-outline"> <VListItem prepend-icon="mdi-email-outline">
<span>Contact: </span><span> {{ v.description?.security_contact }}</span> <span>Contact: </span
><span> {{ v.description?.security_contact }}</span>
</VListItem> </VListItem>
</VList> </VList>
<p class="text-md mt-3"> <p class="text-md mt-3">Validator Status</p>
Validator Status
</p>
<VList class="card-list"> <VList class="card-list">
<VListItem prepend-icon="mdi-shield-account-outline"> <VListItem prepend-icon="mdi-shield-account-outline">
<span>Status: </span><span> {{ String(v.status).replace('BOND_STATUS_', '') }} </span> <span>Status: </span
><span>
{{ String(v.status).replace('BOND_STATUS_', '') }}
</span>
</VListItem> </VListItem>
<VListItem prepend-icon="mdi-shield-alert-outline"> <VListItem prepend-icon="mdi-shield-alert-outline">
<span>Jailed: </span><span> {{ v.jailed || '-' }} </span> <span>Jailed: </span><span> {{ v.jailed || '-' }} </span>
</VListItem> </VListItem>
</VList> </VList>
</VCardText> </VCardText>
</VCol> </VCol>
<VCol cols="12" md="6"> <VCol cols="12" md="6">
<div class="d-flex flex-column py-3 justify-space-between"> <div class="d-flex flex-column py-3 justify-space-between">
<div class="d-flex"> <div class="d-flex">
<VAvatar color="secondary" rounded variant="outlined" icon="mdi-coin"></VAvatar> <VAvatar
color="secondary"
rounded
variant="outlined"
icon="mdi-coin"
></VAvatar>
<div class="ml-3 d-flex flex-column justify-center"> <div class="ml-3 d-flex flex-column justify-center">
<h4>{{ format.formatToken2({amount: v.tokens, denom: staking.params.bond_denom}) }}</h4> <h4>
{{
format.formatToken2({
amount: v.tokens,
denom: staking.params.bond_denom,
})
}}
</h4>
<span class="text-sm">Total Bonded Tokens</span> <span class="text-sm">Total Bonded Tokens</span>
</div> </div>
</div> </div>
<div class="d-flex"> <div class="d-flex">
<VAvatar color="secondary" rounded variant="outlined" icon="mdi-percent"></VAvatar> <VAvatar
color="secondary"
rounded
variant="outlined"
icon="mdi-percent"
></VAvatar>
<div class="ml-3 d-flex flex-column justify-center"> <div class="ml-3 d-flex flex-column justify-center">
<h4>{{ format.formatToken(selfBonded.balance) }} ({{ selfRate }})</h4> <h4>
{{ format.formatToken(selfBonded.balance) }} ({{
selfRate
}})
</h4>
<span class="text-sm">Self Bonded</span> <span class="text-sm">Self Bonded</span>
</div> </div>
</div> </div>
<div class="d-flex"> <div class="d-flex">
<VAvatar color="secondary" rounded variant="outlined" icon="mdi-account-tie"></VAvatar> <VAvatar
color="secondary"
rounded
variant="outlined"
icon="mdi-account-tie"
></VAvatar>
<div class="ml-3 d-flex flex-column justify-center"> <div class="ml-3 d-flex flex-column justify-center">
<h4>{{ v.min_self_delegation }} {{ staking.params.bond_denom }}</h4> <h4>
{{ v.min_self_delegation }} {{ staking.params.bond_denom }}
</h4>
<span class="text-sm">Min Self Delegation:</span> <span class="text-sm">Min Self Delegation:</span>
</div> </div>
</div> </div>
<div class="d-flex"> <div class="d-flex">
<VAvatar color="secondary" rounded variant="outlined" icon="mdi-finance"></VAvatar> <VAvatar
color="secondary"
rounded
variant="outlined"
icon="mdi-finance"
></VAvatar>
<div class="ml-3 d-flex flex-column justify-center"> <div class="ml-3 d-flex flex-column justify-center">
<h4>{{ apr }}</h4> <h4>{{ apr }}</h4>
<span class="text-sm">Annual Profit</span> <span class="text-sm">Annual Profit</span>
@ -175,7 +247,12 @@ onMounted(()=> {
</div> </div>
<div class="d-flex"> <div class="d-flex">
<VAvatar color="secondary" rounded variant="outlined" icon="mdi-stairs-up"></VAvatar> <VAvatar
color="secondary"
rounded
variant="outlined"
icon="mdi-stairs-up"
></VAvatar>
<div class="ml-3 d-flex flex-column justify-center"> <div class="ml-3 d-flex flex-column justify-center">
<h4>{{ v.unbonding_height }}</h4> <h4>{{ v.unbonding_height }}</h4>
<span class="text-sm">Unbonding Height</span> <span class="text-sm">Unbonding Height</span>
@ -183,7 +260,12 @@ onMounted(()=> {
</div> </div>
<div class="d-flex"> <div class="d-flex">
<VAvatar color="secondary" rounded variant="outlined" icon="mdi-clock"></VAvatar> <VAvatar
color="secondary"
rounded
variant="outlined"
icon="mdi-clock"
></VAvatar>
<div class="ml-3 d-flex flex-column justify-center"> <div class="ml-3 d-flex flex-column justify-center">
<h4>{{ format.toDay(v.unbonding_time, 'from') }}</h4> <h4>{{ format.toDay(v.unbonding_time, 'from') }}</h4>
<span class="text-sm">Unbonding Time</span> <span class="text-sm">Unbonding Time</span>
@ -199,21 +281,42 @@ onMounted(()=> {
<VRow class="mt-3"> <VRow class="mt-3">
<VCol md="4" sm="12" class="h-100"> <VCol md="4" sm="12" class="h-100">
<ValidatorCommissionRate :commission="v.commission"></ValidatorCommissionRate> <ValidatorCommissionRate
:commission="v.commission"
></ValidatorCommissionRate>
</VCol> </VCol>
<VCol md="4" sm="12"> <VCol md="4" sm="12">
<VCard class="h-100"> <VCard class="h-100">
<VCardTitle>Commissions & Rewards</VCardTitle> <VCardTitle>Commissions & Rewards</VCardTitle>
<VCardItem class="pt-0 pb-0"> <VCardItem class="pt-0 pb-0">
<div class="overflow-auto" style="max-height: 280px;"> <div class="overflow-auto" style="max-height: 280px">
<VCardSubtitle>Commissions <VBtn size="small" class="float-right" variant="text">Withdraw</VBtn></VCardSubtitle> <VCardSubtitle
>Commissions
<VBtn size="small" class="float-right" variant="text"
>Withdraw</VBtn
></VCardSubtitle
>
<VDivider class="mb-2"></VDivider> <VDivider class="mb-2"></VDivider>
<VChip v-for="(i, k) in commission" :key="`reward-${k}`" color="info" label variant="outlined" class="mr-1 mb-1"> <VChip
v-for="(i, k) in commission"
:key="`reward-${k}`"
color="info"
label
variant="outlined"
class="mr-1 mb-1"
>
{{ format.formatToken2(i) }} {{ format.formatToken2(i) }}
</VChip> </VChip>
<VCardSubtitle class="mt-2">Outstanding Rewards</VCardSubtitle> <VCardSubtitle class="mt-2">Outstanding Rewards</VCardSubtitle>
<VDivider class="mb-2"></VDivider> <VDivider class="mb-2"></VDivider>
<VChip v-for="(i, k) in rewards" :key="`reward-${k}`" color="success" label variant="outlined" class="mr-1 mb-1"> <VChip
v-for="(i, k) in rewards"
:key="`reward-${k}`"
color="success"
label
variant="outlined"
class="mr-1 mb-1"
>
{{ format.formatToken2(i) }} {{ format.formatToken2(i) }}
</VChip> </VChip>
</div> </div>
@ -225,19 +328,29 @@ onMounted(()=> {
<VList class="pt-0"> <VList class="pt-0">
<VListItem> <VListItem>
<VListItemTitle>Account</VListItemTitle> <VListItemTitle>Account</VListItemTitle>
<VListItemSubtitle class="text-caption text-primary"><RouterLink :to="`/${chain}/account/${addresses.account}`">{{ addresses.account }}</RouterLink></VListItemSubtitle> <VListItemSubtitle class="text-caption text-primary"
><RouterLink :to="`/${chain}/account/${addresses.account}`">{{
addresses.account
}}</RouterLink></VListItemSubtitle
>
</VListItem> </VListItem>
<VListItem> <VListItem>
<VListItemTitle>Operator Address</VListItemTitle> <VListItemTitle>Operator Address</VListItemTitle>
<VListItemSubtitle class="text-caption">{{ v.operator_address }}</VListItemSubtitle> <VListItemSubtitle class="text-caption">{{
v.operator_address
}}</VListItemSubtitle>
</VListItem> </VListItem>
<VListItem> <VListItem>
<VListItemTitle>Hex Address</VListItemTitle> <VListItemTitle>Hex Address</VListItemTitle>
<VListItemSubtitle class="text-caption">{{ addresses.hex }}</VListItemSubtitle> <VListItemSubtitle class="text-caption">{{
addresses.hex
}}</VListItemSubtitle>
</VListItem> </VListItem>
<VListItem> <VListItem>
<VListItemTitle>Signer Address</VListItemTitle> <VListItemTitle>Signer Address</VListItemTitle>
<VListItemSubtitle class="text-caption">{{ addresses.valCons }}</VListItemSubtitle> <VListItemSubtitle class="text-caption">{{
addresses.valCons
}}</VListItemSubtitle>
</VListItem> </VListItem>
</VList> </VList>
</VCard> </VCard>
@ -254,12 +367,23 @@ onMounted(()=> {
</thead> </thead>
<tbody> <tbody>
<tr v-for="(item, i) in txs.tx_responses"> <tr v-for="(item, i) in txs.tx_responses">
<td class="text-sm text-primary"><RouterLink :to="`/${props.chain}/block/${item.height}`">{{ item.height }}</RouterLink></td> <td class="text-sm text-primary">
<td class="text-truncate" style="max-width: 200px;">{{ item.txhash }}</td> <RouterLink :to="`/${props.chain}/block/${item.height}`">{{
item.height
}}</RouterLink>
</td>
<td class="text-truncate" style="max-width: 200px">
{{ item.txhash }}
</td>
<td> <td>
{{ format.messages(item.tx.body.messages) }} {{ format.messages(item.tx.body.messages) }}
<VIcon v-if="item.code === 0" icon="mdi-check" color="success"></VIcon> <VIcon
<VIcon v-else icon="mdi-multiply" color="error"></VIcon> </td> v-if="item.code === 0"
icon="mdi-check"
color="success"
></VIcon>
<VIcon v-else icon="mdi-multiply" color="error"></VIcon>
</td>
<td width="150">{{ format.toDay(item.timestamp, 'from') }}</td> <td width="150">{{ format.toDay(item.timestamp, 'from') }}</td>
</tr> </tr>
</tbody> </tbody>

View File

@ -5,75 +5,96 @@ import { fromBase64, toHex } from '@cosmjs/encoding';
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { computed } from 'vue'; import { computed } from 'vue';
const props = defineProps(['hash', 'chain']) const props = defineProps(['hash', 'chain']);
const blockchain = useBlockchain() const blockchain = useBlockchain();
const base = useBaseStore() const base = useBaseStore();
const nodeInfo = ref({} as NodeInfo) const nodeInfo = ref({} as NodeInfo);
const state = computed(() => { const state = computed(() => {
const rpcs = blockchain.current?.endpoints?.rpc?.map(x => x.address).join(',') const rpcs = blockchain.current?.endpoints?.rpc
?.map((x) => x.address)
.join(',');
return `[statesync] return `[statesync]
enable = true enable = true
rpc_servers = "${rpcs}" rpc_servers = "${rpcs}"
trust_height = ${base.latest.block?.header?.height || 'loading'} trust_height = ${base.latest.block?.header?.height || 'loading'}
trust_hash = "${base.latest.block_id? toHex(fromBase64(base.latest.block_id?.hash)) : ''}" trust_hash = "${
base.latest.block_id ? toHex(fromBase64(base.latest.block_id?.hash)) : ''
}"
trust_period = "168h" # 2/3 of unbonding time" trust_period = "168h" # 2/3 of unbonding time"
` `;
}) });
const appName = computed(() => { const appName = computed(() => {
return nodeInfo.value.application_version?.app_name || "gaiad" return nodeInfo.value.application_version?.app_name || 'gaiad';
}) });
onMounted(() => { onMounted(() => {
blockchain.rpc.getBaseNodeInfo().then(x => { blockchain.rpc.getBaseNodeInfo().then((x) => {
console.log('node info', x) console.log('node info', x);
nodeInfo.value = x nodeInfo.value = x;
}) });
}) });
</script> </script>
<template> <template>
<div> <div>
<VCard> <VCard>
<VCardTitle>What's State Sync?</VCardTitle> <VCardTitle>What's State Sync?</VCardTitle>
<VCardText> <VCardText>
The Tendermint Core 0.34 release includes support for state sync, which allows a new node to join a network by fetching a snapshot of the application state at a recent height instead of fetching and replaying all historical blocks. This can reduce the time needed to sync with the network from days to minutes. Click <a class="text-primary" href="https://blog.cosmos.network/cosmos-sdk-state-sync-guide-99e4cf43be2f">here</a> for more infomation. The Tendermint Core 0.34 release includes support for state sync, which
allows a new node to join a network by fetching a snapshot of the
application state at a recent height instead of fetching and replaying
all historical blocks. This can reduce the time needed to sync with the
network from days to minutes. Click
<a
class="text-primary"
href="https://blog.cosmos.network/cosmos-sdk-state-sync-guide-99e4cf43be2f"
>here</a
>
for more infomation.
</VCardText> </VCardText>
</VCard> </VCard>
<VCard class="my-5"> <VCard class="my-5">
<VCardTitle>Starting New Node From State Sync</VCardTitle> <VCardTitle>Starting New Node From State Sync</VCardTitle>
<VCardItem> <VCardItem>
1. Install Binary ({{ appName }} Version: {{ nodeInfo.application_version?.version || "" }}) 1. Install Binary ({{ appName }} Version:
<br> {{ nodeInfo.application_version?.version || '' }})
We need to install the binary first and make sure that the version is the one currently in use on mainnet. <br />
<br> We need to install the binary first and make sure that the version is
2. Enable State Sync<br> the one currently in use on mainnet.
We can configure Tendermint to use state sync in $DAEMON_HOME/config/config.toml. <br />
2. Enable State Sync<br />
We can configure Tendermint to use state sync in
$DAEMON_HOME/config/config.toml.
<VTextarea auto-grow :model-value="state"></VTextarea> <VTextarea auto-grow :model-value="state"></VTextarea>
3. Start the daemon: <code>{{ appName }} start</code> 3. Start the daemon: <code>{{ appName }} start</code>
<br /> <br />
If you are resetting node, run <code>{{ appName }} unsafe-reset-all</code> or <code>{{ appName }} tendermint unsafe-reset-all --home ~/.HOME</code> before you start the daemon. If you are resetting node, run
<code>{{ appName }} unsafe-reset-all</code> or
<code>{{ appName }} tendermint unsafe-reset-all --home ~/.HOME</code>
before you start the daemon.
</VCardItem> </VCardItem>
</VCard> </VCard>
<VCard> <VCard>
<VCardTitle>Enable Snapshot For State Sync</VCardTitle> <VCardTitle>Enable Snapshot For State Sync</VCardTitle>
<VCardItem> <VCardItem>
To make state sync works, we can enable snapshot in $DAEMON_HOME/config/app.toml To make state sync works, we can enable snapshot in
<VTextarea auto-grow model-value="[state-sync] $DAEMON_HOME/config/app.toml
<VTextarea
auto-grow
model-value="[state-sync]
# snapshot-interval specifies the block interval at which local state sync snapshots are # snapshot-interval specifies the block interval at which local state sync snapshots are
# taken (0 to disable). Must be a multiple of pruning-keep-every. # taken (0 to disable). Must be a multiple of pruning-keep-every.
snapshot-interval = 1000 snapshot-interval = 1000
# snapshot-keep-recent specifies the number of recent snapshots to keep and serve (0 to keep all). Each snapshot is about 500MiB # snapshot-keep-recent specifies the number of recent snapshots to keep and serve (0 to keep all). Each snapshot is about 500MiB
snapshot-keep-recent = 2"> snapshot-keep-recent = 2"
>
</VTextarea> </VTextarea>
</VCardItem> </VCardItem>
</VCard> </VCard>
</div> </div>
</template> </template>

View File

@ -6,20 +6,22 @@ import type { Tx, TxResponse } from '@/types';
import VueJsonPretty from 'vue-json-pretty'; import VueJsonPretty from 'vue-json-pretty';
import 'vue-json-pretty/lib/styles.css'; import 'vue-json-pretty/lib/styles.css';
const props = defineProps(['hash', 'chain']) const props = defineProps(['hash', 'chain']);
const blockchain = useBlockchain() const blockchain = useBlockchain();
const format = useFormatter() const format = useFormatter();
const tx = ref({} as { const tx = ref(
{} as {
tx: Tx; tx: Tx;
tx_response: TxResponse tx_response: TxResponse;
}) }
);
if (props.hash) { 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(() => { const messages = computed(() => {
return tx.value.tx?.body?.messages||[] return tx.value.tx?.body?.messages || [];
}) });
</script> </script>
<template> <template>
<div> <div>
@ -27,29 +29,69 @@ const messages = computed(() => {
<VCardItem class="pt-0"> <VCardItem class="pt-0">
<VTable> <VTable>
<tbody> <tbody>
<tr><td>Tx Hash</td><td>{{ tx.tx_response.txhash }}</td></tr> <tr>
<tr><td>Height</td><td><RouterLink :to="`/${props.chain}/block/${tx.tx_response.height}`">{{ tx.tx_response.height }}</RouterLink></td></tr> <td>Tx Hash</td>
<tr><td>Status</td><td> <td>{{ tx.tx_response.txhash }}</td>
<VChip v-if="tx.tx_response.code === 0" color="success">Success</VChip> </tr>
<tr>
<td>Height</td>
<td>
<RouterLink
:to="`/${props.chain}/block/${tx.tx_response.height}`"
>{{ tx.tx_response.height }}</RouterLink
>
</td>
</tr>
<tr>
<td>Status</td>
<td>
<VChip v-if="tx.tx_response.code === 0" color="success"
>Success</VChip
>
<span v-else><VChip color="error">Failded</VChip></span> <span v-else><VChip color="error">Failded</VChip></span>
</td></tr> </td>
<tr><td>Time</td><td>{{ tx.tx_response.timestamp }} ({{ format.toDay(tx.tx_response.timestamp, "from") }})</td></tr> </tr>
<tr><td>Gas</td><td>{{ tx.tx_response.gas_used }} / {{ tx.tx_response.gas_wanted }}</td></tr> <tr>
<tr><td>Fee</td><td>{{ format.formatTokens(tx.tx?.auth_info?.fee?.amount, true, '0,0.[00]') }}</td></tr> <td>Time</td>
<tr><td>Memo</td><td>{{ tx.tx.body.memo }}</td></tr> <td>
{{ tx.tx_response.timestamp }} ({{
format.toDay(tx.tx_response.timestamp, 'from')
}})
</td>
</tr>
<tr>
<td>Gas</td>
<td>
{{ tx.tx_response.gas_used }} / {{ tx.tx_response.gas_wanted }}
</td>
</tr>
<tr>
<td>Fee</td>
<td>
{{
format.formatTokens(
tx.tx?.auth_info?.fee?.amount,
true,
'0,0.[00]'
)
}}
</td>
</tr>
<tr>
<td>Memo</td>
<td>{{ tx.tx.body.memo }}</td>
</tr>
</tbody> </tbody>
</VTable> </VTable>
</VCardItem> </VCardItem>
</VCard> </VCard>
<VCard :title="`Messages: (${messages.length})`" class="my-5"> <VCard :title="`Messages: (${messages.length})`" class="my-5">
<VCardItem class="pt-0" style="border-top: 2px dotted gray;"> <VCardItem class="pt-0" style="border-top: 2px dotted gray">
<div v-for="(msg, i) in messages"> <div v-for="(msg, i) in messages">
<div><DynamicComponent :value="msg" /></div> <div><DynamicComponent :value="msg" /></div>
</div> </div>
<div v-if="messages.length === 0"> <div v-if="messages.length === 0">No messages</div>
No messages
</div>
</VCardItem> </VCardItem>
</VCard> </VCard>
@ -58,6 +100,5 @@ const messages = computed(() => {
<vue-json-pretty :data="tx" :deep="3" /> <vue-json-pretty :data="tx" :deep="3" />
</VCardItem> </VCardItem>
</VCard> </VCard>
</div> </div>
</template> </template>

View File

@ -1,74 +1,90 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted, computed, watchEffect } from 'vue'; import { ref, onMounted, computed, watchEffect } from 'vue';
import { fromHex, toBase64 } from '@cosmjs/encoding' import { fromHex, toBase64 } from '@cosmjs/encoding';
import { useFormatter, useStakingStore, useBaseStore, useBlockchain } from '@/stores'; import {
useFormatter,
useStakingStore,
useBaseStore,
useBlockchain,
} from '@/stores';
import UptimeBar from '@/components/UptimeBar.vue'; import UptimeBar from '@/components/UptimeBar.vue';
import type { Block, Commit } from '@/types' import type { Block, Commit } from '@/types';
import { consensusPubkeyToHexAddress, valconsToBase64 } from "@/libs"; import { consensusPubkeyToHexAddress, valconsToBase64 } from '@/libs';
import type { SigningInfo } from '@/types/slashing'; import type { SigningInfo } from '@/types/slashing';
const props = defineProps(['chain']) const props = defineProps(['chain']);
const stakingStore = useStakingStore(); const stakingStore = useStakingStore();
const baseStore = useBaseStore(); const baseStore = useBaseStore();
const chainStore = useBlockchain() const chainStore = useBlockchain();
const latest = ref({} as Block) const latest = ref({} as Block);
const commits = ref([] as Commit[]); const commits = ref([] as Commit[]);
const keyword = ref("") const keyword = ref('');
const live = ref(true); const live = ref(true);
// storage local favorite validator ids // storage local favorite validator ids
const local = ref((JSON.parse(localStorage.getItem("uptime-validators") || "{}")) as Record<string, string[]>) const local = ref(
const selected = ref(local.value[chainStore.chainName] as string[]) // favorite validators on selected blockchain JSON.parse(localStorage.getItem('uptime-validators') || '{}') as Record<
string,
string[]
>
);
const selected = ref(local.value[chainStore.chainName] as string[]); // favorite validators on selected blockchain
const signingInfo = ref({} as Record<string, SigningInfo>) const signingInfo = ref({} as Record<string, SigningInfo>);
// filter validators by keywords // filter validators by keywords
const validators = computed(() => { const validators = computed(() => {
if(keyword) return stakingStore.validators.filter(x => x.description.moniker.indexOf(keyword.value) > -1) if (keyword)
return stakingStore.validators return stakingStore.validators.filter(
}) (x) => x.description.moniker.indexOf(keyword.value) > -1
);
return stakingStore.validators;
});
onMounted(() => { onMounted(() => {
live.value = true live.value = true;
baseStore.fetchLatest().then(block => { baseStore.fetchLatest().then((block) => {
latest.value = block latest.value = block;
commits.value.unshift(block.block.last_commit) commits.value.unshift(block.block.last_commit);
const height = Number(block.block.header?.height|| 0) const height = Number(block.block.header?.height || 0);
if (height > 0) { if (height > 0) {
// constructs sequence for loading blocks // constructs sequence for loading blocks
let promise = Promise.resolve() let promise = Promise.resolve();
for (let i = height - 1; i > height - 50; i -= 1) { for (let i = height - 1; i > height - 50; i -= 1) {
if (i > height - 48) { if (i > height - 48) {
promise = promise.then(() => new Promise((resolve, reject) => { promise = promise.then(
if(live.value) { // continue only if the page is living () =>
new Promise((resolve, reject) => {
if (live.value) {
// continue only if the page is living
baseStore.fetchBlock(i).then((x) => { baseStore.fetchBlock(i).then((x) => {
commits.value.unshift(x.block.last_commit) commits.value.unshift(x.block.last_commit);
resolve() resolve();
}) });
} }
})) })
);
} }
} }
} }
}); });
chainStore.rpc.getSlashingSigningInfos().then((x) => { chainStore.rpc.getSlashingSigningInfos().then((x) => {
x.info?.forEach(i => { x.info?.forEach((i) => {
signingInfo.value[valconsToBase64(i.address)] = i signingInfo.value[valconsToBase64(i.address)] = i;
}) });
}) });
}) });
onUnmounted(() => { onUnmounted(() => {
live.value = false live.value = false;
}) });
watchEffect(() => { watchEffect(() => {
local.value[chainStore.chainName] = selected.value local.value[chainStore.chainName] = selected.value;
localStorage.setItem("uptime-validators", JSON.stringify(local.value)) localStorage.setItem('uptime-validators', JSON.stringify(local.value));
}) });
</script> </script>
<template> <template>
@ -76,13 +92,23 @@ watchEffect(() => {
<VRow> <VRow>
<VCol cols="12" md="4"> <VCol cols="12" md="4">
<VCard class="h-full self-center d-flex p-4"> <VCard class="h-full self-center d-flex p-4">
<div class="self-center">Current Height: {{latest.block?.header?.height}} </div> <div class="self-center">
Current Height: {{ latest.block?.header?.height }}
</div>
</VCard> </VCard>
</VCol> </VCol>
<VCol cols="12" md="8" class=""> <VCol cols="12" md="8" class="">
<VTextField v-model="keyword" label="Keywords to filter validators" variant="outlined"> <VTextField
v-model="keyword"
label="Keywords to filter validators"
variant="outlined"
>
<template v-slot:append> <template v-slot:append>
<VBtn><VIcon icon="mdi-star"/><span class="d-none d-md-block">Favorite</span></VBtn> <VBtn
><VIcon icon="mdi-star" /><span class="d-none d-md-block"
>Favorite</span
></VBtn
>
</template> </template>
</VTextField> </VTextField>
</VCol> </VCol>
@ -91,15 +117,44 @@ watchEffect(() => {
<VRow> <VRow>
<VCol v-for="(v, i) in validators" cols="12" md="3" xl="2" class="py-0"> <VCol v-for="(v, i) in validators" cols="12" md="3" xl="2" class="py-0">
<div class="d-flex justify-between"> <div class="d-flex justify-between">
<VCheckbox v-model="selected" color="warning" :value="v.operator_address"> <VCheckbox
v-model="selected"
color="warning"
:value="v.operator_address"
>
<template v-slot:label> <template v-slot:label>
<span class="text-truncate">{{i + 1}}. {{v.description.moniker}}</span> <span class="text-truncate"
>{{ i + 1 }}. {{ v.description.moniker }}</span
>
</template> </template>
</VCheckbox> </VCheckbox>
<VChip v-if="Number(signingInfo[consensusPubkeyToHexAddress(v.consensus_pubkey)]?.missed_blocks_counter || 0) > 0" size="small" class="mt-1" label color="error">{{ signingInfo[consensusPubkeyToHexAddress(v.consensus_pubkey)]?.missed_blocks_counter }}</VChip> <VChip
<VChip v-else size="small" class="mt-1" label color="success">{{ signingInfo[consensusPubkeyToHexAddress(v.consensus_pubkey)]?.missed_blocks_counter }}</VChip> v-if="
Number(
signingInfo[consensusPubkeyToHexAddress(v.consensus_pubkey)]
?.missed_blocks_counter || 0
) > 0
"
size="small"
class="mt-1"
label
color="error"
>{{
signingInfo[consensusPubkeyToHexAddress(v.consensus_pubkey)]
?.missed_blocks_counter
}}</VChip
>
<VChip v-else size="small" class="mt-1" label color="success">{{
signingInfo[consensusPubkeyToHexAddress(v.consensus_pubkey)]
?.missed_blocks_counter
}}</VChip>
</div> </div>
<UptimeBar :blocks="commits" :validator="toBase64(fromHex(consensusPubkeyToHexAddress(v.consensus_pubkey)))" /> <UptimeBar
:blocks="commits"
:validator="
toBase64(fromHex(consensusPubkeyToHexAddress(v.consensus_pubkey)))
"
/>
</VCol> </VCol>
</VRow> </VRow>
</div> </div>

View File

@ -2,13 +2,13 @@
import { CosmosRestClient } from '@/libs/client'; import { CosmosRestClient } from '@/libs/client';
async function tt() { async function tt() {
const address = "echelon1uattqtrtv8944qkmh44ll97qjacj6tgrekqzm9" const address = 'echelon1uattqtrtv8944qkmh44ll97qjacj6tgrekqzm9';
const validator = "echelonvaloper1uattqtrtv8944qkmh44ll97qjacj6tgr2cupk4" const validator = 'echelonvaloper1uattqtrtv8944qkmh44ll97qjacj6tgr2cupk4';
const client = new CosmosRestClient("https://api.ech.network") const client = new CosmosRestClient('https://api.ech.network');
let response = await client.getBaseBlockLatest(); let response = await client.getBaseBlockLatest();
console.log('response:', response) console.log('response:', response);
} }
tt() tt();
</script> </script>
<template> <template>
<div>Hello wallet</div> <div>Hello wallet</div>

View File

@ -1,35 +1,50 @@
<script setup lang="ts"> <script setup lang="ts">
import misc404 from '@images/pages/404.png' import misc404 from '@images/pages/404.png';
import miscObj from '@images/pages/misc-404-object.png' import miscObj from '@images/pages/misc-404-object.png';
import miscMaskDark from '@images/pages/misc-mask-dark.png' import miscMaskDark from '@images/pages/misc-mask-dark.png';
import miscMaskLight from '@images/pages/misc-mask-light.png' import miscMaskLight from '@images/pages/misc-mask-light.png';
import { useGenerateImageVariant } from '@/plugins/vuetify/@core/composable/useGenerateImageVariant' import { useGenerateImageVariant } from '@/plugins/vuetify/@core/composable/useGenerateImageVariant';
const miscThemeMask = useGenerateImageVariant(miscMaskLight, miscMaskDark) const miscThemeMask = useGenerateImageVariant(miscMaskLight, miscMaskDark);
</script> </script>
<template> <template>
<div class="misc-wrapper"> <div class="misc-wrapper">
<ErrorHeader error-code="404" error-title="Page Not Found " <ErrorHeader
error-description="We couldn't find the page you are looking for." /> error-code="404"
error-title="Page Not Found ⚠️"
error-description="We couldn't find the page you are looking for."
/>
<!-- 👉 Image --> <!-- 👉 Image -->
<div class="misc-avatar w-100 text-center"> <div class="misc-avatar w-100 text-center">
<VImg :src="misc404" alt="Coming Soon" :height="$vuetify.display.xs ? 400 : 500" class="my-sm-4" /> <VImg
<VBtn to="/" class="mt-10"> :src="misc404"
Back to Home alt="Coming Soon"
</VBtn> :height="$vuetify.display.xs ? 400 : 500"
<VImg :src="miscThemeMask" class="d-none d-md-block footer-coming-soon" cover /> class="my-sm-4"
/>
<VBtn to="/" class="mt-10"> Back to Home </VBtn>
<VImg
:src="miscThemeMask"
class="d-none d-md-block footer-coming-soon"
cover
/>
<VImg :src="miscObj" class="d-none d-md-block footer-coming-soon-obj" :max-width="174" height="158" /> <VImg
:src="miscObj"
class="d-none d-md-block footer-coming-soon-obj"
:max-width="174"
height="158"
/>
</div> </div>
</div> </div>
</template> </template>
<style lang="scss"> <style lang="scss">
@use "@core/scss/template/pages/misc.scss"; @use '@core/scss/template/pages/misc.scss';
</style> </style>
<route lang="yaml"> <route lang="yaml">

View File

@ -4,9 +4,8 @@
<VCardText>This is your second page.</VCardText> <VCardText>This is your second page.</VCardText>
<VCardText> <VCardText>
Chocolate sesame snaps pie carrot cake pastry pie lollipop muffin. Chocolate sesame snaps pie carrot cake pastry pie lollipop muffin.
Carrot cake dragée chupa chups jujubes. Macaroon liquorice cookie Carrot cake dragée chupa chups jujubes. Macaroon liquorice cookie wafer
wafer tart marzipan bonbon. Gingerbread jelly-o dragée tart marzipan bonbon. Gingerbread jelly-o dragée chocolate.
chocolate.
</VCardText> </VCardText>
</VCard> </VCard>
</div> </div>

View File

@ -1,15 +1,15 @@
import { createI18n } from 'vue-i18n' import { createI18n } from 'vue-i18n';
const messages = Object.fromEntries( const messages = Object.fromEntries(
Object.entries( Object.entries(
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
import.meta.glob<{ default: any }>('./locales/*.json', { eager: true })) import.meta.glob<{ default: any }>('./locales/*.json', { eager: true })
.map(([key, value]) => [key.slice(10, -5), value.default]), ).map(([key, value]) => [key.slice(10, -5), value.default])
) );
export default createI18n({ export default createI18n({
legacy: false, legacy: false,
locale: localStorage.getItem('lang') || 'en', locale: localStorage.getItem('lang') || 'en',
fallbackLocale: 'en', fallbackLocale: 'en',
messages, messages,
}) });

View File

@ -1,11 +1,11 @@
import type { CosmosRestClient } from '@/libs/client' import type { CosmosRestClient } from '@/libs/client';
import 'pinia' import 'pinia';
import type { Ref } from 'vue' import type { Ref } from 'vue';
declare module 'pinia' { declare module 'pinia' {
export interface PiniaCustomProperties { export interface PiniaCustomProperties {
// by using a setter we can allow both strings and refs // by using a setter we can allow both strings and refs
set rpc(value: CosmosRestClient | Ref<CosmosRestClient>) set rpc(value: CosmosRestClient | Ref<CosmosRestClient>);
get rpc(): CosmosRestClient get rpc(): CosmosRestClient;
} }
} }

View File

@ -1,12 +1,12 @@
export * from './useBankStore' export * from './useBankStore';
export * from './useBlockchain' export * from './useBlockchain';
export * from './useCoinGecko' export * from './useCoinGecko';
export * from './useDashboard' export * from './useDashboard';
export * from './useBaseStore' export * from './useBaseStore';
export * from './useFormatter' export * from './useFormatter';
export * from './useGovStore' export * from './useGovStore';
export * from './useMintStore' export * from './useMintStore';
export * from './useStakingStore' export * from './useStakingStore';
export * from './useDistributionStore' export * from './useDistributionStore';
export * from './useParamsStore' export * from './useParamsStore';
export * from './useWalletStore' export * from './useWalletStore';

View File

@ -1,13 +1,9 @@
import { defineStore } from "pinia"; import { defineStore } from 'pinia';
export const useStoreName = defineStore('bankstore', { export const useStoreName = defineStore('bankstore', {
state: () => { state: () => {
return { return {};
}
}, },
getters: { getters: {},
actions: {},
}, });
actions: {
}
})

View File

@ -1,8 +1,8 @@
import { defineStore } from "pinia"; import { defineStore } from 'pinia';
import { useBlockchain } from "./useBlockchain"; import { useBlockchain } from './useBlockchain';
import { useStakingStore } from "./useStakingStore"; import { useStakingStore } from './useStakingStore';
import type { Coin, DenomTrace } from "@/types"; import type { Coin, DenomTrace } from '@/types';
export const useBankStore = defineStore('bankstore', { export const useBankStore = defineStore('bankstore', {
state: () => { state: () => {
@ -10,39 +10,41 @@ export const useBankStore = defineStore('bankstore', {
supply: {} as Coin, supply: {} as Coin,
balances: {} as Record<string, Coin[]>, balances: {} as Record<string, Coin[]>,
totalSupply: { supply: [] as Coin[] }, totalSupply: { supply: [] as Coin[] },
} };
}, },
getters: { getters: {
blockchain() { blockchain() {
return useBlockchain() return useBlockchain();
}, },
staking() { staking() {
return useStakingStore() return useStakingStore();
} },
}, },
actions: { actions: {
initial() { initial() {
this.$reset() this.$reset();
this.supply = {} as Coin this.supply = {} as Coin;
const denom = this.staking.params.bond_denom || this.blockchain.current?.assets[0].base const denom =
this.staking.params.bond_denom ||
this.blockchain.current?.assets[0].base;
if (denom) { if (denom) {
this.blockchain.rpc.getBankSupplyByDenom(denom).then(res => { this.blockchain.rpc.getBankSupplyByDenom(denom).then((res) => {
if(res.amount) this.supply = res.amount if (res.amount) this.supply = res.amount;
}) });
} }
}, },
async fetchSupply(denom: string) { async fetchSupply(denom: string) {
return this.blockchain.rpc.getBankSupplyByDenom( denom ) return this.blockchain.rpc.getBankSupplyByDenom(denom);
}, },
async fetchDenomTrace(denom: string) { async fetchDenomTrace(denom: string) {
const hash = denom.replace("ibc/", "") const hash = denom.replace('ibc/', '');
let trace = this.ibcDenoms[hash] let trace = this.ibcDenoms[hash];
if (!trace) { if (!trace) {
trace = (await this.blockchain.rpc.getIBCAppTransferDenom( hash )).denom_trace trace = (await this.blockchain.rpc.getIBCAppTransferDenom(hash))
this.ibcDenoms[hash] = trace .denom_trace;
this.ibcDenoms[hash] = trace;
} }
return trace return trace;
} },
} },
}) });

View File

@ -1,65 +1,74 @@
import { defineStore } from "pinia"; import { defineStore } from 'pinia';
import { useBlockchain } from "@/stores"; import { useBlockchain } from '@/stores';
import dayjs from "dayjs"; import dayjs from 'dayjs';
import type { Block } from "@/types"; import type { Block } from '@/types';
export const useBaseStore = defineStore('baseStore', { export const useBaseStore = defineStore('baseStore', {
state: () => { state: () => {
return { return {
earlest: {} as Block, earlest: {} as Block,
latest: {} as Block, latest: {} as Block,
recents: [] as Block[] recents: [] as Block[],
} };
}, },
getters: { getters: {
blocktime(): number { blocktime(): number {
if (this.earlest && this.latest) { if (this.earlest && this.latest) {
if(this.latest.block?.header?.height !== this.earlest.block?.header?.height) { if (
const diff = dayjs(this.latest.block?.header?.time).diff(this.earlest.block?.header?.time) this.latest.block?.header?.height !==
return diff this.earlest.block?.header?.height
) {
const diff = dayjs(this.latest.block?.header?.time).diff(
this.earlest.block?.header?.time
);
return diff;
} }
} }
return 6000 return 6000;
}, },
blockchain() { blockchain() {
return useBlockchain() return useBlockchain();
} },
}, },
actions: { actions: {
async initial() { async initial() {
this.fetchLatest() this.fetchLatest();
}, },
async clearRecentBlocks() { async clearRecentBlocks() {
this.recents = [] this.recents = [];
}, },
async fetchLatest() { async fetchLatest() {
this.latest = await this.blockchain.rpc.getBaseBlockLatest() this.latest = await this.blockchain.rpc.getBaseBlockLatest();
if(!this.earlest || this.earlest.block?.header?.chain_id != this.latest.block?.header?.chain_id) { if (
!this.earlest ||
this.earlest.block?.header?.chain_id !=
this.latest.block?.header?.chain_id
) {
//reset earlest and recents //reset earlest and recents
this.earlest = this.latest this.earlest = this.latest;
this.recents = [] this.recents = [];
} }
if (this.recents.length >= 50) { if (this.recents.length >= 50) {
this.recents.pop() this.recents.pop();
} }
this.recents.push(this.latest) this.recents.push(this.latest);
return this.latest return this.latest;
}, },
async fetchValidatorByHeight(height?: number, offset = 0) { async fetchValidatorByHeight(height?: number, offset = 0) {
return this.blockchain.rpc.getBaseValidatorsetAt(String(height)) return this.blockchain.rpc.getBaseValidatorsetAt(String(height));
}, },
async fetchLatestValidators(offset = 0) { async fetchLatestValidators(offset = 0) {
return this.blockchain.rpc.getBaseValidatorsetLatest() return this.blockchain.rpc.getBaseValidatorsetLatest();
}, },
async fetchBlock(height?: number) { async fetchBlock(height?: number) {
return this.blockchain.rpc.getBaseBlockAt(String(height)) return this.blockchain.rpc.getBaseBlockAt(String(height));
}, },
async fetchAbciInfo() { async fetchAbciInfo() {
return this.blockchain.rpc.getBaseNodeInfo() return this.blockchain.rpc.getBaseNodeInfo();
} },
// async fetchNodeInfo() { // async fetchNodeInfo() {
// return this.blockchain.rpc.no() // return this.blockchain.rpc.no()
// } // }
} },
}) });

View File

@ -1,51 +1,54 @@
import { defineStore } from "pinia"; import { defineStore } from 'pinia';
import { get } from '../libs/http' import { get } from '../libs/http';
import type { LoadingStatus } from "./useDashboard"; import type { LoadingStatus } from './useDashboard';
export interface PriceMeta { export interface PriceMeta {
usd?: string, usd?: string;
usd_24h_change?: string, usd_24h_change?: string;
cny?: string, cny?: string;
cny_24h_change? : string, cny_24h_change?: string;
eur?: string, eur?: string;
eur_24h_change?: string, eur_24h_change?: string;
} }
const LocalStoreKey = 'currency' const LocalStoreKey = 'currency';
export const useCoingecko = defineStore('coingecko', { export const useCoingecko = defineStore('coingecko', {
state: () => { state: () => {
const currency = localStorage.getItem(LocalStoreKey) const currency = localStorage.getItem(LocalStoreKey);
return { return {
currency, // secondary currency currency, // secondary currency
loadStatus: {} as Record<string, LoadingStatus | undefined>, loadStatus: {} as Record<string, LoadingStatus | undefined>,
prices: {} as Record<string, PriceMeta>, prices: {} as Record<string, PriceMeta>,
marketChart: {} marketChart: {},
} };
},
getters: {
}, },
getters: {},
actions: { actions: {
getMarketChart(days = 30, coinId = 'cosmos') { getMarketChart(days = 30, coinId = 'cosmos') {
return get(`https://api.coingecko.com/api/v3/coins/${coinId}/market_chart?vs_currency=usd&days=${days}`) return get(
`https://api.coingecko.com/api/v3/coins/${coinId}/market_chart?vs_currency=usd&days=${days}`
);
}, },
fetchCoinPrice(ids: string[]) { fetchCoinPrice(ids: string[]) {
const url = `https://api.coingecko.com/api/v3/simple/price?include_24hr_change=true&vs_currencies=${['usd', this.currency].join(',')}&ids=${ids.join(',')}` const url = `https://api.coingecko.com/api/v3/simple/price?include_24hr_change=true&vs_currencies=${[
get(url).then(data => { 'usd',
this.prices = {...this.prices, ...data} this.currency,
}) ].join(',')}&ids=${ids.join(',')}`;
get(url).then((data) => {
this.prices = { ...this.prices, ...data };
});
}, },
getCoinInfo(coinId: string) { getCoinInfo(coinId: string) {
return get(`https://api.coingecko.com/api/v3/coins/${coinId}`) return get(`https://api.coingecko.com/api/v3/coins/${coinId}`);
}, },
setSecondaryCurrency(currency: string) { setSecondaryCurrency(currency: string) {
if (currency !== 'usd') { if (currency !== 'usd') {
localStorage.setItem(LocalStoreKey, currency) localStorage.setItem(LocalStoreKey, currency);
this.currency = currency this.currency = currency;
} }
} },
} },
}) });

View File

@ -1,19 +1,18 @@
import { defineStore } from "pinia"; import { defineStore } from 'pinia';
import { useBlockchain } from "./useBlockchain"; import { useBlockchain } from './useBlockchain';
export const useDistributionStore = defineStore('distributionStore', { export const useDistributionStore = defineStore('distributionStore', {
state: () => { state: () => {
return { return {};
}
}, },
getters: { getters: {
blockchain() { blockchain() {
return useBlockchain() return useBlockchain();
} },
}, },
actions: { actions: {
async fetchCommunityPool() { async fetchCommunityPool() {
return this.blockchain.rpc.getDistributionCommunityPool() return this.blockchain.rpc.getDistributionCommunityPool();
} },
} },
}) });

View File

@ -1,23 +1,23 @@
import { defineStore } from "pinia"; import { defineStore } from 'pinia';
import { useBlockchain } from "./useBlockchain"; import { useBlockchain } from './useBlockchain';
import numeral from "numeral"; import numeral from 'numeral';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import duration from 'dayjs/plugin/duration' import duration from 'dayjs/plugin/duration';
import relativeTime from 'dayjs/plugin/relativeTime' import relativeTime from 'dayjs/plugin/relativeTime';
import updateLocale from 'dayjs/plugin/updateLocale' import updateLocale from 'dayjs/plugin/updateLocale';
import utc from 'dayjs/plugin/utc' import utc from 'dayjs/plugin/utc';
import localeData from 'dayjs/plugin/localeData' import localeData from 'dayjs/plugin/localeData';
import { useStakingStore } from "./useStakingStore"; import { useStakingStore } from './useStakingStore';
import { fromBase64, fromBech32, fromHex, toHex } from "@cosmjs/encoding"; import { fromBase64, fromBech32, fromHex, toHex } from '@cosmjs/encoding';
import { consensusPubkeyToHexAddress } from "@/libs"; import { consensusPubkeyToHexAddress } from '@/libs';
import { useBankStore } from "./useBankStore"; import { useBankStore } from './useBankStore';
import type { DenomTrace } from "@/types"; import type { DenomTrace } from '@/types';
dayjs.extend(localeData) dayjs.extend(localeData);
dayjs.extend(duration) dayjs.extend(duration);
dayjs.extend(relativeTime) dayjs.extend(relativeTime);
dayjs.extend(updateLocale) dayjs.extend(updateLocale);
dayjs.extend(utc) dayjs.extend(utc);
dayjs.updateLocale('en', { dayjs.updateLocale('en', {
relativeTime: { relativeTime: {
future: 'in %s', future: 'in %s',
@ -34,168 +34,190 @@ dayjs.updateLocale('en', {
y: 'a year', y: 'a year',
yy: '%d years', yy: '%d years',
}, },
}) });
export const useFormatter = defineStore('formatter', { export const useFormatter = defineStore('formatter', {
state: () => { state: () => {
return { return {
ibcDenoms: {} as Record<string, DenomTrace> ibcDenoms: {} as Record<string, DenomTrace>,
} };
}, },
getters: { getters: {
blockchain() { blockchain() {
return useBlockchain() return useBlockchain();
}, },
staking() { staking() {
return useStakingStore() return useStakingStore();
}, },
useBank() { useBank() {
return useBankStore() return useBankStore();
} },
}, },
actions: { actions: {
async fetchDenomTrace(denom: string) { async fetchDenomTrace(denom: string) {
const hash = denom.replace("ibc/", "") const hash = denom.replace('ibc/', '');
let trace = this.ibcDenoms[hash] let trace = this.ibcDenoms[hash];
if (!trace) { if (!trace) {
trace = (await this.blockchain.rpc.getIBCAppTransferDenom( hash )).denom_trace trace = (await this.blockchain.rpc.getIBCAppTransferDenom(hash))
this.ibcDenoms[hash] = trace .denom_trace;
this.ibcDenoms[hash] = trace;
} }
return trace return trace;
}, },
formatTokenAmount(token: {denom: string, amount: string;}) { formatTokenAmount(token: { denom: string; amount: string }) {
return this.formatToken(token, false) return this.formatToken(token, false);
}, },
formatToken2(token: { denom: string, amount: string;}, withDenom = true) { formatToken2(token: { denom: string; amount: string }, withDenom = true) {
return this.formatToken(token, true, '0,0.[00]') return this.formatToken(token, true, '0,0.[00]');
}, },
formatToken(token: { denom: string, amount: string;}, withDenom = true, fmt='0.0a') : string { formatToken(
token: { denom: string; amount: string },
withDenom = true,
fmt = '0.0a'
): string {
if (token && token.amount) { if (token && token.amount) {
let amount = Number(token.amount) let amount = Number(token.amount);
let denom = token.denom let denom = token.denom;
if( denom && denom.startsWith("ibc/")) { if (denom && denom.startsWith('ibc/')) {
let ibcDenom = this.ibcDenoms[denom.replace("ibc/", "")] let ibcDenom = this.ibcDenoms[denom.replace('ibc/', '')];
if (ibcDenom) { if (ibcDenom) {
denom = ibcDenom.base_denom denom = ibcDenom.base_denom;
} }
} }
const conf = this.blockchain.current?.assets?.find(x => x.base === token.denom || x.base.denom === token.denom) const conf = this.blockchain.current?.assets?.find(
(x) => x.base === token.denom || x.base.denom === token.denom
);
if (conf) { if (conf) {
let unit = {exponent: 6, denom: ''} let unit = { exponent: 6, denom: '' };
// find the max exponent for display // find the max exponent for display
conf.denom_units.forEach(x => { conf.denom_units.forEach((x) => {
if (x.exponent >= unit.exponent) { if (x.exponent >= unit.exponent) {
unit = x unit = x;
} }
}) });
if (unit && unit.exponent > 0) { if (unit && unit.exponent > 0) {
amount = amount / Math.pow(10, unit.exponent || 6) amount = amount / Math.pow(10, unit.exponent || 6);
denom = unit.denom.toUpperCase() denom = unit.denom.toUpperCase();
} }
} }
return `${numeral(amount).format(fmt)} ${withDenom ? denom.substring(0, 10): ''}` return `${numeral(amount).format(fmt)} ${
withDenom ? denom.substring(0, 10) : ''
}`;
} }
return '-' return '-';
}, },
formatTokens(tokens?: { denom: string, amount: string;}[], withDenom = true, fmt='0.0a') : string { formatTokens(
if(!tokens) return '' tokens?: { denom: string; amount: string }[],
return tokens.map(x => this.formatToken(x, withDenom, fmt)).join(', ') withDenom = true,
fmt = '0.0a'
): string {
if (!tokens) return '';
return tokens.map((x) => this.formatToken(x, withDenom, fmt)).join(', ');
}, },
calculateBondedRatio(pool: {bonded_tokens: string, not_bonded_tokens: string}|undefined) { calculateBondedRatio(
pool: { bonded_tokens: string; not_bonded_tokens: string } | undefined
) {
if (pool && pool.bonded_tokens) { if (pool && pool.bonded_tokens) {
const b = Number(pool.bonded_tokens) const b = Number(pool.bonded_tokens);
const nb = Number(pool.not_bonded_tokens) const nb = Number(pool.not_bonded_tokens);
const p = b/(b+nb) const p = b / (b + nb);
return numeral(p).format('0.[00]%') return numeral(p).format('0.[00]%');
} }
return '-' return '-';
}, },
validator(address: string) { validator(address: string) {
if(!address) return address if (!address) return address;
const txt = toHex(fromBase64(address)).toUpperCase() const txt = toHex(fromBase64(address)).toUpperCase();
const validator = this.staking.validators.find(x => consensusPubkeyToHexAddress(x.consensus_pubkey) === txt) const validator = this.staking.validators.find(
return validator?.description?.moniker (x) => consensusPubkeyToHexAddress(x.consensus_pubkey) === txt
);
return validator?.description?.moniker;
}, },
validatorFromBech32(address: string) { validatorFromBech32(address: string) {
if(!address) return address if (!address) return address;
const validator = this.staking.validators.find(x => x.operator_address === address) const validator = this.staking.validators.find(
return validator?.description?.moniker (x) => x.operator_address === address
);
return validator?.description?.moniker;
}, },
calculatePercent(input?: string | number, total?: string | number) { calculatePercent(input?: string | number, total?: string | number) {
if(!input || !total) return '0' if (!input || !total) return '0';
const percent = Number(input)/Number(total) const percent = Number(input) / Number(total);
return numeral(percent>0.0001?percent: 0).format("0.[00]%") return numeral(percent > 0.0001 ? percent : 0).format('0.[00]%');
}, },
formatDecimalToPercent(decimal: string) { formatDecimalToPercent(decimal: string) {
return numeral(decimal).format('0.[00]%') return numeral(decimal).format('0.[00]%');
}, },
formatCommissionRate(rate?: string) { formatCommissionRate(rate?: string) {
if(!rate) return '-' if (!rate) return '-';
return this.percent(rate) return this.percent(rate);
}, },
percent(decimal?: string | number) { percent(decimal?: string | number) {
return decimal ? numeral(decimal).format('0.[00]%') : '-' return decimal ? numeral(decimal).format('0.[00]%') : '-';
}, },
numberAndSign(input: number, fmt="+0,0") { numberAndSign(input: number, fmt = '+0,0') {
return numeral(input).format(fmt) return numeral(input).format(fmt);
}, },
toDay(time?: string, format = 'long') { toDay(time?: string, format = 'long') {
if(!time) return '' if (!time) return '';
if (format === 'long') { if (format === 'long') {
return dayjs(time).format('YYYY-MM-DD HH:mm') return dayjs(time).format('YYYY-MM-DD HH:mm');
} }
if (format === 'date') { if (format === 'date') {
return dayjs(time).format('YYYY-MM-DD') return dayjs(time).format('YYYY-MM-DD');
} }
if (format === 'time') { if (format === 'time') {
return dayjs(time).format('HH:mm:ss') return dayjs(time).format('HH:mm:ss');
} }
if (format === 'from') { if (format === 'from') {
return dayjs(time).fromNow() return dayjs(time).fromNow();
} }
if (format === 'to') { if (format === 'to') {
return dayjs(time).toNow() return dayjs(time).toNow();
} }
return dayjs(time).format('YYYY-MM-DD HH:mm:ss') return dayjs(time).format('YYYY-MM-DD HH:mm:ss');
}, },
messages(msgs: {"@type": string}[]) { messages(msgs: { '@type': string }[]) {
if (msgs) { if (msgs) {
const sum: Record<string, number> = msgs.map(msg => { const sum: Record<string, number> = msgs
return msg["@type"].substring(msg["@type"].lastIndexOf('.') + 1).replace('Msg', '') .map((msg) => {
}).reduce((s, c) => { return msg['@type']
const sh: Record<string, number> = s .substring(msg['@type'].lastIndexOf('.') + 1)
if (sh[c]) { .replace('Msg', '');
sh[c] += 1
} else {
sh[c] = 1
}
return sh
}, {})
const output: string[] = []
Object.keys(sum).forEach(k => {
output.push(sum[k] > 1 ? `${k}×${sum[k]}` : k)
}) })
return output.join(', ') .reduce((s, c) => {
const sh: Record<string, number> = s;
if (sh[c]) {
sh[c] += 1;
} else {
sh[c] = 1;
}
return sh;
}, {});
const output: string[] = [];
Object.keys(sum).forEach((k) => {
output.push(sum[k] > 1 ? `${k}×${sum[k]}` : k);
});
return output.join(', ');
} }
}, },
multiLine(v: string) { multiLine(v: string) {
return v? v.replaceAll("\\n","\n"): "" return v ? v.replaceAll('\\n', '\n') : '';
}, },
hexToString(hex: string) { hexToString(hex: string) {
if (hex) { if (hex) {
return new TextDecoder().decode(fromHex(hex)) return new TextDecoder().decode(fromHex(hex));
} }
return "" return '';
}, },
base64ToString(hex: string) { base64ToString(hex: string) {
if (hex) { if (hex) {
return new TextDecoder().decode(fromBase64(hex)) return new TextDecoder().decode(fromBase64(hex));
} }
return "" return '';
} },
} },
}) });

View File

@ -1,8 +1,8 @@
import { defineStore } from "pinia"; import { defineStore } from 'pinia';
import { useBlockchain } from "./useBlockchain"; import { useBlockchain } from './useBlockchain';
import type { PageRequest, PaginatedProposals } from "@/types"; import type { PageRequest, PaginatedProposals } from '@/types';
import { LoadingStatus } from "./useDashboard"; import { LoadingStatus } from './useDashboard';
import {reactive} from 'vue' import { reactive } from 'vue';
export const useGovStore = defineStore('govStore', { export const useGovStore = defineStore('govStore', {
state: () => { state: () => {
@ -13,33 +13,35 @@ export const useGovStore = defineStore('govStore', {
tally: {}, tally: {},
}, },
proposals: {} as Record<string, PaginatedProposals>, proposals: {} as Record<string, PaginatedProposals>,
loading: {} as Record<string, LoadingStatus> loading: {} as Record<string, LoadingStatus>,
} };
}, },
getters: { getters: {
blockchain() { blockchain() {
return useBlockchain() return useBlockchain();
} },
}, },
actions: { actions: {
initial() { initial() {
this.fetchParams() this.fetchParams();
}, },
async fetchProposals(status: string, pagination?: PageRequest) { async fetchProposals(status: string, pagination?: PageRequest) {
if (!this.loading[status]) { if (!this.loading[status]) {
this.loading[status] = LoadingStatus.Loading this.loading[status] = LoadingStatus.Loading;
const proposals = reactive(await this.blockchain.rpc.getGovProposals(status)) const proposals = reactive(
await this.blockchain.rpc.getGovProposals(status)
);
if (status === '2') { if (status === '2') {
proposals.proposals.forEach(async (x1) => { proposals.proposals.forEach(async (x1) => {
await this.fetchTally(x1.proposal_id).then(res => { await this.fetchTally(x1.proposal_id).then((res) => {
x1.final_tally_result = res?.tally x1.final_tally_result = res?.tally;
}) });
}) });
} }
this.loading[status] = LoadingStatus.Loaded this.loading[status] = LoadingStatus.Loaded;
this.proposals[status] = proposals this.proposals[status] = proposals;
} }
return this.proposals[status] return this.proposals[status];
}, },
async fetchParams() { async fetchParams() {
// this.blockchain.rpc.getGovParamsDeposit().then(x => { // this.blockchain.rpc.getGovParamsDeposit().then(x => {
@ -47,17 +49,16 @@ export const useGovStore = defineStore('govStore', {
// }) // })
}, },
async fetchTally(proposalId: string) { async fetchTally(proposalId: string) {
return await this.blockchain.rpc.getGovProposalTally(proposalId) return await this.blockchain.rpc.getGovProposalTally(proposalId);
}, },
async fetchProposal(proposalId: string) { async fetchProposal(proposalId: string) {
return this.blockchain.rpc.getGovProposal(proposalId) return this.blockchain.rpc.getGovProposal(proposalId);
}, },
async fetchProposalDeposits(proposalId: string) { async fetchProposalDeposits(proposalId: string) {
return this.blockchain.rpc.getGovProposalDeposits(proposalId) return this.blockchain.rpc.getGovProposalDeposits(proposalId);
}, },
async fetchProposalVotes(proposalId: string, next_key?: string) { async fetchProposalVotes(proposalId: string, next_key?: string) {
return this.blockchain.rpc.getGovProposalVotes(proposalId, next_key) return this.blockchain.rpc.getGovProposalVotes(proposalId, next_key);
}
}, },
},
}) });

View File

@ -1,27 +1,30 @@
import { defineStore } from "pinia"; import { defineStore } from 'pinia';
import { useBlockchain } from "./useBlockchain"; import { useBlockchain } from './useBlockchain';
export const useMintStore = defineStore('mintStore', { export const useMintStore = defineStore('mintStore', {
state: () => { state: () => {
return { return {
inflation: "0", inflation: '0',
} };
}, },
getters: { getters: {
blockchain() { blockchain() {
return useBlockchain() return useBlockchain();
} },
}, },
actions: { actions: {
initial() { initial() {
this.fetchInflation() this.fetchInflation();
}, },
async fetchInflation() { async fetchInflation() {
this.blockchain.rpc.getMintInflation().then(x => { this.blockchain.rpc
this.inflation = x.inflation .getMintInflation()
}).catch(() => { .then((x) => {
this.inflation = "0" this.inflation = x.inflation;
})
}
}
}) })
.catch(() => {
this.inflation = '0';
});
},
},
});

View File

@ -1,27 +1,47 @@
import { defineStore } from "pinia"; import { defineStore } from 'pinia';
import { useBlockchain } from "./useBlockchain"; import { useBlockchain } from './useBlockchain';
import { percent,formatNumber,formatTokenAmount } from '@/libs/utils' import { percent, formatNumber, formatTokenAmount } from '@/libs/utils';
export interface stakingItem { export interface stakingItem {
unbonding_time: string unbonding_time: string;
max_validators: number max_validators: number;
max_entries:number max_entries: number;
historical_entries:number historical_entries: number;
bond_denom: string bond_denom: string;
min_commission_rate: string min_commission_rate: string;
min_self_delegation:string min_self_delegation: string;
} }
export const useParamStore = defineStore("paramstore", { export const useParamStore = defineStore('paramstore', {
state: () => ({ state: () => ({
latestTime: '', latestTime: '',
chain: { chain: {
title: '', title: '',
class: 'border-primary', class: 'border-primary',
items: [ items: [
{ subtitle: 'height', icon: 'BoxIcon', color: 'light-success', value: '-' }, {
{ subtitle: 'bonded_and_supply', icon: 'DollarSignIcon', color: 'light-danger', value: '-' }, subtitle: 'height',
{ subtitle: 'bonded_ratio', icon: 'PercentIcon', color: 'light-warning', value: '-' }, icon: 'BoxIcon',
{ subtitle: 'inflation', icon: 'TrendingUpIcon', color: 'light-primary', value: '-' }, color: 'light-success',
value: '-',
},
{
subtitle: 'bonded_and_supply',
icon: 'DollarSignIcon',
color: 'light-danger',
value: '-',
},
{
subtitle: 'bonded_ratio',
icon: 'PercentIcon',
color: 'light-warning',
value: '-',
},
{
subtitle: 'inflation',
icon: 'TrendingUpIcon',
color: 'light-primary',
value: '-',
},
], ],
}, },
mint: { mint: {
@ -55,110 +75,147 @@ export const useParamStore = defineStore("paramstore", {
}), }),
getters: { getters: {
blockchain() { blockchain() {
return useBlockchain() return useBlockchain();
}, },
}, },
actions: { actions: {
initial() { initial() {
this.handleBaseBlockLatest() this.handleBaseBlockLatest();
// this.handleMintParam() // this.handleMintParam()
this.handleStakingParams() this.handleStakingParams();
this.handleSlashingParams() this.handleSlashingParams();
this.handleDistributionParams() this.handleDistributionParams();
this.handleGovernanceParams() this.handleGovernanceParams();
this.handleAbciInfo() this.handleAbciInfo();
}, },
async handleBaseBlockLatest() { async handleBaseBlockLatest() {
try { try {
const res = await this.getBaseTendermintBlockLatest() const res = await this.getBaseTendermintBlockLatest();
const height = this.chain.items.findIndex(x => x.subtitle === 'height') const height = this.chain.items.findIndex(
this.chain.title = `Chain ID: ${res.block.header.chain_id}` (x) => x.subtitle === 'height'
this.chain.items[height].value = res.block.header.height );
this.chain.title = `Chain ID: ${res.block.header.chain_id}`;
this.chain.items[height].value = res.block.header.height;
// if (timeIn(res.block.header.time, 3, 'm')) { // if (timeIn(res.block.header.time, 3, 'm')) {
// this.syncing = true // this.syncing = true
// } else { // } else {
// this.syncing = false // this.syncing = false
// } // }
// this.latestTime = toDay(res.block.header.time, 'long') // this.latestTime = toDay(res.block.header.time, 'long')
this.latestTime = res.block.header.time this.latestTime = res.block.header.time;
} catch (error) { } catch (error) {
console.warn(error) console.warn(error);
} }
}, },
async handleStakingParams() { async handleStakingParams() {
const res = await this.getStakingParams() const res = await this.getStakingParams();
const bond_denom = res?.params.bond_denom const bond_denom = res?.params.bond_denom;
this.staking.items = Object.entries(res.params).map(([key, value]) => ({ subtitle:key, this.staking.items = Object.entries(res.params)
value: value })).filter((item: any) => { .map(([key, value]) => ({ subtitle: key, value: value }))
if (!['min_commission_rate','min_self_delegation'].includes(item.subtitle)) return item .filter((item: any) => {
}) if (
Promise.all([this.getStakingPool(), this.getBankTotal(bond_denom)]) !['min_commission_rate', 'min_self_delegation'].includes(
.then(resArr => { item.subtitle
const pool = resArr[0]?.pool )
const amount =resArr[1]?.amount?.amount )
const assets = this.blockchain.current?.assets return item;
const bondedAndSupply = this.chain.items.findIndex(x => x.subtitle === 'bonded_and_supply') });
this.chain.items[bondedAndSupply].value = `${formatNumber(formatTokenAmount(assets,pool.bonded_tokens, 2, bond_denom, false), true, 0)}/${formatNumber(formatTokenAmount(assets,amount, 2, bond_denom, false), true, 0)}` Promise.all([this.getStakingPool(), this.getBankTotal(bond_denom)]).then(
const bondedRatio = this.chain.items.findIndex(x => x.subtitle === 'bonded_ratio') (resArr) => {
this.chain.items[bondedRatio].value = `${percent(Number(pool.bonded_tokens) /Number(amount)) }%` const pool = resArr[0]?.pool;
}) const amount = resArr[1]?.amount?.amount;
const assets = this.blockchain.current?.assets;
const bondedAndSupply = this.chain.items.findIndex(
(x) => x.subtitle === 'bonded_and_supply'
);
this.chain.items[bondedAndSupply].value = `${formatNumber(
formatTokenAmount(assets, pool.bonded_tokens, 2, bond_denom, false),
true,
0
)}/${formatNumber(
formatTokenAmount(assets, amount, 2, bond_denom, false),
true,
0
)}`;
const bondedRatio = this.chain.items.findIndex(
(x) => x.subtitle === 'bonded_ratio'
);
this.chain.items[bondedRatio].value = `${percent(
Number(pool.bonded_tokens) / Number(amount)
)}%`;
}
);
}, },
async handleMintParam() { async handleMintParam() {
const excludes = this.blockchain.current?.excludes const excludes = this.blockchain.current?.excludes;
if (excludes && excludes.indexOf('mint') > -1) { if (excludes && excludes.indexOf('mint') > -1) {
return return;
} }
// this.getMintingInflation().then(res => { // this.getMintingInflation().then(res => {
// const chainIndex = this.chain.items.findIndex(x => x.subtitle === 'inflation') // const chainIndex = this.chain.items.findIndex(x => x.subtitle === 'inflation')
// this.chain.items[chainIndex].value = `${percent(res)}%` // this.chain.items[chainIndex].value = `${percent(res)}%`
// }) // })
const res = await this.getMintParam() const res = await this.getMintParam();
console.log(res, 'mint') console.log(res, 'mint');
}, },
async handleSlashingParams() { async handleSlashingParams() {
const res = await this.getSlashingParams() const res = await this.getSlashingParams();
this.slashing.items = Object.entries(res.params).map(([key, value]) => ({ subtitle:key, this.slashing.items = Object.entries(res.params).map(([key, value]) => ({
value: value })) subtitle: key,
value: value,
}));
}, },
async handleDistributionParams() { async handleDistributionParams() {
const res = await this.getDistributionParams() const res = await this.getDistributionParams();
this.distribution.items = Object.entries(res.params).map(([key, value]) => ({ subtitle: key, this.distribution.items = Object.entries(res.params).map(
value: value })) ([key, value]) => ({ subtitle: key, value: value })
);
}, },
async handleGovernanceParams() { async handleGovernanceParams() {
const excludes = this.blockchain.current?.excludes const excludes = this.blockchain.current?.excludes;
if (excludes && excludes.indexOf('governance') > -1) { if (excludes && excludes.indexOf('governance') > -1) {
return return;
} }
Promise.all([this.getGovParamsVoting(),this.getGovParamsDeposit(),this.getGovParamsTally()]).then((resArr) => { Promise.all([
const govParams = {...resArr[0]?.voting_params,...resArr[1]?.deposit_params,...resArr[2]?.tally_params} this.getGovParamsVoting(),
this.gov.items = Object.entries(govParams).map(([key, value]) => ({ subtitle:key, this.getGovParamsDeposit(),
value: value })) this.getGovParamsTally(),
}) ]).then((resArr) => {
const govParams = {
...resArr[0]?.voting_params,
...resArr[1]?.deposit_params,
...resArr[2]?.tally_params,
};
this.gov.items = Object.entries(govParams).map(([key, value]) => ({
subtitle: key,
value: value,
}));
});
}, },
async handleAbciInfo() { async handleAbciInfo() {
const res = await this.fetchAbciInfo() const res = await this.fetchAbciInfo();
this.appVersion.items = Object.entries(res.application_version).map(([key, value]) => ({ subtitle:key, this.appVersion.items = Object.entries(res.application_version).map(
value: value })) ([key, value]) => ({ subtitle: key, value: value })
this.nodeVersion.items = Object.entries(res.default_node_info).map(([key, value]) => ({ subtitle:key, );
value: value })) this.nodeVersion.items = Object.entries(res.default_node_info).map(
console.log('handleAbciInfo', this.nodeVersion.items) ([key, value]) => ({ subtitle: key, value: value })
);
console.log('handleAbciInfo', this.nodeVersion.items);
}, },
async getBaseTendermintBlockLatest() { async getBaseTendermintBlockLatest() {
return await this.blockchain.rpc.getBaseBlockLatest() return await this.blockchain.rpc.getBaseBlockLatest();
}, },
async getMintParam() { async getMintParam() {
return await this.blockchain.rpc.getMintParam() return await this.blockchain.rpc.getMintParam();
}, },
async getStakingParams() { async getStakingParams() {
return await this.blockchain.rpc.getStakingParams() return await this.blockchain.rpc.getStakingParams();
}, },
async getStakingPool() { async getStakingPool() {
return await this.blockchain.rpc.getStakingPool() return await this.blockchain.rpc.getStakingPool();
}, },
async getBankTotal(denom: string) { async getBankTotal(denom: string) {
return await this.blockchain.rpc.getBankSupplyByDenom(denom) return await this.blockchain.rpc.getBankSupplyByDenom(denom);
// if (compareVersions(this.config.sdk_version, '0.46.2') > 0) { // if (compareVersions(this.config.sdk_version, '0.46.2') > 0) {
// return this.get(`/cosmos/bank/v1beta1/supply/by_denom?denom=${denom}`).then(data => commonProcess(data).amount) // return this.get(`/cosmos/bank/v1beta1/supply/by_denom?denom=${denom}`).then(data => commonProcess(data).amount)
// } // }
@ -168,28 +225,22 @@ export const useParamStore = defineStore("paramstore", {
// return this.get(`/cosmos/bank/v1beta1/supply/${denom}`).then(data => commonProcess(data).amount) // return this.get(`/cosmos/bank/v1beta1/supply/${denom}`).then(data => commonProcess(data).amount)
}, },
async getSlashingParams() { async getSlashingParams() {
return await this.blockchain.rpc.getSlashingParams() return await this.blockchain.rpc.getSlashingParams();
}, },
async getDistributionParams() { async getDistributionParams() {
return await this.blockchain.rpc.getDistributionParams() return await this.blockchain.rpc.getDistributionParams();
}, },
async getGovParamsVoting() { async getGovParamsVoting() {
return await this.blockchain.rpc.getGovParamsVoting() return await this.blockchain.rpc.getGovParamsVoting();
}, },
async getGovParamsDeposit() { async getGovParamsDeposit() {
return await this.blockchain.rpc.getGovParamsDeposit() return await this.blockchain.rpc.getGovParamsDeposit();
}, },
async getGovParamsTally() { async getGovParamsTally() {
return await this.blockchain.rpc.getGovParamsTally() return await this.blockchain.rpc.getGovParamsTally();
}, },
async fetchAbciInfo() { async fetchAbciInfo() {
return this.blockchain.rpc.getBaseNodeInfo() return this.blockchain.rpc.getBaseNodeInfo();
} },
},
});
}
})

View File

@ -1,77 +1,89 @@
import { defineStore } from "pinia"; import { defineStore } from 'pinia';
import { useBlockchain } from "./useBlockchain"; import { useBlockchain } from './useBlockchain';
import { get } from "@/libs/http"; import { get } from '@/libs/http';
import type { StakingParam, StakingPool, Validator } from "@/types"; import type { StakingParam, StakingPool, Validator } from '@/types';
export const useStakingStore = defineStore('stakingStore', { export const useStakingStore = defineStore('stakingStore', {
state: () => { state: () => {
return { return {
validators: [] as Validator[], validators: [] as Validator[],
params: {} as { params: {} as {
"unbonding_time": string, unbonding_time: string;
"max_validators": number, max_validators: number;
"max_entries": number, max_entries: number;
"historical_entries": number, historical_entries: number;
"bond_denom": string, bond_denom: string;
"min_commission_rate": string, min_commission_rate: string;
"min_self_delegation": string min_self_delegation: string;
}, },
pool: {} as { pool: {} as {
bonded_tokens: string, bonded_tokens: string;
not_bonded_tokens: string, not_bonded_tokens: string;
}, },
} };
}, },
getters: { getters: {
totalPower(): number { totalPower(): number {
const sum = (s:number, e: Validator) => { return s + parseInt(e.delegator_shares) } const sum = (s: number, e: Validator) => {
return this.validators ? this.validators.reduce(sum, 0): 0 return s + parseInt(e.delegator_shares);
};
return this.validators ? this.validators.reduce(sum, 0) : 0;
}, },
blockchain() { blockchain() {
return useBlockchain() return useBlockchain();
} },
}, },
actions: { actions: {
async init() { async init() {
this.$reset() this.$reset();
this.fetchPool() this.fetchPool();
this.fetchAcitveValdiators() this.fetchAcitveValdiators();
return await this.fetchParams() return await this.fetchParams();
}, },
async keybase(identity: string) { async keybase(identity: string) {
return get(`https://keybase.io/_/api/1.0/user/lookup.json?key_suffix=${identity}&fields=pictures`) return get(
`https://keybase.io/_/api/1.0/user/lookup.json?key_suffix=${identity}&fields=pictures`
);
}, },
async fetchParams() { async fetchParams() {
const response = await this.blockchain.rpc.getStakingParams() const response = await this.blockchain.rpc.getStakingParams();
if(response.params) this.params = response.params if (response.params) this.params = response.params;
return this.params return this.params;
}, },
async fetchPool() { async fetchPool() {
const response = await this.blockchain.rpc.getStakingPool() const response = await this.blockchain.rpc.getStakingPool();
response.pool.bonded_tokens response.pool.bonded_tokens;
this.pool = response.pool this.pool = response.pool;
}, },
async fetchAcitveValdiators() { async fetchAcitveValdiators() {
return this.fetchValidators('BOND_STATUS_BONDED') return this.fetchValidators('BOND_STATUS_BONDED');
}, },
async fetchInacitveValdiators() { async fetchInacitveValdiators() {
return this.fetchValidators('BOND_STATUS_UNBONDED') return this.fetchValidators('BOND_STATUS_UNBONDED');
}, },
async fetchValidator(validatorAddr: string) { async fetchValidator(validatorAddr: string) {
return this.blockchain.rpc.getStakingValidator(validatorAddr) return this.blockchain.rpc.getStakingValidator(validatorAddr);
}, },
async fetchValidatorDelegation(validatorAddr: string, delegatorAddr: string) { async fetchValidatorDelegation(
return await this.blockchain.rpc.getStakingValidatorsDelegationsDelegator(validatorAddr, delegatorAddr) validatorAddr: string,
delegatorAddr: string
) {
return await this.blockchain.rpc.getStakingValidatorsDelegationsDelegator(
validatorAddr,
delegatorAddr
);
}, },
async fetchValidators(status: string) { async fetchValidators(status: string) {
return this.blockchain.rpc.getStakingValidators(status).then(res => { return this.blockchain.rpc.getStakingValidators(status).then((res) => {
const vals = res.validators.sort((a, b) => (Number(b.delegator_shares) - Number(a.delegator_shares))) const vals = res.validators.sort(
(a, b) => Number(b.delegator_shares) - Number(a.delegator_shares)
);
if (status === 'BOND_STATUS_BONDED') { if (status === 'BOND_STATUS_BONDED') {
this.validators = vals this.validators = vals;
} }
return vals return vals;
}) });
} },
} },
}) });

View File

@ -1,13 +1,9 @@
import { defineStore } from "pinia"; import { defineStore } from 'pinia';
export const useWalletStore = defineStore('walletStore', { export const useWalletStore = defineStore('walletStore', {
state: () => { state: () => {
return { return {};
}
}, },
getters: { getters: {},
actions: {},
}, });
actions: {
}
})

View File

@ -1,16 +1,16 @@
import { fileURLToPath, URL } from "node:url"; import { fileURLToPath, URL } from 'node:url';
import { defineConfig } from "vite"; import { defineConfig } from 'vite';
import vue from "@vitejs/plugin-vue"; import vue from '@vitejs/plugin-vue';
import vueJsx from "@vitejs/plugin-vue-jsx"; import vueJsx from '@vitejs/plugin-vue-jsx';
import vuetify from "vite-plugin-vuetify"; import vuetify from 'vite-plugin-vuetify';
import Layouts from "vite-plugin-vue-layouts"; import Layouts from 'vite-plugin-vue-layouts';
import DefineOptions from "unplugin-vue-define-options/vite"; import DefineOptions from 'unplugin-vue-define-options/vite';
import Components from "unplugin-vue-components/vite"; import Components from 'unplugin-vue-components/vite';
import AutoImport from "unplugin-auto-import/vite"; import AutoImport from 'unplugin-auto-import/vite';
import Pages from "vite-plugin-pages"; import Pages from 'vite-plugin-pages';
import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite' import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite';
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
@ -19,61 +19,70 @@ export default defineConfig({
vueJsx(), vueJsx(),
vuetify({ vuetify({
styles: { styles: {
configFile: "src/plugins/vuetify/styles/variables/_vuetify.scss", configFile: 'src/plugins/vuetify/styles/variables/_vuetify.scss',
}, },
}), }),
Pages({ Pages({
dirs: ["./src/modules", "./src/pages", ], dirs: ['./src/modules', './src/pages'],
exclude: ['**/*.ts'], // only load .vue as modules exclude: ['**/*.ts'], // only load .vue as modules
}), }),
Layouts({ Layouts({
layoutsDirs: "./src/layouts/", layoutsDirs: './src/layouts/',
}), }),
Components({ Components({
dirs: ["src/plugins/vuetify/@core/components"], dirs: ['src/plugins/vuetify/@core/components'],
dts: true, dts: true,
}), }),
AutoImport({ AutoImport({
imports: ["vue", "vue-router", "@vueuse/core", "@vueuse/math", "vue-i18n", "pinia"], imports: [
'vue',
'vue-router',
'@vueuse/core',
'@vueuse/math',
'vue-i18n',
'pinia',
],
vueTemplate: true, vueTemplate: true,
}), }),
VueI18nPlugin({ VueI18nPlugin({
runtimeOnly: true, runtimeOnly: true,
compositionOnly: true, compositionOnly: true,
include: [ include: [
fileURLToPath(new URL('./src/plugins/i18n/locales/**', import.meta.url)), fileURLToPath(
new URL('./src/plugins/i18n/locales/**', import.meta.url)
),
], ],
}), }),
DefineOptions(), DefineOptions(),
], ],
resolve: { resolve: {
alias: { alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)), '@': fileURLToPath(new URL('./src', import.meta.url)),
"@themeConfig": fileURLToPath( '@themeConfig': fileURLToPath(
new URL("./themeConfig.ts", import.meta.url) new URL('./themeConfig.ts', import.meta.url)
), ),
"@configured-variables": fileURLToPath( '@configured-variables': fileURLToPath(
new URL( new URL(
"./src/plugins/vuetify/styles/variables/_template.scss", './src/plugins/vuetify/styles/variables/_template.scss',
import.meta.url import.meta.url
) )
), ),
"@core": fileURLToPath( '@core': fileURLToPath(
new URL("./src/plugins/vuetify/@core", import.meta.url) new URL('./src/plugins/vuetify/@core', import.meta.url)
), ),
"@layouts": fileURLToPath( '@layouts': fileURLToPath(
new URL("./src/plugins/vuetify/@layouts", import.meta.url) new URL('./src/plugins/vuetify/@layouts', import.meta.url)
), ),
"@images": fileURLToPath( '@images': fileURLToPath(
new URL("./src/plugins/vuetify/images/", import.meta.url) new URL('./src/plugins/vuetify/images/', import.meta.url)
), ),
"@styles": fileURLToPath( '@styles': fileURLToPath(
new URL("./src/plugins/vuetify/styles/", import.meta.url) new URL('./src/plugins/vuetify/styles/', import.meta.url)
), ),
}, },
}, },
optimizeDeps: { optimizeDeps: {
exclude: ["vuetify"], exclude: ['vuetify'],
entries: ["./src/**/*.vue"], entries: ['./src/**/*.vue'],
}, },
}); });